Parsing support for more XPath operators.
This still messes up some tests due to botched token precedence (by the looks of it).
This commit is contained in:
parent
514c342cab
commit
6a2f4fa82d
|
@ -6,12 +6,13 @@ class Oga::XPath::Parser
|
||||||
token T_AXIS T_COLON T_COMMA T_FLOAT T_INT T_IDENT
|
token T_AXIS T_COLON T_COMMA T_FLOAT T_INT T_IDENT
|
||||||
token T_LBRACK T_RBRACK T_LPAREN T_RPAREN T_SLASH T_STRING
|
token T_LBRACK T_RBRACK T_LPAREN T_RPAREN T_SLASH T_STRING
|
||||||
token T_PIPE T_AND T_OR T_ADD T_DIV T_MOD T_EQ T_NEQ T_LT T_GT T_LTE T_GTE
|
token T_PIPE T_AND T_OR T_ADD T_DIV T_MOD T_EQ T_NEQ T_LT T_GT T_LTE T_GTE
|
||||||
|
token T_SUB T_MUL
|
||||||
|
|
||||||
options no_result_var
|
options no_result_var
|
||||||
|
|
||||||
prechigh
|
prechigh
|
||||||
left T_EQ T_AXIS
|
left T_PIPE T_MOD T_DIV T_MUL T_SUB T_ADD
|
||||||
right T_OR
|
left T_GT T_GTE T_LT T_LTE T_NEQ T_EQ T_AND T_OR
|
||||||
preclow
|
preclow
|
||||||
|
|
||||||
rule
|
rule
|
||||||
|
@ -57,15 +58,32 @@ rule
|
||||||
;
|
;
|
||||||
|
|
||||||
operator
|
operator
|
||||||
: expression T_EQ expression { s(:eq, val[0], val[2]) }
|
: expression T_PIPE expression { s(:pipe, val[0], val[2]) }
|
||||||
| expression T_OR expression { s(:or, val[0], val[2]) }
|
| expression T_AND expression { s(:and, val[0], val[2]) }
|
||||||
|
| expression T_OR expression { s(:or, val[0], val[2]) }
|
||||||
|
| expression T_ADD expression { s(:add, val[0], val[2]) }
|
||||||
|
| expression T_DIV expression { s(:div, val[0], val[2]) }
|
||||||
|
| expression T_MOD expression { s(:mod, val[0], val[2]) }
|
||||||
|
| expression T_EQ expression { s(:eq, val[0], val[2]) }
|
||||||
|
| expression T_NEQ expression { s(:neq, val[0], val[2]) }
|
||||||
|
| expression T_LT expression { s(:lt, val[0], val[2]) }
|
||||||
|
| expression T_GT expression { s(:gt, val[0], val[2]) }
|
||||||
|
| expression T_LTE expression { s(:lte, val[0], val[2]) }
|
||||||
|
| expression T_GTE expression { s(:gte, val[0], val[2]) }
|
||||||
|
| expression T_MUL expression { s(:mul, val[0], val[2]) }
|
||||||
|
| expression T_SUB expression { s(:sub, val[0], val[2]) }
|
||||||
;
|
;
|
||||||
|
|
||||||
axis
|
axis
|
||||||
: T_AXIS expression { s(:axis, val[0], val[1]) }
|
: T_AXIS axis_value { s(:axis, val[0], val[1]) }
|
||||||
| T_AXIS { s(:axis, val[0]) }
|
| T_AXIS { s(:axis, val[0]) }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
axis_value
|
||||||
|
: node_test
|
||||||
|
| call
|
||||||
|
;
|
||||||
|
|
||||||
call
|
call
|
||||||
: T_IDENT T_LPAREN T_RPAREN { s(:call, val[0]) }
|
: T_IDENT T_LPAREN T_RPAREN { s(:call, val[0]) }
|
||||||
| T_IDENT T_LPAREN call_args T_RPAREN { s(:call, val[0], *val[2]) }
|
| T_IDENT T_LPAREN call_args T_RPAREN { s(:call, val[0], *val[2]) }
|
||||||
|
|
|
@ -0,0 +1,148 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Oga::XPath::Parser do
|
||||||
|
context 'operator precedence' do
|
||||||
|
example 'parse "A or B or C"' do
|
||||||
|
parse_xpath('A or B or C').should == s(
|
||||||
|
:path,
|
||||||
|
s(
|
||||||
|
:or,
|
||||||
|
s(:or, s(:test, nil, 'A'), s(:test, nil, 'B')),
|
||||||
|
s(:test, nil, 'C')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
example 'parse "A and B and C"' do
|
||||||
|
parse_xpath('A and B and C').should == s(
|
||||||
|
:path,
|
||||||
|
s(
|
||||||
|
:and,
|
||||||
|
s(:and, s(:test, nil, 'A'), s(:test, nil, 'B')),
|
||||||
|
s(:test, nil, 'C')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
example 'parse "A = B = C"' do
|
||||||
|
parse_xpath('A = B = C').should == s(
|
||||||
|
:path,
|
||||||
|
s(
|
||||||
|
:eq,
|
||||||
|
s(:eq, s(:test, nil, 'A'), s(:test, nil, 'B')),
|
||||||
|
s(:test, nil, 'C')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
example 'parse "A != B != C"' do
|
||||||
|
parse_xpath('A != B != C').should == s(
|
||||||
|
:path,
|
||||||
|
s(
|
||||||
|
:neq,
|
||||||
|
s(:neq, s(:test, nil, 'A'), s(:test, nil, 'B')),
|
||||||
|
s(:test, nil, 'C')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
example 'parse "A <= B <= C"' do
|
||||||
|
parse_xpath('A <= B <= C').should == s(
|
||||||
|
:path,
|
||||||
|
s(
|
||||||
|
:lte,
|
||||||
|
s(:lte, s(:test, nil, 'A'), s(:test, nil, 'B')),
|
||||||
|
s(:test, nil, 'C')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
example 'parse "A < B < C"' do
|
||||||
|
parse_xpath('A < B < C').should == s(
|
||||||
|
:path,
|
||||||
|
s(
|
||||||
|
:lt,
|
||||||
|
s(:lt, s(:test, nil, 'A'), s(:test, nil, 'B')),
|
||||||
|
s(:test, nil, 'C')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
example 'parse "A >= B >= C"' do
|
||||||
|
parse_xpath('A >= B >= C').should == s(
|
||||||
|
:path,
|
||||||
|
s(
|
||||||
|
:gte,
|
||||||
|
s(:gte, s(:test, nil, 'A'), s(:test, nil, 'B')),
|
||||||
|
s(:test, nil, 'C')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
example 'parse "A > B > C"' do
|
||||||
|
parse_xpath('A > B > C').should == s(
|
||||||
|
:path,
|
||||||
|
s(
|
||||||
|
:gt,
|
||||||
|
s(:gt, s(:test, nil, 'A'), s(:test, nil, 'B')),
|
||||||
|
s(:test, nil, 'C')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
example 'parse "A or B and C"' do
|
||||||
|
parse_xpath('A or B and C').should == s(
|
||||||
|
:path,
|
||||||
|
s(
|
||||||
|
:and,
|
||||||
|
s(:or, s(:test, nil, 'A'), s(:test, nil, 'B')),
|
||||||
|
s(:test, nil, 'C')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
example 'parse "A and B = C"' do
|
||||||
|
parse_xpath('A and B = C').should == s(
|
||||||
|
:path,
|
||||||
|
s(
|
||||||
|
:eq,
|
||||||
|
s(:and, s(:test, nil, 'A'), s(:test, nil, 'B')),
|
||||||
|
s(:test, nil, 'C')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
example 'parse "A = B < C"' do
|
||||||
|
parse_xpath('A = B < C').should == s(
|
||||||
|
:path,
|
||||||
|
s(
|
||||||
|
:lt,
|
||||||
|
s(:eq, s(:test, nil, 'A'), s(:test, nil, 'B')),
|
||||||
|
s(:test, nil, 'C')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
example 'parse "A < B | C"' do
|
||||||
|
parse_xpath('A < B | C').should == s(
|
||||||
|
:path,
|
||||||
|
s(
|
||||||
|
:lt,
|
||||||
|
s(:test, nil, 'A'),
|
||||||
|
s(:pipe, s(:test, nil, 'B'), s(:test, nil, 'C'))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
example 'parse "A > B or C"' do
|
||||||
|
parse_xpath('A > B or C').should == s(
|
||||||
|
:path,
|
||||||
|
s(
|
||||||
|
:or,
|
||||||
|
s(:gt, s(:test, nil, 'A'), s(:test, nil, 'B')),
|
||||||
|
s(:test, nil, 'C')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,103 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Oga::XPath::Parser do
|
||||||
|
context 'operators' do
|
||||||
|
example 'parse the pipe operator' do
|
||||||
|
parse_xpath('A | B').should == s(
|
||||||
|
:path,
|
||||||
|
s(:pipe, s(:test, nil, 'A'), s(:test, nil, 'B'))
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
example 'parse the and operator' do
|
||||||
|
parse_xpath('A and B').should == s(
|
||||||
|
:path,
|
||||||
|
s(:and, s(:test, nil, 'A'), s(:test, nil, 'B'))
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
example 'parse the or operator' do
|
||||||
|
parse_xpath('A or B').should == s(
|
||||||
|
:path,
|
||||||
|
s(:or, s(:test, nil, 'A'), s(:test, nil, 'B'))
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
example 'parse the plus operator' do
|
||||||
|
parse_xpath('A + B').should == s(
|
||||||
|
:path,
|
||||||
|
s(:add, s(:test, nil, 'A'), s(:test, nil, 'B'))
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
example 'parse the div operator' do
|
||||||
|
parse_xpath('A div B').should == s(
|
||||||
|
:path,
|
||||||
|
s(:div, s(:test, nil, 'A'), s(:test, nil, 'B'))
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
example 'parse the mod operator' do
|
||||||
|
parse_xpath('A mod B').should == s(
|
||||||
|
:path,
|
||||||
|
s(:mod, s(:test, nil, 'A'), s(:test, nil, 'B'))
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
example 'parse the equals operator' do
|
||||||
|
parse_xpath('A = B').should == s(
|
||||||
|
:path,
|
||||||
|
s(:eq, s(:test, nil, 'A'), s(:test, nil, 'B'))
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
example 'parse the not-equals operator' do
|
||||||
|
parse_xpath('A != B').should == s(
|
||||||
|
:path,
|
||||||
|
s(:neq, s(:test, nil, 'A'), s(:test, nil, 'B'))
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
example 'parse the lower-than operator' do
|
||||||
|
parse_xpath('A < B').should == s(
|
||||||
|
:path,
|
||||||
|
s(:lt, s(:test, nil, 'A'), s(:test, nil, 'B'))
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
example 'parse the greater-than operator' do
|
||||||
|
parse_xpath('A > B').should == s(
|
||||||
|
:path,
|
||||||
|
s(:gt, s(:test, nil, 'A'), s(:test, nil, 'B'))
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
example 'parse the lower-or-equal operator' do
|
||||||
|
parse_xpath('A <= B').should == s(
|
||||||
|
:path,
|
||||||
|
s(:lte, s(:test, nil, 'A'), s(:test, nil, 'B'))
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
example 'parse the greater-or-equal operator' do
|
||||||
|
parse_xpath('A >= B').should == s(
|
||||||
|
:path,
|
||||||
|
s(:gte, s(:test, nil, 'A'), s(:test, nil, 'B'))
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
example 'parse the mul operator' do
|
||||||
|
parse_xpath('A * B').should == s(
|
||||||
|
:path,
|
||||||
|
s(:mul, s(:test, nil, 'A'), s(:test, nil, 'B'))
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
example 'parse the subtraction operator' do
|
||||||
|
parse_xpath('A - B').should == s(
|
||||||
|
:path,
|
||||||
|
s(:sub, s(:test, nil, 'A'), s(:test, nil, 'B'))
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -3,21 +3,18 @@ require 'spec_helper'
|
||||||
describe Oga::XPath::Parser do
|
describe Oga::XPath::Parser do
|
||||||
context 'predicates' do
|
context 'predicates' do
|
||||||
example 'parse a single predicate' do
|
example 'parse a single predicate' do
|
||||||
parse_xpath('/foo[@class="bar"]').should == s(
|
parse_xpath('foo[@class="bar"]').should == s(
|
||||||
:absolute,
|
:path,
|
||||||
s(
|
s(
|
||||||
:path,
|
:test,
|
||||||
|
nil,
|
||||||
|
'foo',
|
||||||
s(
|
s(
|
||||||
:test,
|
:path,
|
||||||
nil,
|
|
||||||
'foo',
|
|
||||||
s(
|
s(
|
||||||
:path,
|
:eq,
|
||||||
s(
|
s(:axis, 'attribute', s(:test, nil, 'class')),
|
||||||
:eq,
|
s(:string, 'bar')
|
||||||
s(:axis, 'attribute', s(:test, nil, 'class')),
|
|
||||||
s(:string, 'bar')
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -25,28 +22,25 @@ describe Oga::XPath::Parser do
|
||||||
end
|
end
|
||||||
|
|
||||||
example 'parse a predicate using the or operator' do
|
example 'parse a predicate using the or operator' do
|
||||||
parse_xpath('/foo[@class="bar" or @class="baz"]').should == s(
|
parse_xpath('foo[@class="bar" or @class="baz"]').should == s(
|
||||||
:absolute,
|
:path,
|
||||||
s(
|
s(
|
||||||
:path,
|
:test,
|
||||||
|
nil,
|
||||||
|
'foo',
|
||||||
s(
|
s(
|
||||||
:test,
|
:path,
|
||||||
nil,
|
|
||||||
'foo',
|
|
||||||
s(
|
s(
|
||||||
:path,
|
:or,
|
||||||
s(
|
s(
|
||||||
:or,
|
:eq,
|
||||||
s(
|
s(:axis, 'attribute', s(:test, nil, 'class')),
|
||||||
:eq,
|
s(:string, 'bar')
|
||||||
s(:axis, 'attribute', s(:test, nil, 'class')),
|
),
|
||||||
s(:string, 'bar')
|
s(
|
||||||
),
|
:eq,
|
||||||
s(
|
s(:axis, 'attribute', s(:test, nil, 'class')),
|
||||||
:eq,
|
s(:string, 'baz')
|
||||||
s(:axis, 'attribute', s(:test, nil, 'class')),
|
|
||||||
s(:string, 'baz')
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue