sdl4r 0.9.9 → 0.9.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. data/CHANGELOG +70 -1
  2. data/LICENSE +499 -497
  3. data/Rakefile +38 -28
  4. data/TODO +194 -45
  5. data/doc/classes/SDL4R/AbbreviationTimezoneProxy.html +151 -0
  6. data/doc/classes/SDL4R/ConstantTimezone.html +129 -0
  7. data/doc/classes/SDL4R/Element.html +148 -0
  8. data/doc/classes/SDL4R/Reader.html +683 -0
  9. data/doc/classes/SDL4R/RelativeTimezone.html +187 -0
  10. data/doc/classes/SDL4R/SdlBinary.html +188 -0
  11. data/doc/classes/SDL4R/SdlParseError.html +110 -0
  12. data/doc/classes/SDL4R/SdlTimeSpan.html +651 -0
  13. data/doc/classes/SDL4R/Serializer.html +557 -0
  14. data/doc/classes/SDL4R/Serializer/Ref.html +138 -0
  15. data/doc/classes/SDL4R/TZAbbreviationDB/Record.html +117 -0
  16. data/doc/classes/SDL4R/Tag.html +1274 -0
  17. data/doc/classes/SDL4R/Token.html +131 -0
  18. data/doc/created.rid +1 -0
  19. data/doc/files/CHANGELOG.html +290 -0
  20. data/doc/files/LICENSE.html +53 -0
  21. data/doc/files/README.html +405 -0
  22. data/doc/files/lib/sdl4r/abbreviation_timezone_proxy_rb.html +63 -0
  23. data/doc/files/lib/sdl4r/constant_timezone_rb.html +54 -0
  24. data/doc/files/lib/sdl4r/element_rb.html +54 -0
  25. data/doc/files/lib/sdl4r/reader_rb.html +68 -0
  26. data/doc/files/lib/sdl4r/relative_timezone_rb.html +62 -0
  27. data/doc/files/lib/sdl4r/sdl4r_rb.html +66 -0
  28. data/doc/files/lib/sdl4r/sdl4r_tzinfo_rb.html +64 -0
  29. data/doc/files/lib/sdl4r/sdl4r_version_rb.html +54 -0
  30. data/doc/files/lib/sdl4r/sdl_binary_rb.html +54 -0
  31. data/doc/files/lib/sdl4r/sdl_parse_error_rb.html +54 -0
  32. data/doc/files/lib/sdl4r/sdl_time_span_rb.html +54 -0
  33. data/doc/files/lib/sdl4r/serializer_rb.html +62 -0
  34. data/doc/files/lib/sdl4r/tag_rb.html +66 -0
  35. data/doc/files/lib/sdl4r/token_rb.html +54 -0
  36. data/doc/files/lib/sdl4r/tokenizer_rb.html +64 -0
  37. data/doc/files/lib/sdl4r/tz_abbreviation_db_rb.html +67 -0
  38. data/doc/files/lib/sdl4r_rb.html +54 -0
  39. data/doc/files/lib/sdl4r_tzinfo_rb.html +54 -0
  40. data/doc/fr_class_index.html +23 -0
  41. data/doc/fr_file_index.html +40 -0
  42. data/doc/fr_method_index.html +4711 -0
  43. data/doc/index.html +15 -0
  44. data/doc/rdoc-style.css +328 -0
  45. data/lib/sdl4r.rb +3 -1
  46. data/lib/sdl4r/abbreviation_timezone_proxy.rb +38 -0
  47. data/lib/sdl4r/constant_timezone.rb +58 -0
  48. data/{test/sdl4r/sdl_test.rb → lib/sdl4r/element.rb} +19 -14
  49. data/lib/sdl4r/reader.rb +772 -0
  50. data/lib/sdl4r/relative_timezone.rb +156 -0
  51. data/lib/sdl4r/sdl4r.rb +187 -45
  52. data/lib/sdl4r/sdl4r_tzinfo.rb +75 -0
  53. data/lib/sdl4r/sdl4r_version.rb +24 -0
  54. data/lib/sdl4r/sdl_parse_error.rb +1 -1
  55. data/lib/sdl4r/sdl_time_span.rb +5 -1
  56. data/lib/sdl4r/serializer.rb +473 -60
  57. data/lib/sdl4r/tag.rb +126 -51
  58. data/lib/sdl4r/token.rb +49 -0
  59. data/lib/sdl4r/tokenizer.rb +431 -0
  60. data/lib/sdl4r/tz_abbreviation_db.rb +266 -0
  61. data/read_jprof.html +16934 -0
  62. data/read_jprof_pull.html +14451 -0
  63. data/read_prof.html +4983 -0
  64. data/read_prof_pull.html +4896 -0
  65. data/test/sdl4r/parser_test.rb +577 -503
  66. data/test/sdl4r/read_jprof.rb +58 -0
  67. data/test/sdl4r/read_prof.rb +70 -0
  68. data/test/sdl4r/reader_test.rb +173 -0
  69. data/test/sdl4r/relative_timezone_test.rb +102 -0
  70. data/test/sdl4r/sdl4r_test.rb +611 -528
  71. data/test/sdl4r/sdl4r_tzinfo_test.rb +108 -0
  72. data/test/sdl4r/sdl_test_case.rb +60 -0
  73. data/test/sdl4r/serializer_test.rb +448 -11
  74. data/test/sdl4r/tag_test.rb +84 -5
  75. data/test/sdl4r/tokenizer_test.rb +128 -0
  76. metadata +69 -11
  77. data/lib/sdl4r/parser.rb +0 -648
  78. data/lib/sdl4r/parser/reader.rb +0 -184
  79. data/lib/sdl4r/parser/time_span_with_zone.rb +0 -57
  80. data/lib/sdl4r/parser/token.rb +0 -138
  81. data/lib/sdl4r/parser/tokenizer.rb +0 -507
@@ -18,15 +18,23 @@
18
18
  # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
19
  #++
