XPath compiler support for a bucket of functions
This includes the following functions: * boolean() * ceiling() * floor() * round() * concat() * contains() * count()
This commit is contained in:
parent
e3b45fddfc
commit
2ff1f9ab4f
|
@ -649,31 +649,151 @@ module Oga
|
||||||
# @return [Oga::Ruby::Node]
|
# @return [Oga::Ruby::Node]
|
||||||
#
|
#
|
||||||
def on_call(ast, input, &block)
|
def on_call(ast, input, &block)
|
||||||
name, args = *ast
|
name, *args = *ast
|
||||||
|
|
||||||
handler = name.gsub('-', '_')
|
handler = name.gsub('-', '_')
|
||||||
|
|
||||||
send(:"on_call_#{handler}", input, *args, &block)
|
send(:"on_call_#{handler}", input, *args, &block)
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
|
||||||
# Processes the `true()` function call.
|
|
||||||
#
|
|
||||||
# @return [Oga::Ruby::Node]
|
# @return [Oga::Ruby::Node]
|
||||||
#
|
|
||||||
def on_call_true(*)
|
def on_call_true(*)
|
||||||
self.true
|
self.true
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
|
||||||
# Processes the `false()` function call.
|
|
||||||
#
|
|
||||||
# @return [Oga::Ruby::Node]
|
# @return [Oga::Ruby::Node]
|
||||||
#
|
|
||||||
def on_call_false(*)
|
def on_call_false(*)
|
||||||
self.false
|
self.false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @param [Oga::Ruby::Node] input
|
||||||
|
# @param [AST::Node] arg
|
||||||
|
# @return [Oga::Ruby::Node]
|
||||||
|
def on_call_boolean(input, arg)
|
||||||
|
arg_ast = try_match_first_node(arg, input)
|
||||||
|
call_arg = unique_literal(:call_arg)
|
||||||
|
conversion = literal(Conversion)
|
||||||
|
|
||||||
|
call_arg.assign(arg_ast)
|
||||||
|
.followed_by(conversion.to_boolean(call_arg))
|
||||||
|
end
|
||||||
|
|
||||||
|
# @param [Oga::Ruby::Node] input
|
||||||
|
# @param [AST::Node] arg
|
||||||
|
# @return [Oga::Ruby::Node]
|
||||||
|
def on_call_ceiling(input, arg)
|
||||||
|
arg_ast = try_match_first_node(arg, input)
|
||||||
|
call_arg = unique_literal(:call_arg)
|
||||||
|
conversion = literal(Conversion)
|
||||||
|
|
||||||
|
initial_assign = call_arg.assign(arg_ast)
|
||||||
|
float_assign = call_arg.assign(conversion.to_float(call_arg))
|
||||||
|
|
||||||
|
if_nan = call_arg.nan?
|
||||||
|
.if_true { call_arg }
|
||||||
|
.else { call_arg.ceil.to_f }
|
||||||
|
|
||||||
|
initial_assign.followed_by(float_assign)
|
||||||
|
.followed_by(if_nan)
|
||||||
|
end
|
||||||
|
|
||||||
|
# @param [Oga::Ruby::Node] input
|
||||||
|
# @param [AST::Node] arg
|
||||||
|
# @return [Oga::Ruby::Node]
|
||||||
|
def on_call_floor(input, arg)
|
||||||
|
arg_ast = try_match_first_node(arg, input)
|
||||||
|
call_arg = unique_literal(:call_arg)
|
||||||
|
conversion = literal(Conversion)
|
||||||
|
|
||||||
|
initial_assign = call_arg.assign(arg_ast)
|
||||||
|
float_assign = call_arg.assign(conversion.to_float(call_arg))
|
||||||
|
|
||||||
|
if_nan = call_arg.nan?
|
||||||
|
.if_true { call_arg }
|
||||||
|
.else { call_arg.floor.to_f }
|
||||||
|
|
||||||
|
initial_assign.followed_by(float_assign)
|
||||||
|
.followed_by(if_nan)
|
||||||
|
end
|
||||||
|
|
||||||
|
# @param [Oga::Ruby::Node] input
|
||||||
|
# @param [AST::Node] arg
|
||||||
|
# @return [Oga::Ruby::Node]
|
||||||
|
def on_call_round(input, arg)
|
||||||
|
arg_ast = try_match_first_node(arg, input)
|
||||||
|
call_arg = unique_literal(:call_arg)
|
||||||
|
conversion = literal(Conversion)
|
||||||
|
|
||||||
|
initial_assign = call_arg.assign(arg_ast)
|
||||||
|
float_assign = call_arg.assign(conversion.to_float(call_arg))
|
||||||
|
|
||||||
|
if_nan = call_arg.nan?
|
||||||
|
.if_true { call_arg }
|
||||||
|
.else { call_arg.round.to_f }
|
||||||
|
|
||||||
|
initial_assign.followed_by(float_assign)
|
||||||
|
.followed_by(if_nan)
|
||||||
|
end
|
||||||
|
|
||||||
|
# @param [Oga::Ruby::Node] input
|
||||||
|
# @param [Array<AST::Node>] args
|
||||||
|
# @return [Oga::Ruby::Node]
|
||||||
|
def on_call_concat(input, *args)
|
||||||
|
conversion = literal(Conversion)
|
||||||
|
assigns = []
|
||||||
|
conversions = []
|
||||||
|
|
||||||
|
args.each do |arg|
|
||||||
|
arg_var = unique_literal(:concat_arg)
|
||||||
|
arg_ast = try_match_first_node(arg, input)
|
||||||
|
|
||||||
|
assigns << arg_var.assign(arg_ast)
|
||||||
|
conversions << conversion.to_string(arg_var)
|
||||||
|
end
|
||||||
|
|
||||||
|
assigns.inject(:followed_by)
|
||||||
|
.followed_by(conversions.inject(:+))
|
||||||
|
end
|
||||||
|
|
||||||
|
# @param [Oga::Ruby::Node] input
|
||||||
|
# @param [AST::Node] haystack_ast
|
||||||
|
# @param [AST::Node] needle_ast
|
||||||
|
# @return [Oga::Ruby::Node]
|
||||||
|
def on_call_contains(input, haystack, needle)
|
||||||
|
haystack_lit = unique_literal(:haystack)
|
||||||
|
needle_lit = unique_literal(:needle)
|
||||||
|
conversion = literal(Conversion)
|
||||||
|
|
||||||
|
haystack_ast = try_match_first_node(haystack, input)
|
||||||
|
needle_ast = try_match_first_node(needle, input)
|
||||||
|
|
||||||
|
include_call = conversion.to_string(haystack_lit)
|
||||||
|
.include?(conversion.to_string(needle_lit))
|
||||||
|
|
||||||
|
haystack_lit.assign(haystack_ast)
|
||||||
|
.followed_by(needle_lit.assign(needle_ast))
|
||||||
|
.followed_by(include_call)
|
||||||
|
end
|
||||||
|
|
||||||
|
# @param [Oga::Ruby::Node] input
|
||||||
|
# @param [AST::Node] arg
|
||||||
|
# @return [Oga::Ruby::Node]
|
||||||
|
def on_call_count(input, arg)
|
||||||
|
count = unique_literal(:count)
|
||||||
|
assign = count.assign(literal('0.0'))
|
||||||
|
|
||||||
|
unless return_nodeset?(arg)
|
||||||
|
raise TypeError, 'count() can only operate on NodeSet instances'
|
||||||
|
end
|
||||||
|
|
||||||
|
increment = process(arg, input) do
|
||||||
|
count.assign(count + literal('1'))
|
||||||
|
end
|
||||||
|
|
||||||
|
assign.followed_by(increment)
|
||||||
|
.followed_by(count)
|
||||||
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
# Delegates type tests to specific handlers.
|
# Delegates type tests to specific handlers.
|
||||||
#
|
#
|
||||||
|
@ -818,6 +938,19 @@ module Oga
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Tries to match the first node in a set, otherwise processes it as usual.
|
||||||
|
#
|
||||||
|
# @see [#match_first_node]
|
||||||
|
#
|
||||||
|
def try_match_first_node(ast, input, optimize_first = true)
|
||||||
|
if return_nodeset?(ast) and optimize_first
|
||||||
|
match_first_node(ast, input)
|
||||||
|
else
|
||||||
|
process(ast, input)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
# Generates the code for an operator.
|
# Generates the code for an operator.
|
||||||
#
|
#
|
||||||
|
@ -887,17 +1020,8 @@ module Oga
|
||||||
left_var = unique_literal(:op_left)
|
left_var = unique_literal(:op_left)
|
||||||
right_var = unique_literal(:op_right)
|
right_var = unique_literal(:op_right)
|
||||||
|
|
||||||
if return_nodeset?(left) and optimize_first
|
left_ast = try_match_first_node(left, input, optimize_first)
|
||||||
left_ast = match_first_node(left, input)
|
right_ast = try_match_first_node(right, input, optimize_first)
|
||||||
else
|
|
||||||
left_ast = process(left, input)
|
|
||||||
end
|
|
||||||
|
|
||||||
if return_nodeset?(right) and optimize_first
|
|
||||||
right_ast = match_first_node(right, input)
|
|
||||||
else
|
|
||||||
right_ast = process(right, input)
|
|
||||||
end
|
|
||||||
|
|
||||||
initial_assign = left_var.assign(left_ast.wrap)
|
initial_assign = left_var.assign(left_ast.wrap)
|
||||||
.followed_by(right_var.assign(right_ast.wrap))
|
.followed_by(right_var.assign(right_ast.wrap))
|
||||||
|
|
Loading…
Reference in New Issue