fontoxpath
Version:
A minimalistic XPath 3.1 engine in JavaScript
84 lines (78 loc) • 2.17 kB
JavaScript
import Selector from './Selector';
import Sequence from './dataTypes/Sequence';
import Specificity from './Specificity';
import { DONE_TOKEN } from './util/iterators';
function buildVarName ({ prefix, namespaceURI, name }) {
if (namespaceURI) {
throw new Error('Not implemented: for expressions with a namespace URI in the binding.');
}
return prefix ? `${prefix}:${name}` : name;
}
/**
* @extends {Selector}
*/
class ForExpression extends Selector {
/**
* @param {!{varName:{prefix:string, namespaceURI:string, name:string}, expression}} clause
* @param {!Selector} expression
*/
constructor (clause, expression) {
super(new Specificity({}), {
canBeStaticallyEvaluated: false
});
this._varName = buildVarName(clause.varName);
/**
* @type {!Selector}
*/
this._clauseExpression = clause.expression;
/**
* @type {!Selector}
*/
this._returnExpression = expression;
}
evaluate (dynamicContext) {
/**
* @type {!./util/iterators.AsyncIterator<!./dataTypes/Value>}
*/
const clauseIterator = this._clauseExpression.evaluateMaybeStatically(dynamicContext).value();
/**
* @type {?./util/iterators.AsyncIterator<!./dataTypes/Value>}
*/
let returnIterator = null;
let done = false;
return new Sequence({
next: () => {
while (!done) {
if (returnIterator === null) {
var currentClauseValue = clauseIterator.next();
if (!currentClauseValue.ready) {
return currentClauseValue;
}
if (currentClauseValue.done) {
done = true;
break;
}
/**
* @type {!./DynamicContext}
*/
const contextWithVars = dynamicContext.scopeWithVariables({
[this._varName]: () => {
return Sequence.singleton(currentClauseValue.value);
}
});
returnIterator = this._returnExpression.evaluateMaybeStatically(contextWithVars).value();
}
const returnValue = returnIterator.next();
if (returnValue.done) {
returnIterator = null;
// Get the next one
continue;
}
return returnValue;
}
return DONE_TOKEN;
}
});
}
}
export default ForExpression;