diff options
author | Aaron Patterson <aaron.patterson@gmail.com> | 2011-02-06 17:13:27 -0800 |
---|---|---|
committer | Aaron Patterson <aaron.patterson@gmail.com> | 2011-02-06 17:13:27 -0800 |
commit | 8c60e9fa2a5e4932d8762469000b232d57c696b5 (patch) | |
tree | 29ba5ec4de61ecb966f68e11146de6cb6ca266f3 | |
parent | e008a513f5e33031c5e9341cb4da09a225693840 (diff) | |
download | psych-8c60e9fa2a5e4932d8762469000b232d57c696b5.tar.gz |
merging from ruby
-rw-r--r-- | ext/psych/parser.c | 14 | ||||
-rw-r--r-- | lib/psych/nodes/node.rb | 9 | ||||
-rw-r--r-- | lib/psych/visitors/depth_first.rb | 18 | ||||
-rw-r--r-- | lib/psych/visitors/to_ruby.rb | 20 | ||||
-rw-r--r-- | test/psych/nodes/test_enumerable.rb | 43 | ||||
-rw-r--r-- | test/psych/test_json_tree.rb | 5 | ||||
-rw-r--r-- | test/psych/test_merge_keys.rb | 72 | ||||
-rw-r--r-- | test/psych/test_parser.rb | 27 | ||||
-rw-r--r-- | test/psych/test_yaml.rb | 5 | ||||
-rw-r--r-- | test/psych/visitors/test_depth_first.rb | 48 |
10 files changed, 238 insertions, 23 deletions
diff --git a/ext/psych/parser.c b/ext/psych/parser.c index 071b8c0..7bfdf4a 100644 --- a/ext/psych/parser.c +++ b/ext/psych/parser.c @@ -4,6 +4,7 @@ VALUE cPsychParser; VALUE ePsychSyntaxError; static ID id_read; +static ID id_path; static ID id_empty; static ID id_start_stream; static ID id_end_stream; @@ -93,10 +94,20 @@ static VALUE parse(VALUE self, VALUE yaml) while(!done) { if(!yaml_parser_parse(parser, &event)) { + VALUE path; size_t line = parser->mark.line; size_t column = parser->mark.column; - rb_raise(ePsychSyntaxError, "couldn't parse YAML at line %d column %d", + if(rb_respond_to(yaml, id_path)) + path = rb_funcall(yaml, id_path, 0); + else + path = rb_str_new2("<unknown>"); + + yaml_parser_delete(parser); + yaml_parser_initialize(parser); + + rb_raise(ePsychSyntaxError, "(%s): couldn't parse YAML at line %d column %d", + StringValuePtr(path), (int)line, (int)column); } @@ -357,6 +368,7 @@ void Init_psych_parser() rb_define_method(cPsychParser, "external_encoding=", set_external_encoding, 1); id_read = rb_intern("read"); + id_path = rb_intern("path"); id_empty = rb_intern("empty"); id_start_stream = rb_intern("start_stream"); id_end_stream = rb_intern("end_stream"); diff --git a/lib/psych/nodes/node.rb b/lib/psych/nodes/node.rb index 35de224..7c040ec 100644 --- a/lib/psych/nodes/node.rb +++ b/lib/psych/nodes/node.rb @@ -6,6 +6,8 @@ module Psych # The base class for any Node in a YAML parse tree. This class should # never be instantiated. class Node + include Enumerable + # The children of this node attr_reader :children @@ -17,7 +19,12 @@ module Psych @children = [] end - def each + ### + # Iterate over each node in the tree. Yields each node to +block+ depth + # first. + def each &block + return enum_for :each unless block_given? + Visitors::DepthFirst.new(block).accept self end ### diff --git a/lib/psych/visitors/depth_first.rb b/lib/psych/visitors/depth_first.rb index f202d4d..c6eb814 100644 --- a/lib/psych/visitors/depth_first.rb +++ b/lib/psych/visitors/depth_first.rb @@ -1,8 +1,26 @@ module Psych module Visitors class DepthFirst < Psych::Visitors::Visitor + def initialize block + @block = block + end + private + def nary o + o.children.each { |x| visit x } + @block.call o + end + alias :visit_Psych_Nodes_Stream :nary + alias :visit_Psych_Nodes_Document :nary + alias :visit_Psych_Nodes_Sequence :nary + alias :visit_Psych_Nodes_Mapping :nary + + def terminal o + @block.call o + end + alias :visit_Psych_Nodes_Scalar :terminal + alias :visit_Psych_Nodes_Alias :terminal end end end diff --git a/lib/psych/visitors/to_ruby.rb b/lib/psych/visitors/to_ruby.rb index 745338a..f8b1585 100644 --- a/lib/psych/visitors/to_ruby.rb +++ b/lib/psych/visitors/to_ruby.rb @@ -60,7 +60,7 @@ module Psych when "tag:yaml.org,2002:float", "!float" Float(@ss.tokenize(o.value)) when "!ruby/regexp" - o.value =~ /^\/(.*)\/([mix]*)$/ + o.value =~ /^\/(.*)\/([mixn]*)$/ source = $1 options = 0 lang = nil @@ -69,6 +69,7 @@ module Psych when 'x' then options |= Regexp::EXTENDED when 'i' then options |= Regexp::IGNORECASE when 'm' then options |= Regexp::MULTILINE + when 'n' then options |= Regexp::NOENCODING else lang = option end end @@ -187,14 +188,17 @@ module Psych o.children.each_slice(2) { |k,v| key = accept(k) - if key == '<<' && Nodes::Alias === v - # FIXME: remove this when "<<" syntax is deprecated - if $VERBOSE - where = caller.find { |x| x !~ /psych/ } - warn where - warn "\"<<: *#{v.anchor}\" is no longer supported, please switch to \"*#{v.anchor}\"" + if key == '<<' + case v + when Nodes::Alias + hash.merge! accept(v) + when Nodes::Sequence + accept(v).reverse_each do |value| + hash.merge! value + end + else + hash[key] = accept(v) end - return accept(v) else hash[key] = accept(v) end diff --git a/test/psych/nodes/test_enumerable.rb b/test/psych/nodes/test_enumerable.rb new file mode 100644 index 0000000..57d4e89 --- /dev/null +++ b/test/psych/nodes/test_enumerable.rb @@ -0,0 +1,43 @@ +require_relative '../helper' + +module Psych + module Nodes + class TestEnumerable < TestCase + def test_includes_enumerable + yaml = '--- hello' + assert_equal 3, Psych.parse_stream(yaml).to_a.length + end + + def test_returns_enumerator + yaml = '--- hello' + assert_equal 3, Psych.parse_stream(yaml).each.map { |x| x }.length + end + + def test_scalar + assert_equal 3, calls('--- hello').length + end + + def test_sequence + assert_equal 4, calls("---\n- hello").length + end + + def test_mapping + assert_equal 5, calls("---\nhello: world").length + end + + def test_alias + assert_equal 5, calls("--- &yay\n- foo\n- *yay\n").length + end + + private + + def calls yaml + calls = [] + Psych.parse_stream(yaml).each do |node| + calls << node + end + calls + end + end + end +end diff --git a/test/psych/test_json_tree.rb b/test/psych/test_json_tree.rb index 5fcb0d6..423a528 100644 --- a/test/psych/test_json_tree.rb +++ b/test/psych/test_json_tree.rb @@ -41,8 +41,9 @@ module Psych end def test_time - time = Time.new(2010, 10, 10).utc - assert_equal "{\"a\": \"2010-10-10 07:00:00.000000000Z\"}\n", Psych.to_json({'a' => time }) + time = Time.utc(2010, 10, 10) + assert_equal "{\"a\": \"2010-10-10 00:00:00.000000000Z\"}\n", +Psych.to_json({'a' => time }) end def test_datetime diff --git a/test/psych/test_merge_keys.rb b/test/psych/test_merge_keys.rb new file mode 100644 index 0000000..fef8892 --- /dev/null +++ b/test/psych/test_merge_keys.rb @@ -0,0 +1,72 @@ +require_relative 'helper' + +module Psych + class TestMergeKeys < TestCase + # [ruby-core:34679] + def test_merge_key + yaml = <<-eoyml +foo: &foo + hello: world +bar: + << : *foo + baz: boo + eoyml + + hash = { + "foo" => { "hello" => "world"}, + "bar" => { "hello" => "world", "baz" => "boo" } } + assert_equal hash, Psych.load(yaml) + end + + def test_multiple_maps + yaml = <<-eoyaml +--- +- &CENTER { x: 1, y: 2 } +- &LEFT { x: 0, y: 2 } +- &BIG { r: 10 } +- &SMALL { r: 1 } + +# All the following maps are equal: + +- # Merge multiple maps + << : [ *CENTER, *BIG ] + label: center/big + eoyaml + + hash = { + 'x' => 1, + 'y' => 2, + 'r' => 10, + 'label' => 'center/big' + } + + assert_equal hash, Psych.load(yaml)[4] + end + + def test_override + yaml = <<-eoyaml +--- +- &CENTER { x: 1, y: 2 } +- &LEFT { x: 0, y: 2 } +- &BIG { r: 10 } +- &SMALL { r: 1 } + +# All the following maps are equal: + +- # Override + << : [ *BIG, *LEFT, *SMALL ] + x: 1 + label: center/big + eoyaml + + hash = { + 'x' => 1, + 'y' => 2, + 'r' => 10, + 'label' => 'center/big' + } + + assert_equal hash, Psych.load(yaml)[4] + end + end +end diff --git a/test/psych/test_parser.rb b/test/psych/test_parser.rb index cd22914..a60a0c6 100644 --- a/test/psych/test_parser.rb +++ b/test/psych/test_parser.rb @@ -128,6 +128,33 @@ module Psych end end + def test_syntax_error_twice + assert_raises(Psych::SyntaxError) do + @parser.parse("---\n\"foo\"\n\"bar\"\n") + end + + assert_raises(Psych::SyntaxError) do + @parser.parse("---\n\"foo\"\n\"bar\"\n") + end + end + + def test_syntax_error_has_path_for_string + e = assert_raises(Psych::SyntaxError) do + @parser.parse("---\n\"foo\"\n\"bar\"\n") + end + assert_match '(<unknown>):', e.message + end + + def test_syntax_error_has_path_for_io + io = StringIO.new "---\n\"foo\"\n\"bar\"\n" + def io.path; "hello!"; end + + e = assert_raises(Psych::SyntaxError) do + @parser.parse(io) + end + assert_match "(#{io.path}):", e.message + end + def test_mapping_end @parser.parse("---\n!!map { key: value }") assert_called :end_mapping diff --git a/test/psych/test_yaml.rb b/test/psych/test_yaml.rb index 45cd39d..20bf26e 100644 --- a/test/psych/test_yaml.rb +++ b/test/psych/test_yaml.rb @@ -13,6 +13,11 @@ class Psych_Unit_Tests < Psych::TestCase def teardown Psych.domain_types.clear end + + # [ruby-core:34969] + def test_regexp_with_n + assert_cycle(Regexp.new('',0,'n')) + end # # Tests modified from 00basic.t in Psych.pm # diff --git a/test/psych/visitors/test_depth_first.rb b/test/psych/visitors/test_depth_first.rb index a783960..a84f5b6 100644 --- a/test/psych/visitors/test_depth_first.rb +++ b/test/psych/visitors/test_depth_first.rb @@ -3,21 +3,47 @@ require_relative '../helper' module Psych module Visitors class TestDepthFirst < TestCase + class Collector < Struct.new(:calls) + def initialize(calls = []) + super + end + + def call obj + calls << obj + end + end + def test_scalar - collector = Class.new(Struct.new(:calls)) { - def initialize(calls = []) - super - end - - def call obj - calls << obj - end - }.new - visitor = Visitors::DepthFirst.new collector - visitor.accept Psych.parse '--- hello' + collector = Collector.new + visitor = Visitors::DepthFirst.new collector + visitor.accept Psych.parse_stream '--- hello' assert_equal 3, collector.calls.length end + + def test_sequence + collector = Collector.new + visitor = Visitors::DepthFirst.new collector + visitor.accept Psych.parse_stream "---\n- hello" + + assert_equal 4, collector.calls.length + end + + def test_mapping + collector = Collector.new + visitor = Visitors::DepthFirst.new collector + visitor.accept Psych.parse_stream "---\nhello: world" + + assert_equal 5, collector.calls.length + end + + def test_alias + collector = Collector.new + visitor = Visitors::DepthFirst.new collector + visitor.accept Psych.parse_stream "--- &yay\n- foo\n- *yay\n" + + assert_equal 5, collector.calls.length + end end end end |