# class REXML::Parsers::XPathParser

You don't want to use this class. Really. Use `XPath`, which is a wrapper for this class. Believe me. You don't want to poke around in here. There is strange, dark magic at work in this code. Beware. Go back! Go back while you still can!

### Constants

AXIS

  `RelativeLocationPath`

      |                                                    Step
        | (AXIS_NAME '::' | '@' | '')                     AxisSpecifier
          NodeTest
            Predicate
        | '.' | '..'                                      AbbreviatedStep
      |  RelativeLocationPath '/' Step
      | RelativeLocationPath '//' Step

LITERAL
LOCAL_NAME_WILDCARD
NODE_TYPE
NT
NUMBER
PI
PREFIX_WILDCARD

      Returns a 1-1 map of the nodeset
      The contents of the resulting array are either:
        true/false, if a positive match
        String, if a name match

  `NodeTest`

      | ('*' | NCNAME ':' '*' | QNAME)                NameTest
      | '*' ':' NCNAME                                NameTest since XPath 2.0
      | NODE_TYPE '(' ')'                             NodeType
      | PI '(' LITERAL ')'                            PI
        | '[' expr ']'                                Predicate

QNAME
VARIABLE_REFERENCE

  | `VARIABLE_REFERENCE` | '(' expr ')' | `LITERAL` | `NUMBER` | `FunctionCall`

### Public Instance Methods

abbreviate( path ) click to toggle source

    # File lib/rexml/parsers/xpathparser.rb, line 35
    def abbreviate( path )
      path = path.kind_of?(String) ? parse( path ) : path
      string = ""
      document = false
      while path.size > 0
        op = path.shift
        case op
        when :node
        when :attribute
          string << "/" if string.size > 0
          string << "@"
        when :child
          string << "/" if string.size > 0
        when :descendant_or_self
          string << "/"
        when :self
          string << "."
        when :parent
          string << ".."
        when :any
          string << "*"
        when :text
          string << "text()"
        when :following, :following_sibling,
              :ancestor, :ancestor_or_self, :descendant,
              :namespace, :preceding, :preceding_sibling
          string << "/" unless string.size == 0
          string << op.to_s.tr("_", "-")
          string << "::"
        when :qname
          prefix = path.shift
          name = path.shift
          string << prefix+":" if prefix.size > 0
          string << name
        when :predicate
          string << '['
          string << predicate_to_string( path.shift ) {|x| abbreviate( x ) }
          string << ']'
        when :document
          document = true
        when :function
          string << path.shift
          string << "( "
          string << predicate_to_string( path.shift[0] ) {|x| abbreviate( x )}
          string << " )"
        when :literal
          string << %Q{ "#{path.shift}" }
        else
          string << "/" unless string.size == 0
          string << "UNKNOWN("
          string << op.inspect
          string << ")"
        end
      end
      string = "/"+string if document
      return string
    end

expand( path ) click to toggle source

    # File lib/rexml/parsers/xpathparser.rb, line 93
    def expand( path )
      path = path.kind_of?(String) ? parse( path ) : path
      string = ""
      document = false
      while path.size > 0
        op = path.shift
        case op
        when :node
          string << "node()"
        when :attribute, :child, :following, :following_sibling,
              :ancestor, :ancestor_or_self, :descendant, :descendant_or_self,
              :namespace, :preceding, :preceding_sibling, :self, :parent
          string << "/" unless string.size == 0
          string << op.to_s.tr("_", "-")
          string << "::"
        when :any
          string << "*"
        when :qname
          prefix = path.shift
          name = path.shift
          string << prefix+":" if prefix.size > 0
          string << name
        when :predicate
          string << '['
          string << predicate_to_string( path.shift ) { |x| expand(x) }
          string << ']'
        when :document
          document = true
        else
          string << "/" unless string.size == 0
          string << "UNKNOWN("
          string << op.inspect
          string << ")"
        end
      end
      string = "/"+string if document
      return string
    end

namespaces=( namespaces ) click to toggle source

    # File lib/rexml/parsers/xpathparser.rb, line 15
    def namespaces=( namespaces )
      Functions::namespace_context = namespaces
      @namespaces = namespaces
    end

parse(path) click to toggle source

    # File lib/rexml/parsers/xpathparser.rb, line 20
    def parse path
      path = path.dup
      path.gsub!(/([\(\[])\s+/, '\1') # Strip ignorable spaces
      path.gsub!( /\s+([\]\)])/, '\1')
      parsed = []
      OrExpr(path, parsed)
      parsed
    end

predicate(path) click to toggle source

    # File lib/rexml/parsers/xpathparser.rb, line 29
    def predicate path
      parsed = []
      Predicate( "[#{path}]", parsed )
      parsed
    end

predicate_to_string( path ) { |path| ... } click to toggle source

    # File lib/rexml/parsers/xpathparser.rb, line 132
    def predicate_to_string( path, &block )
      string = ""
      case path[0]
      when :and, :or, :mult, :plus, :minus, :neq, :eq, :lt, :gt, :lteq, :gteq, :div, :mod, :union
        op = path.shift
        case op
        when :eq
          op = "="
        when :lt
          op = "<"
        when :gt
          op = ">"
        when :lteq
          op = "<="
        when :gteq
          op = ">="
        when :neq
          op = "!="
        when :union
          op = "|"
        end
        left = predicate_to_string( path.shift, &block )
        right = predicate_to_string( path.shift, &block )
        string << " "
        string << left
        string << " "
        string << op.to_s
        string << " "
        string << right
        string << " "
      when :function
        path.shift
        name = path.shift
        string << name
        string << "( "
        string << predicate_to_string( path.shift, &block )
        string << " )"
      when :literal
        path.shift
        string << " "
        string << path.shift.inspect
        string << " "
      else
        string << " "
        string << yield( path )
        string << " "
      end
      return string.squeeze(" ")
    end

### Private Instance Methods

AdditiveExpr(path, parsed) click to toggle source

| `AdditiveExpr` ('+' | '-') `MultiplicativeExpr` | `MultiplicativeExpr`

    # File lib/rexml/parsers/xpathparser.rb, line 443
    def AdditiveExpr path, parsed
      n = []
      rest = MultiplicativeExpr( path, n )
      if rest != path
        while rest =~ /^\s*(\+|-)\s*/
          if $1[0] == ?+
            n = [ :plus, n, [] ]
          else
            n = [ :minus, n, [] ]
          end
          rest = MultiplicativeExpr( $', n[-1] )
        end
      end
      if parsed.size == 0 and n.size != 0
        parsed.replace(n)
      elsif n.size > 0
        parsed << n
      end
      rest
    end

AndExpr(path, parsed) click to toggle source

| `AndExpr` S 'and' S `EqualityExpr` | `EqualityExpr`

    # File lib/rexml/parsers/xpathparser.rb, line 376
    def AndExpr path, parsed
      n = []
      rest = EqualityExpr( path, n )
      if rest != path
        while rest =~ /^\s*( and )/
          n = [ :and, n, [] ]
          rest = EqualityExpr( $', n[-1] )
        end
      end
      if parsed.size == 0 and n.size != 0
        parsed.replace(n)
      elsif n.size > 0
        parsed << n
      end
      rest
    end

EqualityExpr(path, parsed) click to toggle source

| `EqualityExpr` ('=' | '!=') `RelationalExpr` | `RelationalExpr`

    # File lib/rexml/parsers/xpathparser.rb, line 395
    def EqualityExpr path, parsed
      n = []
      rest = RelationalExpr( path, n )
      if rest != path
        while rest =~ /^\s*(!?=)\s*/
          if $1[0] == ?!
            n = [ :neq, n, [] ]
          else
            n = [ :eq, n, [] ]
          end
          rest = RelationalExpr( $', n[-1] )
        end
      end
      if parsed.size == 0 and n.size != 0
        parsed.replace(n)
      elsif n.size > 0
        parsed << n
      end
      rest
    end

FilterExpr(path, parsed) click to toggle source

| `FilterExpr` `Predicate` | `PrimaryExpr`

    # File lib/rexml/parsers/xpathparser.rb, line 546
    def FilterExpr path, parsed
      n = []
      path = PrimaryExpr( path, n )
      path = Predicate(path, n)
      parsed.concat(n)
      path
    end

FunctionCall(rest, parsed) click to toggle source

| FUNCTION_NAME '(' ( expr ( ',' expr )* )? ')'

    # File lib/rexml/parsers/xpathparser.rb, line 599
    def FunctionCall rest, parsed
      path, arguments = parse_args(rest)
      argset = []
      for argument in arguments
        args = []
        OrExpr( argument, args )
        argset << args
      end
      parsed << argset
      path
    end

LocationPath(path, parsed) click to toggle source

`LocationPath`

    | RelativeLocationPath
    | '/' RelativeLocationPath?
    | '//' RelativeLocationPath

    # File lib/rexml/parsers/xpathparser.rb, line 187
    def LocationPath path, parsed
      path = path.lstrip
      if path[0] == ?/
        parsed << :document
        if path[1] == ?/
          parsed << :descendant_or_self
          parsed << :node
          path = path[2..-1]
        else
          path = path[1..-1]
        end
      end
      return RelativeLocationPath( path, parsed ) if path.size > 0
    end

MultiplicativeExpr(path, parsed) click to toggle source

| `MultiplicativeExpr` ('*' | S ('div' | 'mod') S) `UnaryExpr` | `UnaryExpr`

    # File lib/rexml/parsers/xpathparser.rb, line 466
    def MultiplicativeExpr path, parsed
      n = []
      rest = UnaryExpr( path, n )
      if rest != path
        while rest =~ /^\s*(\*| div | mod )\s*/
          if $1[0] == ?*
            n = [ :mult, n, [] ]
          elsif $1.include?( "div" )
            n = [ :div, n, [] ]
          else
            n = [ :mod, n, [] ]
          end
          rest = UnaryExpr( $', n[-1] )
        end
      end
      if parsed.size == 0 and n.size != 0
        parsed.replace(n)
      elsif n.size > 0
        parsed << n
      end
      rest
    end

NodeTest(path, parsed) click to toggle source

    # File lib/rexml/parsers/xpathparser.rb, line 283
    def NodeTest path, parsed
      original_path = path
      path = path.lstrip
      case path
      when PREFIX_WILDCARD
        prefix = nil
        name = $1
        path = $'
        parsed << :qname
        parsed << prefix
        parsed << name
      when /^\*/
        path = $'
        parsed << :any
      when NODE_TYPE
        type = $1
        path = $'
        parsed << type.tr('-', '_').intern
      when PI
        path = $'
        literal = nil
        if path !~ /^\s*\)/
          path =~ LITERAL
          literal = $1
          path = $'
          raise ParseException.new("Missing ')' after processing instruction") if path[0] != ?)
          path = path[1..-1]
        end
        parsed << :processing_instruction
        parsed << (literal || '')
      when LOCAL_NAME_WILDCARD
        prefix = $1
        path = $'
        parsed << :namespace
        parsed << prefix
      when QNAME
        prefix = $1
        name = $2
        path = $'
        prefix = "" unless prefix
        parsed << :qname
        parsed << prefix
        parsed << name
      else
        path = original_path
      end
      return path
    end

OrExpr(path, parsed) click to toggle source

| `OrExpr` S 'or' S `AndExpr` | `AndExpr`

    # File lib/rexml/parsers/xpathparser.rb, line 357
    def OrExpr path, parsed
      n = []
      rest = AndExpr( path, n )
      if rest != path
        while rest =~ /^\s*( or )/
          n = [ :or, n, [] ]
          rest = AndExpr( $', n[-1] )
        end
      end
      if parsed.size == 0 and n.size != 0
        parsed.replace(n)
      elsif n.size > 0
        parsed << n
      end
      rest
    end

PathExpr(path, parsed) click to toggle source

| `LocationPath` | `FilterExpr` ('/' | '//') `RelativeLocationPath`

    # File lib/rexml/parsers/xpathparser.rb, line 528
    def PathExpr path, parsed
      path = path.lstrip
      n = []
      rest = FilterExpr( path, n )
      if rest != path
        if rest and rest[0] == ?/
          rest = RelativeLocationPath(rest, n)
          parsed.concat(n)
          return rest
        end
      end
      rest = LocationPath(rest, n) if rest =~ /\A[\/\.\@\[\w*]/
      parsed.concat(n)
      return rest
    end

Predicate(path, parsed) click to toggle source

Filters the supplied nodeset on the predicate(s)

    # File lib/rexml/parsers/xpathparser.rb, line 333
    def Predicate path, parsed
      original_path = path
      path = path.lstrip
      return original_path unless path[0] == ?[
      predicates = []
      while path[0] == ?[
        path, expr = get_group(path)
        predicates << expr[1..-2] if expr
      end
      predicates.each{ |pred|
        preds = []
        parsed << :predicate
        parsed << preds
        OrExpr(pred, preds)
      }
      path
    end

PrimaryExpr(path, parsed) click to toggle source

    # File lib/rexml/parsers/xpathparser.rb, line 562
    def PrimaryExpr path, parsed
      case path
      when VARIABLE_REFERENCE
        varname = $1
        path = $'
        parsed << :variable
        parsed << varname
        #arry << @variables[ varname ]
      when /^(\w[-\w]*)(?:\()/
        fname = $1
        tmp = $'
        return path if fname =~ NT
        path = tmp
        parsed << :function
        parsed << fname
        path = FunctionCall(path, parsed)
      when NUMBER
        varname = $1.nil? ? $2 : $1
        path = $'
        parsed << :literal
        parsed << (varname.include?('.') ? varname.to_f : varname.to_i)
      when LITERAL
        varname = $1.nil? ? $2 : $1
        path = $'
        parsed << :literal
        parsed << varname
      when /^\(/                                               #/
        path, contents = get_group(path)
        contents = contents[1..-2]
        n = []
        OrExpr( contents, n )
        parsed.concat(n)
      end
      path
    end

RelationalExpr(path, parsed) click to toggle source

| `RelationalExpr` ('<' | '>' | '<=' | '>=') `AdditiveExpr` | `AdditiveExpr`

    # File lib/rexml/parsers/xpathparser.rb, line 418
    def RelationalExpr path, parsed
      n = []
      rest = AdditiveExpr( path, n )
      if rest != path
        while rest =~ /^\s*([<>]=?)\s*/
          if $1[0] == ?<
            sym = "lt"
          else
            sym = "gt"
          end
          sym << "eq" if $1[-1] == ?=
          n = [ sym.intern, n, [] ]
          rest = AdditiveExpr( $', n[-1] )
        end
      end
      if parsed.size == 0 and n.size != 0
        parsed.replace(n)
      elsif n.size > 0
        parsed << n
      end
      rest
    end

RelativeLocationPath(path, parsed) click to toggle source

    # File lib/rexml/parsers/xpathparser.rb, line 211
    def RelativeLocationPath path, parsed
      loop do
        original_path = path
        path = path.lstrip

        return original_path if path.empty?

        # (axis or @ or <child::>) nodetest predicate  >
        # OR                                          >  / Step
        # (. or ..)                                    >
        if path[0] == ?.
          if path[1] == ?.
            parsed << :parent
            parsed << :node
            path = path[2..-1]
          else
            parsed << :self
            parsed << :node
            path = path[1..-1]
          end
        else
          if path[0] == ?@
            parsed << :attribute
            path = path[1..-1]
            # Goto Nodetest
          elsif path =~ AXIS
            parsed << $1.tr('-','_').intern
            path = $'
            # Goto Nodetest
          else
            parsed << :child
          end

          n = []
          path = NodeTest( path, n)

          path = Predicate( path, n )

          parsed.concat(n)
        end

        original_path = path
        path = path.lstrip
        return original_path if path.empty?

        return original_path if path[0] != ?/

        if path[1] == ?/
          parsed << :descendant_or_self
          parsed << :node
          path = path[2..-1]
        else
          path = path[1..-1]
        end
      end
    end

UnaryExpr(path, parsed) click to toggle source

| '-' `UnaryExpr` | `UnionExpr`

    # File lib/rexml/parsers/xpathparser.rb, line 491
    def UnaryExpr path, parsed
      path =~ /^(\-*)/
      path = $'
      if $1 and (($1.size % 2) != 0)
        mult = -1
      else
        mult = 1
      end
      parsed << :neg if mult < 0

      n = []
      path = UnionExpr( path, n )
      parsed.concat( n )
      path
    end

UnionExpr(path, parsed) click to toggle source

| `UnionExpr` '|' `PathExpr` | `PathExpr`

    # File lib/rexml/parsers/xpathparser.rb, line 509
    def UnionExpr path, parsed
      n = []
      rest = PathExpr( path, n )
      if rest != path
        while rest =~ /^\s*(\|)\s*/
          n = [ :union, n, [] ]
          rest = PathExpr( $', n[-1] )
        end
      end
      if parsed.size == 0 and n.size != 0
        parsed.replace( n )
      elsif n.size > 0
        parsed << n
      end
      rest
    end

get_group(string) click to toggle source

`get_group`( '[foo]bar' ) -> ['bar', '[foo]']

    # File lib/rexml/parsers/xpathparser.rb, line 612
    def get_group string
      ind = 0
      depth = 0
      st = string[0,1]
      en = (st == "(" ? ")" : "]")
      begin
        case string[ind,1]
        when st
          depth += 1
        when en
          depth -= 1
        end
        ind += 1
      end while depth > 0 and ind < string.length
      return nil unless depth==0
      [string[ind..-1], string[0..ind-1]]
    end

parse_args( string ) click to toggle source

    # File lib/rexml/parsers/xpathparser.rb, line 630
    def parse_args( string )
      arguments = []
      ind = 0
      inquot = false
      inapos = false
      depth = 1
      begin
        case string[ind]
        when ?"
          inquot = !inquot unless inapos
        when ?'
          inapos = !inapos unless inquot
        else
          unless inquot or inapos
            case string[ind]
            when ?(
              depth += 1
              if depth == 1
                string = string[1..-1]
                ind -= 1
              end
            when ?)
              depth -= 1
              if depth == 0
                s = string[0,ind].strip
                arguments << s unless s == ""
                string = string[ind+1..-1]
              end
            when ?,
              if depth == 1
                s = string[0,ind].strip
                arguments << s unless s == ""
                string = string[ind+1..-1]
                ind = -1
              end
            end
          end
        end
        ind += 1
      end while depth > 0 and ind < string.length
      return nil unless depth==0
      [string,arguments]
    end