20
20
 
21
+ # Work-around a bug in NetBeans (http://netbeans.org/bugzilla/show_bug.cgi?id=188653)
22
+ if ENV["NB_EXEC_EXTEXECUTION_PROCESS_UUID"]
23
+ $:[0] = File.join(File.dirname(__FILE__),'../../lib')
24
+ $:.unshift(File.join(File.dirname(__FILE__),'../../test'))
25
+ end
26
+
21
27
  module SDL4R
22
28
 
23
29
  require 'test/unit'
24
30
  require "rexml/document"
25
31
 
26
32
  require 'sdl4r/tag'
27
-
33
+
34
+ require "sdl4r/sdl_test_case"
28
35
 
29
36
  class TagTest < Test::Unit::TestCase
37
+ include SdlTestCase
30
38
 
31
39
  public
32
40
 
@@ -357,6 +365,9 @@ module SDL4R
357
365
 
358
366
  tag.set_attributes("", {}) # removes all attributes in the default namespace
359
367
  assert_equal({"ns1:a2" => 12, "ns1:a3" => 13}, tag.attributes)
368
+
369
+ tag.set_attributes("ns1", {})
370
+ assert_equal({}, tag.attributes)
360
371
  end
361
372
 
362
373
  def test_to_child_hash
@@ -452,7 +463,7 @@ module SDL4R
452
463
  assert !(tag1 == tag2)
453
464
  assert !(tag1.equal?(tag2))
454
465
  assert_not_equal tag1.hash, tag2.hash
455
- end
466
+ end
456
467
 
457
468
  def test_children_values
458
469
  root = Tag.new "root"
@@ -504,7 +515,7 @@ EOF
504
515
  assert_equal "ns1", xml_doc[0].namespace
505
516
 
506
517
  tag = Tag.new "tag1" do
507
- self << 123
518
+ self << "123"
508
519
  end
509
520
  xml_doc = REXML::Document.new(tag.to_xml_string(options))
510
521
  assert_equal "tag1", xml_doc[0].name
@@ -618,9 +629,9 @@ EOF
618
629
  root.write(output, true)
619
630
  assert_equal(
620
631
  "root 1876/07/01 1592/09/13 09:05:00 1592/09/13 09:05:07" +
621
- " -98/06/15 13:47:12.340 3112/01/10 00:00:00+01:00",
632
+ " -98/06/15 13:47:12.340 3112/01/10 00:00:00-GMT+01:00",
622
633
  output)
623
- end
634
+ end
624
635
 
625
636
  # Check that having a timespan after a date in a tag values doesn't fail when loaded.
626
637
  #
@@ -642,5 +653,73 @@ EOF
642
653
  assert_equal "square", tag1.attribute("type")
643
654
  end
644
655
 
656
+ def test_symbol_as_identifier
657
+ # Tag identifiers
658
+ tag = Tag.new(:my_tag)
659
+ assert_equal "my_tag", tag.name
660
+
661
+ tag = Tag.new(:ns, :my_tag)
662
+ assert_equal "ns", tag.namespace
663
+ assert_equal "my_tag", tag.name
664
+
665
+ tag.namespace = :ns2
666
+ tag.name = :your_tag
667
+ assert_equal "ns2", tag.namespace
668
+ assert_equal "your_tag", tag.name
669
+
670
+ child = tag.new_child(:ns, :my_child)
671
+ assert_equal "ns", child.namespace
672
+ assert_equal "my_child", child.name
673
+
674
+ assert_equal child, tag.child(:ns, :my_child)
675
+ assert_equal [child], tag.children(false, :ns, :my_child)
676
+ assert tag.has_child?(:ns, :my_child)
677
+ assert !tag.has_child?(:ns2, :my_child)
678
+
679
+ child << 123
680
+ assert_equal [123], tag.children_values(:my_child)
681
+
682
+ # Attribute identifiers
683
+ tag.set_attribute(:ns1, :attr1, 2)
684
+ tag.set_attribute(:attr1, 1)
685
+ tag.set_attribute(:ns2, :attr1, 3)
686
+ assert_equal 1, tag.attribute(:attr1)
687
+ assert_equal 2, tag.attribute(:ns1, :attr1)
688
+ assert_equal 3, tag.attribute(:ns2, :attr1)
689
+ assert_equal({"attr1" => 1, "ns1:attr1" => 2, "ns2:attr1" => 3}, tag.attributes)
690
+ assert_equal({"attr1" => 2}, tag.attributes(:ns1))
691
+
692
+ tag.set_attributes(:attr2 => 20, :attr4 => 40)
693
+ tag.set_attributes(:ns1, :attr3 => 30)
694
+ assert_equal(
695
+ {"attr2" => 20, "ns1:attr3" => 30, "attr4" => 40, "ns2:attr1" => 3}, tag.attributes)
696
+ assert_equal({"attr3" => 30}, tag.attributes(:ns1))
697
+ assert tag.has_attribute?(:attr2)
698
+ assert tag.has_attribute?(:ns1, :attr3)
699
+ assert !tag.has_attribute?(:attr3)
700
+
701
+ tag.remove_attribute(:attr2)
702
+ assert_equal nil, tag.attribute(:attr2)
703
+ assert tag.has_attribute?(:ns1, :attr3)
704
+ tag.remove_attribute(:ns1, :attr3)
705
+ assert !tag.has_attribute?(:ns1, :attr3)
706
+
707
+ tag.clear_attributes()
708
+
709
+ tag.set_attributes(:ns2, :attr2 => 50, :attr3 => 60, :attr4 => 70)
710
+ assert_equal({"ns2:attr2" => 50, "ns2:attr3" => 60, "ns2:attr4" => 70}, tag.attributes)
711
+
712
+ tag.clear_attributes(:ns1)
713
+ assert_equal({"ns2:attr2" => 50, "ns2:attr3" => 60, "ns2:attr4" => 70}, tag.attributes)
714
+ tag.clear_attributes(:ns2)
715
+ assert !tag.has_attribute?
716
+ assert !tag.has_attributes?
717
+
718
+ tag.clear_attributes
719
+
720
+ tag << {:attr10 => 100, :"ns20:attr20" => 200}
721
+ assert_equal({"attr10" => 100, "ns20:attr20" => 200}, tag.attributes)
722
+ end
723
+
645
724
  end
