summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ext/psych/parser.c23
-rw-r--r--lib/psych/coder.rb24
-rw-r--r--lib/psych/json/tree_builder.rb2
-rw-r--r--lib/psych/nodes/node.rb3
-rw-r--r--lib/psych/parser.rb3
-rw-r--r--lib/psych/visitors.rb1
-rw-r--r--lib/psych/visitors/depth_first.rb8
-rw-r--r--lib/psych/visitors/json_tree.rb11
-rw-r--r--lib/psych/visitors/visitor.rb26
-rw-r--r--lib/psych/visitors/yaml_tree.rb19
-rw-r--r--test/psych/test_coder.rb15
-rw-r--r--test/psych/test_json_tree.rb18
-rw-r--r--test/psych/test_nil.rb18
-rw-r--r--test/psych/test_parser.rb60
-rw-r--r--test/psych/visitors/test_depth_first.rb23
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