summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAaron Patterson <aaron.patterson@gmail.com>2017-09-10 20:17:05 -0700
committerGitHub <noreply@github.com>2017-09-10 20:17:05 -0700
commitbbdd6637e37d4b821a8c7075e9ccd17ea0e07569 (patch)
tree2d43a27216316f248533ab6e3e36aa47ef733166
parentb572261900893df0352d3ad71c17dfa81ab17ccc (diff)
parent00d1ab44a9ede3f016c640659a1202de701d6648 (diff)
downloadpsych-bbdd6637e37d4b821a8c7075e9ccd17ea0e07569.tar.gz
Merge pull request #326 from asterite/feature/scalar_location
Add Psych::Handler#event_location
-rw-r--r--ext/java/PsychParser.java7
-rw-r--r--ext/psych/psych_parser.c49
-rw-r--r--lib/psych/handler.rb5
-rw-r--r--lib/psych/nodes/node.rb12
-rw-r--r--lib/psych/tree_builder.rb48
-rw-r--r--test/psych/test_parser.rb62
-rw-r--r--test/psych/test_tree_builder.rb14
7 files changed, 175 insertions, 22 deletions
diff --git a/ext/java/PsychParser.java b/ext/java/PsychParser.java
index f5b6faf..db0306b 100644
--- a/ext/java/PsychParser.java
+++ b/ext/java/PsychParser.java
@@ -197,6 +197,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()));
@@ -284,6 +290,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/lib/psych/nodes/node.rb b/lib/psych/nodes/node.rb
index 1c76721..6d86669 100644
--- a/lib/psych/nodes/node.rb
+++ b/lib/psych/nodes/node.rb
@@ -17,6 +17,18 @@ module Psych
# An associated tag
attr_reader :tag
+ # The line number where this node start
+ attr_accessor :start_line
+
+ # The column number where this node start
+ attr_accessor :start_column
+
+ # The line number where this node ends
+ attr_accessor :end_line
+
+ # The column number where this node ends
+ attr_accessor :end_column
+
# Create a new Psych::Nodes::Node
def initialize
@children = []
diff --git a/lib/psych/tree_builder.rb b/lib/psych/tree_builder.rb
index b10fd5c..47a1695 100644
--- a/lib/psych/tree_builder.rb
+++ b/lib/psych/tree_builder.rb
@@ -23,6 +23,18 @@ module Psych
@stack = []
@last = nil
@root = nil
+
+ @start_line = nil
+ @start_column = nil
+ @end_line = nil
+ @end_column = nil
+ end
+
+ def event_location(start_line, start_column, end_line, end_column)
+ @start_line = start_line
+ @start_column = start_column
+ @end_line = end_line
+ @end_column = end_column
end
%w{
@@ -32,12 +44,15 @@ module Psych
class_eval %{
def start_#{node.downcase}(anchor, tag, implicit, style)
n = Nodes::#{node}.new(anchor, tag, implicit, style)
+ set_start_location(n)
@last.children << n
push n
end
def end_#{node.downcase}
- pop
+ n = pop
+ set_end_location(n)
+ n
end
}
end
@@ -49,6 +64,7 @@ module Psych
# See Psych::Handler#start_document
def start_document version, tag_directives, implicit
n = Nodes::Document.new version, tag_directives, implicit
+ set_start_location(n)
@last.children << n
push n
end
@@ -60,26 +76,35 @@ module Psych
# See Psych::Handler#start_document
def end_document implicit_end = !streaming?
@last.implicit_end = implicit_end
- pop
+ n = pop
+ set_end_location(n)
+ n
end
def start_stream encoding
@root = Nodes::Stream.new(encoding)
+ set_start_location(@root)
push @root
end
def end_stream
- pop
+ n = pop
+ set_end_location(n)
+ n
end
def scalar value, anchor, tag, plain, quoted, style
s = Nodes::Scalar.new(value,anchor,tag,plain,quoted,style)
+ set_location(s)
@last.children << s
s
end
def alias anchor
- @last.children << Nodes::Alias.new(anchor)
+ a = Nodes::Alias.new(anchor)
+ set_location(a)
+ @last.children << a
+ a
end
private
@@ -93,5 +118,20 @@ module Psych
@last = @stack.last
x
end
+
+ def set_location(node)
+ set_start_location(node)
+ set_end_location(node)
+ end
+
+ def set_start_location(node)
+ node.start_line = @start_line
+ node.start_column = @start_column
+ end
+
+ def set_end_location(node)
+ node.end_line = @end_line
+ node.end_column = @end_column
+ end
end
end
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|
diff --git a/test/psych/test_tree_builder.rb b/test/psych/test_tree_builder.rb
index 3e35788..dfb5da9 100644
--- a/test/psych/test_tree_builder.rb
+++ b/test/psych/test_tree_builder.rb
@@ -21,6 +21,7 @@ module Psych
def test_stream
assert_instance_of Nodes::Stream, @tree
+ assert_location 0, 0, 8, 0, @tree
end
def test_documents
@@ -31,6 +32,7 @@ module Psych
assert_equal [1,1], doc.version
assert_equal [], doc.tag_directives
assert_equal false, doc.implicit
+ assert_location 0, 0, 8, 0, doc
end
def test_sequence
@@ -43,6 +45,7 @@ module Psych
assert_nil seq.tag
assert_equal true, seq.implicit
assert_equal Nodes::Sequence::BLOCK, seq.style
+ assert_location 2, 0, 8, 0, seq
end
def test_scalar
@@ -58,6 +61,7 @@ module Psych
assert_equal true, scalar.plain
assert_equal false, scalar.quoted
assert_equal Nodes::Scalar::PLAIN, scalar.style
+ assert_location 2, 2, 2, 5, scalar
end
def test_mapping
@@ -66,6 +70,7 @@ module Psych
map = seq.children[1]
assert_instance_of Nodes::Mapping, map
+ assert_location 3, 2, 6, 1, map
end
def test_alias
@@ -75,6 +80,15 @@ module Psych
al = seq.children[2]
assert_instance_of Nodes::Alias, al
assert_equal 'A', al.anchor
+ assert_location 7, 2, 7, 4, al
+ end
+
+ private
+ def assert_location(start_line, start_column, end_line, end_column, node)
+ assert_equal start_line, node.start_line
+ assert_equal start_column, node.start_column
+ assert_equal end_line, node.end_line
+ assert_equal end_column, node.end_column
end
end
end