Support for parsing basic XPath expressions.
This commit is contained in:
parent
70f3b7fa92
commit
eba2d9954d
|
@ -3,30 +3,74 @@
|
||||||
#
|
#
|
||||||
class Oga::XPath::Parser
|
class Oga::XPath::Parser
|
||||||
|
|
||||||
token T_AXIS T_COLON T_COMMA T_FLOAT T_INT T_IDENT T_OP T_STAR
|
token T_AXIS T_COLON T_COMMA T_FLOAT T_INT T_IDENT T_STAR
|
||||||
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
|
||||||
|
|
||||||
options no_result_var
|
options no_result_var
|
||||||
|
|
||||||
|
prechigh
|
||||||
|
left T_EQ
|
||||||
|
right T_OR
|
||||||
|
preclow
|
||||||
|
|
||||||
rule
|
rule
|
||||||
expressions
|
xpath
|
||||||
: expressions_ { s(:xpath, *val[0]) }
|
: expressions { s(:xpath, *val[0]) }
|
||||||
| /* none */ { s(:xpath) }
|
| /* none */ { s(:xpath) }
|
||||||
;
|
;
|
||||||
|
|
||||||
expressions_
|
expressions
|
||||||
: expressions_ expression { val[0] << val[1] }
|
: expressions expression { val[0] << val[1] }
|
||||||
| expression { val }
|
| expression { val }
|
||||||
;
|
;
|
||||||
|
|
||||||
expression
|
expression
|
||||||
: node_test
|
: path
|
||||||
|
| node_test
|
||||||
|
| operator
|
||||||
|
| axis
|
||||||
|
| string
|
||||||
|
| number
|
||||||
|
;
|
||||||
|
|
||||||
|
path
|
||||||
|
: T_SLASH node_test { s(:path, val[1]) }
|
||||||
;
|
;
|
||||||
|
|
||||||
node_test
|
node_test
|
||||||
: T_IDENT { s(:node, nil, val[0]) }
|
: node_name { s(:node_test, val[0]) }
|
||||||
| T_IDENT T_COLON T_IDENT { s(:node, val[0], val[2]) }
|
| node_name predicate { s(:node_test, val[0], *val[1]) }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
node_name
|
||||||
|
# foo
|
||||||
|
: T_IDENT { s(:name, nil, val[0]) }
|
||||||
|
|
||||||
|
# foo:bar
|
||||||
|
| T_IDENT T_COLON T_IDENT { s(:name, val[0], val[1]) }
|
||||||
|
;
|
||||||
|
|
||||||
|
predicate
|
||||||
|
: T_LBRACK expressions T_RBRACK { val[1] }
|
||||||
|
;
|
||||||
|
|
||||||
|
operator
|
||||||
|
: expression T_EQ expression { s(:eq, val[0], val[2]) }
|
||||||
|
| expression T_OR expression { s(:or, val[0], val[2]) }
|
||||||
|
;
|
||||||
|
|
||||||
|
axis
|
||||||
|
: T_AXIS T_IDENT { s(:axis, val[0], val[1]) }
|
||||||
|
;
|
||||||
|
|
||||||
|
string
|
||||||
|
: T_STRING { s(:string, val[0]) }
|
||||||
|
;
|
||||||
|
|
||||||
|
number
|
||||||
|
: T_INT { s(:int, val[0]) }
|
||||||
|
| T_FLOAT { s(:float, val[0]) }
|
||||||
end
|
end
|
||||||
|
|
||||||
---- inner
|
---- inner
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Oga::XPath::Parser do
|
||||||
|
context 'axes' do
|
||||||
|
example 'parse an absolute path' do
|
||||||
|
parse_xpath('/foo').should == s(
|
||||||
|
:xpath,
|
||||||
|
s(:path, s(:node_test, s(:name, nil, 'foo')))
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
example 'parse a relative path' do
|
||||||
|
parse_xpath('foo').should == s(:xpath, s(:node_test, s(:name, nil, 'foo')))
|
||||||
|
end
|
||||||
|
|
||||||
|
example 'parse an expression using two paths' do
|
||||||
|
parse_xpath('/foo/bar').should == s(
|
||||||
|
:xpath,
|
||||||
|
s(:path, s(:node_test, s(:name, nil, 'foo'))),
|
||||||
|
s(:path, s(:node_test, s(:name, nil, 'bar')))
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,37 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Oga::XPath::Parser do
|
||||||
|
context 'predicates' do
|
||||||
|
example 'parse a single predicate' do
|
||||||
|
parse_xpath('/foo[@class="bar"]').should == s(
|
||||||
|
:xpath,
|
||||||
|
s(
|
||||||
|
:path,
|
||||||
|
s(
|
||||||
|
:node_test,
|
||||||
|
s(:name, nil, 'foo'),
|
||||||
|
s(:eq, s(:axis, 'attribute', 'class'), s(:string, 'bar'))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
example 'parse a predicate using the or operator' do
|
||||||
|
parse_xpath('/foo[@class="bar" or @class="baz"]').should == s(
|
||||||
|
:xpath,
|
||||||
|
s(
|
||||||
|
:path,
|
||||||
|
s(
|
||||||
|
:node_test,
|
||||||
|
s(:name, nil, 'foo'),
|
||||||
|
s(
|
||||||
|
:or,
|
||||||
|
s(:eq, s(:axis, 'attribute', 'class'), s(:string, 'bar')),
|
||||||
|
s(:eq, s(:axis, 'attribute', 'class'), s(:string, 'baz'))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,5 +0,0 @@
|
||||||
require 'spec_helper'
|
|
||||||
|
|
||||||
describe Oga::XPath::Parser do
|
|
||||||
pending 'Write me!'
|
|
||||||
end
|
|
|
@ -5,10 +5,11 @@ module Oga
|
||||||
#
|
#
|
||||||
# @param [Symbol] type
|
# @param [Symbol] type
|
||||||
# @param [Array] cihldren
|
# @param [Array] cihldren
|
||||||
# @return [Oga::AST::Node]
|
# @return [AST::Node]
|
||||||
#
|
#
|
||||||
def s(type, *children)
|
def s(type, *children)
|
||||||
return Oga::AST::Node.new(type, children)
|
# TODO: add support for CSS AST nodes.
|
||||||
|
return Oga::XPath::Node.new(type, children)
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -32,6 +33,16 @@ module Oga
|
||||||
return Oga::XPath::Lexer.new(input).lex
|
return Oga::XPath::Lexer.new(input).lex
|
||||||
end
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Parses an XPath expression.
|
||||||
|
#
|
||||||
|
# @param [String] input
|
||||||
|
# @return [Oga::XPath::Node]
|
||||||
|
#
|
||||||
|
def parse_xpath(input)
|
||||||
|
return Oga::XPath::Parser.new(input).parse
|
||||||
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
# Parses the given XML and returns an AST.
|
# Parses the given XML and returns an AST.
|
||||||
#
|
#
|
||||||
|
|
Loading…
Reference in New Issue