diff options
author | Ary Borenszweig <asterite@gmail.com> | 2017-08-02 11:13:59 -0300 |
---|---|---|
committer | Ary Borenszweig <asterite@gmail.com> | 2017-08-02 13:57:51 -0300 |
commit | 5473c56cb6dc11a11e4bcae4adbe1d7971e79319 (patch) | |
tree | 00434353c13aa10a515a493fccaf9bf37ee3e001 | |
parent | 4039a811248de7b0b9ae9e4a97854f00ba985255 (diff) | |
download | psych-5473c56cb6dc11a11e4bcae4adbe1d7971e79319.zip |
Add Psych::Handler#event_location
This adds a new reported event to Psych::Handler, event_location, with precise start/end line/column information.
The line/column information provided by Psych::Parser#mark is not very useful because it points to the location past the event.
-rw-r--r-- | ext/java/PsychParser.java | 7 | ||||
-rw-r--r-- | ext/psych/psych_parser.c | 49 | ||||
-rw-r--r-- | lib/psych/handler.rb | 5 | ||||
-rw-r--r-- | test/psych/test_parser.rb | 62 |
4 files changed, 105 insertions, 18 deletions
diff --git a/ext/java/PsychParser.java b/ext/java/PsychParser.java index b3e747e..0069819 100644 --- a/ext/java/PsychParser.java +++ b/ext/java/PsychParser.java @@ -190,6 +190,12 @@ public class PsychParser extends RubyObject { while (true) { event = parser.getEvent(); + IRubyObject start_line = runtime.newFixnum(event.getStartMark().getLine()); + IRubyObject start_column = runtime.newFixnum(event.getStartMark().getColumn()); + IRubyObject end_line = runtime.newFixnum(event.getEndMark().getLine()); + IRubyObject end_column = runtime.newFixnum(event.getEndMark().getColumn()); + invoke(context, handler, "event_location", start_line, start_column, end_line, end_column); + // FIXME: Event should expose a getID, so it can be switched if (event.is(ID.StreamStart)) { invoke(context, handler, "start_stream", runtime.newFixnum(YAML_ANY_ENCODING.ordinal())); @@ -277,6 +283,7 @@ public class PsychParser extends RubyObject { private void handleScalar(ThreadContext context, ScalarEvent se, boolean tainted, IRubyObject handler) { Ruby runtime = context.runtime; + IRubyObject anchor = stringOrNilFor(runtime, se.getAnchor(), tainted); IRubyObject tag = stringOrNilFor(runtime, se.getTag(), tainted); IRubyObject plain_implicit = runtime.newBoolean(se.getImplicit().canOmitTagInPlainScalar()); diff --git a/ext/psych/psych_parser.c b/ext/psych/psych_parser.c index 47ed874..4b18c94 100644 --- a/ext/psych/psych_parser.c +++ b/ext/psych/psych_parser.c @@ -16,6 +16,7 @@ static ID id_start_sequence; static ID id_end_sequence; static ID id_start_mapping; static ID id_end_mapping; +static ID id_event_location; #define PSYCH_TRANSCODE(_str, _yaml_enc, _internal_enc) \ do { \ @@ -232,6 +233,12 @@ static VALUE protected_end_stream(VALUE handler) return rb_funcall(handler, id_end_stream, 0); } +static VALUE protected_event_location(VALUE pointer) +{ + VALUE *args = (VALUE *)pointer; + return rb_funcall3(args[0], id_event_location, 4, args + 1); +} + /* * call-seq: * parser.parse(yaml) @@ -295,6 +302,21 @@ static VALUE parse(int argc, VALUE *argv, VALUE self) rb_exc_raise(exception); } + VALUE event_args[5]; + VALUE start_line, start_column, end_line, end_column; + + start_line = INT2NUM((long)event.start_mark.line); + start_column = INT2NUM((long)event.start_mark.column); + end_line = INT2NUM((long)event.end_mark.line); + end_column = INT2NUM((long)event.end_mark.column); + + event_args[0] = handler; + event_args[1] = start_line; + event_args[2] = start_column; + event_args[3] = end_line; + event_args[4] = end_column; + rb_protect(protected_event_location, (VALUE)event_args, &state); + switch(event.type) { case YAML_STREAM_START_EVENT: { @@ -551,18 +573,19 @@ void Init_psych_parser(void) rb_define_method(cPsychParser, "parse", parse, -1); rb_define_method(cPsychParser, "mark", mark, 0); - 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"); - id_start_document = rb_intern("start_document"); - id_end_document = rb_intern("end_document"); - id_alias = rb_intern("alias"); - id_scalar = rb_intern("scalar"); - id_start_sequence = rb_intern("start_sequence"); - id_end_sequence = rb_intern("end_sequence"); - id_start_mapping = rb_intern("start_mapping"); - id_end_mapping = rb_intern("end_mapping"); + 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"); + id_start_document = rb_intern("start_document"); + id_end_document = rb_intern("end_document"); + id_alias = rb_intern("alias"); + id_scalar = rb_intern("scalar"); + id_start_sequence = rb_intern("start_sequence"); + id_end_sequence = rb_intern("end_sequence"); + id_start_mapping = rb_intern("start_mapping"); + id_end_mapping = rb_intern("end_mapping"); + id_event_location = rb_intern("event_location"); } /* vim: set noet sws=4 sw=4: */ diff --git a/lib/psych/handler.rb b/lib/psych/handler.rb index 1074c18..84a3b4f 100644 --- a/lib/psych/handler.rb +++ b/lib/psych/handler.rb @@ -242,6 +242,11 @@ module Psych end ### + # Called before each event with line/column information. + def event_location(start_line, start_column, end_line, end_column) + end + + ### # Is this handler a streaming handler? def streaming? false diff --git a/test/psych/test_parser.rb b/test/psych/test_parser.rb index 6b554ce..e8225da 100644 --- a/test/psych/test_parser.rb +++ b/test/psych/test_parser.rb @@ -87,13 +87,22 @@ module Psych 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], + assert_equal [ + [0, :event_location], + [0, :start_stream], + [0, :event_location], [0, :start_document], + [1, :event_location], [1, :start_sequence], + [2, :event_location], [2, :scalar], + [3, :event_location], [3, :scalar], + [3, :event_location], [3, :end_sequence], + [3, :event_location], [3, :end_document], + [3, :event_location], [3, :end_stream]], line_calls assert_equal 3, @parser.mark.line @@ -103,13 +112,22 @@ module Psych 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], + assert_equal [ + [0, :event_location], + [0, :start_stream], + [3, :event_location], [3, :start_document], + [1, :event_location], [1, :start_sequence], + [0, :event_location], [0, :scalar], + [0, :event_location], [0, :scalar], + [0, :event_location], [0, :end_sequence], + [0, :event_location], [0, :end_document], + [0, :event_location], [0, :end_stream]], col_calls assert_equal 0, @parser.mark.column @@ -119,13 +137,22 @@ module Psych 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], + assert_equal [ + [0, :event_location], + [0, :start_stream], + [3, :event_location], [3, :start_document], + [5, :event_location], [5, :start_sequence], + [12, :event_location], [12, :scalar], + [19, :event_location], [19, :scalar], + [19, :event_location], [19, :end_sequence], + [19, :event_location], [19, :end_document], + [19, :event_location], [19, :end_stream]], idx_calls assert_equal 19, @parser.mark.index @@ -137,7 +164,7 @@ module Psych # BOM + text yml = "\uFEFF#{tadpole}".encode('UTF-16LE') @parser.parse yml - assert_equal tadpole, @parser.handler.calls[2][1].first + assert_equal tadpole, @parser.handler.calls.find { |method, args| method == :scalar }[1].first end def test_external_encoding @@ -145,7 +172,7 @@ module Psych @parser.external_encoding = Psych::Parser::UTF16LE @parser.parse tadpole.encode 'UTF-16LE' - assert_equal tadpole, @parser.handler.calls[2][1].first + assert_equal tadpole, @parser.handler.calls.find { |method, args| method == :scalar }[1].first end def test_bogus_io @@ -324,6 +351,31 @@ module Psych assert_called :start_document, [[], [['!yaml!', 'tag:yaml.org,2002']], false] end + def test_event_location + @parser.parse "foo:\n" \ + " barbaz: [1, 2]" + + events = @handler.calls.each_slice(2).map do |location, event| + [event[0], location[1]] + end + + assert_equal [ + [:start_stream, [0, 0, 0, 0]], + [:start_document, [0, 0, 0, 0]], + [:start_mapping, [0, 0, 0, 0]], + [:scalar, [0, 0, 0, 3]], + [:start_mapping, [1, 2, 1, 2]], + [:scalar, [1, 2, 1, 8]], + [:start_sequence, [1, 10, 1, 11]], + [:scalar, [1, 11, 1, 12]], + [:scalar, [1, 14, 1, 15]], + [:end_sequence, [1, 15, 1, 16]], + [:end_mapping, [2, 0, 2, 0]], + [:end_mapping, [2, 0, 2, 0]], + [:end_document, [2, 0, 2, 0]], + [:end_stream, [2, 0, 2, 0]]], events + end + def assert_called call, with = nil, parser = @parser if with call = parser.handler.calls.find { |x| |