646
725
  end
@@ -0,0 +1,128 @@
1
+ #!/usr/bin/env ruby -w
2
+ # encoding: UTF-8
3
+
4
+ #--
5
+ # Simple Declarative Language (SDL) for Ruby
6
+ # Copyright 2005 Ikayzo, inc.
7
+ #
8
+ # This program is free software. You can distribute or modify it under the
9
+ # terms of the GNU Lesser General Public License version 2.1 as published by
10
+ # the Free Software Foundation.
11
+ #
12
+ # This program is distributed AS IS and WITHOUT WARRANTY. OF ANY KIND,
13
+ # INCLUDING MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
14
+ # See the GNU Lesser General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Lesser General Public License
17
+ # along with this program; if not, contact the Free Software Foundation, Inc.,
18
+ # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
+ #++
20
+
21
+ # Work-around a bug in NetBeans (http://netbeans.org/bugzilla/show_bug.cgi?id=188653)
22
+ if ENV["NB_EXEC_EXTEXECUTION_PROCESS_UUID"]
23
+ $:[0] = File.join(File.dirname(__FILE__),'../../../lib')
24
+ $:.unshift(File.join(File.dirname(__FILE__),'../../../test'))
25
+ end
26
+
27
+ if RUBY_VERSION < '1.9.0'
28
+ $KCODE = 'u'
29
+ require 'jcode'
30
+ end
31
+
32
+ module SDL4R
33
+ module Parser
34
+
35
+ require 'stringio'
36
+ require 'test/unit'
37
+
38
+ require 'sdl4r/tag'
39
+ require 'sdl4r/tokenizer'
40
+
41
+ require 'sdl4r/sdl_test_case'
42
+
43
+
44
+ class TokenizerTest < Test::Unit::TestCase
45
+ include SdlTestCase
46
+
47
+ # Fully tokenizes 's' and returns an array of the found token types plus an array of the found
48
+ # tokens
49
+ #
50
+ def tokenize(s)
51
+ tokenizer = Tokenizer.new(StringIO.new(s))
52
+ token_types = []
53
+ tokens = []
54
+
55
+ while tokenizer.read
56
+ token_types << tokenizer.token_type
57
+ tokens << tokenizer.token
58
+ end
59
+
60
+ assert_equal :EOF, token_types.last
61
+ assert_equal nil, tokens.last
62
+
63
+ token_types.pop
64
+ tokens.pop
65
+
66
+ return token_types, tokens
67
+ end
68
+ private :tokenize
69
+
70
+ def test_backquote_strings
71
+ types, tokens = tokenize("``")
72
+ assert_equal [:INLINE_BACKQUOTE_STRING], types
73
+ assert_equal [""], tokens
74
+
75
+ types, tokens = tokenize("` `")
76
+ assert_equal [:INLINE_BACKQUOTE_STRING], types
77
+ assert_equal [" "], tokens
78
+
79
+ types, tokens = tokenize("`abc\\\ndef`")
80
+ assert_equal(
81
+ [:MULTILINE_BACKQUOTE_STRING_START, :MULTILINE_BACKQUOTE_STRING_END], types)
82
+ assert_equal ["abc\\\n", "def"], tokens
83
+
84
+ types, tokens = tokenize("`abc\\\n def`")
85
+ assert_equal(
86
+ [:MULTILINE_BACKQUOTE_STRING_START, :MULTILINE_BACKQUOTE_STRING_END], types)
87
+ assert_equal ["abc\\\n", " def"], tokens
88
+
89
+ types, tokens = tokenize("`\ndef`")
90
+ assert_equal(
91
+ [:MULTILINE_BACKQUOTE_STRING_START, :MULTILINE_BACKQUOTE_STRING_END], types)
92
+ assert_equal ["\n", "def"], tokens
93
+
94
+ types, tokens = tokenize("`abc\n def \n ghi`")
95
+ assert_equal [
96
+ :MULTILINE_BACKQUOTE_STRING_START,
97
+ :MULTILINE_BACKQUOTE_STRING_PART,
98
+ :MULTILINE_BACKQUOTE_STRING_END],
99
+ types
100
+ assert_equal ["abc\n", " def \n", " ghi"], tokens
101
+
102
+
103
+ types, tokens = tokenize("`abc\n def \n ghi\n`")
104
+ assert_equal [
105
+ :MULTILINE_BACKQUOTE_STRING_START,
106
+ :MULTILINE_BACKQUOTE_STRING_PART,
107
+ :MULTILINE_BACKQUOTE_STRING_PART,
108
+ :MULTILINE_BACKQUOTE_STRING_END],
109
+ types
110
+ assert_equal ["abc\n", " def \n", " ghi\n", ""], tokens
111
+ end
112
+
113
+ def test_double_quote_string
114
+ types, tokens = tokenize('""')
115
+ assert_equal [:INLINE_DOUBLE_QUOTE_STRING], types
116
+ assert_equal [""], tokens
117
+
118
+ types, tokens = tokenize('"tutu"')
119
+ assert_equal [:INLINE_DOUBLE_QUOTE_STRING], types
120
+ assert_equal ["tutu"], tokens
121
+
122
+ types, tokens = tokenize('"\\t"')
123
+ assert_equal [:INLINE_DOUBLE_QUOTE_STRING], types
124
+ assert_equal ["\\t"], tokens
125
+ end
126
+ end
127
+ end
128
+ end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 9
8
- - 9
9
- version: 0.9.9
8
+ - 11
9
+ version: 0.9.11
10
10
  platform: ruby
11
11
  authors:
