Class | REXML::Parsers::XPathParser |
In: |
lib/rexml/parsers/xpathparser.rb
|
Parent: | Object |
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!
LITERAL | = | /^'([^']*)'|^"([^"]*)"/u | ||
AXIS | = | /^(ancestor|ancestor-or-self|attribute|child|descendant|descendant-or-self|following|following-sibling|namespace|parent|preceding|preceding-sibling|self)::/ |
RelativeLocationPath
| Step | (AXIS_NAME '::' | '@' | '') AxisSpecifier NodeTest Predicate | '.' | '..' AbbreviatedStep | RelativeLocationPath '/' Step | RelativeLocationPath '//' Step |
|
NCNAMETEST | = | /^(#{NCNAME_STR}):\*/u | 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 matchNodeTest | ('*' | NCNAME ':' '*' | QNAME) NameTest | NODE_TYPE '(' ')' NodeType | PI '(' LITERAL ')' PI | '[' expr ']' Predicate |
|
QNAME | = | Namespace::NAMESPLIT | ||
NODE_TYPE | = | /^(comment|text|node)\(\s*\)/m | ||
PI | = | /^processing-instruction\(/ | ||
VARIABLE_REFERENCE | = | /^\$(#{NAME_STR})/u | | VARIABLE_REFERENCE | ’(’ expr ’)’ | LITERAL | NUMBER | FunctionCall | |
NUMBER | = | /^(\d*\.?\d+)/ | ||
NT | = | /^comment|text|processing-instruction|node$/ |
# File lib/rexml/parsers/xpathparser.rb, line 33 33: def abbreviate( path ) 34: path = path.kind_of?(String) ? parse( path ) : path 35: string = "" 36: document = false 37: while path.size > 0 38: op = path.shift 39: case op 40: when :node 41: when :attribute 42: string << "/" if string.size > 0 43: string << "@" 44: when :child 45: string << "/" if string.size > 0 46: when :descendant_or_self 47: string << "/" 48: when :self 49: string << "." 50: when :parent 51: string << ".." 52: when :any 53: string << "*" 54: when :text 55: string << "text()" 56: when :following, :following_sibling, 57: :ancestor, :ancestor_or_self, :descendant, 58: :namespace, :preceding, :preceding_sibling 59: string << "/" unless string.size == 0 60: string << op.to_s.tr("_", "-") 61: string << "::" 62: when :qname 63: prefix = path.shift 64: name = path.shift 65: string << prefix+":" if prefix.size > 0 66: string << name 67: when :predicate 68: string << '[' 69: string << predicate_to_string( path.shift ) {|x| abbreviate( x ) } 70: string << ']' 71: when :document 72: document = true 73: when :function 74: string << path.shift 75: string << "( " 76: string << predicate_to_string( path.shift[0] ) {|x| abbreviate( x )} 77: string << " )" 78: when :literal 79: string << %Q{ "#{path.shift}" } 80: else 81: string << "/" unless string.size == 0 82: string << "UNKNOWN(" 83: string << op.inspect 84: string << ")" 85: end 86: end 87: string = "/"+string if document 88: return string 89: end
# File lib/rexml/parsers/xpathparser.rb, line 91 91: def expand( path ) 92: path = path.kind_of?(String) ? parse( path ) : path 93: string = "" 94: document = false 95: while path.size > 0 96: op = path.shift 97: case op 98: when :node 99: string << "node()" 100: when :attribute, :child, :following, :following_sibling, 101: :ancestor, :ancestor_or_self, :descendant, :descendant_or_self, 102: :namespace, :preceding, :preceding_sibling, :self, :parent 103: string << "/" unless string.size == 0 104: string << op.to_s.tr("_", "-") 105: string << "::" 106: when :any 107: string << "*" 108: when :qname 109: prefix = path.shift 110: name = path.shift 111: string << prefix+":" if prefix.size > 0 112: string << name 113: when :predicate 114: string << '[' 115: string << predicate_to_string( path.shift ) { |x| expand(x) } 116: string << ']' 117: when :document 118: document = true 119: else 120: string << "/" unless string.size == 0 121: string << "UNKNOWN(" 122: string << op.inspect 123: string << ")" 124: end 125: end 126: string = "/"+string if document 127: return string 128: end
# File lib/rexml/parsers/xpathparser.rb, line 14 14: def namespaces=( namespaces ) 15: Functions::namespace_context = namespaces 16: @namespaces = namespaces 17: end
# File lib/rexml/parsers/xpathparser.rb, line 19 19: def parse path 20: path.gsub!(/([\(\[])\s+/, '\1') # Strip ignorable spaces 21: path.gsub!( /\s+([\]\)])/, '\1' ) 22: parsed = [] 23: path = OrExpr(path, parsed) 24: parsed 25: end
# File lib/rexml/parsers/xpathparser.rb, line 27 27: def predicate path 28: parsed = [] 29: Predicate( "[#{path}]", parsed ) 30: parsed 31: end
# File lib/rexml/parsers/xpathparser.rb, line 130 130: def predicate_to_string( path, &block ) 131: string = "" 132: case path[0] 133: when :and, :or, :mult, :plus, :minus, :neq, :eq, :lt, :gt, :lteq, :gteq, :div, :mod, :union 134: op = path.shift 135: case op 136: when :eq 137: op = "=" 138: when :lt 139: op = "<" 140: when :gt 141: op = ">" 142: when :lteq 143: op = "<=" 144: when :gteq 145: op = ">=" 146: when :neq 147: op = "!=" 148: when :union 149: op = "|" 150: end 151: left = predicate_to_string( path.shift, &block ) 152: right = predicate_to_string( path.shift, &block ) 153: string << " " 154: string << left 155: string << " " 156: string << op.to_s 157: string << " " 158: string << right 159: string << " " 160: when :function 161: path.shift 162: name = path.shift 163: string << name 164: string << "( " 165: string << predicate_to_string( path.shift, &block ) 166: string << " )" 167: when :literal 168: path.shift 169: string << " " 170: string << path.shift.inspect 171: string << " " 172: else 173: string << " " 174: string << yield( path ) 175: string << " " 176: end 177: return string.squeeze(" ") 178: end
| AdditiveExpr (’+’ | S ’-’) MultiplicativeExpr | MultiplicativeExpr
# File lib/rexml/parsers/xpathparser.rb, line 448 448: def AdditiveExpr path, parsed 449: #puts "ADDITIVE >>> #{path}" 450: n = [] 451: rest = MultiplicativeExpr( path, n ) 452: #puts "ADDITIVE <<< #{rest}" 453: if rest != path 454: while rest =~ /^\s*(\+| -)\s*/ 455: if $1[0] == ?+ 456: n = [ :plus, n, [] ] 457: else 458: n = [ :minus, n, [] ] 459: end 460: rest = MultiplicativeExpr( $', n[-1] ) 461: end 462: end 463: if parsed.size == 0 and n.size != 0 464: parsed.replace(n) 465: elsif n.size > 0 466: parsed << n 467: end 468: rest 469: end
| AndExpr S ‘and’ S EqualityExpr | EqualityExpr
# File lib/rexml/parsers/xpathparser.rb, line 373 373: def AndExpr path, parsed 374: #puts "AND >>> #{path}" 375: n = [] 376: rest = EqualityExpr( path, n ) 377: #puts "AND <<< #{rest}" 378: if rest != path 379: while rest =~ /^\s*( and )/ 380: n = [ :and, n, [] ] 381: #puts "AND >>> #{rest}" 382: rest = EqualityExpr( $', n[-1] ) 383: #puts "AND <<< #{rest}" 384: end 385: end 386: if parsed.size == 0 and n.size != 0 387: parsed.replace(n) 388: elsif n.size > 0 389: parsed << n 390: end 391: rest 392: end
| EqualityExpr (’=’ | ’!=’) RelationalExpr | RelationalExpr
# File lib/rexml/parsers/xpathparser.rb, line 396 396: def EqualityExpr path, parsed 397: #puts "EQUALITY >>> #{path}" 398: n = [] 399: rest = RelationalExpr( path, n ) 400: #puts "EQUALITY <<< #{rest}" 401: if rest != path 402: while rest =~ /^\s*(!?=)\s*/ 403: if $1[0] == ?! 404: n = [ :neq, n, [] ] 405: else 406: n = [ :eq, n, [] ] 407: end 408: rest = RelationalExpr( $', n[-1] ) 409: end 410: end 411: if parsed.size == 0 and n.size != 0 412: parsed.replace(n) 413: elsif n.size > 0 414: parsed << n 415: end 416: rest 417: end
| FilterExpr Predicate | PrimaryExpr
# File lib/rexml/parsers/xpathparser.rb, line 561 561: def FilterExpr path, parsed 562: #puts "FILTER >>> #{path}" 563: n = [] 564: path = PrimaryExpr( path, n ) 565: #puts "FILTER <<< #{path}" 566: path = Predicate(path, n) if path and path[0] == ?[ 567: #puts "FILTER <<< #{path}" 568: parsed.concat(n) 569: path 570: end
| FUNCTION_NAME ’(’ ( expr ( ’,’ expr )* )? ’)’
# File lib/rexml/parsers/xpathparser.rb, line 622 622: def FunctionCall rest, parsed 623: path, arguments = parse_args(rest) 624: argset = [] 625: for argument in arguments 626: args = [] 627: OrExpr( argument, args ) 628: argset << args 629: end 630: parsed << argset 631: path 632: end
| RelativeLocationPath | '/' RelativeLocationPath? | '//' RelativeLocationPath
# File lib/rexml/parsers/xpathparser.rb, line 185 185: def LocationPath path, parsed 186: #puts "LocationPath '#{path}'" 187: path = path.strip 188: if path[0] == ?/ 189: parsed << :document 190: if path[1] == ?/ 191: parsed << :descendant_or_self 192: parsed << :node 193: path = path[2..-1] 194: else 195: path = path[1..-1] 196: end 197: end 198: #puts parsed.inspect 199: return RelativeLocationPath( path, parsed ) if path.size > 0 200: end
| MultiplicativeExpr (’*’ | S (‘div’ | ‘mod’) S) UnaryExpr | UnaryExpr
# File lib/rexml/parsers/xpathparser.rb, line 473 473: def MultiplicativeExpr path, parsed 474: #puts "MULT >>> #{path}" 475: n = [] 476: rest = UnaryExpr( path, n ) 477: #puts "MULT <<< #{rest}" 478: if rest != path 479: while rest =~ /^\s*(\*| div | mod )\s*/ 480: if $1[0] == ?* 481: n = [ :mult, n, [] ] 482: elsif $1.include?( "div" ) 483: n = [ :div, n, [] ] 484: else 485: n = [ :mod, n, [] ] 486: end 487: rest = UnaryExpr( $', n[-1] ) 488: end 489: end 490: if parsed.size == 0 and n.size != 0 491: parsed.replace(n) 492: elsif n.size > 0 493: parsed << n 494: end 495: rest 496: end
# File lib/rexml/parsers/xpathparser.rb, line 283 283: def NodeTest path, parsed 284: #puts "NodeTest with #{path}" 285: res = nil 286: case path 287: when /^\*/ 288: path = $' 289: parsed << :any 290: when NODE_TYPE 291: type = $1 292: path = $' 293: parsed << type.tr('-', '_').intern 294: when PI 295: path = $' 296: literal = nil 297: if path !~ /^\s*\)/ 298: path =~ LITERAL 299: literal = $1 300: path = $' 301: raise ParseException.new("Missing ')' after processing instruction") if path[0] != ?) 302: path = path[1..-1] 303: end 304: parsed << :processing_instruction 305: parsed << (literal || '') 306: when NCNAMETEST 307: #puts "NCNAMETEST" 308: prefix = $1 309: path = $' 310: parsed << :namespace 311: parsed << prefix 312: when QNAME 313: #puts "QNAME" 314: prefix = $1 315: name = $2 316: path = $' 317: prefix = "" unless prefix 318: parsed << :qname 319: parsed << prefix 320: parsed << name 321: end 322: return path 323: end
| OrExpr S ‘or’ S AndExpr | AndExpr
# File lib/rexml/parsers/xpathparser.rb, line 352 352: def OrExpr path, parsed 353: #puts "OR >>> #{path}" 354: n = [] 355: rest = AndExpr( path, n ) 356: #puts "OR <<< #{rest}" 357: if rest != path 358: while rest =~ /^\s*( or )/ 359: n = [ :or, n, [] ] 360: rest = AndExpr( $', n[-1] ) 361: end 362: end 363: if parsed.size == 0 and n.size != 0 364: parsed.replace(n) 365: elsif n.size > 0 366: parsed << n 367: end 368: rest 369: end
| LocationPath | FilterExpr (’/’ | ’//’) RelativeLocationPath
# File lib/rexml/parsers/xpathparser.rb, line 541 541: def PathExpr path, parsed 542: path =~ /^\s*/ 543: path = $' 544: #puts "PATH >>> #{path}" 545: n = [] 546: rest = FilterExpr( path, n ) 547: #puts "PATH <<< '#{rest}'" 548: if rest != path 549: if rest and rest[0] == ?/ 550: return RelativeLocationPath(rest, n) 551: end 552: end 553: #puts "BEFORE WITH '#{rest}'" 554: rest = LocationPath(rest, n) if rest =~ /\A[\/\.\@\[\w_*]/ 555: parsed.concat(n) 556: return rest 557: end
Filters the supplied nodeset on the predicate(s)
# File lib/rexml/parsers/xpathparser.rb, line 326 326: def Predicate path, parsed 327: #puts "PREDICATE with #{path}" 328: return nil unless path[0] == ?[ 329: predicates = [] 330: while path[0] == ?[ 331: path, expr = get_group(path) 332: predicates << expr[1..-2] if expr 333: end 334: #puts "PREDICATES = #{predicates.inspect}" 335: predicates.each{ |expr| 336: #puts "ORING #{expr}" 337: preds = [] 338: parsed << :predicate 339: parsed << preds 340: OrExpr(expr, preds) 341: } 342: #puts "PREDICATES = #{predicates.inspect}" 343: path 344: end
# File lib/rexml/parsers/xpathparser.rb, line 580 580: def PrimaryExpr path, parsed 581: arry = [] 582: case path 583: when VARIABLE_REFERENCE 584: varname = $1 585: path = $' 586: parsed << :variable 587: parsed << varname 588: #arry << @variables[ varname ] 589: when /^(\w[-\w]*)(?:\()/ 590: #puts "PrimaryExpr :: Function >>> #$1 -- '#$''" 591: fname = $1 592: tmp = $' 593: #puts "#{fname} =~ #{NT.inspect}" 594: return path if fname =~ NT 595: path = tmp 596: parsed << :function 597: parsed << fname 598: path = FunctionCall(path, parsed) 599: when NUMBER 600: #puts "LITERAL or NUMBER: #$1" 601: varname = $1.nil? ? $2 : $1 602: path = $' 603: parsed << :literal 604: parsed << (varname.include?('.') ? varname.to_f : varname.to_i) 605: when LITERAL 606: #puts "LITERAL or NUMBER: #$1" 607: varname = $1.nil? ? $2 : $1 608: path = $' 609: parsed << :literal 610: parsed << varname 611: when /^\(/ #/ 612: path, contents = get_group(path) 613: contents = contents[1..-2] 614: n = [] 615: OrExpr( contents, n ) 616: parsed.concat(n) 617: end 618: path 619: end
| RelationalExpr (’<’ | ’>’ | ’<=’ | ’>=’) AdditiveExpr | AdditiveExpr
# File lib/rexml/parsers/xpathparser.rb, line 421 421: def RelationalExpr path, parsed 422: #puts "RELATION >>> #{path}" 423: n = [] 424: rest = AdditiveExpr( path, n ) 425: #puts "RELATION <<< #{rest}" 426: if rest != path 427: while rest =~ /^\s*([<>]=?)\s*/ 428: if $1[0] == ?< 429: sym = "lt" 430: else 431: sym = "gt" 432: end 433: sym << "eq" if $1[-1] == ?= 434: n = [ sym.intern, n, [] ] 435: rest = AdditiveExpr( $', n[-1] ) 436: end 437: end 438: if parsed.size == 0 and n.size != 0 439: parsed.replace(n) 440: elsif n.size > 0 441: parsed << n 442: end 443: rest 444: end
# File lib/rexml/parsers/xpathparser.rb, line 211 211: def RelativeLocationPath path, parsed 212: #puts "RelativeLocationPath #{path}" 213: while path.size > 0 214: # (axis or @ or <child::>) nodetest predicate > 215: # OR > / Step 216: # (. or ..) > 217: if path[0] == ?. 218: if path[1] == ?. 219: parsed << :parent 220: parsed << :node 221: path = path[2..-1] 222: else 223: parsed << :self 224: parsed << :node 225: path = path[1..-1] 226: end 227: else 228: if path[0] == ?@ 229: #puts "ATTRIBUTE" 230: parsed << :attribute 231: path = path[1..-1] 232: # Goto Nodetest 233: elsif path =~ AXIS 234: parsed << $1.tr('-','_').intern 235: path = $' 236: # Goto Nodetest 237: else 238: parsed << :child 239: end 240: 241: #puts "NODETESTING '#{path}'" 242: n = [] 243: path = NodeTest( path, n) 244: #puts "NODETEST RETURNED '#{path}'" 245: 246: if path[0] == ?[ 247: path = Predicate( path, n ) 248: end 249: 250: parsed.concat(n) 251: end 252: 253: if path.size > 0 254: if path[0] == ?/ 255: if path[1] == ?/ 256: parsed << :descendant_or_self 257: parsed << :node 258: path = path[2..-1] 259: else 260: path = path[1..-1] 261: end 262: else 263: return path 264: end 265: end 266: end 267: return path 268: end
# File lib/rexml/parsers/xpathparser.rb, line 500 500: def UnaryExpr path, parsed 501: path =~ /^(\-*)/ 502: path = $' 503: if $1 and (($1.size % 2) != 0) 504: mult = -1 505: else 506: mult = 1 507: end 508: parsed << :neg if mult < 0 509: 510: #puts "UNARY >>> #{path}" 511: n = [] 512: path = UnionExpr( path, n ) 513: #puts "UNARY <<< #{path}" 514: parsed.concat( n ) 515: path 516: end
| UnionExpr ’|’ PathExpr | PathExpr
# File lib/rexml/parsers/xpathparser.rb, line 520 520: def UnionExpr path, parsed 521: #puts "UNION >>> #{path}" 522: n = [] 523: rest = PathExpr( path, n ) 524: #puts "UNION <<< #{rest}" 525: if rest != path 526: while rest =~ /^\s*(\|)\s*/ 527: n = [ :union, n, [] ] 528: rest = PathExpr( $', n[-1] ) 529: end 530: end 531: if parsed.size == 0 and n.size != 0 532: parsed.replace( n ) 533: elsif n.size > 0 534: parsed << n 535: end 536: rest 537: end
get_group( ’[foo]bar’ ) -> [‘bar’, ’[foo]’]
# File lib/rexml/parsers/xpathparser.rb, line 635 635: def get_group string 636: ind = 0 637: depth = 0 638: st = string[0,1] 639: en = (st == "(" ? ")" : "]") 640: begin 641: case string[ind,1] 642: when st 643: depth += 1 644: when en 645: depth -= 1 646: end 647: ind += 1 648: end while depth > 0 and ind < string.length 649: return nil unless depth==0 650: [string[ind..-1], string[0..ind-1]] 651: end
# File lib/rexml/parsers/xpathparser.rb, line 653 653: def parse_args( string ) 654: arguments = [] 655: ind = 0 656: inquot = false 657: inapos = false 658: depth = 1 659: begin 660: case string[ind] 661: when ?" 662: inquot = !inquot unless inapos 663: when ?' 664: inapos = !inapos unless inquot 665: else 666: unless inquot or inapos 667: case string[ind] 668: when ?( 669: depth += 1 670: if depth == 1 671: string = string[1..-1] 672: ind -= 1 673: end 674: when ?) 675: depth -= 1 676: if depth == 0 677: s = string[0,ind].strip 678: arguments << s unless s == "" 679: string = string[ind+1..-1] 680: end 681: when ?, 682: if depth == 1 683: s = string[0,ind].strip 684: arguments << s unless s == "" 685: string = string[ind+1..-1] 686: ind = -1 687: end 688: end 689: end 690: end 691: ind += 1 692: end while depth > 0 and ind < string.length 693: return nil unless depth==0 694: [string,arguments] 695: end