# # xpath.rb: generated by racc (runtime embedded) # ###### racc/parser.rb unless $".index 'racc/parser.rb' then $".push 'racc/parser.rb' type.module_eval <<'..end /home/katsu/local/lib/site_ruby/racc/parser.rb modeval..id92db944ac5', '/home/katsu/local/lib/site_ruby/racc/parser.rb', 1 # # parser.rb # # Copyright (c) 1999,2000 Minero Aoki # # This program is free software. # You can distribute/modify this program under the terms of # the GNU Lesser General Public License version 2 or later. # # As a special exception, when this code is copied by Racc # into a Racc output file, you may use that output file # without restriction. # module Racc class ParseError < StandardError; end end unless defined? ParseError then ParseError = Racc::ParseError end module Racc class Parser private begin if defined? Racc_Debug_Ruby_Parser then raise LoadError, 'debug ruby routine' end require 'racc/cparse' unless new.respond_to? :_racc_do_parse_c, true then raise LoadError, 'old cparse.so' end Racc_Main_Parsing_Routine = :_racc_do_parse_c Racc_YY_Parse_Method = :_racc_yyparse_c rescue LoadError Racc_Main_Parsing_Routine = :_racc_do_parse_rb Racc_YY_Parse_Method = :_racc_yyparse_rb end Racc_ruby_parser_version = '1.3.3' Racc_parser_version = Racc_ruby_parser_version def self.racc_runtime_type if Racc_Main_Parsing_Routine == :_racc_do_parse_c then 'c' else 'ruby' end end def _racc_setup t = self.type unless t::Racc_debug_parser then @yydebug = false end @yydebug = false unless defined? @yydebug if @yydebug then @racc_debug_out = $stderr unless defined? @racc_debug_out @racc_debug_out ||= $stderr end arg = t::Racc_arg if arg.size < 14 then arg[13] = true end arg end def _racc_init_sysvars @racc_state = [ 0 ] @racc_tstack = [] @racc_vstack = [] @racc_t = nil @racc_val = nil @racc_read_next = true @racc_user_yyerror = false @racc_error_status = 0 end ### ### do_parse ### def do_parse __send__ Racc_Main_Parsing_Routine, _racc_setup(), false end def next_token raise NotImplementError, "#{self.type}\#next_token must be defined" end def _racc_do_parse_rb( arg, in_debug ) action_table, action_check, action_default, action_pointer, goto_table, goto_check, goto_default, goto_pointer, nt_base, reduce_table, token_table, shift_n, reduce_n, use_result = arg _racc_init_sysvars act = i = nil nerr = 0 catch( :racc_end_parse ) { while true do if i = action_pointer[ @racc_state[-1] ] then if @racc_read_next then if @racc_t != 0 then # $ tok, @racc_val = next_token() @racc_t = (token_table[tok] or 1) # error token racc_read_token( @racc_t, tok, @racc_val ) if @yydebug @racc_read_next = false end end i += @racc_t if i >= 0 and act = action_table[i] and action_check[i] == @racc_state[-1] then ; else act = action_default[ @racc_state[-1] ] end else act = action_default[ @racc_state[-1] ] end while act = _racc_evalact( act, arg ) do end end } end ### ### yyparse ### def yyparse( recv, mid ) __send__ Racc_YY_Parse_Method, recv, mid, _racc_setup(), true end def _racc_yyparse_rb( recv, mid, arg, c_debug ) action_table, action_check, action_default, action_pointer, goto_table, goto_check, goto_default, goto_pointer, nt_base, reduce_table, token_table, shift_n, reduce_n, use_result, = arg _racc_init_sysvars tok = nil act = nil i = nil nerr = 0 catch( :racc_end_parse ) { until i = action_pointer[ @racc_state[-1] ] do while act = _racc_evalact( action_default[ @racc_state[-1] ], arg ) do end end recv.__send__( mid ) do |tok, val| # $stderr.puts "rd: tok=#{tok}, val=#{val}" @racc_val = val @racc_t = (token_table[tok] or 1) # error token @racc_read_next = false i += @racc_t if i >= 0 and act = action_table[i] and action_check[i] == @racc_state[-1] then # $stderr.puts "01: act=#{act}" else act = action_default[ @racc_state[-1] ] # $stderr.puts "02: act=#{act}" # $stderr.puts "curstate=#{@racc_state[-1]}" end while act = _racc_evalact( act, arg ) do end while not (i = action_pointer[ @racc_state[-1] ]) or not @racc_read_next or @racc_t == 0 do # $ if i and i += @racc_t and i >= 0 and act = action_table[i] and action_check[i] == @racc_state[-1] then # $stderr.puts "03: act=#{act}" ; else # $stderr.puts "04: act=#{act}" act = action_default[ @racc_state[-1] ] end while act = _racc_evalact( act, arg ) do end end end } end ### ### common ### def _racc_evalact( act, arg ) # $stderr.puts "ea: act=#{act}" action_table, action_check, action_default, action_pointer, goto_table, goto_check, goto_default, goto_pointer, nt_base, reduce_table, token_table, shift_n, reduce_n, use_result, = arg nerr = 0 # tmp if act > 0 and act < shift_n then # # shift # if @racc_error_status > 0 then @racc_error_status -= 1 unless @racc_t == 1 # error token end @racc_vstack.push @racc_val @racc_state.push act @racc_read_next = true if @yydebug then @racc_tstack.push @racc_t racc_shift( @racc_t, @racc_tstack, @racc_vstack ) end elsif act < 0 and act > -reduce_n then # # reduce # code = catch( :racc_jump ) { @racc_state.push _racc_do_reduce( arg, act ) false } if code then case code when 1 # yyerror @racc_user_yyerror = true # user_yyerror return -reduce_n when 2 # yyaccept return shift_n else raise RuntimeError, '[Racc Bug] unknown jump code' end end elsif act == shift_n then # # accept # racc_accept if @yydebug throw :racc_end_parse, @racc_vstack[0] elsif act == -reduce_n then # # error # case @racc_error_status when 0 unless arg[21] then # user_yyerror nerr += 1 on_error @racc_t, @racc_val, @racc_vstack end when 3 if @racc_t == 0 then # is $ throw :racc_end_parse, nil end @racc_read_next = true end @racc_user_yyerror = false @racc_error_status = 3 while true do if i = action_pointer[ @racc_state[-1] ] then i += 1 # error token if i >= 0 and (act = action_table[i]) and action_check[i] == @racc_state[-1] then break end end throw :racc_end_parse, nil if @racc_state.size < 2 @racc_state.pop @racc_vstack.pop if @yydebug then @racc_tstack.pop racc_e_pop( @racc_state, @racc_tstack, @racc_vstack ) end end return act else raise RuntimeError, "[Racc Bug] unknown action #{act.inspect}" end racc_next_state( @racc_state[-1], @racc_state ) if @yydebug nil end def _racc_do_reduce( arg, act ) action_table, action_check, action_default, action_pointer, goto_table, goto_check, goto_default, goto_pointer, nt_base, reduce_table, token_table, shift_n, reduce_n, use_result, = arg state = @racc_state vstack = @racc_vstack tstack = @racc_tstack i = act * -3 len = reduce_table[i] reduce_to = reduce_table[i+1] method_id = reduce_table[i+2] void_array = [] tmp_t = tstack[ -len, len ] if @yydebug tmp_v = vstack[ -len, len ] tstack[ -len, len ] = void_array if @yydebug vstack[ -len, len ] = void_array state[ -len, len ] = void_array # tstack must be updated AFTER method call if use_result then vstack.push __send__(method_id, tmp_v, vstack, tmp_v[0]) else vstack.push __send__(method_id, tmp_v, vstack) end tstack.push reduce_to racc_reduce( tmp_t, reduce_to, tstack, vstack ) if @yydebug k1 = reduce_to - nt_base if i = goto_pointer[ k1 ] then i += state[-1] if i >= 0 and (curstate = goto_table[i]) and goto_check[i] == k1 then return curstate end end goto_default[ k1 ] end def on_error( t, val, vstack ) raise ParseError, "\nparse error on value #{val.inspect}" end def yyerror throw :racc_jump, 1 end def yyaccept throw :racc_jump, 2 end def yyerrok @racc_error_status = 0 end # for debugging output def racc_read_token( t, tok, val ) @racc_debug_out.print 'read ' @racc_debug_out.print tok.inspect, '(internaly ', racc_token2str(t), ') ' @racc_debug_out.puts val.inspect @racc_debug_out.puts end def racc_shift( tok, tstack, vstack ) @racc_debug_out.puts "shift #{racc_token2str tok}" racc_print_stacks tstack, vstack @racc_debug_out.puts end def racc_reduce( toks, sim, tstack, vstack ) out = @racc_debug_out out.print 'reduce ' if toks.empty? then out.print ' ' else toks.each {|t| out.print ' ', racc_token2str(t) } end out.puts " --> #{racc_token2str(sim)}" racc_print_stacks tstack, vstack @racc_debug_out.puts end def racc_accept @racc_debug_out.puts 'accept' @racc_debug_out.puts end def racc_e_pop( state, tstack, vstack ) @racc_debug_out.puts 'error recovering mode: pop token' racc_print_states state racc_print_stacks tstack, vstack @racc_debug_out.puts end def racc_next_state( curstate, state ) @racc_debug_out.puts "goto #{curstate}" racc_print_states state @racc_debug_out.puts end def racc_print_stacks( t, v ) out = @racc_debug_out out.print ' [' t.each_index do |i| out.print ' (', racc_token2str(t[i]), ' ', v[i].inspect, ')' end out.puts ' ]' end def racc_print_states( s ) out = @racc_debug_out out.print ' [' s.each {|st| out.print ' ', st } out.puts ' ]' end def racc_token2str( tok ) type::Racc_token_to_s_table[tok] or raise RuntimeError, "[Racc Bug] can't convert token #{tok} to string" end end end ..end /home/katsu/local/lib/site_ruby/racc/parser.rb modeval..id92db944ac5 end # end of racc/parser.rb # # xpath.rb : generated by racc # module XPath class Error < StandardError ; end class CompileError < Error ; end class TypeError < Error ; end class NameError < Error ; end class ArgumentError < Error ; end class InvalidOperation < Error ; end class XPathProc def initialize(proc, source) @proc = proc @source = source end attr_reader :source def call(context) @proc.call context end end def self.compile(src, pattern = false) @compiler = Compiler.new unless defined? @compiler @compiler.compile src, pattern end module XPathObject def _type type.name.sub(/\A.*::(?:XPath)?(?=[^:]+\z)/, '') end private :_type def type_error(into) raise XPath::TypeError, "failed to convert #{_type} into #{into}" end private :type_error def to_str # => to Ruby String type_error 'String' end def to_f # => to Ruby Float type_error 'Float' end def true? # => to Ruby Boolean type_error 'Boolean' end def to_ruby # => to Ruby Object self end def to_predicate # => to Ruby Float, true or false. shouldn't override. true? end def to_string(context) # => to XPath String. shouldn't override. context.make_string to_str end def to_number(context) # => to XPath Number. shouldn't override. context.make_number to_f end def to_boolean(context) # => to XPath Boolean. shouldn't override. context.make_boolean true? end public # called from compiled XPath expression def ==(other) if other.is_a? XPathNodeSet or other.is_a? XPathBoolean or other.is_a? XPathNumber then other == self else to_str == other.to_str end end def <(other) if other.is_a? XPathNodeSet then other > self else to_f < other.to_f end end def >(other) if other.is_a? XPathNodeSet then other < self else to_f > other.to_f end end def <=(other) if other.is_a? XPathNodeSet then other >= self else to_f <= other.to_f end end def >=(other) if other.is_a? XPathNodeSet then other <= self else to_f >= other.to_f end end def **(other) type_error 'NodeSet' end def predicate(&block) type_error 'NodeSet' end def at(pos) type_error 'NodeSet' end def funcall(name) # for XPointer raise XPath::NameError, "undefined function `#{name}' for #{_type}" end end class XPathBoolean include XPathObject class << self attr_reader :instance private :new end def to_str true?.to_s end # def to_f # def true? def to_ruby true? end def to_boolean(context) self end def ==(other) true? == other.true? end end class XPathTrueClass < XPathBoolean @instance = new def to_f 1.0 end def true? true end end class XPathFalseClass < XPathBoolean @instance = new def to_f 0.0 end def true? false end end XPathTrue = XPathTrueClass.instance XPathFalse = XPathFalseClass.instance class XPathNumber include XPathObject def initialize(num) raise ::TypeError, "must be a Float" unless num.is_a? Float @value = num end def to_str if @value.nan? then 'NaN' elsif @value.infinite? then if @value < 0 then '-Infinity' else 'Infinity' end else sprintf("%.100f", @value).gsub(/\.?0+\z/, '') # enough? end end def to_f @value end def true? @value != 0.0 and not @value.nan? end def to_ruby to_f end def to_predicate to_f end def to_number(context) self end def ==(other) if other.is_a? XPathNodeSet or other.is_a? XPathBoolean then other == self else @value == other.to_f end end def +(other) @value += other.to_f self end def -(other) @value -= other.to_f self end def *(other) @value *= other.to_f self end def /(other) @value /= other.to_f self end def %(other) n = other.to_f f = @value % n f = -f if @value < 0 f = -f if n < 0 @value = f self end def -@ @value = -@value self end def floor @value = @value.floor.to_f self end def ceil @value = @value.ceil.to_f self end def round f = @value unless f.nan? or f.infinite? then if f >= 0.0 then @value = f.round.to_f elsif f - f.truncate >= -0.5 then @value = f.ceil.to_f else @value = f.floor.to_f end end self end end class XPathString include XPathObject def initialize(str) raise ::TypeError, "must be a String" unless str.is_a? String @value = str end def to_str @value end def to_f if /\A\s*(-?\d+\.?\d*)(?:\s|\z)/ =~ @value then $1.to_f else 0.0 / 0.0 # NaN end end def true? not @value.empty? end def to_ruby to_str end def to_string(context) self end def concat(s) @value = @value + s self end def start_with?(s) /\A#{Regexp.quote(s)}/ =~ @value end def contain?(s) /#{Regexp.quote(s)}/ =~ @value end def substring_before(s) if /#{Regexp.quote(s)}/ =~ @value then @value = $` else @value = '' end self end def substring_after(s) if /#{Regexp.quote(s)}/ =~ @value then @value = $' else @value = '' end self end def substring(start, len) start = start.round.to_f if start.infinite? or start.nan? then @value = '' elsif len then len = len.round.to_f maxlen = start + len len = maxlen - 1.0 if len >= maxlen if start <= 1.0 then start = 0 else start = start.to_i - 1 end if len.nan? or len < 1.0 then @value = '' elsif len.infinite? then # @value = @value[start..-1] /\A[\W\w]{0,#{start}}/ =~ @value @value = $' else # @value = @value[start, len.to_i] /\A[\W\w]{0,#{start}}([\W\w]{0,#{len.to_i}})/ =~ @value @value = $1 end elsif start > 1.0 then # @value = @value[(start-1)..-1] /\A[\W\w]{0,#{start.to_i-1}}/ =~ @value @value = $' end raise "BUG" unless @value self end def size @value.gsub(/[^\Wa-zA-Z_\d]/, ' ').size end def normalize_space @value = @value.strip @value.gsub!(/\s+/, ' ') self end def translate(from, to) to = to.split(//) h = {} from.split(//).each_with_index { |i,n| h[i] = to[n] unless h.key? i } @value = @value.gsub(/[#{Regexp.quote(h.keys.join)}]/) { |s| h[s] } self end def replace(str) @value = str self end end class Compiler < Racc::Parser module_eval <<'..end xpath.ry modeval..idcc62899492', 'xpath.ry', 268 module CompilePhaseObject def invoke_conv(expr, conv_method) return unless conv_method if conv_method == '.to_number' or conv_method == '.to_string' or conv_method == '.to_boolean' then expr.push conv_method, '(', nil, ')' else expr.push conv_method end end private :invoke_conv end module ConstantObject include CompilePhaseObject def to_string StringConstant.new to_str end def to_number NumberConstant.new self end def to_boolean if true? then ConstantTrue else ConstantFalse end end end module BooleanConstant include ConstantObject def value_type :boolean end def expr(conv_method = nil) if conv_method == '.to_ruby' or conv_method == '.true?' then [ true?.to_s ] else ret = [ nil, '.make_boolean(', true?.to_s, ')' ] invoke_conv ret, conv_method unless conv_method == '.to_boolean' ret end end end class ConstantTrueClass < XPathTrueClass include BooleanConstant @instance = new end class ConstantFalseClass < XPathFalseClass include BooleanConstant @instance = new end ConstantTrue = ConstantTrueClass.instance ConstantFalse = ConstantFalseClass.instance class NumberConstant < XPathNumber include ConstantObject def value_type :number end def initialize(src) f = src.to_f if src.is_a? ConstantObject and s = dump_float(f) then src = s end @src = [ src ] @precedence = 1 super f end attr_reader :precedence protected :precedence def to_number self end def expr(conv_method = nil) @src.collect! { |i| if i.is_a? ConstantObject then i.expr '.to_f' else i end } expr = @src expr.flatten! @src = :draff # for debug unless conv_method == '.to_ruby' or conv_method == '.to_f' then expr[0, 0] = [ nil, '.make_number(' ] expr.push(')') invoke_conv expr, conv_method unless conv_method == '.to_number' end expr end private def dump_float(f) if f.finite? and f == eval(s = f.to_s) then s elsif f.infinite? then if f > 0 then '(1.0 / 0.0)' else '(-1.0 / 0.0)' end elsif f.nan? then '(0.0 / 0.0)' else nil end end def concat(op, other, prec) @src.unshift('(').push(')') if @precedence < prec if other.precedence < prec then @src.push(op).push('(').concat(other.expr('.to_f')).push(')') else @src.push(op).concat(other.expr('.to_f')) end @precedence = prec end public def self.def_arithmetic_operator(op, precedence) module_eval <<_, __FILE__, __LINE__ + 1 def #{op}(other) super other if s = dump_float(@value) then @src.clear @src.push s else concat ' #{op} ', other, #{precedence} end self end _ end def_arithmetic_operator '+', 0 def_arithmetic_operator '-', 0 def_arithmetic_operator '*', 1 def_arithmetic_operator '/', 1 class << self undef def_arithmetic_operator end def %(other) orig = @value super other if s = dump_float(@value) then @src.clear @src.push s else f = other.to_f other = -other if orig % f == -@value concat ' % ', other, 1 end self end def -@ super if s = dump_float(@value) then @src.clear @src.push s else if @src.size == 1 then @src.unshift '-' else @src.unshift('-(').push(')') end @precedence = 1 end self end end class StringConstant < XPathString include ConstantObject def value_type :string end def to_string self end def expr(conv_method = nil) if conv_method == '.to_ruby' or conv_method == '.to_str' then [ @value.dump ] else ret = [ nil, '.make_string(', @value.dump, ')' ] invoke_conv ret, conv_method unless conv_method == '.to_string' ret end end end class Expression include CompilePhaseObject def initialize(expr) if expr.is_a? ConstantObject then @value = expr else raise "BUG" unless expr.is_a? Array @value = nil @valuetype = nil @expr = expr end @unary = true end attr_reader :value def value_type if @value then @value.value_type else @valuetype end end def unarize unless @unary then @expr.unshift('(').push(')') @unary = true end self end def self.def_comparison_operator(name, op) module_eval <<_, __FILE__, __LINE__ + 1 def #{name}(other) if @value and other.value then if @value #{op} other.value then @value = ConstantTrue else @value = ConstantFalse end @unary = true else @expr = expr.push(' #{op} ').concat(other.expr) @valuetype = :ruby_boolean @unary = false end self end _ end def self.def_arithmetic_operator(*ops) ops.each { |op| module_eval <<_, __FILE__, __LINE__ + 1 def #{op}(other) if @value and other.value then @value = @value.to_number #{op} other.value.to_number else @expr = expr('.to_number').push(' #{op} ') # not 'to_number', for a little speed up :-) @expr.concat other.expr('.to_f') @valuetype = :number @unary = false end self end _ } end def_comparison_operator 'eq', '==' def_comparison_operator 'neq', '!=' def_comparison_operator 'lt', '<' def_comparison_operator 'gt', '>' def_comparison_operator 'le', '<=' def_comparison_operator 'ge', '>=' def_arithmetic_operator '+', '-', '*', '/', '%' class << self undef def_comparison_operator undef def_arithmetic_operator end def -@ if @value then @value = -@value.to_number else unarize @expr = expr('.to_number').unshift('-') end self end def logical_or(other) if @value and @value.true? then @value = ConstantTrue @unary = true @expr = @valuetype = nil else @expr = expr('.true?').push(' || ').concat(other.expr('.true?')) @valuetype = :ruby_boolean @unary = false end self end def logical_and(other) if @value and not @value.true? then @value = ConstantFalse @unary = true @expr = @valuetype = nil else @expr = expr('.true?').push(' && ').concat(other.expr('.true?')) @valuetype = :ruby_boolean @unary = false end self end def **(other) @expr = expr.push(' ** ').concat(other.expr) @valuetype = nil @unary = false self end def add_predicate(pred) unarize @expr = expr.concat(pred) @valuetype = nil self end def <<(other) path = other.expr path.shift # nil path.shift # .to_nodeset add_predicate path end def add_step(axis) add_predicate [ ".step(:#{axis.tr('-','_')})" ] end def expr(conv_method = nil) if @value then ret = @value.expr(conv_method) @value = nil elsif @valuetype == :ruby_boolean then ret = @expr unless conv_method == '.to_ruby' or conv_method == '.true?' then ret[0, 0] = [ nil, '.make_boolean(' ] ret.push ')' invoke_conv ret, conv_method unless conv_method == '.to_boolean' end elsif @valuetype == :number and conv_method == '.to_number' then ret = @expr elsif @valuetype == :string and conv_method == '.to_string' then ret = @expr elsif @valuetype == :boolean and conv_method == '.to_boolean' then ret = @expr else if conv_method then unarize invoke_conv @expr, conv_method end ret = @expr end @expr = :draff # for debug ret end end class LocationPath include CompilePhaseObject def initialize @root = false @steps = [] # [ axis, [ tests ], predicates ] end attr_reader :root, :steps protected :root, :steps def absolute! @root = true self end def add_step(axis, nodetype = false, localpart = false, namespace = false, predicate = nil) if nodetype == false and localpart == false and namespace == false then append_step axis, [], predicate else append_step axis, [ [ nodetype, localpart, namespace ] ], predicate end self end def <<(other) raise "BUG" if other.root other = other.steps other.each { |step| if step[0] then append_step(*step) else add_predicate(step[2]) end } self end def add_predicate(pred) @steps.push [ nil, nil, pred ] self end def **(other) unless other.is_a? LocationPath then ret = nil else othersteps = other.steps size = @steps.size unless size == othersteps.size then othersize = othersteps.size if size >= othersize then ret = (@steps[0, othersize] == othersize and self) else ret = (othersteps[0, size] == @steps and other) end else last = @steps.pop otherlast = othersteps.pop if @steps == othersteps and mix_step(last, otherlast) then ret = self else ret = nil end @steps.push last othersteps.push otherlast end end ret or Expression.new(expr) ** other end private UnifiableAxes = { 'descendant' => { 'descendant-or-self' => 'descendant', }, 'descendant-or-self' => { 'child' => 'descendant', 'descendant' => 'descendant', 'descendant-or-self' => 'descendant-or-self', }, 'ancestor' => { 'ancestor-or-self' => 'ancestor', }, 'ancestor-or-self' => { 'parent' => 'ancestor', 'ancestor' => 'ancestor', 'ancestor-or-self' => 'ancestor-or-self', }, 'following-sibling' => { 'following-sibling' => 'following-sibling', }, 'preceding-sibling' => { 'preceding-sibling' => 'preceding-sibling', }, 'following' => { 'following' => 'following', 'following-sibling' => 'following', }, 'preceding' => { 'preceding' => 'preceding', 'preceding-sibling' => 'preceding', }, 'child' => { 'following-sibling' => 'child', 'preceding-sibling' => 'child', }, } UnifiableAxes.default = {} def append_step(axis, test, predicate) lastaxis, lasttest, lastpred = laststep = @steps.last if axis == 'self' and test.empty? then @steps.push [ nil, nil, predicate ] if predicate elsif lastaxis and lasttest.empty? and not lastpred and not predicate and w = UnifiableAxes[lastaxis][axis] then laststep[0] = w laststep[1] = test else @steps.push [ axis, test, predicate ] end end def mix_step(step, other) if step[0] and step[0] == other[0] and step[2] == other[2] then step[1].concat other[1] step else nil end end public def expr(conv_method = nil) if @root then expr = [ nil, '.root_nodeset' ] else expr = [ nil, '.to_nodeset' ] end @steps.each { |axis,test,predicate| if axis.nil? then # predicate only expr.concat predicate elsif test.empty? and not predicate then expr.push ".select_all(:#{axis.tr('-','_')})" else expr.push ".step(:#{axis.tr('-','_')})" if test.empty? then expr.push ' { |n| n.select_all' else expr.push ' { |n| n.select { |i| ' test.each { |nodetype,localpart,namespace| if nodetype then expr.push "i.node_type == :#{nodetype.tr('-','_')}", ' && ' end if localpart then expr.push "i.name_localpart == #{localpart.dump}", ' && ' end if namespace.nil? then expr.push 'i.namespace_uri.nil?', ' && ' elsif namespace then namespace = namespace.dump expr.push('i.namespace_uri == ', nil, ".get_namespace(#{namespace})", ' && ') end expr[-1] = ' or ' } expr[-1] = ' }' end expr.concat predicate if predicate expr.push ' }' end } @steps = :draff # for debug invoke_conv expr, conv_method expr end def value_type nil end def value nil end def unarize self end def self.redirect_to_expr(*ops) ops.each { |op| name = op name = op[1..-1] if op[0] == ?. module_eval <<_, __FILE__, __LINE__ + 1 def #{name}(arg) ; Expression.new(expr) #{op} arg ; end _ } end redirect_to_expr('.eq', '.neq', '.lt', '.gt', '.le', '.ge', '+', '-', '*', '/', '%', '.logical_or', '.logical_and') class << self undef redirect_to_expr end def -@ -Expression.new(expr) end end Delim = '\\s\\(\\)\\[\\]\\.@,\\/\\|\\*\\+"\'=!<>:' Name = "[^-#{Delim}][^#{Delim}]*" Operator = { '@' => true, '::' => true, '(' => true, '[' => true, :MUL => true, 'and' => true, 'or' => true, 'mod' => true, 'div' => true, '/' => true, '//' => true, '|' => true, '+' => true, '-' => true, '=' => true, '!=' => true, '<' => true, '<=' => true, '>' => true, '>=' => true, ':' => false # ':' '*' => '*' must not be a MultiplyOperator # ':' 'and' => 'and' must be a OperatorName } NodeType = { 'comment' => true, 'text' => true, 'processing-instruction' => true, 'node' => true, } private def axis?(s) /\A[-a-zA-Z]+\z/ =~ s end def nodetype?(s) NodeType.key? s end def tokenize(src) token = [] src.scan(/(\.\.?|\/\/?|::?|!=|[<>]=?|[-()\[\].@,|+=*])| ("[^"]*"|'[^']*')|(\d+\.?\d*)| (\$?#{Name}(?::#{Name})?)| \s+|./ox) { |delim,literal,number,name| #/ if delim then if delim == '*' then delim = :MUL if (prev = token[-1]) and not Operator.key? prev[0] elsif delim == '::' then prev = token[-1] if prev and prev[0] == :Name and axis? prev[1] then prev[0] = :AxisName end elsif delim == '(' then if (prev = token[-1]) and prev[0] == :Name then if nodetype? prev[1] then prev[0] = :NodeType else prev[0] = :FuncName end end end token.push [ delim, delim ] elsif name then prev = token[-1] if name[0] == ?$ then name[0,1] = '' token.push [ :Variable, name ] elsif Operator.key? name and (prev = token[-1]) and not Operator[prev[0]] then token.push [ name, name ] else token.push [ :Name, name ] end elsif number then number << '.0' unless number.include? ?. token.push [ :Number, number ] elsif literal then literal.chop! literal[0,1] = '' token.push [ :Literal, literal ] else s = $&.strip token.push [ s, s ] unless s.empty? end } token end public def compile(src, pattern = false) @token = tokenize(src) @token.push [ false, :end ] @token.each { |i| p i } if @yydebug @token.reverse! @token.push [ :PATTERN, nil ] if pattern @context = 'context0' ret = do_parse ret = ret.unshift("proc { |context0| ").push(" }").join print ">>>>\n", ret, "\n<<<<\n" if @yydebug XPathProc.new eval(ret), src end def initialize(debug = false) super() @yydebug = debug end private def next_token @token.pop end def is_xpointer? false end def on_error(*args) # tok, val, values raise CompileError, 'parse error' end ..end xpath.ry modeval..idcc62899492 ##### racc 1.3.3 generates ### racc_reduce_table = [ 0, 0, :racc_error, 0, 39, :_reduce_1, 1, 39, :_reduce_2, 2, 39, :_reduce_3, 1, 41, :_reduce_none, 3, 41, :_reduce_5, 3, 40, :_reduce_6, 3, 40, :_reduce_7, 3, 40, :_reduce_8, 3, 40, :_reduce_9, 3, 40, :_reduce_10, 3, 40, :_reduce_11, 3, 40, :_reduce_12, 3, 40, :_reduce_13, 3, 40, :_reduce_14, 3, 40, :_reduce_15, 2, 40, :_reduce_16, 3, 40, :_reduce_17, 3, 40, :_reduce_18, 3, 40, :_reduce_19, 3, 40, :_reduce_20, 1, 40, :_reduce_none, 1, 40, :_reduce_none, 3, 40, :_reduce_23, 3, 40, :_reduce_24, 1, 43, :_reduce_25, 3, 43, :_reduce_26, 1, 43, :_reduce_27, 1, 43, :_reduce_28, 1, 43, :_reduce_29, 2, 43, :_reduce_30, 4, 45, :_reduce_31, 0, 47, :_reduce_32, 1, 47, :_reduce_33, 3, 47, :_reduce_34, 0, 48, :_reduce_35, 0, 49, :_reduce_36, 5, 46, :_reduce_37, 1, 42, :_reduce_38, 2, 42, :_reduce_39, 2, 42, :_reduce_40, 1, 42, :_reduce_none, 1, 44, :_reduce_42, 3, 44, :_reduce_43, 3, 44, :_reduce_44, 0, 51, :_reduce_45, 0, 52, :_reduce_46, 8, 44, :_reduce_47, 1, 50, :_reduce_48, 1, 50, :_reduce_49, 3, 50, :_reduce_50, 0, 55, :_reduce_none, 2, 55, :_reduce_52, 1, 54, :_reduce_53, 1, 54, :_reduce_54, 3, 54, :_reduce_55, 4, 54, :_reduce_56, 0, 56, :_reduce_none, 1, 56, :_reduce_none, 0, 53, :_reduce_59, 1, 53, :_reduce_60, 2, 53, :_reduce_none ] racc_reduce_n = 62 racc_shift_n = 100 racc_action_table = [ -1, 75, 87, 16, 19, 16, 19, 82, 2, 9, 12, 9, 12, 42, 44, 47, 48, 5, 7, 10, 14, 18, 43, 20, 1, 4, 71, 2, 84, 16, 19, 26, 83, 47, 48, 9, 12, 7, 10, 14, 18, 26, 20, 1, 4, 46, 2, 26, 16, 19, 52, 49, 54, 50, 9, 12, 7, 10, 14, 18, -32, 20, 1, 4, -32, 2, 88, 16, 19, 47, 48, 47, 48, 9, 12, 7, 10, 14, 18, -32, 20, 1, 4, -32, 2, 90, 16, 19, 47, 48, 43, 79, 9, 12, 7, 10, 14, 18, 78, 20, 1, 4, 95, 2, 26, 16, 19, 96, 37, 83, 99, 9, 12, 7, 10, 14, 18, nil, 20, 1, 4, nil, 2, nil, 16, 19, nil, nil, nil, nil, 9, 12, 7, 10, 14, 18, nil, 20, 1, 4, nil, 2, nil, 16, 19, nil, nil, nil, nil, 9, 12, 7, 10, 14, 18, nil, 20, 1, 4, nil, 2, nil, 16, 19, nil, nil, nil, nil, 9, 12, 7, 10, 14, 18, nil, 20, 1, 4, nil, 2, nil, 16, 19, nil, nil, nil, nil, 9, 12, 7, 10, 14, 18, nil, 20, 1, 4, nil, 2, nil, 16, 19, nil, nil, nil, nil, 9, 12, 7, 10, 14, 18, nil, 20, 1, 4, nil, 2, nil, 16, 19, nil, nil, nil, nil, 9, 12, 7, 10, 14, 18, nil, 20, 1, 4, nil, 2, nil, 16, 19, nil, nil, nil, nil, 9, 12, 7, 10, 14, 18, nil, 20, 1, 4, nil, 2, nil, 16, 19, nil, nil, nil, nil, 9, 12, 7, 10, 14, 18, nil, 20, 1, 4, nil, 2, nil, 16, 19, nil, nil, nil, nil, 9, 12, 7, 10, 14, 18, nil, 20, 1, 4, nil, 2, nil, 16, 19, nil, nil, nil, nil, 9, 12, 7, 10, 14, 18, nil, 20, 1, 4, nil, 2, nil, 16, 19, nil, nil, nil, nil, 9, 12, 7, 10, 14, 18, nil, 20, 1, 4, nil, 2, nil, 16, 19, nil, nil, nil, nil, 9, 12, 7, 10, 14, 18, nil, 20, 1, 4, nil, 2, nil, 16, 19, nil, nil, nil, nil, 9, 12, 7, 10, 14, 18, nil, 20, 1, 4, nil, 2, nil, 16, 19, nil, nil, nil, nil, 9, 12, 7, 10, 14, 18, nil, 20, 1, 4, nil, 2, nil, 16, 19, nil, nil, nil, nil, 9, 12, 7, 10, 14, 18, nil, 20, 1, 4, 16, 19, nil, 16, 19, nil, 9, 12, nil, 9, 12, 26, nil, 29, 31, 33, 35, 23, 25, 27, 28, 30, 32, 34, 36, 24, 7, 10, 7, 10, 26, 81, 29, 31, 33, 16, 19, 16, 19, 16, 19, 9, 12, 9, 12, 9, 12, 26, nil, 29, 31, 33, 35, 23, 25, 27, 28, 30, 32, 34, 36, 24, 26, nil, 29, 31, 33, 35, 23, 25, 27, 28, 30, 32, 34, 36, 24, 26, nil, 29, 31, 33, 35, 23, 25, 27, 28, 30, 32, 34, 36, 24, 26, nil, 29, 31, 33, 35, 23, 25, 27, 28, 30, 32, 34, 36, 24, 26, nil, 29, 31, 33, 35, 23, 25, 27, 28, 30, 32, 34, 36, 26, nil, 29, 31, 33, 35, 23, 25, 27, 28, 30, 32, 34, 26, nil, 29, 31, 33, 35, 23, 25, 27, 28, 30, 26, nil, 29, 31, 33, 35, 23, 25, 27, 28, 30, 16, 19, -59, -59, nil, -59, 9, 12, 16, 19, nil, nil, nil, nil, 9, 12, 26, nil, 29, 31, 33, 35, 23, 26, nil, 29, 31, 33, 35, 23, 26, nil, 29, 31, 33, 35, 23, 26, nil, 29, 31, 33, 35, 23, 26, nil, 29, 31, 33 ] racc_action_check = [ 0, 47, 75, 48, 48, 47, 47, 54, 0, 48, 48, 47, 47, 11, 11, 40, 40, 0, 0, 0, 0, 0, 11, 0, 0, 0, 38, 23, 70, 0, 0, 61, 70, 13, 13, 0, 0, 23, 23, 23, 23, 22, 23, 23, 23, 12, 37, 63, 23, 23, 15, 15, 21, 15, 23, 23, 37, 37, 37, 37, 37, 37, 37, 37, 37, 94, 78, 37, 37, 41, 41, 74, 74, 37, 37, 94, 94, 94, 94, 94, 94, 94, 94, 94, 36, 79, 94, 94, 72, 72, 80, 50, 94, 94, 36, 36, 36, 36, 49, 36, 36, 36, 89, 35, 65, 36, 36, 93, 4, 97, 98, 36, 36, 35, 35, 35, 35, nil, 35, 35, 35, nil, 34, nil, 35, 35, nil, nil, nil, nil, 35, 35, 34, 34, 34, 34, nil, 34, 34, 34, nil, 83, nil, 34, 34, nil, nil, nil, nil, 34, 34, 83, 83, 83, 83, nil, 83, 83, 83, nil, 73, nil, 83, 83, nil, nil, nil, nil, 83, 83, 73, 73, 73, 73, nil, 73, 73, 73, nil, 33, nil, 73, 73, nil, nil, nil, nil, 73, 73, 33, 33, 33, 33, nil, 33, 33, 33, nil, 32, nil, 33, 33, nil, nil, nil, nil, 33, 33, 32, 32, 32, 32, nil, 32, 32, 32, nil, 18, nil, 32, 32, nil, nil, nil, nil, 32, 32, 18, 18, 18, 18, nil, 18, 18, 18, nil, 31, nil, 18, 18, nil, nil, nil, nil, 18, 18, 31, 31, 31, 31, nil, 31, 31, 31, nil, 30, nil, 31, 31, nil, nil, nil, nil, 31, 31, 30, 30, 30, 30, nil, 30, 30, 30, nil, 2, nil, 30, 30, nil, nil, nil, nil, 30, 30, 2, 2, 2, 2, nil, 2, 2, 2, nil, 29, nil, 2, 2, nil, nil, nil, nil, 2, 2, 29, 29, 29, 29, nil, 29, 29, 29, nil, 25, nil, 29, 29, nil, nil, nil, nil, 29, 29, 25, 25, 25, 25, nil, 25, 25, 25, nil, 26, nil, 25, 25, nil, nil, nil, nil, 25, 25, 26, 26, 26, 26, nil, 26, 26, 26, nil, 27, nil, 26, 26, nil, nil, nil, nil, 26, 26, 27, 27, 27, 27, nil, 27, 27, 27, nil, 28, nil, 27, 27, nil, nil, nil, nil, 27, 27, 28, 28, 28, 28, nil, 28, 28, 28, nil, 24, nil, 28, 28, nil, nil, nil, nil, 28, 28, 24, 24, 24, 24, nil, 24, 24, 24, 44, 44, nil, 24, 24, nil, 44, 44, nil, 24, 24, 53, nil, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 71, 71, 5, 5, 67, 53, 67, 67, 67, 42, 42, 71, 71, 5, 5, 42, 42, 71, 71, 5, 5, 3, nil, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 69, nil, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 92, nil, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 86, nil, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 56, nil, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 68, nil, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 66, nil, 66, 66, 66, 66, 66, 66, 66, 66, 66, 64, nil, 64, 64, 64, 64, 64, 64, 64, 64, 64, 7, 7, 7, 7, nil, 7, 7, 7, 10, 10, nil, nil, nil, nil, 10, 10, 57, nil, 57, 57, 57, 57, 57, 60, nil, 60, 60, 60, 60, 60, 59, nil, 59, 59, 59, 59, 59, 62, nil, 62, 62, 62, 62, 62, 55, nil, 55, 55, 55 ] racc_action_pointer = [ 0, nil, 266, 451, 87, 416, nil, 533, nil, nil, 541, -5, 8, 15, nil, 19, nil, nil, 209, nil, nil, 52, 39, 19, 380, 304, 323, 342, 361, 285, 247, 228, 190, 171, 114, 95, 76, 38, 24, nil, -3, 51, 412, nil, 377, nil, nil, -24, -26, 65, 70, nil, nil, 415, 7, 604, 511, 576, nil, 590, 583, 29, 597, 45, 549, 102, 538, 434, 525, 466, 6, 414, 70, 152, 53, -19, nil, nil, 35, 62, 63, nil, nil, 133, nil, nil, 496, nil, nil, 80, nil, nil, 481, 79, 57, nil, nil, 83, 88, nil ] racc_action_default = [ -59, -28, -59, -2, -62, -59, -42, -38, -21, -60, -59, -22, -62, -41, -25, -62, -48, -29, -59, -49, -27, -62, -16, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -3, -4, -39, -40, -59, -35, -59, -30, -61, -59, -59, -54, -62, -51, -53, -62, -62, -15, -6, -10, -20, -11, -12, -17, -13, -18, -8, -19, -9, -14, -7, -33, -62, -59, -23, -59, -24, -62, -43, -44, -62, -57, -50, -26, 100, -59, -31, -5, -36, -45, -55, -62, -58, -52, -34, -62, -59, -56, -37, -46, -62, -47 ] racc_goto_table = [ 3, 45, 22, 39, 70, 40, 76, 77, 41, 73, 38, 93, 21, 94, 98, 51, 80, 89, 53, nil, nil, nil, nil, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, nil, nil, nil, 72, nil, 74, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 97, nil, nil, nil, nil, nil, nil, nil, 85, 91, nil, nil, 86, nil, nil, nil, nil, nil, nil, nil, nil, nil, 92 ] racc_goto_check = [ 2, 8, 2, 4, 9, 6, 12, 12, 6, 10, 3, 11, 1, 13, 14, 16, 17, 18, 2, nil, nil, nil, nil, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, nil, nil, nil, 6, nil, 6, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 9, nil, nil, nil, nil, nil, nil, nil, 4, 8, nil, nil, 2, nil, nil, nil, nil, nil, nil, nil, nil, nil, 2 ] racc_goto_pointer = [ nil, 12, 0, 5, -2, nil, -2, nil, -10, -33, -34, -75, -41, -74, -83, nil, 0, -35, -62 ] racc_goto_default = [ nil, nil, 69, nil, 8, 11, 13, 17, nil, nil, nil, nil, 6, nil, nil, 15, nil, nil, nil ] racc_token_table = { false => 0, Object.new => 1, "|" => 2, :NEG => 3, :MUL => 4, "div" => 5, "mod" => 6, "+" => 7, "-" => 8, "<" => 9, ">" => 10, "<=" => 11, ">=" => 12, "=" => 13, "!=" => 14, "and" => 15, "or" => 16, :PATTERN => 17, "/" => 18, "//" => 19, :Variable => 20, "(" => 21, ")" => 22, :Literal => 23, :Number => 24, :FuncName => 25, "," => 26, "[" => 27, "]" => 28, "." => 29, ".." => 30, "*" => 31, :Name => 32, ":" => 33, :NodeType => 34, "@" => 35, :AxisName => 36, "::" => 37 } racc_use_result_var = false racc_nt_base = 38 Racc_arg = [ racc_action_table, racc_action_check, racc_action_default, racc_action_pointer, racc_goto_table, racc_goto_check, racc_goto_default, racc_goto_pointer, racc_nt_base, racc_reduce_table, racc_token_table, racc_shift_n, racc_reduce_n, racc_use_result_var ] Racc_debug_parser = false ##### racc system variables end ##### # reduce 0 omitted module_eval <<'.,.,', 'xpath.ry', 27 def _reduce_1( val, _values) [] end .,., module_eval <<'.,.,', 'xpath.ry', 30 def _reduce_2( val, _values) expr = val[0].expr('.to_ruby') expr.collect! { |i| i or @context } expr end .,., module_eval <<'.,.,', 'xpath.ry', 36 def _reduce_3( val, _values) expr = val[0].expr('.to_ruby') expr.collect! { |i| i or @context } expr end .,., # reduce 4 omitted module_eval <<'.,.,', 'xpath.ry', 43 def _reduce_5( val, _values) val[0] ** val[2] end .,., module_eval <<'.,.,', 'xpath.ry', 46 def _reduce_6( val, _values) val[0].logical_or val[2] end .,., module_eval <<'.,.,', 'xpath.ry', 48 def _reduce_7( val, _values) val[0].logical_and val[2] end .,., module_eval <<'.,.,', 'xpath.ry', 50 def _reduce_8( val, _values) val[0].eq val[2] end .,., module_eval <<'.,.,', 'xpath.ry', 52 def _reduce_9( val, _values) val[0].neq val[2] end .,., module_eval <<'.,.,', 'xpath.ry', 54 def _reduce_10( val, _values) val[0].lt val[2] end .,., module_eval <<'.,.,', 'xpath.ry', 56 def _reduce_11( val, _values) val[0].gt val[2] end .,., module_eval <<'.,.,', 'xpath.ry', 58 def _reduce_12( val, _values) val[0].le val[2] end .,., module_eval <<'.,.,', 'xpath.ry', 60 def _reduce_13( val, _values) val[0].ge val[2] end .,., module_eval <<'.,.,', 'xpath.ry', 62 def _reduce_14( val, _values) val[0] + val[2] end .,., module_eval <<'.,.,', 'xpath.ry', 64 def _reduce_15( val, _values) val[0] - val[2] end .,., module_eval <<'.,.,', 'xpath.ry', 66 def _reduce_16( val, _values) -val[1] end .,., module_eval <<'.,.,', 'xpath.ry', 68 def _reduce_17( val, _values) val[0] * val[2] end .,., module_eval <<'.,.,', 'xpath.ry', 70 def _reduce_18( val, _values) val[0] / val[2] end .,., module_eval <<'.,.,', 'xpath.ry', 72 def _reduce_19( val, _values) val[0] % val[2] end .,., module_eval <<'.,.,', 'xpath.ry', 75 def _reduce_20( val, _values) # Why `**' is used for unionizing node-sets is that its # precedence is higher than any other binary operators # in Ruby. val[0] ** val[2] end .,., # reduce 21 omitted # reduce 22 omitted module_eval <<'.,.,', 'xpath.ry', 83 def _reduce_23( val, _values) val[0] << val[2] end .,., module_eval <<'.,.,', 'xpath.ry', 85 def _reduce_24( val, _values) val[0].add_step('descendant-or-self') << val[2] end .,., module_eval <<'.,.,', 'xpath.ry', 89 def _reduce_25( val, _values) Expression.new [ nil,'.get_variable(',val[0].dump,')' ] end .,., module_eval <<'.,.,', 'xpath.ry', 92 def _reduce_26( val, _values) val[1].unarize end .,., module_eval <<'.,.,', 'xpath.ry', 94 def _reduce_27( val, _values) Expression.new StringConstant.new(val[0]) end .,., module_eval <<'.,.,', 'xpath.ry', 96 def _reduce_28( val, _values) Expression.new NumberConstant.new(val[0]) end .,., module_eval <<'.,.,', 'xpath.ry', 98 def _reduce_29( val, _values) Expression.new val[0] end .,., module_eval <<'.,.,', 'xpath.ry', 100 def _reduce_30( val, _values) val[0].add_predicate val[1] end .,., module_eval <<'.,.,', 'xpath.ry', 104 def _reduce_31( val, _values) val[2][0,0] = [ nil, ".funcall(#{val[0].dump}" ] val[2].push(')') end .,., module_eval <<'.,.,', 'xpath.ry', 109 def _reduce_32( val, _values) [] end .,., module_eval <<'.,.,', 'xpath.ry', 111 def _reduce_33( val, _values) val[0].expr.unshift ', ' end .,., module_eval <<'.,.,', 'xpath.ry', 113 def _reduce_34( val, _values) val[0].push(', ').concat(val[2].expr) end .,., module_eval <<'.,.,', 'xpath.ry', 117 def _reduce_35( val, _values) c = @context @context = c.succ c end .,., module_eval <<'.,.,', 'xpath.ry', 123 def _reduce_36( val, _values) c = @context @context = _values[-2] c end .,., module_eval <<'.,.,', 'xpath.ry', 129 def _reduce_37( val, _values) expr = val[2] valuetype = expr.value_type value = expr.value if valuetype == :number then if value then f = value.to_f if f > 0 and f.truncate == f then [ ".at(#{f.to_i})" ] else [ '.at(0)' ] # clear end else expr.expr('.to_f'). unshift('.at(').push(')') end elsif value then if value.true? then [] else [ '.at(0)' ] # clear end else c = val[3] if valuetype == :ruby_boolean then conv = '.true?' else conv = '.to_predicate' end a = expr.expr(conv) a.collect! { |i| i or c } a.unshift(".predicate { |#{c}| ").push(' }') end end .,., module_eval <<'.,.,', 'xpath.ry', 164 def _reduce_38( val, _values) LocationPath.new.absolute! end .,., module_eval <<'.,.,', 'xpath.ry', 166 def _reduce_39( val, _values) val[1].absolute! end .,., module_eval <<'.,.,', 'xpath.ry', 169 def _reduce_40( val, _values) path = LocationPath.new path.absolute! path.add_step('descendant-or-self') << val[1] end .,., # reduce 41 omitted module_eval <<'.,.,', 'xpath.ry', 176 def _reduce_42( val, _values) LocationPath.new.add_step(*val[0]) end .,., module_eval <<'.,.,', 'xpath.ry', 178 def _reduce_43( val, _values) val[0].add_step(*val[2]) end .,., module_eval <<'.,.,', 'xpath.ry', 181 def _reduce_44( val, _values) val[0].add_step('descendant-or-self').add_step(*val[2]) end .,., module_eval <<'.,.,', 'xpath.ry', 186 def _reduce_45( val, _values) c = @context @context = c.succ c end .,., module_eval <<'.,.,', 'xpath.ry', 192 def _reduce_46( val, _values) c = @context @context = _values[-2] c end .,., module_eval <<'.,.,', 'xpath.ry', 198 def _reduce_47( val, _values) on_error unless is_xpointer? args = val[5] c = val[6] args.collect! { |i| i or c } args[0] = ".funcall(#{val[2].dump}) { |#{c}| [" args.push '] }' val[0].add_predicate args end .,., module_eval <<'.,.,', 'xpath.ry', 208 def _reduce_48( val, _values) [ 'self', false, false, false, nil ] end .,., module_eval <<'.,.,', 'xpath.ry', 210 def _reduce_49( val, _values) [ 'parent', false, false, false, nil ] end .,., module_eval <<'.,.,', 'xpath.ry', 213 def _reduce_50( val, _values) nodetest = val[1] unless nodetest[0] then axis = val[0] if axis != 'attribute' and axis != 'namespace' then nodetest[0] = 'element' end end nodetest[0] = false if nodetest[0] == 'node' nodetest.unshift(val[0]).push(val[2]) end .,., # reduce 51 omitted module_eval <<'.,.,', 'xpath.ry', 226 def _reduce_52( val, _values) (val[0] || []).concat val[1] end .,., module_eval <<'.,.,', 'xpath.ry', 229 def _reduce_53( val, _values) [ false, false, false ] end .,., module_eval <<'.,.,', 'xpath.ry', 232 def _reduce_54( val, _values) if /:/ =~ val[0] then [ false, $', $` ] #' <= for racc else [ false, val[0], nil ] end end .,., module_eval <<'.,.,', 'xpath.ry', 240 def _reduce_55( val, _values) on_error if /:/ =~ val[0] [ false, false, val[0] ] end .,., module_eval <<'.,.,', 'xpath.ry', 245 def _reduce_56( val, _values) nodetype = val[0] arg = val[2] if arg and nodetype != 'processing-instruction' then raise CompileError, "nodetest #{nodetype}() requires no argument" end [ nodetype, arg || false, false ] end .,., # reduce 57 omitted # reduce 58 omitted module_eval <<'.,.,', 'xpath.ry', 258 def _reduce_59( val, _values) 'child' end .,., module_eval <<'.,.,', 'xpath.ry', 260 def _reduce_60( val, _values) 'attribute' end .,., # reduce 61 omitted def _reduce_none( val, _values) val[0] end end # class Compiler # # Client NodeVisitor a NodeAdapter a Node # | | | | # |=| | | | # | |--{visit(node)}-->|=| | | # | | | |---{accept(self)}----------------->|=| # | | |=| | | | # | | | | | | # | | |=|<------------------{on_**(self)}---|=| # | | | | | | # | | | |--{wrap(node)}-->|=| | # | | | | | | | # | | | | |=| | # | |<--[NodeAdapter]--|=| | | # | | | | | # | |-----{request}----------------------->|=| | # | | | | |--{request}--->|=| # | | | | | | | # | | | | |<-----[Data]---|=| # | |<--------------------------[Data]-----|=| | # | | | | | # |=| | | | # | | | | # class TransparentNodeVisitor def visit(node) node end end class NullNodeAdapter def node self end def root nil end def parent nil end def children [] end def each_following_siblings end def each_preceding_siblings end def attributes [] end def namespaces [] end def index 0 end def node_type nil end def name_localpart nil end def qualified_name name_localpart end def namespace_uri nil end def string_value '' end def lang nil end def select_id(*ids) raise XPath::Error, "selection by ID is not supported" end end class AxisIterator def reverse_order? false end end class ReverseAxisIterator < AxisIterator def reverse_order? true end end class SelfIterator < AxisIterator def each(node, visitor) yield visitor.visit(node) end end class ChildIterator < AxisIterator def each(node, visitor, &block) visitor.visit(node).children.each { |i| yield visitor.visit(i) } end end class ParentIterator < AxisIterator def each(node, visitor) parent = visitor.visit(node).parent yield visitor.visit(parent) if parent end end class AncestorIterator < ReverseAxisIterator def each(node, visitor) node = visitor.visit(node).parent while node i = visitor.visit(node) parent = i.parent yield i node = parent end end end class AncestorOrSelfIterator < AncestorIterator def each(node, visitor) yield visitor.visit(node) super end end class DescendantIterator < AxisIterator def each(node, visitor) stack = visitor.visit(node).children.reverse while node = stack.pop i = visitor.visit(node) stack.concat i.children.reverse yield i end end end class DescendantOrSelfIterator < DescendantIterator def each(node, visitor) yield visitor.visit(node) super end end class FollowingSiblingIterator < AxisIterator def each(node, visitor) visitor.visit(node).each_following_siblings { |i| yield visitor.visit(i) } end end class PrecedingSiblingIterator < ReverseAxisIterator def each(node, visitor) visitor.visit(node).each_preceding_siblings { |i| yield visitor.visit(i) } end end class FollowingIterator < DescendantOrSelfIterator def each(node, visitor) while parent = (a = visitor.visit(node)).parent a.each_following_siblings { |i| super i, visitor } node = parent end end end class PrecedingIterator < ReverseAxisIterator def each(node, visitor) while parent = (adaptor = visitor.visit(node)).parent adaptor.each_preceding_siblings { |i| stack = visitor.visit(i).children.dup while node = stack.pop a = visitor.visit(node) stack.concat a.children yield a end yield visitor.visit(i) } node = parent end end end class AttributeIterator < AxisIterator def each(node, visitor) visitor.visit(node).attributes.each { |i| yield visitor.visit(i) } end end class NamespaceIterator < AxisIterator def each(node, visitor) visitor.visit(node).namespaces.each { |i| yield visitor.visit(i) } end end class XPathNodeSet class LocationStep < XPathNodeSet def initialize(context) @context = context @visitor = context.visitor @nodes = [] end def set_iterator(iterator) @iterator = iterator end def reuse(node) @node = node @nodes.clear end def select @iterator.each(@node, @visitor) { |i| node = i.node @nodes.push node if yield(i) } self end def select_all @iterator.each(@node, @visitor) { |i| @nodes.push i.node } self end end include XPathObject def initialize(context, *nodes) @context = context.dup @visitor = context.visitor nodes.sort! { |a,b| compare_position a, b } @nodes = nodes end attr_reader :nodes protected :nodes def to_str if @nodes.empty? then '' else @visitor.visit(@nodes[0]).string_value end end def to_f to_string(@context).to_f end def true? not @nodes.empty? end def to_ruby @nodes end def self.def_comparison_operator(*ops) ops.each { |op| module_eval <<_, __FILE__, __LINE__ + 1 def #{op}(other) if other.is_a? XPathBoolean then other #{op} self.to_boolean else visitor = @visitor str = @context.make_string('') ret = false @nodes.each { |node| str.replace visitor.visit(node).string_value break if ret = (other #{op} str) } ret end end _ } end def_comparison_operator '==', '<', '>', '<=', '>=' class << self undef def_comparison_operator end def **(other) super unless other.is_a? XPathNodeSet merge other.nodes self end def count @nodes.size end def first @nodes[0] end def each(&block) @nodes.each(&block) end def funcall(name) # for XPointer raise "BUG" unless block_given? func = ('f_' + name.tr('-', '_')).intern super unless respond_to? func, true size = @nodes.size pos = 1 c = @context.dup begin @nodes.collect! { |node| c.reuse node, pos, size pos += 1 args = yield(c) send(func, node, *args) } rescue Object::ArgumentError if $@[1] == "#{__FILE__}:#{__LINE__-3}:in `send'" then raise XPath::ArgumentError, "#{$!} for `#{name}'" end raise end self end private def compare_position(node1, node2) visitor = @visitor ancestors1 = [] ancestors2 = [] p1 = visitor.visit(node1).parent while p1 ancestors1.push node1 p1 = visitor.visit(node1 = p1).parent end p2 = visitor.visit(node2).parent while p2 ancestors2.push node2 p2 = visitor.visit(node2 = p2).parent end unless node1 == node2 then raise XPath::Error, "can't compare the positions of given two nodes" end n = -1 ancestors1.reverse_each { |node1| node2 = ancestors2[n] unless node1 == node2 then break unless node2 return visitor.visit(node1).index - visitor.visit(node2).index end n -= 1 } ancestors1.size - ancestors2.size end def merge(other) if @nodes.empty? or other.empty? then @nodes.concat other elsif (n = compare_position(@nodes.last, other.first)) <= 0 then @nodes.pop if n == 0 @nodes.concat other elsif (n = compare_position(other.last, @nodes.first)) <= 0 then other.pop if n == 0 @nodes = other.concat(@nodes) else newnodes = [] nodes = @nodes until nodes.empty? or other.empty? n = compare_position(nodes.last, other.last) if n > 0 then newnodes.push nodes.pop elsif n < 0 then newnodes.push other.pop else newnodes.push nodes.pop other.pop end end newnodes.reverse! @nodes.concat(other).concat(newnodes) end end IteratorForAxis = { :self => SelfIterator.new, :child => ChildIterator.new, :parent => ParentIterator.new, :ancestor => AncestorIterator.new, :ancestor_or_self => AncestorOrSelfIterator.new, :descendant => DescendantIterator.new, :descendant_or_self => DescendantOrSelfIterator.new, :following => FollowingIterator.new, :preceding => PrecedingIterator.new, :following_sibling => FollowingSiblingIterator.new, :preceding_sibling => PrecedingSiblingIterator.new, :attribute => AttributeIterator.new, :namespace => NamespaceIterator.new, } def get_iterator(axis) ret = IteratorForAxis[axis] unless ret then raise XPath::NameError, "invalid axis `#{axis.id2name.tr('_','-')}'" end ret end def make_location_step if defined? @__lstep__ then @__lstep__ else @__lstep__ = LocationStep.new(@context) end end public def step(axis) iterator = get_iterator(axis) lstep = make_location_step lstep.set_iterator iterator oldnodes = @nodes @nodes = [] oldnodes.each { |node| lstep.reuse node nodes = yield(lstep).nodes nodes.reverse! if iterator.reverse_order? merge nodes } self end def select_all(axis) iterator = get_iterator(axis) visitor = @visitor oldnodes = @nodes @nodes = [] oldnodes.each { |start| nodes = [] iterator.each(start, visitor) { |i| nodes.push i.node } nodes.reverse! if iterator.reverse_order? merge nodes } self end def predicate context = @context size = @nodes.size pos = 1 result = nil newnodes = @nodes.reject { |node| context.reuse node, pos, size pos += 1 result = yield(context) break if result.is_a? Numeric not result } if result.is_a? Numeric then at result else @nodes = newnodes end self end def at(pos) n = pos.to_i if n != pos or n <= 0 then node = nil else node = @nodes[n - 1] end @nodes.clear @nodes.push node if node self end end class Context def initialize(node, namespace = nil, variable = nil, visitor = nil) visitor = TransparentNodeVisitor.new unless visitor @visitor = visitor @node = node @context_position = 1 @context_size = 1 @variables = variable @namespaces = namespace || {} end attr_reader :visitor, :node, :context_position, :context_size def reuse(node, pos = 1, size = 1) @variables = nil @node, @context_position, @context_size = node, pos, size end def get_variable(name) value = @variables && @variables[name] # value should be a XPathObjcect. raise XPath::NameError, "undefined variable `#{name}'" unless value value end PredefinedNamespace = { 'xml' => 'http://www.w3.org/XML/1998/namespace', } def get_namespace(prefix) ret = @namespaces[prefix] || PredefinedNamespace[prefix] raise XPath::Error, "undeclared namespace `#{prefix}'" unless ret ret end def make_string(str) XPathString.new str end def make_number(num) XPathNumber.new num end def make_boolean(f) if f then XPathTrue else XPathFalse end end def make_nodeset(*nodes) XPathNodeSet.new(self, *nodes) end def to_nodeset make_nodeset @node end def root_nodeset make_nodeset @visitor.visit(@node).root end def funcall(name, *args) begin send('f_' + name.tr('-', '_'), *args) rescue Object::NameError if $@[0] == "#{__FILE__}:#{__LINE__-2}:in `send'" then raise XPath::NameError, "undefined function `#{name}'" end raise rescue Object::ArgumentError if $@[1] == "#{__FILE__}:#{__LINE__-7}:in `send'" then raise XPath::ArgumentError, "#{$!} for `#{name}'" end raise end end private def must(type, *args) args.each { |i| unless i.is_a? type then s = type.name.sub(/\A.*::(?:XPath)?(?=[^:]+\z)/, '') raise XPath::TypeError, "argument must be #{s}" end } end def must_be_nodeset(*args) must XPathNodeSet, *args end def f_last make_number @context_size.to_f end def f_position make_number @context_position.to_f end def f_count(nodeset) must_be_nodeset nodeset make_number nodeset.count.to_f end def f_id(obj) unless obj.is_a? XPathNodeSet then ids = obj.to_str.strip.split(/\s+/) else ids = [] obj.each { |node| ids.push @visitor.visit(node).string_value } end root = @visitor.visit(@node).root make_nodeset(*@visitor.visit(root).select_id(*ids)) end def f_local_name(nodeset = nil) unless nodeset then n = @node else must_be_nodeset nodeset n = nodeset.first end n = @visitor.visit(n) if n n = n.name_localpart if n n = '' unless n make_string n end def f_namespace_uri(nodeset = nil) unless nodeset then n = @node else must_be_nodeset nodeset n = nodeset.first end n = @visitor.visit(n) if n n = n.namespace_uri if n n = '' unless n make_string n end def f_name(nodeset = nil) unless nodeset then n = @node else must_be_nodeset nodeset n = nodeset.first end n = @visitor.visit(n) if n n = n.qualified_name if n n = '' unless n make_string n end def f_string(obj = nil) obj = to_nodeset unless obj obj.to_string self end def f_concat(str, str2, *strs) s = str2.to_str.dup strs.each { |i| s << i.to_str } str.to_string(self).concat(s) end def f_starts_with(str, sub) make_boolean str.to_string(self).start_with?(sub.to_str) end def f_contains(str, sub) make_boolean str.to_string(self).contain?(sub.to_str) end def f_substring_before(str, sub) str.to_string(self).substring_before sub.to_str end def f_substring_after(str, sub) str.to_string(self).substring_after sub.to_str end def f_substring(str, start, len = nil) len = len.to_number(self) if len str.to_string(self).substring start.to_number(self), len end def f_string_length(str = nil) if str then str = str.to_string(self) else str = make_string(@node.string_value) end make_number str.size.to_f end def f_normalize_space(str = nil) if str then str = str.to_string(self) else str = make_string(@node.string_value) end str.normalize_space end def f_translate(str, from, to) str.to_string(self).translate from.to_str, to.to_str end def f_boolean(obj) obj.to_boolean self end def f_not(bool) make_boolean(!bool.true?) end def f_true make_boolean true end def f_false make_boolean false end def f_lang(str) lang = @visitor.visit(@node).lang make_boolean(lang && /\A#{Regexp.quote(str.to_str)}(?:-|\z)/i =~ lang) end def f_number(obj = nil) obj = to_nodeset unless obj obj.to_number self end def f_sum(nodeset) must_be_nodeset nodeset sum = 0.0 nodeset.each { |node| sum += make_string(@visitor.visit(node).string_value).to_f } make_number sum end def f_floor(num) num.to_number(self).floor end def f_ceiling(num) num.to_number(self).ceil end def f_round(num) num.to_number(self).round end end end