sdl4r 0.9.6 → 0.9.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. data/CHANGELOG +49 -2
  2. data/Rakefile +35 -5
  3. data/TODO +29 -3
  4. data/doc/classes/SDL4R.html +386 -674
  5. data/doc/classes/SDL4R/Parser.html +183 -307
  6. data/doc/classes/SDL4R/ParserTest.html +357 -0
  7. data/doc/classes/SDL4R/SDL4RTest.html +532 -0
  8. data/doc/classes/SDL4R/SDLTest.html +77 -0
  9. data/doc/classes/SDL4R/SdlBinary.html +180 -295
  10. data/doc/classes/SDL4R/SdlParseError.html +105 -180
  11. data/doc/classes/SDL4R/SdlTimeSpan.html +628 -939
  12. data/doc/classes/SDL4R/Tag.html +1236 -2036
  13. data/doc/classes/SDL4R/TagTest.html +292 -0
  14. data/doc/created.rid +1 -1
  15. data/doc/files/CHANGELOG.html +183 -184
  16. data/doc/files/LICENSE.html +496 -755
  17. data/doc/files/README.html +399 -623
  18. data/doc/files/lib/sdl4r/parser/reader_rb.html +53 -106
  19. data/doc/files/lib/sdl4r/parser/time_span_with_zone_rb.html +53 -106
  20. data/doc/files/lib/sdl4r/parser/token_rb.html +53 -106
  21. data/doc/files/lib/sdl4r/parser/tokenizer_rb.html +53 -106
  22. data/doc/files/lib/sdl4r/parser_rb.html +60 -112
  23. data/doc/files/lib/sdl4r/sdl4r_rb.html +62 -114
  24. data/doc/files/lib/sdl4r/sdl_binary_rb.html +53 -106
  25. data/doc/files/lib/sdl4r/sdl_parse_error_rb.html +53 -106
  26. data/doc/files/lib/sdl4r/sdl_time_span_rb.html +53 -106
  27. data/doc/files/lib/sdl4r/tag_rb.html +62 -114
  28. data/doc/files/lib/sdl4r_rb.html +53 -106
  29. data/doc/files/test/sdl4r/parser_test_rb.html +63 -0
  30. data/doc/files/test/sdl4r/sdl4r_test_rb.html +66 -0
  31. data/doc/files/test/sdl4r/sdl_test_rb.html +63 -0
  32. data/doc/files/test/sdl4r/tag_test_rb.html +63 -0
  33. data/doc/fr_class_index.html +19 -32
  34. data/doc/fr_file_index.html +37 -40
  35. data/doc/fr_method_index.html +4707 -114
  36. data/doc/index.html +14 -23
  37. data/doc/rdoc-style.css +323 -203
  38. data/lib/sdl4r/parser/reader.rb +26 -19
  39. data/lib/sdl4r/parser/token.rb +3 -3
  40. data/lib/sdl4r/parser/tokenizer.rb +93 -98
  41. data/lib/sdl4r/sdl_parse_error.rb +2 -2
  42. data/lib/sdl4r/tag.rb +127 -73
  43. data/test/sdl4r/parser_test.rb +109 -11
  44. data/test/sdl4r/tag_test.rb +73 -4
  45. metadata +15 -12
@@ -36,10 +36,10 @@ module SDL4R
36
36
  def initialize(description, line_no, position, line = nil)
37
37
  super(
38
38
  "#{description} Line " + ((line_no.nil? or line_no < 0)? "unknown" : line_no.to_s) +
39
- ", Position " + ((position.nil? or position < 0)? "unknown" : position.to_s) + "\n" +
39
+ ", Position " + ((position.nil? or position < 0)? "unknown" : position.to_s) + $/ +
40
40
  (line ? line + (position ? " " * (position - 1) : "") + "^" : ""))
41
41
 
42
- @lineNo = line_no
42
+ @line = line_no
43
43
  @position = position
44
44
  end
45
45
 
