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] | ||||
|       # | ||||
|       def on_call(ast, input, &block) | ||||
|         name, args = *ast | ||||
|         name, *args = *ast | ||||
| 
 | ||||
|         handler = name.gsub('-', '_') | ||||
| 
 | ||||
|         send(:"on_call_#{handler}", input, *args, &block) | ||||
|       end | ||||
| 
 | ||||
|       ## | ||||
|       # Processes the `true()` function call. | ||||
|       # | ||||
|       # @return [Oga::Ruby::Node] | ||||
|       # | ||||
|       def on_call_true(*) | ||||
|         self.true | ||||
|       end | ||||
| 
 | ||||
|       ## | ||||
|       # Processes the `false()` function call. | ||||
|       # | ||||
|       # @return [Oga::Ruby::Node] | ||||
|       # | ||||
|       def on_call_false(*) | ||||
|         self.false | ||||
|       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. | ||||
|       # | ||||
|  | @ -818,6 +938,19 @@ module Oga | |||
|         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. | ||||
|       # | ||||
|  | @ -887,17 +1020,8 @@ module Oga | |||
|         left_var  = unique_literal(:op_left) | ||||
|         right_var = unique_literal(:op_right) | ||||
| 
 | ||||
|         if return_nodeset?(left) and optimize_first | ||||
|           left_ast = match_first_node(left, input) | ||||
|         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 | ||||
|         left_ast  = try_match_first_node(left, input, optimize_first) | ||||
|         right_ast = try_match_first_node(right, input, optimize_first) | ||||
| 
 | ||||
|         initial_assign = left_var.assign(left_ast.wrap) | ||||
|           .followed_by(right_var.assign(right_ast.wrap)) | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue