EdgeExpressDB
view release on metacpan or search on metacpan
www/edgeexpress/jscript/SpryAssets/xpath.js view on Meta::CPAN
// An XPath parser and evaluator written in JavaScript. The
// implementation is complete except for functions handling
// namespaces.
//
// Reference: [XPATH] XPath Specification
// <http://www.w3.org/TR/1999/REC-xpath-19991116>.
//
//
// The API of the parser has several parts:
//
// 1. The parser function xpathParse() that takes a string and returns
// an expession object.
//
// 2. The expression object that has an evaluate() method to evaluate the
// XPath expression it represents. (It is actually a hierarchy of
// objects that resembles the parse tree, but an application will call
// evaluate() only on the top node of this hierarchy.)
//
// 3. The context object that is passed as an argument to the evaluate()
// method, which represents the DOM context in which the expression is
// evaluated.
//
// 4. The value object that is returned from evaluate() and represents
// values of the different types that are defined by XPath (number,
// string, boolean, and node-set), and allows to convert between them.
//
// These parts are near the top of the file, the functions and data
// that are used internally follow after them.
//
//
// TODO(mesch): add jsdoc comments. Use more coherent naming.
//
//
// Author: Steffen Meschkat <mesch@google.com>
// The entry point for the parser.
//
// @param expr a string that contains an XPath expression.
// @return an expression object that can be evaluated with an
// expression context.
function xpathParse(expr) {
if (xpathdebug) {
Log.write('XPath parse ' + expr);
}
xpathParseInit();
var cached = xpathCacheLookup(expr);
if (cached) {
if (xpathdebug) {
Log.write(' ... cached');
}
return cached;
}
// Optimize for a few common cases: simple attribute node tests
// (@id), simple element node tests (page), variable references
// ($address), numbers (4), multi-step path expressions where each
// step is a plain element node test
// (page/overlay/locations/location).
if (expr.match(/^(\$|@)?\w+$/i)) {
var ret = makeSimpleExpr(expr);
xpathParseCache[expr] = ret;
if (xpathdebug) {
Log.write(' ... simple');
}
return ret;
}
if (expr.match(/^\w+(\/\w+)*$/i)) {
var ret = makeSimpleExpr2(expr);
xpathParseCache[expr] = ret;
if (xpathdebug) {
Log.write(' ... simple 2');
}
return ret;
}
var cachekey = expr; // expr is modified during parse
if (xpathdebug) {
Timer.start('XPath parse', cachekey);
}
var stack = [];
var ahead = null;
var previous = null;
var done = false;
var parse_count = 0;
var lexer_count = 0;
var reduce_count = 0;
while (!done) {
parse_count++;
expr = expr.replace(/^\s*/, '');
previous = ahead;
ahead = null;
var rule = null;
var match = '';
for (var i = 0; i < xpathTokenRules.length; ++i) {
var result = xpathTokenRules[i].re.exec(expr);
lexer_count++;
if (result && result.length > 0 && result[0].length > match.length) {
rule = xpathTokenRules[i];
match = result[0];
break;
}
}
// Special case: allow operator keywords to be element and
// variable names.
// NOTE(mesch): The parser resolves conflicts by looking ahead,
// and this is the only case where we look back to
// disambiguate. So this is indeed something different, and
// looking back is usually done in the lexer (via states in the
// general case, called "start conditions" in flex(1)). Also,the
// conflict resolution in the parser is not as robust as it could
( run in 0.700 second using v1.01-cache-2.11-cpan-df04353d9ac )