@@ -87,7 +87,18 @@ module SDL4R
87
87
  # orange quantity=2
88
88
  # }
89
89
  #
90
- # ==Raises
90
+ # If you provide a block that takes an argument, you will write the same example, as follows:
91
+ #
92
+ # tag = Tag.new("fruit") do |t|
93
+ # t.add_value 2
94
+ # t.new_child("orange") do
95
+ # set_attribute("quantity", 2)
96
+ # end
97
+ # end
98
+ #
99
+ # In this case, the current context is not the new Tag anymore but the context of your code.
100
+ #
101
+ # === Raises
91
102
  # ArgumentError if the name is not a legal SDL identifier
92
103
  # (see SDL4R#validate_identifier) or the namespace is non-blank
93
104
  # and is not a legal SDL identifier.
@@ -113,7 +124,13 @@ module SDL4R
113
124
  # The default namespace is represented by an empty string.
114
125
  @attributesByNamespace = {}
115
126
 
116
- instance_eval(&block) if block_given?
127
+ if block_given?
128
+ if block.arity > 0
129
+ block[self]
130
+ else
131
+ instance_eval(&block)
132
+ end
133
+ end
117
134
  end
118
135
 
119
136
  # Creates a new child tag.
@@ -125,7 +142,15 @@ module SDL4R
125
142
  # end
126
143
  # end
127
144
  #
128
- # The context of execution of the given block is the child instance
145
+ # The context of execution of the given block is the child instance.
146
+ # If you provide a block that takes a parameter (see below), the context is the context of your
147
+ # code:
148
+ #
149
+ # car = Tag.new("car") do |child|
150
+ # child.new_child("wheels") do |grandchild|
151
+ # grandchild << 4
152
+ # end
153
+ # end
129
154
  #
130
155
  # Returns the created child Tag.
131
156
  #
@@ -135,7 +160,7 @@ module SDL4R
135
160
 
136
161
  # Add a child to this Tag.
137
162
  #
138
- # +child+:: The child to add
163
+ # _child_:: The child to add
139
164
  #
140
165
  # Returns the added child.
141
166
  #
@@ -146,9 +171,9 @@ module SDL4R
146
171
 
147
172
  # Adds the given object as a child if it is a +Tag+, as an attribute if it is a Hash
148
173
  # {key => value} (supports namespaces), or as a value otherwise.
149
- # If it is an array, each of its elements is added to this Tag via this operator. If any of its
150
- # elements is itself an array, then an anonymous tag is created and the array is passed to it
151
- # via this operator (see the examples below)
174
+ # If it is an Enumerable (e.g. Array), each of its elements is added to this Tag via this
175
+ # operator. If any of its elements is itself an Enumerable, then an anonymous tag is created and
176
+ # the Enumerable is passed to it via this operator (see the examples below).
152
177
  #
153
178
  # tag << Tag.new("child")
154
179
  # tag << 123 # new integer value
@@ -162,9 +187,13 @@ module SDL4R
162
187
  # # 4 5 6
163
188
  # # }
164
189
  #
190
+ # Of course, despite the fact that String is an Enumerable, it is considered as the type of
191
+ # values.
192
+ #
165
193
  # Returns +self+.
166
194
  #
167
- # Use other accessors for a stricter and less "magical" behavior.
195
+ # Use other accessors (#add_child, #add_value, #attributes, etc) for a stricter and less
196
+ # "magical" behavior.
168
197
  #
169
198
  def <<(o)
170
199
  if o.is_a?(Tag)
@@ -175,9 +204,11 @@ module SDL4R
175
204
  namespace ||= ""
176
205
  set_attribute(namespace, key, value)
177
206
  }
