Proper XPath operator parsing precedence

This commit is contained in:
Yorick Peterse 2015-03-18 23:56:13 +01:00
parent 6039e1dbeb
commit 588c225c53
1 changed files with 120 additions and 50 deletions

View File

@ -16,60 +16,95 @@
%terminals T_SUB T_MUL T_VAR;
xpath
= expression { val[0] }
| _ { nil }
= expression
| _ { nil }
;
# Expressions And Operators
#
# Operators are handled by using a mixture of iteration (in the form of the *
# operator), recursion and priorities. Priorities are handled by recursing into
# certain rules before processing others.
#
# These rules are largely based on the following resources:
#
# * http://www.w3.org/TR/xquery-xpath-parsing/#XPath-EBNF
# * http://blog.jwbroek.com/2010/07/antlr-grammar-for-parsing-xpath-10.html
#
expression
= expression_or_operator optional_operator_recurse
{
val[1] ? s(val[1][0], val[0], val[1][1]) : val[0]
}
= and_expr expression_follow* { combine_operators(val) }
;
expression_or_operator
= expression_member optional_operator
{
val[1] ? s(val[1][0], val[0], val[1][1]) : val[0]
}
expression_follow
= T_OR and_expr { [:or, val[1]] }
;
and_expr
= equality_expr and_expr_follow* { combine_operators(val) }
;
and_expr_follow
= T_AND equality_expr { [:and, val[1]] }
;
equality_expr
= relational_expr equality_expr_follow* { combine_operators(val) }
;
equality_expr_follow
= T_EQ relational_expr { [:eq, val[1]] }
| T_NEQ relational_expr { [:neq, val[1]] }
;
relational_expr
= additive_expr relational_expr_follow* { combine_operators(val) }
;
relational_expr_follow
= T_LT additive_expr { [:lt, val[1]] }
| T_GT additive_expr { [:gt, val[1]] }
| T_LTE additive_expr { [:lte, val[1]] }
| T_GTE additive_expr { [:gte, val[1]] }
;
additive_expr
= mult_expr additive_expr_follow* { combine_operators(val) }
;
additive_expr_follow
= T_ADD mult_expr { [:add, val[1]] }
| T_SUB mult_expr { [:sub, val[1]] }
;
mult_expr
= union_expr mult_expr_follow { combine_optional_operator(val) }
;
mult_expr_follow
= T_DIV mult_expr { [:div, val[1]] }
| T_MOD mult_expr { [:mod, val[1]] }
| T_MUL mult_expr { [:mul, val[1]] }
| _ { nil }
;
union_expr
= expression_member union_expr_follow* { combine_operators(val) }
;
union_expr_follow
= T_PIPE expression_member { [:pipe, val[1]] }
;
expression_member
= relative_path { val[0] }
| absolute_path { val[0] }
| string { val[0] }
| number { val[0] }
| variable { val[0] }
= relative_path
| absolute_path
| string
| number
| variable
| T_LPAREN expression T_RPAREN { val[1] }
;
optional_operator_recurse
= operator expression_or_operator
| _ { nil }
;
optional_operator
= operator expression_member
| _ { nil }
;
operator
= T_PIPE { :pipe }
| T_AND { :and }
| T_OR { :or }
| T_ADD { :add }
| T_DIV { :div }
| T_MOD { :mod }
| T_EQ { :eq }
| T_NEQ { :neq }
| T_LT { :lt }
| T_GT { :gt }
| T_LTE { :lte }
| T_GTE { :gte }
| T_MUL { :mul }
| T_SUB { :sub }
;
# A, A/B, etc
relative_path
= path_steps { val[0].length > 1 ? s(:path, *val[0]) : val[0][0] }
@ -90,13 +125,13 @@ absolute_path
;
absolute_path_follow
= path_steps { val[0] }
= path_steps
| _
;
path_step_or_axis
= path_step { val[0] }
| axis { val[0] }
= path_step
| axis
;
# A, A(), A(X), etc
@ -168,13 +203,21 @@ call_args_follow
# child::foo, descendant-or-self::foo, etc
axis
= T_AXIS axis_follow { s(:axis, val[0], *val[1]) }
= T_AXIS axis_value predicate
{
ret = s(:axis, val[0], val[1])
if val[2]
ret = s(:predicate, ret, val[2])
end
ret
}
;
axis_follow
axis_value
= test
| type_test
| _
;
string
@ -186,7 +229,8 @@ number
;
variable
= T_VAR { s(:var, val[0]) };
= T_VAR { s(:var, val[0]) }
;
%inner
{
@ -222,4 +266,30 @@ variable
yield [-1, -1]
end
##
# @param [Array] val
#
def combine_operators(val)
ret = val[0]
val[1].each do |expr|
ret = s(expr[0], ret, expr[1])
end
return ret
end
##
# @param [Array] val
#
def combine_optional_operator(val)
ret = val[0]
if val[1]
ret = s(val[1][0], ret, val[1][1])
end
ret
end
}