fontoxpath
Version:
A minimalistic XPath 3.1 engine in JavaScript
128 lines (117 loc) • 3.22 kB
JavaScript
import Sequence from '../dataTypes/Sequence';
import { sortNodeValues } from '../dataTypes/documentOrderUtils';
import createAtomicValue from '../dataTypes/createAtomicValue';
import { ready, notReady, DONE_TOKEN } from '../util/iterators';
function opTo (_dynamicContext, fromSequence, toSequence) {
// shortcut the non-trivial case of both values being known
// RangeExpr is inclusive: 1 to 3 will make (1,2,3)
const from = fromSequence.tryGetFirst();
const to = toSequence.tryGetFirst();
let fromValue = null;
let toValue = null;
if (from.ready && to.ready) {
if (from.value === null || to.value === null) {
return Sequence.empty();
}
fromValue = from.value.value;
toValue = to.value.value;
if (fromValue > toValue) {
return Sequence.empty();
}
// By providing a length, we do not have to hold an end condition into account
return new Sequence({
next: () => ready(createAtomicValue(fromValue++, 'xs:integer'))
}, toValue - fromValue + 1);
}
return new Sequence({
next: () => {
if (fromValue === null) {
const from = fromSequence.tryGetFirst();
if (!from.ready) {
return notReady(from.promise);
}
if (from.value === null) {
return DONE_TOKEN;
}
fromValue = from.value.value;
}
if (toValue === null) {
const to = toSequence.tryGetFirst();
if (!to.ready) {
return notReady(to.promise);
}
if (to.value === null) {
return DONE_TOKEN;
}
toValue = to.value.value;
}
if (fromValue > toValue) {
return DONE_TOKEN;
}
return ready(createAtomicValue(fromValue++, 'xs:integer'));
}
});
}
/**
* @param {../DynamicContext} dynamicContext
* @param {!Sequence} firstNodes
* @param {!Sequence} secondNodes
* @return {!Sequence}
*/
function opExcept (dynamicContext, firstNodes, secondNodes) {
/**
* @type {!Array<!../dataTypes/Value>}
*/
const allSecondNodes = secondNodes.getAllValues();
const allNodes = firstNodes.getAllValues().filter(function (nodeA) {
return allSecondNodes.every(function (nodeB) {
return nodeA !== nodeB;
});
});
return new Sequence(sortNodeValues(dynamicContext.domFacade, allNodes));
}
/**
* @param {../DynamicContext} dynamicContext
* @param {!Sequence} firstNodes
* @param {!Sequence} secondNodes
* @return {!Sequence}
*/
function opIntersect (dynamicContext, firstNodes, secondNodes) {
/**
* @type {!Array<!../dataTypes/Value>}
*/
const allSecondNodes = secondNodes.getAllValues();
const allNodes = firstNodes.getAllValues().filter(function (nodeA) {
return allSecondNodes.some(function (nodeB) {
return nodeA === nodeB;
});
});
return new Sequence(sortNodeValues(dynamicContext.domFacade, allNodes));
}
export default {
declarations: [
{
name: 'op:except',
argumentTypes: ['node()*', 'node()*'],
returnType: 'node()*',
callFunction: opExcept
},
{
name: 'op:intersect',
argumentTypes: ['node()*', 'node()*'],
returnType: 'node()*',
callFunction: opIntersect
},
{
name: 'op:to',
argumentTypes: ['xs:integer?', 'xs:integer?'],
returnType: 'xs:integer*',
callFunction: opTo
},
],
functions: {
except: opExcept,
intersect: opIntersect,
to: opTo
}
};