12
12
  - Philippe Vosges
@@ -15,11 +15,11 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-11-19 00:00:00 +09:00
18
+ date: 2010-12-26 00:00:00 +09:00
19
19
  default_executable:
20
20
  dependencies: []
21
21
 
22
- description: " The Simple Declarative Language provides an easy way to describe lists, maps,\n and trees of typed data in a compact, easy to read representation.\n For property files, configuration files, logs, and simple serialization\n requirements, SDL provides a compelling alternative to XML and Properties\n files.\n"
22
+ description: " The Simple Declarative Language provides an easy way to describe lists, maps,\n and trees of typed data in a compact, easy to read representation.\n For property files, configuration files, logs, and simple serialization\n requirements, SDL provides a compelling alternative to XML and Properties\n files.\n"
23
23
  email: sdl-users@ikayzo.org
24
24
  executables: []
25
25
 
@@ -29,29 +29,84 @@ extra_rdoc_files: []
29
29
 
30
30
  files:
31
31
  - lib/sdl4r.rb
32
- - lib/sdl4r/parser/reader.rb
33
- - lib/sdl4r/parser/time_span_with_zone.rb
34
- - lib/sdl4r/parser/token.rb
35
- - lib/sdl4r/parser/tokenizer.rb
36
- - lib/sdl4r/parser.rb
32
+ - lib/sdl4r/abbreviation_timezone_proxy.rb
33
+ - lib/sdl4r/constant_timezone.rb
34
+ - lib/sdl4r/element.rb
35
+ - lib/sdl4r/reader.rb
36
+ - lib/sdl4r/relative_timezone.rb
37
37
  - lib/sdl4r/sdl4r.rb
38
+ - lib/sdl4r/sdl4r_tzinfo.rb
39
+ - lib/sdl4r/sdl4r_version.rb
38
40
  - lib/sdl4r/sdl_binary.rb
39
41
  - lib/sdl4r/sdl_parse_error.rb
40
42
  - lib/sdl4r/sdl_time_span.rb
41
43
  - lib/sdl4r/serializer.rb
42
44
  - lib/sdl4r/tag.rb
45
+ - lib/sdl4r/token.rb
46
+ - lib/sdl4r/tokenizer.rb
47
+ - lib/sdl4r/tz_abbreviation_db.rb
43
48
  - CHANGELOG
44
49
  - LICENSE
45
50
  - Rakefile
46
51
  - README
52
+ - read_jprof.html
53
+ - read_jprof_pull.html
54
+ - read_prof.html
55
+ - read_prof_pull.html
47
56
  - TODO
48
57
  - test/sdl4r/parser_test.rb
58
+ - test/sdl4r/reader_test.rb
59
+ - test/sdl4r/read_jprof.rb
60
+ - test/sdl4r/read_prof.rb
61
+ - test/sdl4r/relative_timezone_test.rb
49
62
  - test/sdl4r/sdl4r_test.rb
50
- - test/sdl4r/sdl_test.rb
63
+ - test/sdl4r/sdl4r_tzinfo_test.rb
64
+ - test/sdl4r/sdl_test_case.rb
51
65
  - test/sdl4r/serializer_test.rb
52
66
  - test/sdl4r/tag_test.rb
53
67
  - test/sdl4r/test_basic_types.sdl
54
68
  - test/sdl4r/test_structures.sdl
69
+ - test/sdl4r/tokenizer_test.rb
70
+ - doc/classes/SDL4R/AbbreviationTimezoneProxy.html
71
+ - doc/classes/SDL4R/ConstantTimezone.html
72
+ - doc/classes/SDL4R/Element.html
73
+ - doc/classes/SDL4R/Reader.html
74
+ - doc/classes/SDL4R/RelativeTimezone.html
75
+ - doc/classes/SDL4R/SdlBinary.html
76
+ - doc/classes/SDL4R/SdlParseError.html
77
+ - doc/classes/SDL4R/SdlTimeSpan.html
78
+ - doc/classes/SDL4R/Serializer/Ref.html
79
+ - doc/classes/SDL4R/Serializer.html
80
+ - doc/classes/SDL4R/Tag.html
81
+ - doc/classes/SDL4R/Token.html
82
+ - doc/classes/SDL4R/TZAbbreviationDB/Record.html
83
+ - doc/created.rid
84
+ - doc/files/CHANGELOG.html
85
+ - doc/files/lib/sdl4r/abbreviation_timezone_proxy_rb.html
86
+ - doc/files/lib/sdl4r/constant_timezone_rb.html
87
+ - doc/files/lib/sdl4r/element_rb.html
88
+ - doc/files/lib/sdl4r/reader_rb.html
89
+ - doc/files/lib/sdl4r/relative_timezone_rb.html
90
+ - doc/files/lib/sdl4r/sdl4r_rb.html
91
+ - doc/files/lib/sdl4r/sdl4r_tzinfo_rb.html
92
+ - doc/files/lib/sdl4r/sdl4r_version_rb.html
93
+ - doc/files/lib/sdl4r/sdl_binary_rb.html
94
+ - doc/files/lib/sdl4r/sdl_parse_error_rb.html
95
+ - doc/files/lib/sdl4r/sdl_time_span_rb.html
96
+ - doc/files/lib/sdl4r/serializer_rb.html
97
+ - doc/files/lib/sdl4r/tag_rb.html
98
+ - doc/files/lib/sdl4r/tokenizer_rb.html
99
+ - doc/files/lib/sdl4r/token_rb.html
100
+ - doc/files/lib/sdl4r/tz_abbreviation_db_rb.html
101
+ - doc/files/lib/sdl4r_rb.html
102
+ - doc/files/lib/sdl4r_tzinfo_rb.html
103
+ - doc/files/LICENSE.html
104
+ - doc/files/README.html
105
+ - doc/fr_class_index.html
106
+ - doc/fr_file_index.html
107
+ - doc/fr_method_index.html
108
+ - doc/index.html
109
+ - doc/rdoc-style.css
55
110
  has_rdoc: true
