📄 xpath.js
字号:
return new NumberValue(Math.round(num));
},
// TODO(mesch): The following functions are custom. There is a
// standard that defines how to add functions, which should be
// applied here.
'ext-join': function(ctx) {
assert(this.args.length == 2);
var nodes = this.args[0].evaluate(ctx).nodeSetValue();
var delim = this.args[1].evaluate(ctx).stringValue();
var ret = '';
for (var i = 0; i < nodes.length; ++i) {
if (ret) {
ret += delim;
}
ret += xmlValue(nodes[i]);
}
return new StringValue(ret);
},
// ext-if() evaluates and returns its second argument, if the
// boolean value of its first argument is true, otherwise it
// evaluates and returns its third argument.
'ext-if': function(ctx) {
assert(this.args.length == 3);
if (this.args[0].evaluate(ctx).booleanValue()) {
return this.args[1].evaluate(ctx);
} else {
return this.args[2].evaluate(ctx);
}
},
'ext-sprintf': function(ctx) {
assert(this.args.length >= 1);
var args = [];
for (var i = 0; i < this.args.length; ++i) {
args.push(this.args[i].evaluate(ctx).stringValue());
}
return new StringValue(sprintf.apply(null, args));
},
// ext-cardinal() evaluates its single argument as a number, and
// returns the current node that many times. It can be used in the
// select attribute to iterate over an integer range.
'ext-cardinal': function(ctx) {
assert(this.args.length >= 1);
var c = this.args[0].evaluate(ctx).numberValue();
var ret = [];
for (var i = 0; i < c; ++i) {
ret.push(ctx.node);
}
return new NodeSetValue(ret);
}
};
function UnionExpr(expr1, expr2) {
this.expr1 = expr1;
this.expr2 = expr2;
}
UnionExpr.prototype.evaluate = function(ctx) {
var nodes1 = this.expr1.evaluate(ctx).nodeSetValue();
var nodes2 = this.expr2.evaluate(ctx).nodeSetValue();
var I1 = nodes1.length;
for (var i2 = 0; i2 < nodes2.length; ++i2) {
for (var i1 = 0; i1 < I1; ++i1) {
if (nodes1[i1] == nodes2[i2]) {
// break inner loop and continue outer loop, labels confuse
// the js compiler, so we don't use them here.
i1 = I1;
}
}
nodes1.push(nodes2[i2]);
}
return new NodeSetValue(nodes2);
};
function PathExpr(filter, rel) {
this.filter = filter;
this.rel = rel;
}
PathExpr.prototype.evaluate = function(ctx) {
var nodes = this.filter.evaluate(ctx).nodeSetValue();
var nodes1 = [];
for (var i = 0; i < nodes.length; ++i) {
var nodes0 = this.rel.evaluate(ctx.clone(nodes[i], i, nodes)).nodeSetValue();
for (var ii = 0; ii < nodes0.length; ++ii) {
nodes1.push(nodes0[ii]);
}
}
return new NodeSetValue(nodes1);
};
function FilterExpr(expr, predicate) {
this.expr = expr;
this.predicate = predicate;
}
FilterExpr.prototype.evaluate = function(ctx) {
var nodes = this.expr.evaluate(ctx).nodeSetValue();
for (var i = 0; i < this.predicate.length; ++i) {
var nodes0 = nodes;
nodes = [];
for (var j = 0; j < nodes0.length; ++j) {
var n = nodes0[j];
if (this.predicate[i].evaluate(ctx.clone(n, j, nodes0)).booleanValue()) {
nodes.push(n);
}
}
}
return new NodeSetValue(nodes);
}
function UnaryMinusExpr(expr) {
this.expr = expr;
}
UnaryMinusExpr.prototype.evaluate = function(ctx) {
return new NumberValue(-this.expr.evaluate(ctx).numberValue());
};
function BinaryExpr(expr1, op, expr2) {
this.expr1 = expr1;
this.expr2 = expr2;
this.op = op;
}
BinaryExpr.prototype.evaluate = function(ctx) {
var ret;
switch (this.op.value) {
case 'or':
ret = new BooleanValue(this.expr1.evaluate(ctx).booleanValue() ||
this.expr2.evaluate(ctx).booleanValue());
break;
case 'and':
ret = new BooleanValue(this.expr1.evaluate(ctx).booleanValue() &&
this.expr2.evaluate(ctx).booleanValue());
break;
case '+':
ret = new NumberValue(this.expr1.evaluate(ctx).numberValue() +
this.expr2.evaluate(ctx).numberValue());
break;
case '-':
ret = new NumberValue(this.expr1.evaluate(ctx).numberValue() -
this.expr2.evaluate(ctx).numberValue());
break;
case '*':
ret = new NumberValue(this.expr1.evaluate(ctx).numberValue() *
this.expr2.evaluate(ctx).numberValue());
break;
case 'mod':
ret = new NumberValue(this.expr1.evaluate(ctx).numberValue() %
this.expr2.evaluate(ctx).numberValue());
break;
case 'div':
ret = new NumberValue(this.expr1.evaluate(ctx).numberValue() /
this.expr2.evaluate(ctx).numberValue());
break;
case '=':
ret = this.compare(ctx, function(x1, x2) { return x1 == x2; });
break;
case '!=':
ret = this.compare(ctx, function(x1, x2) { return x1 != x2; });
break;
case '<':
ret = this.compare(ctx, function(x1, x2) { return x1 < x2; });
break;
case '<=':
ret = this.compare(ctx, function(x1, x2) { return x1 <= x2; });
break;
case '>':
ret = this.compare(ctx, function(x1, x2) { return x1 > x2; });
break;
case '>=':
ret = this.compare(ctx, function(x1, x2) { return x1 >= x2; });
break;
default:
alert('BinaryExpr.evaluate: ' + this.op.value);
}
return ret;
};
BinaryExpr.prototype.compare = function(ctx, cmp) {
var v1 = this.expr1.evaluate(ctx);
var v2 = this.expr2.evaluate(ctx);
var ret;
if (v1.type == 'node-set' && v2.type == 'node-set') {
var n1 = v1.nodeSetValue();
var n2 = v2.nodeSetValue();
ret = false;
for (var i1 = 0; i1 < n1.length; ++i1) {
for (var i2 = 0; i2 < n2.length; ++i2) {
if (cmp(xmlValue(n1[i1]), xmlValue(n2[i2]))) {
ret = true;
// Break outer loop. Labels confuse the jscompiler and we
// don't use them.
i2 = n2.length;
i1 = n1.length;
}
}
}
} else if (v1.type == 'node-set' || v2.type == 'node-set') {
if (v1.type == 'number') {
var s = v1.numberValue();
var n = v2.nodeSetValue();
ret = false;
for (var i = 0; i < n.length; ++i) {
var nn = xmlValue(n[i]) - 0;
if (cmp(s, nn)) {
ret = true;
break;
}
}
} else if (v2.type == 'number') {
var n = v1.nodeSetValue();
var s = v2.numberValue();
ret = false;
for (var i = 0; i < n.length; ++i) {
var nn = xmlValue(n[i]) - 0;
if (cmp(nn, s)) {
ret = true;
break;
}
}
} else if (v1.type == 'string') {
var s = v1.stringValue();
var n = v2.nodeSetValue();
ret = false;
for (var i = 0; i < n.length; ++i) {
var nn = xmlValue(n[i]);
if (cmp(s, nn)) {
ret = true;
break;
}
}
} else if (v2.type == 'string') {
var n = v1.nodeSetValue();
var s = v2.stringValue();
ret = false;
for (var i = 0; i < n.length; ++i) {
var nn = xmlValue(n[i]);
if (cmp(nn, s)) {
ret = true;
break;
}
}
} else {
ret = cmp(v1.booleanValue(), v2.booleanValue());
}
} else if (v1.type == 'boolean' || v2.type == 'boolean') {
ret = cmp(v1.booleanValue(), v2.booleanValue());
} else if (v1.type == 'number' || v2.type == 'number') {
ret = cmp(v1.numberValue(), v2.numberValue());
} else {
ret = cmp(v1.stringValue(), v2.stringValue());
}
return new BooleanValue(ret);
}
function LiteralExpr(value) {
this.value = value;
}
LiteralExpr.prototype.evaluate = function(ctx) {
return new StringValue(this.value);
};
function NumberExpr(value) {
this.value = value;
}
NumberExpr.prototype.evaluate = function(ctx) {
return new NumberValue(this.value);
};
function VariableExpr(name) {
this.name = name;
}
VariableExpr.prototype.evaluate = function(ctx) {
return ctx.getVariable(this.name);
}
// Factory functions for semantic values (i.e. Expressions) of the
// productions in the grammar. When a production is matched to reduce
// the current parse state stack, the function is called with the
// semantic values of the matched elements as arguments, and returns
// another semantic value. The semantic value is a node of the parse
// tree, an expression object with an evaluate() method that evaluates the
// expression in an actual context. These factory functions are used
// in the specification of the grammar rules, below.
function makeTokenExpr(m) {
return new TokenExpr(m);
}
function passExpr(e) {
return e;
}
function makeLocationExpr1(slash, rel) {
rel.absolute = true;
return rel;
}
function makeLocationExpr2(dslash, rel) {
rel.absolute = true;
rel.prependStep(makeAbbrevStep(dslash.value));
return rel;
}
function makeLocationExpr3(slash) {
var ret = new LocationExpr();
ret.appendStep(makeAbbrevStep('.'));
ret.absolute = true;
return ret;
}
function makeLocationExpr4(dslash) {
var ret = new LocationExpr();
ret.absolute = true;
ret.appendStep(makeAbbrevStep(dslash.value));
return ret;
}
function makeLocationExpr5(step) {
var ret = new LocationExpr();
ret.appendStep(step);
return ret;
}
function makeLocationExpr6(rel, slash, step) {
rel.appendStep(step);
return rel;
}
function makeLocationExpr7(rel, dslash, step) {
rel.appendStep(makeAbbrevStep(dslash.value));
return rel;
}
function makeStepExpr1(dot) {
return makeAbbrevStep(dot.value);
}
function makeStepExpr2(ddot) {
return makeAbbrevStep(ddot.value);
}
function makeStepExpr3(axisname, axis, nodetest) {
return new StepExpr(axisname.value, nodetest);
}
function makeStepExpr4(at, nodetest) {
return new StepExpr('attribute', nodetest);
}
function makeStepExpr5(nodetest) {
return new StepExpr('child', nodetest);
}
function makeStepExpr6(step, predicate) {
step.appendPredicate(predicate);
return step;
}
function makeAbbrevStep(abbrev) {
switch (abbrev) {
case '//':
return new StepExpr('descendant-or-self', new NodeTestAny);
case '.':
return new StepExpr('self', new NodeTestAny);
case '..':
return new StepExpr('parent', new NodeTestAny);
}
}
function makeNodeTestExpr1(asterisk) {
return new NodeTestElement;
}
function makeNodeTestExpr2(ncname, colon, asterisk) {
return new NodeTestNC(ncname.value);
}
function makeNodeTestExpr3(qname) {
return new NodeTestName(qname.value);
}
function makeNodeTestExpr4(typeo, parenc) {
var type = typeo.value.replace(/\s*\($/, '');
switch(type) {
case 'node':
return new NodeTestAny;
case 'text':
return new NodeTestText;
case 'comment':
return new NodeTestComment;
case 'processing-instruction':
return new NodeTestPI;
}
}
function makeNodeTestExpr5(typeo, target, parenc) {
var type = typeo.replace(/\s*\($/, '');
if (type != 'processing-instruction') {
throw type + ' ' + Error().stack;
}
return new NodeTestPI(target.value);
}
function makePredicateExpr(pareno, expr, parenc) {
return new PredicateExpr(expr);
}
function makePrimaryExpr(pareno, expr, parenc) {
return expr;
}
function makeFunctionCallExpr1(name, pareno, parenc) {
return new FunctionCallExpr(name);
}
function makeFunctionCallExpr2(name, pareno, arg1, args, parenc) {
var ret = new FunctionCallExpr(name);
ret.appendArg(arg1);
for (var i = 0; i < args.length; ++i) {
ret.appendArg(args[i]);
}
return ret;
}
function makeArgumentExpr(comma, expr) {
return expr;
}
function makeUnionExpr(expr1, pipe, expr2) {
return new UnionExpr(expr1, expr2);
}
function makePathExpr1(filter, slash, rel) {
return new PathExpr(filter, rel);
}
function makePathExpr2(filter, dslash, rel) {
rel.prependStep(makeAbbrevStep(dslash.value));
return new PathExpr(filter, rel);
}
function makeFilterExpr(expr, predicates) {
if (predicates.length > 0) {
return new FilterExpr(expr, predicates);
} else {
return expr;
}
}
function makeUnaryMinusExpr(minus, expr) {
return new UnaryMinusExpr(expr);
}
function makeBinaryExpr(expr1, op, expr2) {
return new BinaryExpr(expr1, op, expr2);
}
function makeLiteralExpr(token) {
// remove quotes from the parsed value:
var value = token.value.substring(1, token.value.length - 1);
return new LiteralExpr(value);
}
function makeNumberExpr(token) {
return new NumberExpr(token.value);
}
function makeVariableReference(dollar, name) {
return new VariableExpr(name.value);
}
// Used before parsing for optimization of common simple cases. See
// the begin of xpathParse() for which they are.
function makeSimpleExpr(expr) {
if (expr.charAt(0) == '$') {
return new VariableExpr(expr.substr(1));
} else if (expr.charAt(0) == '@') {
var a = new NodeTestName(expr.substr(1));
var b = new StepExpr('attribute', a);
var c = new LocationExpr();
c.appendStep(b);
return c;
} else if (expr.match(/^[0-9]+$/)) {
return new NumberExpr(expr);
} else {
var a = new NodeTestName(expr);
var b = new StepExpr('child', a);
var c = new LocationExpr();
c.appendStep(b);
return c;
}
}
function makeSimpleExpr2(expr) {
var steps = expr.split('/');
var c = new LocationExpr();
for (var i in steps) {
var a = new NodeTestName(steps[i]);
var b = new StepExpr('child', a);
c.appendStep(b);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -