📄 xpath.js
字号:
} return c;}// The axes of XPath expressions.var xpathAxis = { ANCESTOR_OR_SELF: 'ancestor-or-self', ANCESTOR: 'ancestor', ATTRIBUTE: 'attribute', CHILD: 'child', DESCENDANT_OR_SELF: 'descendant-or-self', DESCENDANT: 'descendant', FOLLOWING_SIBLING: 'following-sibling', FOLLOWING: 'following', NAMESPACE: 'namespace', PARENT: 'parent', PRECEDING_SIBLING: 'preceding-sibling', PRECEDING: 'preceding', SELF: 'self'};var xpathAxesRe = [ xpathAxis.ANCESTOR_OR_SELF, xpathAxis.ANCESTOR, xpathAxis.ATTRIBUTE, xpathAxis.CHILD, xpathAxis.DESCENDANT_OR_SELF, xpathAxis.DESCENDANT, xpathAxis.FOLLOWING_SIBLING, xpathAxis.FOLLOWING, xpathAxis.NAMESPACE, xpathAxis.PARENT, xpathAxis.PRECEDING_SIBLING, xpathAxis.PRECEDING, xpathAxis.SELF].join('|');// The tokens of the language. The label property is just used for// generating debug output. The prec property is the precedence used// for shift/reduce resolution. Default precedence is 0 as a lookahead// token and 2 on the stack. TODO(mesch): this is certainly not// necessary and too complicated. Simplify this!// NOTE: tabular formatting is the big exception, but here it should// be OK.var TOK_PIPE = { label: "|", prec: 17, re: new RegExp("^\\|") };var TOK_DSLASH = { label: "//", prec: 19, re: new RegExp("^//") };var TOK_SLASH = { label: "/", prec: 30, re: new RegExp("^/") };var TOK_AXIS = { label: "::", prec: 20, re: new RegExp("^::") };var TOK_COLON = { label: ":", prec: 1000, re: new RegExp("^:") };var TOK_AXISNAME = { label: "[axis]", re: new RegExp('^(' + xpathAxesRe + ')') };var TOK_PARENO = { label: "(", prec: 34, re: new RegExp("^\\(") };var TOK_PARENC = { label: ")", re: new RegExp("^\\)") };var TOK_DDOT = { label: "..", prec: 34, re: new RegExp("^\\.\\.") };var TOK_DOT = { label: ".", prec: 34, re: new RegExp("^\\.") };var TOK_AT = { label: "@", prec: 34, re: new RegExp("^@") };var TOK_COMMA = { label: ",", re: new RegExp("^,") };var TOK_OR = { label: "or", prec: 10, re: new RegExp("^or\\b") };var TOK_AND = { label: "and", prec: 11, re: new RegExp("^and\\b") };var TOK_EQ = { label: "=", prec: 12, re: new RegExp("^=") };var TOK_NEQ = { label: "!=", prec: 12, re: new RegExp("^!=") };var TOK_GE = { label: ">=", prec: 13, re: new RegExp("^>=") };var TOK_GT = { label: ">", prec: 13, re: new RegExp("^>") };var TOK_LE = { label: "<=", prec: 13, re: new RegExp("^<=") };var TOK_LT = { label: "<", prec: 13, re: new RegExp("^<") };var TOK_PLUS = { label: "+", prec: 14, re: new RegExp("^\\+"), left: true };var TOK_MINUS = { label: "-", prec: 14, re: new RegExp("^\\-"), left: true };var TOK_DIV = { label: "div", prec: 15, re: new RegExp("^div\\b"), left: true };var TOK_MOD = { label: "mod", prec: 15, re: new RegExp("^mod\\b"), left: true };var TOK_BRACKO = { label: "[", prec: 32, re: new RegExp("^\\[") };var TOK_BRACKC = { label: "]", re: new RegExp("^\\]") };var TOK_DOLLAR = { label: "$", re: new RegExp("^\\$") };var TOK_NCNAME = { label: "[ncname]", re: new RegExp('^[a-z][-\\w]*','i') };var TOK_ASTERISK = { label: "*", prec: 15, re: new RegExp("^\\*"), left: true };var TOK_LITERALQ = { label: "[litq]", prec: 20, re: new RegExp("^'[^\\']*'") };var TOK_LITERALQQ = { label: "[litqq]", prec: 20, re: new RegExp('^"[^\\"]*"')};var TOK_NUMBER = { label: "[number]", prec: 35, re: new RegExp('^\\d+(\\.\\d*)?') };var TOK_QNAME = { label: "[qname]", re: new RegExp('^([a-z][-\\w]*:)?[a-z][-\\w]*','i')};var TOK_NODEO = { label: "[nodetest-start]", re: new RegExp('^(processing-instruction|comment|text|node)\\(')};// The table of the tokens of our grammar, used by the lexer: first// column the tag, second column a regexp to recognize it in the// input, third column the precedence of the token, fourth column a// factory function for the semantic value of the token.//// NOTE: order of this list is important, because the first match// counts. Cf. DDOT and DOT, and AXIS and COLON.var xpathTokenRules = [ TOK_DSLASH, TOK_SLASH, TOK_DDOT, TOK_DOT, TOK_AXIS, TOK_COLON, TOK_AXISNAME, TOK_NODEO, TOK_PARENO, TOK_PARENC, TOK_BRACKO, TOK_BRACKC, TOK_AT, TOK_COMMA, TOK_OR, TOK_AND, TOK_NEQ, TOK_EQ, TOK_GE, TOK_GT, TOK_LE, TOK_LT, TOK_PLUS, TOK_MINUS, TOK_ASTERISK, TOK_PIPE, TOK_MOD, TOK_DIV, TOK_LITERALQ, TOK_LITERALQQ, TOK_NUMBER, TOK_QNAME, TOK_NCNAME, TOK_DOLLAR];// All the nonterminals of the grammar. The nonterminal objects are// identified by object identity; the labels are used in the debug// output only.var XPathLocationPath = { label: "LocationPath" };var XPathRelativeLocationPath = { label: "RelativeLocationPath" };var XPathAbsoluteLocationPath = { label: "AbsoluteLocationPath" };var XPathStep = { label: "Step" };var XPathNodeTest = { label: "NodeTest" };var XPathPredicate = { label: "Predicate" };var XPathLiteral = { label: "Literal" };var XPathExpr = { label: "Expr" };var XPathPrimaryExpr = { label: "PrimaryExpr" };var XPathVariableReference = { label: "Variablereference" };var XPathNumber = { label: "Number" };var XPathFunctionCall = { label: "FunctionCall" };var XPathArgumentRemainder = { label: "ArgumentRemainder" };var XPathPathExpr = { label: "PathExpr" };var XPathUnionExpr = { label: "UnionExpr" };var XPathFilterExpr = { label: "FilterExpr" };var XPathDigits = { label: "Digits" };var xpathNonTerminals = [ XPathLocationPath, XPathRelativeLocationPath, XPathAbsoluteLocationPath, XPathStep, XPathNodeTest, XPathPredicate, XPathLiteral, XPathExpr, XPathPrimaryExpr, XPathVariableReference, XPathNumber, XPathFunctionCall, XPathArgumentRemainder, XPathPathExpr, XPathUnionExpr, XPathFilterExpr, XPathDigits];// Quantifiers that are used in the productions of the grammar.var Q_01 = { label: "?" };var Q_MM = { label: "*" };var Q_1M = { label: "+" };// Tag for left associativity (right assoc is implied by undefined).var ASSOC_LEFT = true;// The productions of the grammar. Columns of the table://// - target nonterminal,// - pattern,// - precedence,// - semantic value factory//// The semantic value factory is a function that receives parse tree// nodes from the stack frames of the matched symbols as arguments and// returns an a node of the parse tree. The node is stored in the top// stack frame along with the target object of the rule. The node in// the parse tree is an expression object that has an evaluate() method// and thus evaluates XPath expressions.//// The precedence is used to decide between reducing and shifting by// comparing the precendence of the rule that is candidate for// reducing with the precedence of the look ahead token. Precedence of// -1 means that the precedence of the tokens in the pattern is used// instead. TODO: It shouldn't be necessary to explicitly assign// precedences to rules.var xpathGrammarRules = [ [ XPathLocationPath, [ XPathRelativeLocationPath ], 18, passExpr ], [ XPathLocationPath, [ XPathAbsoluteLocationPath ], 18, passExpr ], [ XPathAbsoluteLocationPath, [ TOK_SLASH, XPathRelativeLocationPath ], 18, makeLocationExpr1 ], [ XPathAbsoluteLocationPath, [ TOK_DSLASH, XPathRelativeLocationPath ], 18, makeLocationExpr2 ], [ XPathAbsoluteLocationPath, [ TOK_SLASH ], 0, makeLocationExpr3 ], [ XPathAbsoluteLocationPath, [ TOK_DSLASH ], 0, makeLocationExpr4 ], [ XPathRelativeLocationPath, [ XPathStep ], 31, makeLocationExpr5 ], [ XPathRelativeLocationPath, [ XPathRelativeLocationPath, TOK_SLASH, XPathStep ], 31, makeLocationExpr6 ], [ XPathRelativeLocationPath, [ XPathRelativeLocationPath, TOK_DSLASH, XPathStep ], 31, makeLocationExpr7 ], [ XPathStep, [ TOK_DOT ], 33, makeStepExpr1 ], [ XPathStep, [ TOK_DDOT ], 33, makeStepExpr2 ], [ XPathStep, [ TOK_AXISNAME, TOK_AXIS, XPathNodeTest ], 33, makeStepExpr3 ], [ XPathStep, [ TOK_AT, XPathNodeTest ], 33, makeStepExpr4 ], [ XPathStep, [ XPathNodeTest ], 33, makeStepExpr5 ], [ XPathStep, [ XPathStep, XPathPredicate ], 33, makeStepExpr6 ], [ XPathNodeTest, [ TOK_ASTERISK ], 33, makeNodeTestExpr1 ], [ XPathNodeTest, [ TOK_NCNAME, TOK_COLON, TOK_ASTERISK ], 33, makeNodeTestExpr2 ], [ XPathNodeTest, [ TOK_QNAME ], 33, makeNodeTestExpr3 ], [ XPathNodeTest, [ TOK_NODEO, TOK_PARENC ], 33, makeNodeTestExpr4 ], [ XPathNodeTest, [ TOK_NODEO, XPathLiteral, TOK_PARENC ], 33, makeNodeTestExpr5 ], [ XPathPredicate, [ TOK_BRACKO, XPathExpr, TOK_BRACKC ], 33, makePredicateExpr ], [ XPathPrimaryExpr, [ XPathVariableReference ], 33, passExpr ], [ XPathPrimaryExpr, [ TOK_PARENO, XPathExpr, TOK_PARENC ], 33, makePrimaryExpr ], [ XPathPrimaryExpr, [ XPathLiteral ], 30, passExpr ], [ XPathPrimaryExpr, [ XPathNumber ], 30, passExpr ], [ XPathPrimaryExpr, [ XPathFunctionCall ], 30, passExpr ], [ XPathFunctionCall, [ TOK_QNAME, TOK_PARENO, TOK_PARENC ], -1, makeFunctionCallExpr1 ], [ XPathFunctionCall, [ TOK_QNAME, TOK_PARENO, XPathExpr, XPathArgumentRemainder, Q_MM, TOK_PARENC ], -1, makeFunctionCallExpr2 ], [ XPathArgumentRemainder, [ TOK_COMMA, XPathExpr ], -1, makeArgumentExpr ], [ XPathUnionExpr, [ XPathPathExpr ], 20, passExpr ], [ XPathUnionExpr, [ XPathUnionExpr, TOK_PIPE, XPathPathExpr ], 20, makeUnionExpr ], [ XPathPathExpr, [ XPathLocationPath ], 20, passExpr ], [ XPathPathExpr, [ XPathFilterExpr ], 19, passExpr ], [ XPathPathExpr, [ XPathFilterExpr, TOK_SLASH, XPathRelativeLocationPath ], 20, makePathExpr1 ], [ XPathPathExpr, [ XPathFilterExpr, TOK_DSLASH, XPathRelativeLocationPath ], 20, makePathExpr2 ], [ XPathFilterExpr, [ XPathPrimaryExpr, XPathPredicate, Q_MM ], 20, makeFilterExpr ], [ XPathExpr, [ XPathPrimaryExpr ], 16, passExpr ], [ XPathExpr, [ XPathUnionExpr ], 16, passExpr ], [ XPathExpr, [ TOK_MINUS, XPathExpr ], -1, makeUnaryMinusExpr ], [ XPathExpr, [ XPathExpr, TOK_OR, XPathExpr ], -1, makeBinaryExpr ], [ XPathExpr, [ XPathExpr, TOK_AND, XPathExpr ], -1, makeBinaryExpr ], [ XPathExpr, [ XPathExpr, TOK_EQ, XPathExpr ], -1, makeBinaryExpr ], [ XPathExpr, [ XPathExpr, TOK_NEQ, XPathExpr ], -1, makeBinaryExpr ], [ XPathExpr, [ XPathExpr, TOK_LT, XPathExpr ], -1, makeBinaryExpr ], [ XPathExpr, [ XPathExpr, TOK_LE, XPathExpr ], -1, makeBinaryExpr ], [ XPathExpr, [ XPathExpr, TOK_GT, XPathExpr ], -1, makeBinaryExpr ], [ XPathExpr, [ XPathExpr, TOK_GE, XPathExpr ], -1, makeBinaryExpr ], [ XPathExpr, [ XPathExpr, TOK_PLUS, XPathExpr ], -1, makeBinaryExpr, ASSOC_LEFT ], [ XPathExpr, [ XPathExpr, TOK_MINUS, XPathExpr ], -1, makeBinaryExpr, ASSOC_LEFT ], [ XPathExpr, [ XPathExpr, TOK_ASTERISK, XPathExpr ], -1, makeBinaryExpr, ASSOC_LEFT ], [ XPathExpr, [ XPathExpr, TOK_DIV, XPathExpr ], -1, makeBinaryExpr, ASSOC_LEFT ], [ XPathExpr, [ XPathExpr, TOK_MOD, XPathExpr ], -1, makeBinaryExpr, ASSOC_LEFT ], [ XPathLiteral, [ TOK_LITERALQ ], -1, makeLiteralExpr ], [ XPathLiteral, [ TOK_LITERALQQ ], -1, makeLiteralExpr ], [ XPathNumber, [ TOK_NUMBER ], -1, makeNumberExpr ], [ XPathVariableReference, [ TOK_DOLLAR, TOK_QNAME ], 200, makeVariableReference ] ];// That function computes some optimizations of the above data// structures and will be called right here. It merely takes the// counter variables out of the global scope.var xpathRules = [];function xpathParseInit() { if (xpathRules.length) { return; } // Some simple optimizations for the xpath expression parser: sort // grammar rules descending by length, so that the longest match is // first found. xpathGrammarRules.sort(function(a,b) { var la = a[1].length; var lb = b[1].length; if (la < lb) { return 1; } else if (la > lb) { return -1; } else { return 0; } }); var k = 1; for (var i = 0; i < xpathNonTerminals.length; ++i) { xpathNonTerminals[i].key = k++; } for (i = 0; i < xpathTokenRules.length; ++i) { xpathTokenRules[i].key = k++; } Log.write('XPath parse INIT: ' + k + ' rules'); // Another slight optimization: sort the rules into bins according // to the last element (observing quantifiers), so we can restrict // the match against the stack to the subest of rules that match the // top of the stack. // // TODO(mesch): What we actually want is to compute states as in // bison, so that we don't have to do any explicit and iterated // match against the stack. function push_(array, position, element) { if (!array[position]) { array[position] = []; } array[position].push(element); } for (i = 0; i < xpathGrammarRules.length; ++i) { var rule = xpathGrammarRules[i]; var pattern = rule[1]; for (var j = pattern.length - 1; j >= 0; --j) { if (pattern[j] == Q_1M) { push_(xpathRules, pattern[j-1].key, rule); break; } else if (pattern[j] == Q_MM || pattern[j] == Q_01) { push_(xpathRules, pattern[j-1].key, rule); --j; } else { push_(xpathRules, pattern[j].key, rule); break; } } } Log.write('XPath parse INIT: ' + xpathRules.length + ' rule bins'); var sum = 0; mapExec(xpathRules, function(i) { if (i) { sum += i.length; } }); Log.write('XPath parse INIT: ' + (sum / xpathRules.length) + ' average bin size');}// Local utility functions that are used by the lexer or parser.function xpathCollectDescendants(nodelist, node) { for (var n = node.firstChild; n; n = n.nextSibling) { nodelist.push(n); arguments.callee(nodelist, n); }}function xpathCollectDescendantsReverse(nodelist, node) { for (var n = node.lastChild; n; n = n.previousSibling) { nodelist.push(n); arguments.callee(nodelist, n); }}// The entry point for the library: match an expression against a DOM// node. Returns an XPath value.function xpathDomEval(expr, node) { var expr1 = xpathParse(expr); var ret = expr1.evaluate(new ExprContext(node)); return ret;}// Utility function to sort a list of nodes. Used by xsltSort() and// nxslSelect().function xpathSort(input, sort) { if (sort.length == 0) { return; } var sortlist = []; for (var i = 0; i < input.nodelist.length; ++i) { var node = input.nodelist[i]; var sortitem = { node: node, key: [] }; var context = input.clone(node, 0, [ node ]); for (var j = 0; j < sort.length; ++j) { var s = sort[j]; var value = s.expr.evaluate(context); var evalue; if (s.type == 'text') { evalue = value.stringValue(); } else if (s.type == 'number') { evalue = value.numberValue(); } sortitem.key.push({ value: evalue, order: s.order }); } // Make the sort stable by adding a lowest priority sort by // id. This is very convenient and furthermore required by the // spec ([XSLT] - Section 10 Sorting). sortitem.key.push({ value: i, order: 'ascending' }); sortlist.push(sortitem); } sortlist.sort(xpathSortByKey); var nodes = []; for (var i = 0; i < sortlist.length; ++i) { nodes.push(sortlist[i].node); } input.nodelist = nodes; input.setNode(nodes[0], 0);}// Sorts by all order criteria defined. According to the JavaScript// spec ([ECMA] Section 11.8.5), the compare operators compare strings// as strings and numbers as numbers.//// NOTE: In browsers which do not follow the spec, this breaks only in// the case that numbers should be sorted as strings, which is very// uncommon.function xpathSortByKey(v1, v2) { // NOTE: Sort key vectors of different length never occur in // xsltSort. for (var i = 0; i < v1.key.length; ++i) { var o = v1.key[i].order == 'descending' ? -1 : 1; if (v1.key[i].value > v2.key[i].value) { return +1 * o; } else if (v1.key[i].value < v2.key[i].value) { return -1 * o; } } return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -