diff --git a/lib/oga/xpath/parser.y b/lib/oga/xpath/parser.y index 5e9799d..316086e 100644 --- a/lib/oga/xpath/parser.y +++ b/lib/oga/xpath/parser.y @@ -34,7 +34,7 @@ rule ; expression - : node_test + : node_test_as_axis | operator | axis | string @@ -45,7 +45,7 @@ rule ; path_member - : node_test + : node_test_as_axis | axis | call ; @@ -66,6 +66,13 @@ rule | T_SLASH path_member { s(:absolute_path, val[1]) } ; + # Whenever a bare test is used (e.g. just "A") this actually means "child::A". + # Handling this on lexer level requires a look-behind/look-ahead while solving + # this on evaluator level produces inconsistent/confusing code. + node_test_as_axis + : node_test { s(:axis, 'child', val[0]) } + ; + node_test : node_name { s(:test, *val[0]) } | node_name predicate { s(:test, *val[0], val[1]) } diff --git a/spec/oga/xpath/parser/axes_spec.rb b/spec/oga/xpath/parser/axes_spec.rb index ba71243..1951016 100644 --- a/spec/oga/xpath/parser/axes_spec.rb +++ b/spec/oga/xpath/parser/axes_spec.rb @@ -106,7 +106,7 @@ describe Oga::XPath::Parser do parse_xpath('//A').should == s( :absolute_path, s(:axis, 'descendant-or-self', s(:call, 'node')), - s(:test, nil, 'A') + s(:axis, 'child', s(:test, nil, 'A')) ) end diff --git a/spec/oga/xpath/parser/calls_spec.rb b/spec/oga/xpath/parser/calls_spec.rb index 5631411..9739d37 100644 --- a/spec/oga/xpath/parser/calls_spec.rb +++ b/spec/oga/xpath/parser/calls_spec.rb @@ -10,7 +10,7 @@ describe Oga::XPath::Parser do parse_xpath('count(/foo)').should == s( :call, 'count', - s(:absolute_path, s(:test, nil, 'foo')) + s(:absolute_path, s(:axis, 'child', s(:test, nil, 'foo'))) ) end @@ -18,7 +18,7 @@ describe Oga::XPath::Parser do parse_xpath('count(/foo, "bar")').should == s( :call, 'count', - s(:absolute_path, s(:test, nil, 'foo')), + s(:absolute_path, s(:axis, 'child', s(:test, nil, 'foo'))), s(:string, 'bar') ) end @@ -26,7 +26,7 @@ describe Oga::XPath::Parser do example 'parse a relative path with a function call' do parse_xpath('foo/bar()').should == s( :path, - s(:test, nil, 'foo'), + s(:axis, 'child', s(:test, nil, 'foo')), s(:call, 'bar') ) end @@ -34,7 +34,8 @@ describe Oga::XPath::Parser do example 'parse an absolute path with a function call' do parse_xpath('/foo/bar()').should == s( :absolute_path, - s(:test, nil, 'foo'), s(:call, 'bar') + s(:axis, 'child', s(:test, nil, 'foo')), + s(:call, 'bar') ) end @@ -42,13 +43,17 @@ describe Oga::XPath::Parser do parse_xpath('div[@class="foo"]/bar()').should == s( :path, s( - :test, - nil, - 'div', + :axis, + 'child', s( - :eq, - s(:axis, 'attribute', s(:test, nil, 'class')), - s(:string, 'foo') + :test, + nil, + 'div', + s( + :eq, + s(:axis, 'attribute', s(:test, nil, 'class')), + s(:string, 'foo') + ) ) ), s(:call, 'bar') @@ -58,8 +63,16 @@ describe Oga::XPath::Parser do example 'parse two predicates followed by a function call' do parse_xpath('A[@x]/B[@x]/bar()').should == s( :path, - s(:test, nil, 'A', s(:axis, 'attribute', s(:test, nil, 'x'))), - s(:test, nil, 'B', s(:axis, 'attribute', s(:test, nil, 'x'))), + s( + :axis, + 'child', + s(:test, nil, 'A', s(:axis, 'attribute', s(:test, nil, 'x'))) + ), + s( + :axis, + 'child', + s(:test, nil, 'B', s(:axis, 'attribute', s(:test, nil, 'x'))) + ), s(:call, 'bar') ) end diff --git a/spec/oga/xpath/parser/operator_precedence_spec.rb b/spec/oga/xpath/parser/operator_precedence_spec.rb index 1cfa69d..7de77be 100644 --- a/spec/oga/xpath/parser/operator_precedence_spec.rb +++ b/spec/oga/xpath/parser/operator_precedence_spec.rb @@ -5,104 +5,156 @@ describe Oga::XPath::Parser do example 'parse "A or B or C"' do parse_xpath('A or B or C').should == s( :or, - s(:or, s(:test, nil, 'A'), s(:test, nil, 'B')), - s(:test, nil, 'C') + s( + :or, + s(:axis, 'child', s(:test, nil, 'A')), + s(:axis, 'child', s(:test, nil, 'B')) + ), + s(:axis, 'child', s(:test, nil, 'C')) ) end example 'parse "A and B and C"' do parse_xpath('A and B and C').should == s( :and, - s(:and, s(:test, nil, 'A'), s(:test, nil, 'B')), - s(:test, nil, 'C') + s( + :and, + s(:axis, 'child', s(:test, nil, 'A')), + s(:axis, 'child', s(:test, nil, 'B')) + ), + s(:axis, 'child', s(:test, nil, 'C')) ) end example 'parse "A = B = C"' do parse_xpath('A = B = C').should == s( :eq, - s(:eq, s(:test, nil, 'A'), s(:test, nil, 'B')), - s(:test, nil, 'C') + s( + :eq, + s(:axis, 'child', s(:test, nil, 'A')), + s(:axis, 'child', s(:test, nil, 'B')) + ), + s(:axis, 'child', s(:test, nil, 'C')) ) end example 'parse "A != B != C"' do parse_xpath('A != B != C').should == s( :neq, - s(:neq, s(:test, nil, 'A'), s(:test, nil, 'B')), - s(:test, nil, 'C') + s( + :neq, + s(:axis, 'child', s(:test, nil, 'A')), + s(:axis, 'child', s(:test, nil, 'B')) + ), + s(:axis, 'child', s(:test, nil, 'C')) ) end example 'parse "A <= B <= C"' do parse_xpath('A <= B <= C').should == s( :lte, - s(:lte, s(:test, nil, 'A'), s(:test, nil, 'B')), - s(:test, nil, 'C') + s( + :lte, + s(:axis, 'child', s(:test, nil, 'A')), + s(:axis, 'child', s(:test, nil, 'B')) + ), + s(:axis, 'child', s(:test, nil, 'C')) ) end example 'parse "A < B < C"' do parse_xpath('A < B < C').should == s( :lt, - s(:lt, s(:test, nil, 'A'), s(:test, nil, 'B')), - s(:test, nil, 'C') + s( + :lt, + s(:axis, 'child', s(:test, nil, 'A')), + s(:axis, 'child', s(:test, nil, 'B')) + ), + s(:axis, 'child', s(:test, nil, 'C')) ) end example 'parse "A >= B >= C"' do parse_xpath('A >= B >= C').should == s( :gte, - s(:gte, s(:test, nil, 'A'), s(:test, nil, 'B')), - s(:test, nil, 'C') + s( + :gte, + s(:axis, 'child', s(:test, nil, 'A')), + s(:axis, 'child', s(:test, nil, 'B')) + ), + s(:axis, 'child', s(:test, nil, 'C')) ) end example 'parse "A > B > C"' do parse_xpath('A > B > C').should == s( :gt, - s(:gt, s(:test, nil, 'A'), s(:test, nil, 'B')), - s(:test, nil, 'C') + s( + :gt, + s(:axis, 'child', s(:test, nil, 'A')), + s(:axis, 'child', s(:test, nil, 'B')) + ), + s(:axis, 'child', s(:test, nil, 'C')) ) end example 'parse "A or B and C"' do parse_xpath('A or B and C').should == s( :or, - s(:test, nil, 'A'), - s(:and, s(:test, nil, 'B'), s(:test, nil, 'C')) + s(:axis, 'child', s(:test, nil, 'A')), + s( + :and, + s(:axis, 'child', s(:test, nil, 'B')), + s(:axis, 'child', s(:test, nil, 'C')) + ) ) end example 'parse "A and B = C"' do parse_xpath('A and B = C').should == s( :and, - s(:test, nil, 'A'), - s(:eq, s(:test, nil, 'B'), s(:test, nil, 'C')) + s(:axis, 'child', s(:test, nil, 'A')), + s( + :eq, + s(:axis, 'child', s(:test, nil, 'B')), + s(:axis, 'child', s(:test, nil, 'C')) + ) ) end example 'parse "A = B < C"' do parse_xpath('A = B < C').should == s( :eq, - s(:test, nil, 'A'), - s(:lt, s(:test, nil, 'B'), s(:test, nil, 'C')) + s(:axis, 'child', s(:test, nil, 'A')), + s( + :lt, + s(:axis, 'child', s(:test, nil, 'B')), + s(:axis, 'child', s(:test, nil, 'C')) + ) ) end example 'parse "A < B | C"' do parse_xpath('A < B | C').should == s( :lt, - s(:test, nil, 'A'), - s(:pipe, s(:test, nil, 'B'), s(:test, nil, 'C')) + s(:axis, 'child', s(:test, nil, 'A')), + s( + :pipe, + s(:axis, 'child', s(:test, nil, 'B')), + s(:axis, 'child', s(:test, nil, 'C')) + ) ) end example 'parse "A > B or C"' do parse_xpath('A > B or C').should == s( :or, - s(:gt, s(:test, nil, 'A'), s(:test, nil, 'B')), - s(:test, nil, 'C') + s( + :gt, + s(:axis, 'child', s(:test, nil, 'A')), + s(:axis, 'child', s(:test, nil, 'B')) + ), + s(:axis, 'child', s(:test, nil, 'C')) ) end end diff --git a/spec/oga/xpath/parser/operators_spec.rb b/spec/oga/xpath/parser/operators_spec.rb index 0548dbc..dbdf4d9 100644 --- a/spec/oga/xpath/parser/operators_spec.rb +++ b/spec/oga/xpath/parser/operators_spec.rb @@ -5,120 +5,128 @@ describe Oga::XPath::Parser do example 'parse the pipe operator' do parse_xpath('A | B').should == s( :pipe, - s(:test, nil, 'A'), - s(:test, nil, 'B') + s(:axis, 'child', s(:test, nil, 'A')), + s(:axis, 'child', s(:test, nil, 'B')) ) end example 'parse the pipe operator using two paths' do parse_xpath('A/B | C/D').should == s( :pipe, - s(:path, s(:test, nil, 'A'), s(:test, nil, 'B')), - s(:path, s(:test, nil, 'C'), s(:test, nil, 'D')) + s( + :path, + s(:axis, 'child', s(:test, nil, 'A')), + s(:axis, 'child', s(:test, nil, 'B')) + ), + s( + :path, + s(:axis, 'child', s(:test, nil, 'C')), + s(:axis, 'child', s(:test, nil, 'D')) + ) ) end example 'parse the and operator' do parse_xpath('A and B').should == s( :and, - s(:test, nil, 'A'), - s(:test, nil, 'B') + s(:axis, 'child', s(:test, nil, 'A')), + s(:axis, 'child', s(:test, nil, 'B')) ) end example 'parse the or operator' do parse_xpath('A or B').should == s( :or, - s(:test, nil, 'A'), - s(:test, nil, 'B') + s(:axis, 'child', s(:test, nil, 'A')), + s(:axis, 'child', s(:test, nil, 'B')) ) end example 'parse the plus operator' do parse_xpath('A + B').should == s( :add, - s(:test, nil, 'A'), - s(:test, nil, 'B') + s(:axis, 'child', s(:test, nil, 'A')), + s(:axis, 'child', s(:test, nil, 'B')) ) end example 'parse the div operator' do parse_xpath('A div B').should == s( :div, - s(:test, nil, 'A'), - s(:test, nil, 'B') + s(:axis, 'child', s(:test, nil, 'A')), + s(:axis, 'child', s(:test, nil, 'B')) ) end example 'parse the mod operator' do parse_xpath('A mod B').should == s( :mod, - s(:test, nil, 'A'), - s(:test, nil, 'B') + s(:axis, 'child', s(:test, nil, 'A')), + s(:axis, 'child', s(:test, nil, 'B')) ) end example 'parse the equals operator' do parse_xpath('A = B').should == s( :eq, - s(:test, nil, 'A'), - s(:test, nil, 'B') + s(:axis, 'child', s(:test, nil, 'A')), + s(:axis, 'child', s(:test, nil, 'B')) ) end example 'parse the not-equals operator' do parse_xpath('A != B').should == s( :neq, - s(:test, nil, 'A'), - s(:test, nil, 'B') + s(:axis, 'child', s(:test, nil, 'A')), + s(:axis, 'child', s(:test, nil, 'B')) ) end example 'parse the lower-than operator' do parse_xpath('A < B').should == s( :lt, - s(:test, nil, 'A'), - s(:test, nil, 'B') + s(:axis, 'child', s(:test, nil, 'A')), + s(:axis, 'child', s(:test, nil, 'B')) ) end example 'parse the greater-than operator' do parse_xpath('A > B').should == s( :gt, - s(:test, nil, 'A'), - s(:test, nil, 'B') + s(:axis, 'child', s(:test, nil, 'A')), + s(:axis, 'child', s(:test, nil, 'B')) ) end example 'parse the lower-or-equal operator' do parse_xpath('A <= B').should == s( :lte, - s(:test, nil, 'A'), - s(:test, nil, 'B') + s(:axis, 'child', s(:test, nil, 'A')), + s(:axis, 'child', s(:test, nil, 'B')) ) end example 'parse the greater-or-equal operator' do parse_xpath('A >= B').should == s( :gte, - s(:test, nil, 'A'), - s(:test, nil, 'B') + s(:axis, 'child', s(:test, nil, 'A')), + s(:axis, 'child', s(:test, nil, 'B')) ) end example 'parse the mul operator' do parse_xpath('A * B').should == s( :mul, - s(:test, nil, 'A'), - s(:test, nil, 'B') + s(:axis, 'child', s(:test, nil, 'A')), + s(:axis, 'child', s(:test, nil, 'B')) ) end example 'parse the subtraction operator' do parse_xpath('A - B').should == s( :sub, - s(:test, nil, 'A'), - s(:test, nil, 'B') + s(:axis, 'child', s(:test, nil, 'A')), + s(:axis, 'child', s(:test, nil, 'B')) ) end end diff --git a/spec/oga/xpath/parser/paths_spec.rb b/spec/oga/xpath/parser/paths_spec.rb index f10ff26..482eae6 100644 --- a/spec/oga/xpath/parser/paths_spec.rb +++ b/spec/oga/xpath/parser/paths_spec.rb @@ -3,18 +3,21 @@ require 'spec_helper' describe Oga::XPath::Parser do context 'paths' do example 'parse an absolute path' do - parse_xpath('/A').should == s(:absolute_path, s(:test, nil, 'A')) + parse_xpath('/A').should == s( + :absolute_path, + s(:axis, 'child', s(:test, nil, 'A')) + ) end example 'parse a relative path' do - parse_xpath('A').should == s(:test, nil, 'A') + parse_xpath('A').should == s(:axis, 'child', s(:test, nil, 'A')) end example 'parse an expression using two paths' do parse_xpath('/A/B').should == s( :absolute_path, - s(:test, nil, 'A'), - s(:test, nil, 'B') + s(:axis, 'child', s(:test, nil, 'A')), + s(:axis, 'child', s(:test, nil, 'B')) ) end end diff --git a/spec/oga/xpath/parser/predicates_spec.rb b/spec/oga/xpath/parser/predicates_spec.rb index 41acfce..f6caa0a 100644 --- a/spec/oga/xpath/parser/predicates_spec.rb +++ b/spec/oga/xpath/parser/predicates_spec.rb @@ -4,22 +4,30 @@ describe Oga::XPath::Parser do context 'predicates' do example 'parse a single predicate' do parse_xpath('foo[@class="bar"]').should == s( - :test, - nil, - 'foo', - s(:eq, s(:axis, 'attribute', s(:test, nil, 'class')), s(:string, 'bar')) + :axis, + 'child', + s( + :test, + nil, + 'foo', + s(:eq, s(:axis, 'attribute', s(:test, nil, 'class')), s(:string, 'bar')) + ) ) end example 'parse a predicate using the or operator' do parse_xpath('foo[@x="bar" or @x="baz"]').should == s( - :test, - nil, - 'foo', + :axis, + 'child', s( - :or, - s(:eq, s(:axis, 'attribute', s(:test, nil, 'x')), s(:string, 'bar')), - s(:eq, s(:axis, 'attribute', s(:test, nil, 'x')), s(:string, 'baz')), + :test, + nil, + 'foo', + s( + :or, + s(:eq, s(:axis, 'attribute', s(:test, nil, 'x')), s(:string, 'bar')), + s(:eq, s(:axis, 'attribute', s(:test, nil, 'x')), s(:string, 'baz')), + ) ) ) end diff --git a/spec/oga/xpath/parser/wildcard_spec.rb b/spec/oga/xpath/parser/wildcard_spec.rb index a007e1d..5eeae32 100644 --- a/spec/oga/xpath/parser/wildcard_spec.rb +++ b/spec/oga/xpath/parser/wildcard_spec.rb @@ -3,15 +3,15 @@ require 'spec_helper' describe Oga::XPath::Parser do context 'wildcards' do example 'parse a wildcard name test' do - parse_xpath('*').should == s(:test, nil, '*') + parse_xpath('*').should == s(:axis, 'child', s(:test, nil, '*')) end example 'parse a wildcard namespace test' do - parse_xpath('*:A').should == s(:test, '*', 'A') + parse_xpath('*:A').should == s(:axis, 'child', s(:test, '*', 'A')) end example 'parse a wildcard namespace and name test' do - parse_xpath('*:*').should == s(:test, '*', '*') + parse_xpath('*:*').should == s(:axis, 'child', s(:test, '*', '*')) end end end