diff options
-rw-r--r-- | ext/psych/parser.c | 23 | ||||
-rw-r--r-- | lib/psych/coder.rb | 24 | ||||
-rw-r--r-- | lib/psych/json/tree_builder.rb | 2 | ||||
-rw-r--r-- | lib/psych/nodes/node.rb | 3 | ||||
-rw-r--r-- | lib/psych/parser.rb | 3 | ||||
-rw-r--r-- | lib/psych/visitors.rb | 1 | ||||
-rw-r--r-- | lib/psych/visitors/depth_first.rb | 8 | ||||
-rw-r--r-- | lib/psych/visitors/json_tree.rb | 11 | ||||
-rw-r--r-- | lib/psych/visitors/visitor.rb | 26 | ||||
-rw-r--r-- | lib/psych/visitors/yaml_tree.rb | 19 | ||||
-rw-r--r-- | test/psych/test_coder.rb | 15 | ||||
-rw-r--r-- | test/psych/test_json_tree.rb | 18 | ||||
-rw-r--r-- | test/psych/test_nil.rb | 18 | ||||
-rw-r--r-- | test/psych/test_parser.rb | 60 | ||||
-rw-r--r-- | test/psych/visitors/test_depth_first.rb | 23 |
15 files changed, 213 insertions, 41 deletions
diff --git a/ext/psych/parser.c b/ext/psych/parser.c index c8b92a0..071b8c0 100644 --- a/ext/psych/parser.c +++ b/ext/psych/parser.c @@ -307,6 +307,28 @@ static VALUE set_external_encoding(VALUE self, VALUE encoding) return encoding; } +/* + * call-seq: + * parser.mark # => #<Psych::Parser::Mark> + * + * Returns a Psych::Parser::Mark object that contains line, column, and index + * information. + */ +static VALUE mark(VALUE self) +{ + VALUE mark_klass; + VALUE args[3]; + yaml_parser_t * parser; + + Data_Get_Struct(self, yaml_parser_t, parser); + mark_klass = rb_const_get_at(cPsychParser, rb_intern("Mark")); + args[0] = INT2NUM(parser->mark.index); + args[1] = INT2NUM(parser->mark.line); + args[2] = INT2NUM(parser->mark.column); + + return rb_class_new_instance(3, args, mark_klass); +} + void Init_psych_parser() { #if 0 @@ -331,6 +353,7 @@ void Init_psych_parser() ePsychSyntaxError = rb_define_class_under(mPsych, "SyntaxError", rb_eSyntaxError); rb_define_method(cPsychParser, "parse", parse, 1); + rb_define_method(cPsychParser, "mark", mark, 0); rb_define_method(cPsychParser, "external_encoding=", set_external_encoding, 1); id_read = rb_intern("read"); diff --git a/lib/psych/coder.rb b/lib/psych/coder.rb index c06c9c1..2b830d2 100644 --- a/lib/psych/coder.rb +++ b/lib/psych/coder.rb @@ -6,17 +6,18 @@ module Psych # objects like Sequence and Scalar may be emitted if +seq=+ or +scalar=+ are # called, respectively. class Coder - attr_accessor :tag, :style, :implicit + attr_accessor :tag, :style, :implicit, :object attr_reader :type, :seq def initialize tag - @map = {} - @seq = [] - @implicit = false - @type = :map - @tag = tag - @style = Psych::Nodes::Mapping::BLOCK - @scalar = nil + @map = {} + @seq = [] + @implicit = false + @type = :map + @tag = tag + @style = Psych::Nodes::Mapping::BLOCK + @scalar = nil + @object = nil end def scalar *args @@ -54,6 +55,13 @@ module Psych self.map = map end + # Emit an arbitrary object +obj+ and +tag+ + def represent_object tag, obj + @tag = tag + @type = :object + @object = obj + end + # Emit a scalar with +value+ def scalar= value @type = :scalar diff --git a/lib/psych/json/tree_builder.rb b/lib/psych/json/tree_builder.rb index 720ede7..26fcb11 100644 --- a/lib/psych/json/tree_builder.rb +++ b/lib/psych/json/tree_builder.rb @@ -13,7 +13,7 @@ module Psych end def start_mapping anchor, tag, implicit, style - super(anchor, tag, implicit, Nodes::Mapping::FLOW) + super(anchor, nil, implicit, Nodes::Mapping::FLOW) end def start_sequence anchor, tag, implicit, style diff --git a/lib/psych/nodes/node.rb b/lib/psych/nodes/node.rb index bfd91f9..35de224 100644 --- a/lib/psych/nodes/node.rb +++ b/lib/psych/nodes/node.rb @@ -17,6 +17,9 @@ module Psych @children = [] end + def each + end + ### # Convert this node to Ruby. # diff --git a/lib/psych/parser.rb b/lib/psych/parser.rb index 0e38a4a..5d75605 100644 --- a/lib/psych/parser.rb +++ b/lib/psych/parser.rb @@ -30,6 +30,9 @@ module Psych # construct an AST of the parsed YAML document. class Parser + class Mark < Struct.new(:index, :line, :column) + end + # The handler on which events will be called attr_accessor :handler diff --git a/lib/psych/visitors.rb b/lib/psych/visitors.rb index 10ac4ce..cc98b10 100644 --- a/lib/psych/visitors.rb +++ b/lib/psych/visitors.rb @@ -3,3 +3,4 @@ require 'psych/visitors/to_ruby' require 'psych/visitors/emitter' require 'psych/visitors/yaml_tree' require 'psych/visitors/json_tree' +require 'psych/visitors/depth_first' diff --git a/lib/psych/visitors/depth_first.rb b/lib/psych/visitors/depth_first.rb new file mode 100644 index 0000000..f202d4d --- /dev/null +++ b/lib/psych/visitors/depth_first.rb @@ -0,0 +1,8 @@ +module Psych + module Visitors + class DepthFirst < Psych::Visitors::Visitor + private + + end + end +end diff --git a/lib/psych/visitors/json_tree.rb b/lib/psych/visitors/json_tree.rb index 3502cdb..dd06e80 100644 --- a/lib/psych/visitors/json_tree.rb +++ b/lib/psych/visitors/json_tree.rb @@ -5,8 +5,17 @@ module Psych super end + def visit_Time o + formatted = format_time o + @emitter.scalar formatted, nil, nil, false, true, Nodes::Scalar::DOUBLE_QUOTED + end + + def visit_DateTime o + visit_Time o.to_time + end + def visit_String o - @emitter.scalar o.to_s, nil, nil, false, true, Nodes::Scalar::ANY + @emitter.scalar o.to_s, nil, nil, false, true, Nodes::Scalar::DOUBLE_QUOTED end alias :visit_Symbol :visit_String end diff --git a/lib/psych/visitors/visitor.rb b/lib/psych/visitors/visitor.rb index 3471c43..4d7772f 100644 --- a/lib/psych/visitors/visitor.rb +++ b/lib/psych/visitors/visitor.rb @@ -1,26 +1,18 @@ module Psych module Visitors class Visitor - attr_reader :started, :finished - alias :finished? :finished - alias :started? :started + def accept target + visit target + end - def initialize - @started = false - @finished = false + private + + DISPATCH = Hash.new do |hash, klass| + hash[klass] = "visit_#{klass.name.gsub('::', '_')}" end - def accept target - case target - when Psych::Nodes::Scalar then visit_Psych_Nodes_Scalar target - when Psych::Nodes::Mapping then visit_Psych_Nodes_Mapping target - when Psych::Nodes::Sequence then visit_Psych_Nodes_Sequence target - when Psych::Nodes::Alias then visit_Psych_Nodes_Alias target - when Psych::Nodes::Document then visit_Psych_Nodes_Document target - when Psych::Nodes::Stream then visit_Psych_Nodes_Stream target - else - raise "Can't handle #{target}" - end + def visit target + send DISPATCH[target.class], target end end end diff --git a/lib/psych/visitors/yaml_tree.rb b/lib/psych/visitors/yaml_tree.rb index 620b520..e7d182f 100644 --- a/lib/psych/visitors/yaml_tree.rb +++ b/lib/psych/visitors/yaml_tree.rb @@ -8,8 +8,14 @@ module Psych # builder.tree # => #<Psych::Nodes::Stream .. } # class YAMLTree < Psych::Visitors::Visitor + attr_reader :started, :finished + alias :finished? :finished + alias :started? :started + def initialize options = {}, emitter = Psych::TreeBuilder.new super() + @started = false + @finished = false @emitter = emitter @st = {} @ss = ScalarScanner.new @@ -205,7 +211,7 @@ module Psych quote = false style = Nodes::Scalar::ANY - if o.index("\x00") || o.count("^ -~\t\r\n").fdiv(o.length) > 0.3 + if o.index("\x00") || o.count("\x00-\x7F", "^ -~\t\r\n").fdiv(o.length) > 0.3 str = [o].pack('m').chomp tag = '!binary' # FIXME: change to below when syck is removed #tag = 'tag:yaml.org,2002:binary' @@ -273,7 +279,7 @@ module Psych end def visit_NilClass o - @emitter.scalar('', nil, 'tag:yaml.org,2002:null', false, false, Nodes::Scalar::ANY) + @emitter.scalar('', nil, 'tag:yaml.org,2002:null', true, false, Nodes::Scalar::ANY) end def visit_Symbol o @@ -282,14 +288,11 @@ module Psych private def format_time time - formatted = time.strftime("%Y-%m-%d %H:%M:%S.%9N") if time.utc? - formatted += "Z" + time.strftime("%Y-%m-%d %H:%M:%S.%9NZ") else - zone = time.strftime('%z') - formatted += " #{zone[0,3]}:#{zone[3,5]}" + time.strftime("%Y-%m-%d %H:%M:%S.%9N %:z") end - formatted end # FIXME: remove this method once "to_yaml_properties" is removed @@ -339,6 +342,8 @@ module Psych accept v end @emitter.end_mapping + when :object + accept c.object end end diff --git a/test/psych/test_coder.rb b/test/psych/test_coder.rb index 0fa01ca..4809b13 100644 --- a/test/psych/test_coder.rb +++ b/test/psych/test_coder.rb @@ -89,6 +89,21 @@ module Psych end end + class RepresentWithObject + def encode_with coder + coder.represent_object self.class.name, 20 + end + end + + def test_represent_with_object + thing = Psych.load(Psych.dump(RepresentWithObject.new)) + assert_equal 20, thing + end + + def test_json_dump_exclude_tag + refute_match('TestCoder::InitApi', Psych.to_json(InitApi.new)) + end + def test_map_takes_block coder = Psych::Coder.new 'foo' tag = coder.tag diff --git a/test/psych/test_json_tree.rb b/test/psych/test_json_tree.rb index 84bd36c..5fcb0d6 100644 --- a/test/psych/test_json_tree.rb +++ b/test/psych/test_json_tree.rb @@ -3,11 +3,11 @@ require_relative 'helper' module Psych class TestJSONTree < TestCase def test_string - assert_match(/(['"])foo\1/, Psych.to_json("foo")) + assert_match(/"foo"/, Psych.to_json("foo")) end def test_symbol - assert_match(/(['"])foo\1/, Psych.to_json(:foo)) + assert_match(/"foo"/, Psych.to_json(:foo)) end def test_nil @@ -36,8 +36,18 @@ module Psych json = Psych.to_json(list) assert_match(/]$/, json) assert_match(/^\[/, json) - assert_match(/['"]one['"]/, json) - assert_match(/['"]two['"]/, json) + assert_match(/"one"/, json) + assert_match(/"two"/, json) + 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 }) + end + + def test_datetime + time = Time.new(2010, 10, 10).to_datetime + assert_equal "{\"a\": \"#{time.strftime("%Y-%m-%d %H:%M:%S.%9N %:z")}\"}\n", Psych.to_json({'a' => time }) end end end diff --git a/test/psych/test_nil.rb b/test/psych/test_nil.rb new file mode 100644 index 0000000..e63c6c7 --- /dev/null +++ b/test/psych/test_nil.rb @@ -0,0 +1,18 @@ +require_relative 'helper' + +module Psych + class TestNil < TestCase + def test_nil + yml = Psych.dump nil + assert_equal "--- \n...\n", yml + assert_equal nil, Psych.load(yml) + end + + def test_array_nil + yml = Psych.dump [nil] + assert_equal "---\n- \n", yml + assert_equal [nil], Psych.load(yml) + end + + end +end diff --git a/test/psych/test_parser.rb b/test/psych/test_parser.rb index 0b1e92e..cd22914 100644 --- a/test/psych/test_parser.rb +++ b/test/psych/test_parser.rb @@ -5,9 +5,12 @@ require_relative 'helper' module Psych class TestParser < TestCase class EventCatcher < Handler - attr_reader :calls + attr_accessor :parser + attr_reader :calls, :marks def initialize - @calls = [] + @parser = nil + @calls = [] + @marks = [] end (Handler.instance_methods(true) - @@ -15,6 +18,7 @@ module Psych class_eval %{ def #{m} *args super + @marks << @parser.mark if @parser @calls << [:#{m}, args] end } @@ -23,7 +27,57 @@ module Psych def setup super - @parser = Psych::Parser.new EventCatcher.new + @handler = EventCatcher.new + @parser = Psych::Parser.new @handler + @handler.parser = @parser + end + + def test_line_numbers + assert_equal 0, @parser.mark.line + @parser.parse "---\n- hello\n- world" + line_calls = @handler.marks.map(&:line).zip(@handler.calls.map(&:first)) + assert_equal [[0, :start_stream], + [0, :start_document], + [1, :start_sequence], + [2, :scalar], + [3, :scalar], + [3, :end_sequence], + [3, :end_document], + [3, :end_stream]], line_calls + + assert_equal 3, @parser.mark.line + end + + def test_column_numbers + assert_equal 0, @parser.mark.column + @parser.parse "---\n- hello\n- world" + col_calls = @handler.marks.map(&:column).zip(@handler.calls.map(&:first)) + assert_equal [[0, :start_stream], + [3, :start_document], + [1, :start_sequence], + [0, :scalar], + [0, :scalar], + [0, :end_sequence], + [0, :end_document], + [0, :end_stream]], col_calls + + assert_equal 0, @parser.mark.column + end + + def test_index_numbers + assert_equal 0, @parser.mark.index + @parser.parse "---\n- hello\n- world" + idx_calls = @handler.marks.map(&:index).zip(@handler.calls.map(&:first)) + assert_equal [[0, :start_stream], + [3, :start_document], + [5, :start_sequence], + [12, :scalar], + [19, :scalar], + [19, :end_sequence], + [19, :end_document], + [19, :end_stream]], idx_calls + + assert_equal 19, @parser.mark.index end def test_set_encoding_twice diff --git a/test/psych/visitors/test_depth_first.rb b/test/psych/visitors/test_depth_first.rb new file mode 100644 index 0000000..a783960 --- /dev/null +++ b/test/psych/visitors/test_depth_first.rb @@ -0,0 +1,23 @@ +require_relative '../helper' + +module Psych + module Visitors + class TestDepthFirst < TestCase + 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' + + assert_equal 3, collector.calls.length + end + end + end +end |