56
111
  homepage: http://sdl4r.rubyforge.org/
57
112
  licenses: []
@@ -86,7 +141,10 @@ specification_version: 3
86
141
  summary: Simple Declarative Language for Ruby library
87
142
  test_files:
88
143
  - test/sdl4r/parser_test.rb
144
+ - test/sdl4r/reader_test.rb
145
+ - test/sdl4r/relative_timezone_test.rb
89
146
  - test/sdl4r/sdl4r_test.rb
90
- - test/sdl4r/sdl_test.rb
147
+ - test/sdl4r/sdl4r_tzinfo_test.rb
91
148
  - test/sdl4r/serializer_test.rb
92
149
  - test/sdl4r/tag_test.rb
150
+ - test/sdl4r/tokenizer_test.rb
@@ -1,648 +0,0 @@
1
- #!/usr/bin/env ruby -w
2
- # encoding: UTF-8
3
-
4
- #--
5
- # Simple Declarative Language (SDL) for Ruby
6
- # Copyright 2005 Ikayzo, inc.
7
- #
8
- # This program is free software. You can distribute or modify it under the
9
- # terms of the GNU Lesser General Public License version 2.1 as published by
10
- # the Free Software Foundation.
11
- #
12
- # This program is distributed AS IS and WITHOUT WARRANTY. OF ANY KIND,
13
- # INCLUDING MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
14
- # See the GNU Lesser General Public License for more details.
15
- #
16
- # You should have received a copy of the GNU Lesser General Public License
17
- # along with this program; if not, contact the Free Software Foundation, Inc.,
18
- # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
- #++
20
-
21
-
22
- module SDL4R
23
-
24
- require 'base64'
25
-
26
- require File.dirname(__FILE__) + '/sdl_binary'
27
- require File.dirname(__FILE__) + '/sdl_time_span'
28
- require File.dirname(__FILE__) + '/sdl_parse_error'
29
- require File.dirname(__FILE__) + '/parser/tokenizer'
30
-
31
- # The SDL parser.
32
- #
33
- # In Ruby 1.8, in order to enable UTF-8 support, you may have to declare the following lines:
34
- #
35
- # $KCODE = 'u'
36
- # require 'jcode'
37
- #
38
- # This will give you correct input and output and correct UTF-8 "general" sorting.
39
- # Alternatively you can use the following options when launching the Ruby interpreter:
40
- #
41
- # /path/to/ruby -Ku -rjcode
42
- #
43
- # == Authors
44
- # Daniel Leuck, Philippe Vosges
45
- #
46
- class Parser
47
-
48
- # Passed to parse_error() in order to specify an error that occured on no specific position
49
- # (column).
50
- UNKNOWN_POSITION = -2
51
-
52
- # Creates an SDL parser on the specified +IO+.
53
- #
54
- # IO.open("path/to/sdl_file") { |io|
55
- # parser = SDL4R::Parser.new(io)
56
- # tags = parser.parse()
57
- # }
58
- #
59
- def initialize(io)
60
- raise ArgumentError, "io == nil" if io.nil?
61
-
62
- @tokenizer = Tokenizer.new(io)
63
- end
64
-
65
- # Parses the underlying +IO+ and returns an +Array+ of +Tag+.
66
- #
67
- # ==Errors
68
- # [IOError] If a problem is encountered with the IO
69
- # [SdlParseError] If the document is malformed
70
- def parse
71
- tags = []
72
-
73
- while tokens = @tokenizer.read_line_tokens()
74
- if tokens.last.type == :START_BLOCK
75
- # tag with a block
76
- tag = construct_tag(tokens[0...-1])
77
- add_children(tag)
78
- tags << tag
79
-
80
- elsif tokens.first.type == :END_BLOCK
81
- # we found an block end token that should have been consumed by
82
- # add_children() normally
83
- parse_error(
84
- "No opening block ({) for close block (}).",
85
- tokens.first.line,
86
- tokens.first.position)
87
- else
88
- # tag without block
89
- tags << construct_tag(tokens)
90
- end
91
- end
92
-
93
- @tokenizer.close()
94
-
95
- return tags
96
- end
97
-
98
- # Creates and returns the object representing a datetime (DateTime in the default
99
- # implementation). Can be overriden.
100
- #
101
- # def new_date_time(year, month, day, hour, min, sec, time_zone_offset)
102
- # Time.utc(year, month, day, hour, min, sec)
103
- # end
104
- #
105
- def new_date_time(year, month, day, hour, min, sec, time_zone_offset)
106
- SDL4R::new_date_time(year, month, day, hour, min, sec, time_zone_offset)
107
- end
108
-
109
- private
110
-
111
- # Parses the children tags of +parent+ until an end of block is found.
112
- def add_children(parent)
113
- while tokens = @tokenizer.read_line_tokens()
114
- if tokens.first.type == :END_BLOCK
115
- return
116
-
117
- elsif tokens.last.type == :START_BLOCK
118
- # found a child with a block
119
- tag = construct_tag(tokens[0...-1]);
120
- add_children(tag)
121
- parent.add_child(tag)
122
-
123
- else
124
- parent.add_child(construct_tag(tokens))
125
- end
126
- end
127
-
128
- parse_error("No close block (}).", @tokenizer.line_no, UNKNOWN_POSITION)
129
- end
130
-
131
- # Construct a Tag (but not its children) from a string of tokens
132
- #
133
- # Throws SdlParseError if some bad syntax is found.
134
- def construct_tag(tokens)
135
- raise ArgumentError, "tokens == nil" if tokens.nil?
136
- if tokens.empty?
137
- parse_error("Internal Error: empty token list", @tokenizer.line_no, UNKNOWN_POSITION)
138
- end
139
-
140
- first_token = tokens.first
141
- if first_token.literal?
142
- first_token = Token.new(ANONYMOUS_TAG_NAME)
143
- tokens.insert(0, first_token)
144
-
145
- elsif first_token.type != :IDENTIFIER
146
- expecting_but_got(
147
- "IDENTIFIER",
148
- "#{first_token.type} (#{first_token.text})",
149
- first_token.line,
150
- first_token.position)
151
- end
152
-
153
- tag = nil
154
- if tokens.size == 1
155
- tag = Tag.new(first_token.text)
156
-
157
- else
158
- values_start_index = 1
159
- second_token = tokens[1]
160
-
161
- if second_token.type == :COLON
162
- if tokens.size == 2 or tokens[2].type != :IDENTIFIER
163
- parse_error(
164
- "Colon (:) encountered in unexpected location.",
165
- second_token.line,
166
- second_token.position)
167
- end
168
-
169
- third_token = tokens[2];
170
- tag = Tag.new(first_token.text, third_token.text)
171
- values_start_index = 3
172
-
173
- else
174
- tag = Tag.new(first_token.text)
175
- end
176
-
177
- # read values
178
- attribute_start_index = add_tag_values(tag, tokens, values_start_index)
179
-
180
- # read attributes
181
- if attribute_start_index < tokens.size
182
- add_tag_attributes(tag, tokens, attribute_start_index)
183
- end
184
- end
185
-
186
- return tag
187
- end
188
-
189
- # Return the position at the end of the value list
190
- #
191
- def add_tag_values(tag, tokens, start)
192
- size = tokens.size()
193
- i = start;
194
-
195
- while i < size
196
- token = tokens[i]
197
-
198
- if token.literal?
199
- # if a DATE token is followed by a TIME token combine them
200
- next_token = ((i + 1) < size)? tokens[i + 1] : nil
201
- if token.type == :DATE && next_token && next_token.type == :TIME
202
- date = token.object_for_literal()
203
- time_span_with_zone = next_token.object_for_literal()
204
-
205
- if time_span_with_zone.day
206
- # as there are days specified, it can't be a full precision date
207
- tag.add_value(date);
208
- tag.add_value(
209
- SdlTimeSpan.new(
210
- time_span_with_zone.day || 0,
211
- time_span_with_zone.hour,
212
- time_span_with_zone.min,
213
- time_span_with_zone.sec))
214
-
215
-
216
- if time_span_with_zone.time_zone_offset
217
- parse_error("TimeSpan cannot have a timeZone", t.line, t.position)
218
- end
219
-
220
- else
221
- tag.add_value(combine(date, time_span_with_zone))
222
- end
223
-
224
- i += 1
225
-
226
- else
227
- value = token.object_for_literal()
228
- if value.is_a?(TimeSpanWithZone)
229
- # the literal looks like a time zone
230
- if value.time_zone_offset
231
- expecting_but_got(
232
- "TIME SPAN",
233
- "TIME (component of date/time)",
234
- token.line,
235
- token.position)
236
- end
237
-
238
- tag.add_value(SdlTimeSpan.new(value.day || 0, value.hour, value.min, value.sec))
239
- else
240
- tag.add_value(value)
241
- end
242
- end
243
- elsif token.type == :IDENTIFIER
244
- break
245
- else
246
- expecting_but_got(
247
- "LITERAL or IDENTIFIER", token.type, token.line, token.position)
248
- end
249
-
250
- i += 1
251
- end
252
-
253
- return i
254
- end
255
-
256
- #
257
- # Add attributes to the given tag
258
- #
259
- def add_tag_attributes(tag, tokens, start)
260
- i = start
261
- size = tokens.size
262
-
263
- while i < size
264
- token = tokens[i]
265
- if token.type != :IDENTIFIER
266
- expecting_but_got("IDENTIFIER", token.type, token.line, token.position)
267
- end
268
- name_or_namespace = token.text;
269
-
270
- if i == (size - 1)
271
- expecting_but_got(
272
- "\":\" or \"=\" \"LITERAL\"",
273
- "END OF LINE.",
274
- token.line,
275
- token.position)
276
- end
277
-
278
- i += 1
279
- token = tokens[i]
280
- if token.type == :COLON
281
- if i == (size - 1)
282
- expecting_but_got(
283
- "IDENTIFIER", "END OF LINE", token.line, token.position)
284
- end
285
-
286
- i += 1
287
- token = tokens[i]
288
- if token.type != :IDENTIFIER
289
- expecting_but_got(
290
- "IDENTIFIER", token.type, token.line, token.position)
291
- end
292
- name = token.text
293
-
294
- if i == (size - 1)
295
- expecting_but_got("\"=\"", "END OF LINE", token.line, token.position)
296
- end
297
-
298
- i += 1
299
- token = tokens[i]
300
- if token.type != :EQUALS
301
- expecting_but_got("\"=\"", token.type, token.line, token.position)
302
- end
303
-
304
- if i == (size - 1)
305
- expecting_but_got("LITERAL", "END OF LINE", token.line, token.position)
306
- end
307
-
308
- i += 1
309
- token = tokens[i]
310
- if !token.literal?
311
- expecting_but_got("LITERAL", token.type, token.line, token.position)
312
- end
313
-
314
- if token.type == :DATE and (i + 1) < size and tokens[i + 1].type == :TIME
315
- date = token.get_object_for_literal()
316
- time_span_with_zone = tokens[i + 1].get_object_for_literal()
317
-
318
- if time_span_with_zone.days != 0
319
- expecting_but_got(
320
- "TIME (component of date/time) in attribute value",
321
- "TIME SPAN",
322
- token.line,
323
- token.position)
324
- else
325
- tag.set_attribute(name_or_namespace, name, combine(date, time_span_with_zone))
326
- end
327
-
328
- i += 1
329
- else
330
- value = token.object_for_literal();
331
- if value.is_a?(TimeSpanWithZone)
332
- time_span_with_zone = value
333
-
334
- if time_span_with_zone.time_zone_offset
335
- expecting_but_got(
336
- "TIME SPAN",
337
- "TIME (component of date/time)",
338
- token.line,
339
- token.position)
340
- end
341
-
342
- time_span = SdlTimeSpan.new(
343
- time_span_with_zone.day,
344
- time_span_with_zone.hour,
345
- time_span_with_zone.min,
346
- time_span_with_zone.sec)
347
-
348
- tag.set_attribute(name_or_namespace, name, time_span)
349
- else
350
- tag.set_attribute(name_or_namespace, name, value);
351
- end
352
- end
353
- elsif token.type == :EQUALS
354
- if i == (size - 1)
355
- expecting_but_got("LITERAL", "END OF LINE", token.line, token.position)
356
- end
357
-
358
- i += 1
359
- token = tokens[i]
360
- if !token.literal?
361
- expecting_but_got("LITERAL", token.type, token.line, token.position)
362
- end
363
-
364
- if token.type == :DATE and (i + 1) < size and tokens[i + 1].type == :TIME
365
- date = token.object_for_literal()
366
- time_span_with_zone = tokens[i + 1].object_for_literal()
367
-
368
- if time_span_with_zone.day
369
- expecting_but_got(
370
- "TIME (component of date/time) in attribute value",
371
- "TIME SPAN",
372
- token.line,
373
- token.position)
374
- end
375
- tag.set_attribute(name_or_namespace, combine(date, time_span_with_zone))
376
-
377
- i += 1
378
- else
379
- value = token.object_for_literal()
380
- if value.is_a?(TimeSpanWithZone)
381
- time_span_with_zone = value
382
- if time_span_with_zone.time_zone_offset
383
- expecting_but_got(
384
- "TIME SPAN",
385
- "TIME (component of date/time)",
386
- token.line,
387
- token.position)
388
- end
389
-
390
- time_span = SdlTimeSpan.new(
391
- time_span_with_zone.day || 0,
392
- time_span_with_zone.hour,
393
- time_span_with_zone.min,
394
- time_span_with_zone.sec)
395
- tag.set_attribute(name_or_namespace, time_span)
396
- else
397
- tag.set_attribute(name_or_namespace, value);
398
- end
399
- end
400
- else
401
- expecting_but_got(
402
- "\":\" or \"=\"", token.type, token.line, token.position)
403
- end
404
-
405
- i += 1
406
- end
407
- end
408
-
409
- # Combines a simple Date with a TimeSpanWithZone to create a DateTime
410
- #
411
- def combine(date, time_span_with_zone)
412
- time_zone_offset = time_span_with_zone.time_zone_offset
413
- time_zone_offset = TimeSpanWithZone.default_time_zone_offset if time_zone_offset.nil?
414
-
415
- new_date_time(
416
- date.year,
417
- date.month,
418
- date.day,
419
- time_span_with_zone.hour,
420
- time_span_with_zone.min,
421
- time_span_with_zone.sec,
422
- time_zone_offset)
423
- end
424
-
425
- private
426
- ############################################################################
427
- ## Parsers for types
428
- ############################################################################
429
-
430
- def Parser.parse_string(literal)
431
- unless literal =~ /(^`.*`$)|(^\".*\"$)/m
432
- raise ArgumentError,
433
- "Malformed string <#{literal}>." +
434
- " Strings must start and end with \" or `"
435
- end
436
-
437
- return literal[1..-2]
438
- end
439
-
440
- def Parser.parse_character(literal)
441
- unless literal =~ /(^'.*'$)/
442
- raise ArgumentError,
443
- "Malformed character <#{literal}>." +
444
- " Character must start and end with single quotes"
445
- end
446
-
447
- return literal[1]
448
- end
449
-
450
- def Parser.parse_number(literal)
451
- # we use the fact that Kernel.Integer() and Kernel.Float() raise ArgumentErrors
452
- if literal =~ /(.*)(L)$/i
453
- return Integer($1)
454
- elsif literal =~ /([^BDF]*)(BD)$/i
455
- return BigDecimal($1)
456
- elsif literal =~ /([^BDF]*)(F|D)$/i
457
- return Float($1)
458
- elsif literal.count(".e") == 0
459
- return Integer(literal)
460
- else
461
- return Float(literal)
462
- end
463
- end
464
-
465
- # Parses the given literal into a returned array
466
- # [days, hours, minutes, seconds, time_zone_offset].
467
- # 'days', 'hours' and 'minutes' are integers.
468
- # 'seconds' and 'time_zone_offset' are rational numbers.
469
- # 'days' and 'seconds' are equal to 0 if they're not specified in +literal+.
470
- # 'time_zone_offset' is equal to nil if not specified.
471
- #
472
- # +allowDays+ indicates whether the specification of days is allowed
473
- # in +literal+
474
- # +allowTimeZone+ indicates whether the specification of the timeZone is
475
- # allowed in +literal+
476
- #
477
- # All components are returned disregarding the values of +allowDays+ and
478
- # +allowTimeZone+.
479
- #
480
- # Raises an ArgumentError if +literal+ has a bad format.
481
- def Parser.parse_time_span_and_time_zone(literal, allowDays, allowTimeZone)
482
- overall_sign = (literal =~ /^-/)? -1 : +1
483
-
484
- if literal =~ /^(([+\-]?\d+)d:)/
485
- if allowDays
486
- days = Integer($2)
487
- time_part = literal[($1.length)..-1]
488
- else
489
- # detected a day specification in a pure time literal
490
- raise ArgumentError, "unexpected day specification in #{literal}"
491
- end
492
- else
493
- days = nil
494
- time_part = literal
495
- end
496
-
497
- # We have to parse the string ourselves because AFAIK :
498
- # - strptime() can't parse milliseconds
499
- # - strptime() can't parse the time zone custom offset (CET+02:30)
500
- # - strptime() accepts trailing chars
501
- # (e.g. "12:24-xyz@" ==> "xyz@" is obviously wrong but strptime()
502
- # won't mind)
503
- if time_part =~ /^([+-]?\d+):(\d+)(?::(\d+)(?:\.(\d+))?)?(?:(?:-([a-zA-Z]+))?(?:([\+\-]\d+)(?::(\d+))?)?)?$/i
504
- hours = $1.to_i
505
- minutes = $2.to_i
506
- # seconds and milliseconds are implemented as one rational number
507
- # unless there are no milliseconds
508
- millisecond_part = ($4)? $4.ljust(3, "0") : nil
509
- if millisecond_part
510
- seconds = Rational(($3 + millisecond_part).to_i, 10 ** millisecond_part.length)
511
- else
512
- seconds = ($3)? Integer($3) : 0
513
- end
514
-
515
- if ($5 or $6) and not allowTimeZone
516
- raise ArgumentError, "unexpected time zone specification in #{literal}"
517
- end
518
-
519
- time_zone_code = $5 # might be nil
520
-
521
- if $6
522
- zone_custom_minute_offset = $6.to_i * 60
523
- if $7
524
- if zone_custom_minute_offset > 0
525
- zone_custom_minute_offset = zone_custom_minute_offset + $7.to_i
526
- else
527
- zone_custom_minute_offset = zone_custom_minute_offset - $7.to_i
528
- end
529
- end
530
- end
531
-
532
- time_zone_offset = get_time_zone_offset(time_zone_code, zone_custom_minute_offset)
533
-
534
- if not allowDays and $1 =~ /^[+-]/
535
- # unexpected timeSpan syntax
536
- raise ArgumentError, "unexpected sign on hours : #{literal}"
537
- end
538
-
539
- # take the sign into account
540
- hours *= overall_sign if days # otherwise the sign is already applied to the hours
541
- minutes *= overall_sign
542
- seconds *= overall_sign
543
-
544
- return [ days, hours, minutes, seconds, time_zone_offset ]
545
-
546
- else
547
- raise ArgumentError, "bad time component : #{literal}"
548
- end
549
- end
550
-
551
- # Parses the given literal (String) into a returned DateTime object.
552
- #
553
- # Raises an ArgumentError if +literal+ has a bad format.
554
- def Parser.parse_date_time(literal)
555
- raise ArgumentError("date literal is nil") if literal.nil?
556
-
557
- begin
558
- parts = literal.split(" ")
559
- if parts.length == 1
560
- return parse_date(literal)
561
- else
562
- date = parse_date(parts[0]);
563
- time_part = parts[1]
564
-
565
- days, hours, minutes, seconds, time_zone_offset =
566
- parse_time_span_and_time_zone(time_part, false, true)
567
-
568
- return new_date_time(
569
- date.year, date.month, date.day, hours, minutes, seconds, time_zone_offset)
570
- end
571
-
572
- rescue ArgumentError
573
- raise ArgumentError, "Bad date/time #{literal} : #{$!.message}"
574
- end
575
- end
576
-
577
- ##
578
- # Returns the time zone offset (Rational) corresponding to the provided parameters as a fraction
579
- # of a day. This method adds the two offsets if they are both provided.
580
- #
581
- # +time_zone_code+: can be nil
582
- # +custom_minute_offset+: can be nil
583
- #
584
- def Parser.get_time_zone_offset(time_zone_code, custom_minute_offset)
585
- return nil unless time_zone_code or custom_minute_offset
586
-
587
- time_zone_offset = custom_minute_offset ? Rational(custom_minute_offset, 60 * 24) : 0
588
-
589
- return time_zone_offset unless time_zone_code
590
-
591
- # we have to provide some bogus year/month/day in order to parse our time zone code
592
- d = DateTime.strptime("1999/01/01 #{time_zone_code}", "%Y/%m/%d %Z")
593
- # the offset is a fraction of a day
594
- return d.offset() + time_zone_offset
595
- end
596
-
597
- # Parses the +literal+ into a returned Date object.
598
- #
599
- # Raises an ArgumentError if +literal+ has a bad format.
600
-
601
- def Parser.parse_date(literal)
602
- # here, we're being stricter than strptime() alone as we forbid trailing chars
603
- if literal =~ /^(-?\d+)\/(\d+)\/(\d+)$/
604
- begin
605
- return Date.strptime(literal, "%Y/%m/%d")
606
- rescue ArgumentError
607
- raise ArgumentError, "Malformed Date <#{literal}> : #{$!.message}"
608
- end
609
- end
610
-
611
- raise ArgumentError, "Malformed Date <#{literal}>"
612
- end
613
-
614
- # Returns a String that contains the binary content corresponding to +literal+.
615
- #
616
- # +literal+ : a base-64 encoded literal (e.g. "[V2hvIHdhbnRzIHRvIGxpdmUgZm9yZXZlcj8=]")
617
- def Parser.parse_binary(literal)
618
- clean_literal = literal[1..-2] # remove square brackets
619
- return SdlBinary.decode64(clean_literal)
620
- end
621
-
622
- # Parses +literal+ (String) into the corresponding SDLTimeSpan, which is then
623
- # returned.
624
- #
625
- # Raises an ArgumentError if the literal is not a correct timeSpan literal.
626
- def Parser.parse_time_span(literal)
627
- days, hours, minutes, seconds, time_zone_offset =
628
- parse_time_span_and_time_zone(literal, true, false)
629
-
630
- milliseconds = ((seconds - seconds.to_i) * 1000).to_i
631
- seconds = seconds.to_i
632
-
633
- return SDLTimeSpan.new(days, hours, minutes, seconds, milliseconds)
634
- end
635
-
636
- # Close the reader and throw a SdlParseError using the format
637
- # Was expecting X but got Y.
638
- #
639
- def expecting_but_got(expecting, got, line, position)
640
- @tokenizer.expecting_but_got(expecting, got, line, position)
641
- end
642
-
643
- # Raises a SdlParseError.
644
- def parse_error(description, line_no, position)
645
- @tokenizer.parse_error(description, line_no, position)
646
- end
647
- end
648
- end