178
- elsif o.is_a? Array
207
+ elsif o.is_a? String
208
+ add_value(o)
209
+ elsif o.is_a? Enumerable
179
210
  o.each { |item|
180
- if item.is_a? Array
211
+ if item.is_a? Enumerable and not item.is_a? String
181
212
  anonymous = new_child("content")
182
213
  anonymous << item
183
214
  else
@@ -192,7 +223,7 @@ module SDL4R
192
223
 
193
224
  # Remove a child from this Tag
194
225
  #
195
- # +child+:: the child to remove
226
+ # _child_:: the child to remove
196
227
  #
197
228
  # Returns true if the child exists and is removed
198
229
  #
@@ -208,14 +239,14 @@ module SDL4R
208
239
  end
209
240
 
210
241
  #
211
- # A convenience method that sets the first value in the value list. See
212
- # {@link #addValue(Object)} for legal types.
242
+ # A convenience method that sets the first value in the value list.
243
+ # See # #add_value for legal types.
213
244
  #
214
- # +value+:: The value to be set.
245
+ # _value_:: The value to be set.
215
246
  #
216
247
  # === Raises
217
248
  #
218
- # +ArgumentError+:: if the value is not a legal SDL type
249
+ # _ArgumentError_:: if the value is not a legal SDL type
219
250
  #
220
251
  def value=(value)
221
252
  @values[0] = SDL4R.coerce_or_fail(value)
@@ -245,9 +276,9 @@ module SDL4R
245
276
  #
246
277
  # Returns an Array of the children Tags of this Tag or enumerates them.
247
278
  #
248
- # +recursive+:: if true children and all descendants will be returned. False by default.
249
- # +name+:: if not nil, only children having this name will be returned. Nil by default.
250
- # +namespace+:: use nil for all namespaces and "" for the default one. Nil by default.
279
+ # _recursive_:: if true children and all descendants will be returned. False by default.
280
+ # _name_:: if not nil, only children having this name will be returned. Nil by default.
281
+ # _namespace_:: use nil for all namespaces and "" for the default one. Nil by default.
251
282
  #
252
283
  # tag.children # => array of the children
253
284
  # tag.children(true) { |descendant| ... }
@@ -283,7 +314,7 @@ module SDL4R
283
314
  # more than one value, all the values will be added as an array. If the child
284
315
  # has no value, +nil+ will be added. The search is not recursive.
285
316
  #
286
- # +name+:: if nil, all children are considered (nil by default).
317
+ # _name_:: if nil, all children are considered (nil by default).
287
318
  def children_values(name = nil)
288
319
  children_values = []
289
320
  each_child(false, name) { |child|
@@ -305,7 +336,7 @@ module SDL4R
305
336
  #
306
337
  # Get the first child with the given name, optionally using a recursive search.
307
338
  #
308
- # +name+:: the name of the child Tag. If +nil+, the first child is returned (+nil+ if there are
339
+ # _name_:: the name of the child Tag. If +nil+, the first child is returned (+nil+ if there are
309
340
  # no children at all).
310
341
  #
311
342
  # Returns the first child tag having the given name or +nil+ if no such child exists
@@ -325,7 +356,7 @@ module SDL4R
325
356
 
326
357
  # Indicates whether the child Tag of given name exists.
327
358
  #
328
- # +name+:: name of the searched child Tag
359
+ # _name_:: name of the searched child Tag
329
360
  #
330
361
  def has_child?(name)
331
362
  !child(name).nil?
@@ -340,9 +371,9 @@ module SDL4R
340
371
  # Enumerates the children +Tag+s of this Tag and calls the given block
341
372
  # providing it the child as parameter.
342
373
  #
343
- # +recursive+:: if true, enumerate grand-children, etc, recursively
344
- # +namespace+:: if not nil, indicates the namespace of the children to enumerate
345
- # +name+:: if not nil, indicates the name of the children to enumerate
374
+ # _recursive_:: if true, enumerate grand-children, etc, recursively
375
+ # _namespace_:: if not nil, indicates the namespace of the children to enumerate
376
+ # _name_:: if not nil, indicates the name of the children to enumerate
346
377
  #
347
378
  def each_child(recursive = false, namespace = nil, name = :DEFAULT, &block)
348
379
  if name == :DEFAULT
@@ -402,7 +433,7 @@ module SDL4R
402
433
 
403
434
  # Adds a value to this Tag. See SDL4R#coerce_or_fail to know about the allowable types.
404
435
  #
405
- # +v+:: The value to add
436
+ # _v_:: The value to add
406
437
  #
407
438
  # Raises an +ArgumentError+ if the value is not a legal SDL type
408
439
  #
@@ -419,7 +450,7 @@ module SDL4R
419
450
 
420
451
  # Removes the first occurence of the specified value from this Tag.
421
452
  #
422
- # +v+:: The value to remove
453
+ # _v_:: The value to remove
423
454
  #
424
455
  # Returns true If the value exists and is removed
425
456
  #
@@ -453,12 +484,11 @@ module SDL4R
453
484
  end
454
485
  end
455
486
 
456
- # Set the values for this tag. See {@link #addValue(Object)} for legal
457
- # value types.
487
+ # Set the values for this tag. See #add_value for legal value types.
458
488
  #
459
- # +values+:: The new values
460
- # @throws IllegalArgumentException if the collection contains any values
461
- # which are not legal SDL types
489
+ # _values_:: The new values
490
+ #
491
+ # Raises an +ArgumentError+ if the collection contains any values which are not legal SDL types.
462
492
  #
463
493
  def values=(someValues)
464
494
  @values.clear()
@@ -473,17 +503,15 @@ module SDL4R
473
503
  # set_attribute(namespace, key, value)
474
504
  #
475
505
  # Set an attribute in the given namespace for this tag. The allowable
476
- # attribute value types are the same as those allowed for
477
- # {@link #addValue(Object)}
478
- #
479
- # +namespace+:: The namespace for this attribute
480
- # +key+:: The attribute key
481
- # +value+:: The attribute value
506
+ # attribute value types are the same as those allowed for #add_value.
482
507
  #
483
- # @throws IllegalArgumentException if the key is not a legal SDL
484
- # identifier (see {@link SDL4R#validateIdentifier(String)}), or the
485
- # namespace is non-blank and is not a legal SDL identifier, or the
486
- # value is not a legal SDL type
508
+ # _namespace_:: The namespace for this attribute
509
+ # _key_:: The attribute key
510
+ # _value_:: The attribute value
511
+ #
512
+ # Raises +ArgumentError+ if the key is not a legal SDL identifier (see
513
+ # SDL4R#validate_identifier), or the namespace is non-blank and is not a legal SDL identifier,
514
+ # or thevalue is not a legal SDL type
487
515
  #
488
516
  def set_attribute(namespace, key, value = :default)
489
517
  if value == :default
@@ -549,7 +577,7 @@ module SDL4R
549
577
  # p "#{namespace}:#{key} = #{value}"
550
578
  # end
551
579
  #
552
- # +namespace+::
580
+ # _namespace_::
553
581
  # namespace of the returned attributes. If nil, all attributes are returned with
554
582
  # qualified names (e.g. "meat:color"). If "", attributes of the default namespace are returned.
555
583
  #
@@ -579,8 +607,8 @@ module SDL4R
579
607
  #
580
608
  # Removes the attribute, whose name and namespace are specified.
581
609
  #
582
- # +key+:: name of the removed atribute
583
- # +namespace+:: namespace of the removed attribute (equal to "", default namespace, by default)
610
+ # _key_:: name of the removed atribute
611
+ # _namespace_:: namespace of the removed attribute (equal to "", default namespace, by default)
584
612
  #
585
613
  # Returns the value of the removed attribute or +nil+ if it didn't exist.
586
614
  #
@@ -627,12 +655,11 @@ module SDL4R
627
655
  # previous attributes of the specified +namespace+ are removed.
628
656
  # See #set_attribute for allowable attribute value types.
629
657
  #
630
- # +attributes+:: a Hash where keys are attribute keys
631
- # +namespace+:: "" (default namespace) by default
658
+ # _attributes_:: a Hash where keys are attribute keys
659
+ # _namespace_:: "" (default namespace) by default
632
660
  #
633
- # Raises an +ArgumentError+ if any key in the map is not a legal SDL
634
- # identifier (see SDL4R#validate_identifier), or any value
635
- # is not a legal SDL type.
661
+ # Raises an +ArgumentError+ if any key in the map is not a legal SDL identifier
662
+ # (see SDL4R#validate_identifier), or any value is not a legal SDL type.
636
663
  #
637
664
  def set_attributes(namespace, attribute_hash = nil)
638
665
  if attribute_hash.nil?
@@ -655,7 +682,7 @@ module SDL4R
655
682
  # Sets all the attributes of the default namespace for this Tag in one
656
683
  # operation.
657
684
  #
658
- # See #set_attributes
685
+ # See #set_attributes.
659
686
  #
660
687
  def attributes=(attribute_hash)
661
688
  set_attributes(attribute_hash)
@@ -663,9 +690,9 @@ module SDL4R
663
690
 
664
691
  # Sets the name of this Tag.
665
692
  #
666
- # Raises +ArgumentError+ if the name is not a legal SDL
667
- # identifier (see SDL4R#validate_identifier)
668
-
693
+ # Raises +ArgumentError+ if the name is not a legal SDL identifier
694
+ # (see SDL4R#validate_identifier).
695
+ #
669
696
  def name=(a_name)
670
697
  a_name = a_name.to_s
671
698
  SDL4R.validate_identifier(a_name)
@@ -675,8 +702,8 @@ module SDL4R
675
702
  # The namespace to set. +nil+ will be coerced to the empty string.
676
703
  #
677
704
  # Raises +ArgumentError+ if the namespace is non-blank and is not
678
- # a legal SDL identifier (see {@link SDL4R#validate_identifier(String)})
679
-
705
+ # a legal SDL identifier (see SDL4R#validate_identifier)
706
+ #
680
707
  def namespace=(a_namespace)
681
708
  a_namespace = a_namespace.to_s
682
709
  SDL4R.validate_identifier(a_namespace) unless a_namespace.empty?
@@ -725,7 +752,7 @@ module SDL4R
725
752
  # Write this tag out to the given IO or StringIO or String (optionally clipping the root.)
726
753
  # Returns +output+.
727
754
  #
728
- # +output+:: an IO or StringIO or a String to write to
755
+ # _output_:: an IO or StringIO or a String to write to
729
756
  # +include_root+:: if true this tag will be written out as the root element, if false only the
730
757
  # children will be written. False by default.
731
758
  #
@@ -756,10 +783,9 @@ module SDL4R
756
783
  output
757
784
  end
758
785
 
759
- #
760
786
  # Get a String representation of this SDL Tag. This method returns a
761
787
  # complete description of the Tag's state using SDL (i.e. the output can
762
- # be parsed by {@link #read(String)})
788
+ # be parsed by #read)
763
789
  #
764
790
  # Returns A string representation of this tag using SDL
765
791
  #
@@ -767,8 +793,7 @@ module SDL4R
767
793
  to_string
768
794
  end
769
795
 
770
- #
771
- # +linePrefix+:: A prefix to insert before every line.
796
+ # _linePrefix_:: A prefix to insert before every line.
772
797
  # Returns A string representation of this tag using SDL
773
798
  #
774
799
  # TODO: break up long lines using the backslash
@@ -825,8 +850,8 @@ module SDL4R
825
850
 
826
851
  # Returns a string representation of the children tags.
827
852
  #
828
- # +linePrefix+:: A prefix to insert before every line.
829
- # +s+:: a String that receives the string representation
853
+ # _linePrefix_:: A prefix to insert before every line.
854
+ # _s_:: a String that receives the string representation
830
855
  #
831
856
  # TODO: break up long lines using the backslash
832
857
  #
@@ -857,19 +882,45 @@ module SDL4R
857
882
 
858
883
  # Returns a string containing an XML representation of this tag. Values
859
884
  # will be represented using _val0, _val1, etc.
860
- #
861
- # +line_prefix+:: A prefix to insert before every line.
862
- # +uri_by_namespace+:: a Hash giving the URIs for the namespaces. Nil to ignore this.
863
885
  #
864
- def to_xml_string(line_prefix = "", uri_by_namespace = nil)
865
- line_prefix ||= ""
866
-
886
+ # _options_:: a hash of the options
887
+ #
888
+ # === options:
889
+ #
890
+ # [:line_prefix] a text prefixing each line (default: "")
891
+ # [:uri_by_namespace] a Hash giving the URIs for the namespaces
892
+ # [:indent] text specifying one indentation (default: "\t")
893
+ # [:eol] end of line expression (default: "\n")
894
+ # [:omit_null_attributes]
895
+ # if true, null/nil attributes are not exported (default: false). Otherwise, they are exported
896
+ # as follows:
897
+ # tag attr="null"
898
+ #
899
+ def to_xml_string(options = {})
900
+ options = {
901
+ :uri_by_namespace => nil,
902
+ :indent => "\t",
903
+ :line_prefix => "",
904
+ :eol => "\n",
905
+ :omit_null_attributes => false
906
+ }.merge(options)
907
+ _to_xml_string(options[:line_prefix], options)
908
+ end
909
+
910
+ protected
911
+
912
+ # Implementation of #to_xml_string but without the extra-treatment on parameters for default
913
+ # values.
914
+ def _to_xml_string(line_prefix, options)
915
+ eol = options[:eol]
916
+
867
917
  s = ""
868
918
  s << line_prefix << ?<
869
919
  s << "#{namespace}:" unless namespace.empty?
870
920
  s << name
871
921
 
872
922
  # output namespace declarations
923
+ uri_by_namespace = options[:uri_by_namespace]
873
924
  if uri_by_namespace
874
925
  uri_by_namespace.each_pair do |namespace, uri|
875
926
  if namespace
@@ -891,19 +942,22 @@ module SDL4R
891
942
 
892
943
  # output attributes
893
944
  if has_attribute?
945
+ omit_null_attributes = options[:omit_null_attributes]
894
946
  attributes do |attribute_namespace, attribute_name, attribute_value|
895
- s << " "
896
- s << "#{attribute_namespace}:" unless attribute_namespace.empty?
897
- s << attribute_name << "=\"" << SDL4R.format(attribute_value, false) << ?"
947
+ unless omit_null_attributes and attribute_value.nil?
948
+ s << " "
949
+ s << "#{attribute_namespace}:" unless attribute_namespace.empty?
950
+ s << attribute_name << "=\"" << SDL4R.format(attribute_value, false) << ?"
951
+ end
898
952
  end
899
953
  end
900
954
 
901
955
  if @children.empty?
902
956
  s << "/>"
903
957
  else
904
- s << ">\n"
958
+ s << ">" << eol
905
959
  @children.each do |child|
906
- s << child.to_xml_string(line_prefix + " ") << ?\n
960
+ s << child._to_xml_string(line_prefix + options[:indent], options) << eol
907
961
  end
908
962
 
909
963
  s << line_prefix << "</"
@@ -86,12 +86,12 @@ module SDL4R
86
86
  assert_equal("d`e`f", values[1], "values[1]")
87
87
  assert_equal("g\"h\"i", values[2], "values[2]")
88
88
  assert_equal("j\\k+l", values[3], "values[3]")
89
- assert_equal("m\\\nn\\\n \t o\n", values[4], "values[4]")
89
+ assert_equal("m\\\nn\\\n \t o\r", values[4], "values[4]")
90
90
  end
91
91
 
92
92
  def test_tag_with_base64_values
93
93
  tag1 = parse_one_tag1(
94
- <<EOF
94
+ <<EOS
95
95
  tag1 [V2VsY29tZSB0byB0aGUgY3J1ZWwgd29ybGQu] [
96
96
  SG9wZSB5
97
97
  b3UnbGwg
@@ -99,7 +99,7 @@ tag1 [V2VsY29tZSB0byB0aGUgY3J1ZWwgd29ybGQu] [
99
99
  b3VyIHdh
100
100
  eS4=
101
101
  ]
102
- EOF
102
+ EOS
103
103
  )
104
104
  assert_not_nil(tag1, "tag1")
105
105
  values = tag1.values
@@ -117,6 +117,11 @@ EOF
117
117
  attributes = tag1.attributes
118
118
  assert_equal(1, attributes.size, "attribute count")
119
119
  assert_equal(99, attributes["attr1"], "attr1")
120
+
121
+ # check the parsing with line continuations
122
+ tag1 = parse_one_tag1("tag1\\\nattr1=\\\n99")
123
+ assert_not_nil(tag1, "tag1")
124
+ assert_equal(99, tag1.attribute("attr1"), "attr1")
120
125
  end
121
126
 
122
127
  def test_tag_with_attributes
@@ -208,6 +213,16 @@ EOF
208
213
  assert_equal(BigDecimal("171.8"), values[9])
209
214
  assert_equal(BigDecimal("1.920"), values[10])
210
215
  assert_equal(BigDecimal("12345678901234567890"), values[11])
216
+
217
+ assert_raise SdlParseError do
218
+ parse_one_tag1("tag1 123.2.2")
219
+ end
220
+ assert_raise SdlParseError do
221
+ parse_one_tag1("tag1 123.2e")
222
+ end
223
+ assert_raise SdlParseError do
224
+ parse_one_tag1("tag1 +-123.2")
225
+ end
211
226
  end
212
227
 
213
228
  def test_booleans
@@ -230,7 +245,7 @@ EOF
230
245
  def test_comments
231
246
  root = Tag.new("root")
232
247
  root.read(
233
- <<EOF
248
+ <<EOS
234
249
  tag1 123
235
250
  #tag2 456
236
251
  tag3 789
@@ -241,7 +256,7 @@ tag7 901
241
256
  /*tag8 234
242
257
  tag9 567*/
243
258
  tag10 890
244
- EOF
259
+ EOS
245
260
  )
246
261
  children = root.children
247
262
  assert_equal(5, children.size, "children count")
@@ -259,7 +274,7 @@ EOF
259
274
 
260
275
  def test_double_quote_strings
261
276
  root = SDL4R::read(
262
- <<EOF
277
+ <<EOS
263
278
  tag1 "cheese and cherry jam"
264
279
  tag2 "cheese and \\
265
280
  cherry jam"
@@ -271,7 +286,7 @@ tag5 "Even my dog wouldn't\\thave\\tit!"
271
286
  tag6 "\\"\\t\\r\\n\\\\"
272
287
  tag7 equation="is not x=y*z" color="blue \\
273
288
  and yellow"
274
- EOF
289
+ EOS
275
290
  )
276
291
 
277
292
  assert_equal "cheese and cherry jam", root.child("tag1").value, "double-quote string"
@@ -288,8 +303,32 @@ EOF
288
303
  assert_equal "blue and yellow", root.child("tag7").attribute("color")
289
304
  end
290
305
 
306
+ def test_characters
307
+ root = SDL4R::read "tag1 ' ' 'a' '\\\\' '\\t' '\\n' '\\r'"
308
+ assert_equal [" ", "a", "\\", "\t", "\n", "\r"], root.child("tag1").values
309
+
310
+ assert_raise SdlParseError do
311
+ SDL4R::read "tag1 '"
312
+ end
313
+ assert_raise SdlParseError do
314
+ SDL4R::read "tag1 'a"
315
+ end
316
+ assert_raise SdlParseError do
317
+ SDL4R::read "tag1 'abc'"
318
+ end
319
+ assert_raise SdlParseError do
320
+ SDL4R::read "tag1 ''"
321
+ end
322
+ assert_raise SdlParseError do
323
+ SDL4R::read "tag1 '\\'"
324
+ end
325
+ assert_raise SdlParseError do
326
+ SDL4R::read "tag1 '\\"
327
+ end
328
+ end
329
+
291
330
  def test_backquote_strings
292
- root = SDL4R::read <<EOF
331
+ root = SDL4R::read <<EOS
293
332
  winfile `c:\\directory\\myfile.xls`
294
333
  talk `I said "something"`
295
334
  xml `
@@ -298,7 +337,7 @@ xml `
298
337
  </product>
299
338
  `
300
339
  regex `\\w+\\.suite\\(\\)`
301
- EOF
340
+ EOS
302
341
 
303
342
  assert_equal "c:\\directory\\myfile.xls", root.child("winfile").value
304
343
  assert_equal 'I said "something"', root.child("talk").value
@@ -308,7 +347,7 @@ EOF
308
347
  end
309
348
 
310
349
  def test_sub_tags
311
- root = SDL4R::read <<EOF
350
+ root = SDL4R::read <<EOS
312
351
  wax {
313
352
  }
314
353
  steack {
@@ -322,7 +361,7 @@ steack {
322
361
  }
323
362
  }
324
363
  peanut.butter
325
- EOF
364
+ EOS
326
365
 
327
366
  expected = Tag.new("root") do
328
367
  new_child("wax")
@@ -341,8 +380,67 @@ EOF
341
380
  assert_equal expected, root
342
381
  end
343
382
 
383
+ def test_parse_error
384
+ # WARNING: the line and col of an error is not accurate science. The goal here is to point to
385
+ # coordinates that are useful to the user.
386
+ # Exampe for a string litteral that spans over several line, some errors could be point to
387
+ # the start or to the end without too much ambiguity.
388
+ # Consequently, feel free to change the coordinates, if a change in the implementation
389
+ # modifies the x/y of the error and they still make sense.
390
+ assert_error_xy "=", 1, 1
391
+ assert_error_xy "tag1 xyz", 1, 6
392
+ assert_error_xy "tag1 \\\nxyz", 2, 1
393
+ assert_error_xy "tag1 \\\n xyz", 2, 4
394
+
395
+ source = <<EOS
396
+ -- my comment
397
+ =
398
+ EOS
399
+ assert_error_xy source, 2, 1
400
+
401
+ source = <<EOS
402
+ murder_plot 123 \\
403
+ weight=456 \\
404
+ * \\
405
+ length=789
406
+ EOS
407
+ assert_error_xy source, 3, 6
408
+
409
+ assert_error_xy 'tag1 "text\\"', 1, 13
410
+
411
+ source = <<EOS
412
+ murder_plot "abcd\\
413
+ efghij\\
414
+ kl\\
415
+ mnopq
416
+ EOS
417
+ assert_error_xy source, 4, 13
418
+ end
419
+
344
420
  private
345
421
 
422
+ def assert_error_xy source, expected_line, expected_pos
423
+ error = get_parse_error_or_fail source
424
+ begin
425
+ assert_equal expected_line, error.line, "line"
426
+ assert_equal expected_pos, error.position, "position"
427
+ rescue
428
+ puts "Expected error: #{error}"
429
+ puts error.backtrace
430
+ raise $!
431
+ end
432
+ end
433
+
434
+ def get_parse_error_or_fail source
435
+ begin
436
+ SDL4R::read source
437
+
438
+ rescue
439
+ return $! if $!.is_a? SdlParseError
440
+ raise AssertionFailedError, "was expecting a SdlParseError"
441
+ end
442
+ end
443
+
346
444
  # Creates and returns a DateTime where an unspecified +zone_offset+ means 'the local zone
347
445
  # offset' (contrarily to DateTime#civil())
348
446
  def local_civil_date(year, month, day, hour = 0, min = 0, sec = 0, zone_offset = nil)