Handle is?, is-not?, and set! predicate functions in queries
This commit is contained in:
parent
b15e90bd26
commit
27149902f8
4 changed files with 256 additions and 142 deletions
|
|
@ -719,7 +719,9 @@ class Language {
|
|||
stringValues[i] = UTF8ToString(valueAddress, nameLength);
|
||||
}
|
||||
|
||||
const properties = new Array(patternCount);
|
||||
const setProperties = new Array(patternCount);
|
||||
const assertedProperties = new Array(patternCount);
|
||||
const refutedProperties = new Array(patternCount);
|
||||
const predicates = new Array(patternCount);
|
||||
for (let i = 0; i < patternCount; i++) {
|
||||
const predicatesAddress = C._ts_query_predicates_for_pattern(
|
||||
|
|
@ -730,7 +732,6 @@ class Language {
|
|||
const stepCount = getValue(TRANSFER_BUFFER, 'i32');
|
||||
|
||||
predicates[i] = [];
|
||||
properties[i] = null;
|
||||
|
||||
const steps = [];
|
||||
let stepAddress = predicatesAddress;
|
||||
|
|
@ -744,18 +745,96 @@ class Language {
|
|||
} else if (stepType === PREDICATE_STEP_TYPE_STRING) {
|
||||
steps.push({type: 'string', value: stringValues[stepValueId]});
|
||||
} else if (steps.length > 0) {
|
||||
const predicate = buildQueryPredicate(steps);
|
||||
if (typeof predicate === 'function') {
|
||||
predicates[i].push(predicate);
|
||||
} else {
|
||||
if (!properties[i]) properties[i] = {};
|
||||
properties[i][predicate.key] = predicate.value;
|
||||
if (steps[0].type !== 'string') {
|
||||
throw new Error('Predicates must begin with a literal value');
|
||||
}
|
||||
const operator = steps[0].value;
|
||||
switch (operator) {
|
||||
case 'eq?':
|
||||
if (steps.length !== 3) throw new Error(
|
||||
`Wrong number of arguments to \`eq?\` predicate. Expected 2, got ${steps.length - 1}`
|
||||
);
|
||||
if (steps[1].type !== 'capture') throw new Error(
|
||||
`First argument of \`eq?\` predicate must be a capture. Got "${steps[1].value}"`
|
||||
);
|
||||
if (steps[2].type === 'capture') {
|
||||
const captureName1 = steps[1].name;
|
||||
const captureName2 = steps[2].name;
|
||||
predicates[i].push(function(captures) {
|
||||
let node1, node2
|
||||
for (const c of captures) {
|
||||
if (c.name === captureName1) node1 = c.node;
|
||||
if (c.name === captureName2) node2 = c.node;
|
||||
}
|
||||
return node1.text === node2.text
|
||||
});
|
||||
} else {
|
||||
const captureName = steps[1].name;
|
||||
const stringValue = steps[2].value;
|
||||
predicates[i].push(function(captures) {
|
||||
for (const c of captures) {
|
||||
if (c.name === captureName) return c.node.text === stringValue;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case 'match?':
|
||||
if (steps.length !== 3) throw new Error(
|
||||
`Wrong number of arguments to \`match?\` predicate. Expected 2, got ${steps.length - 1}.`
|
||||
);
|
||||
if (steps[1].type !== 'capture') throw new Error(
|
||||
`First argument of \`match?\` predicate must be a capture. Got "${steps[1].value}".`
|
||||
);
|
||||
if (steps[2].type !== 'string') throw new Error(
|
||||
`Second argument of \`match?\` predicate must be a string. Got @${steps[2].value}.`
|
||||
);
|
||||
const captureName = steps[1].name;
|
||||
const regex = new RegExp(steps[2].value);
|
||||
predicates[i].push(function(captures) {
|
||||
for (const c of captures) {
|
||||
if (c.name === captureName) return regex.test(c.node.text);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
break;
|
||||
|
||||
case 'set!':
|
||||
if (steps.length < 2 || steps.length > 3) throw new Error(
|
||||
`Wrong number of arguments to \`set!\` predicate. Expected 1 or 2. Got ${steps.length - 1}.`
|
||||
);
|
||||
if (steps.some(s => s.type !== 'string')) throw new Error(
|
||||
`Arguments to \`set!\` predicate must be a strings.".`
|
||||
);
|
||||
if (!setProperties[i]) setProperties[i] = {};
|
||||
setProperties[i][steps[1].value] = steps[2] ? steps[2].value : null;
|
||||
break;
|
||||
|
||||
case 'is?':
|
||||
case 'is-not?':
|
||||
if (steps.length < 2 || steps.length > 3) throw new Error(
|
||||
`Wrong number of arguments to \`${operator}\` predicate. Expected 1 or 2. Got ${steps.length - 1}.`
|
||||
);
|
||||
if (steps.some(s => s.type !== 'string')) throw new Error(
|
||||
`Arguments to \`${operator}\` predicate must be a strings.".`
|
||||
);
|
||||
const properties = operator === 'is?' ? assertedProperties : refutedProperties;
|
||||
if (!properties[i]) properties[i] = {};
|
||||
properties[i][steps[1].value] = steps[2] ? steps[2].value : null;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error(`Unknown query predicate \`${steps[0].value}\``);
|
||||
}
|
||||
|
||||
steps.length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Object.freeze(properties[i]);
|
||||
Object.freeze(setProperties[i]);
|
||||
Object.freeze(assertedProperties[i]);
|
||||
Object.freeze(refutedProperties[i]);
|
||||
}
|
||||
|
||||
C._free(sourceAddress);
|
||||
|
|
@ -764,7 +843,9 @@ class Language {
|
|||
address,
|
||||
captureNames,
|
||||
predicates,
|
||||
Object.freeze(properties)
|
||||
Object.freeze(setProperties),
|
||||
Object.freeze(assertedProperties),
|
||||
Object.freeze(refutedProperties)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -801,12 +882,17 @@ class Language {
|
|||
}
|
||||
|
||||
class Query {
|
||||
constructor(internal, address, captureNames, predicates, properties) {
|
||||
constructor(
|
||||
internal, address, captureNames, predicates,
|
||||
setProperties, assertedProperties, refutedProperties
|
||||
) {
|
||||
assertInternal(internal);
|
||||
this[0] = address;
|
||||
this.captureNames = captureNames;
|
||||
this.predicates = predicates;
|
||||
this.patternProperties = properties;
|
||||
this.setProperties = setProperties;
|
||||
this.assertedProperties = assertedProperties;
|
||||
this.refutedProperties = refutedProperties;
|
||||
}
|
||||
|
||||
delete() {
|
||||
|
|
@ -843,10 +929,13 @@ class Query {
|
|||
address = unmarshalCaptures(this, node.tree, address, captures);
|
||||
if (this.predicates[pattern].every(p => p(captures))) {
|
||||
result[i] = {pattern, captures};
|
||||
const setProperties = this.setProperties[pattern];
|
||||
if (setProperties) result[i].setProperties = setProperties;
|
||||
const assertedProperties = this.assertedProperties[pattern];
|
||||
if (assertedProperties) result[i].assertedProperties = assertedProperties;
|
||||
const refutedProperties = this.refutedProperties[pattern];
|
||||
if (refutedProperties) result[i].refutedProperties = refutedProperties;
|
||||
}
|
||||
|
||||
const properties = this.patternProperties[pattern];
|
||||
if (properties) result[i].properties = properties;
|
||||
}
|
||||
|
||||
C._free(startAddress);
|
||||
|
|
@ -887,8 +976,12 @@ class Query {
|
|||
|
||||
if (this.predicates[pattern].every(p => p(captures))) {
|
||||
const capture = captures[captureIndex];
|
||||
const properties = this.patternProperties[pattern];
|
||||
if (properties) capture.properties = properties;
|
||||
const setProperties = this.setProperties[pattern];
|
||||
if (setProperties) capture.setProperties = setProperties;
|
||||
const assertedProperties = this.assertedProperties[pattern];
|
||||
if (assertedProperties) capture.assertedProperties = assertedProperties;
|
||||
const refutedProperties = this.refutedProperties[pattern];
|
||||
if (refutedProperties) capture.refutedProperties = refutedProperties;
|
||||
result.push(capture);
|
||||
}
|
||||
}
|
||||
|
|
@ -898,77 +991,6 @@ class Query {
|
|||
}
|
||||
}
|
||||
|
||||
function buildQueryPredicate(steps) {
|
||||
if (steps[0].type !== 'string') {
|
||||
throw new Error('Predicates must begin with a literal value');
|
||||
}
|
||||
|
||||
switch (steps[0].value) {
|
||||
case 'eq?':
|
||||
if (steps.length !== 3) throw new Error(
|
||||
`Wrong number of arguments to \`eq?\` predicate. Expected 2, got ${steps.length - 1}`
|
||||
);
|
||||
if (steps[1].type !== 'capture') throw new Error(
|
||||
`First argument of \`eq?\` predicate must be a capture. Got "${steps[1].value}"`
|
||||
);
|
||||
if (steps[2].type === 'capture') {
|
||||
const captureName1 = steps[1].name;
|
||||
const captureName2 = steps[2].name;
|
||||
return function(captures) {
|
||||
let node1, node2
|
||||
for (const c of captures) {
|
||||
if (c.name === captureName1) node1 = c.node;
|
||||
if (c.name === captureName2) node2 = c.node;
|
||||
}
|
||||
return node1.text === node2.text
|
||||
}
|
||||
} else {
|
||||
const captureName = steps[1].name;
|
||||
const stringValue = steps[2].value;
|
||||
return function(captures) {
|
||||
for (const c of captures) {
|
||||
if (c.name === captureName) return c.node.text === stringValue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
case 'match?':
|
||||
if (steps.length !== 3) throw new Error(
|
||||
`Wrong number of arguments to \`match?\` predicate. Expected 2, got ${steps.length - 1}.`
|
||||
);
|
||||
if (steps[1].type !== 'capture') throw new Error(
|
||||
`First argument of \`match?\` predicate must be a capture. Got "${steps[1].value}".`
|
||||
);
|
||||
if (steps[2].type !== 'string') throw new Error(
|
||||
`Second argument of \`match?\` predicate must be a string. Got @${steps[2].value}.`
|
||||
);
|
||||
const captureName = steps[1].name;
|
||||
const regex = new RegExp(steps[2].value);
|
||||
return function(captures) {
|
||||
for (const c of captures) {
|
||||
if (c.name === captureName) return regex.test(c.node.text);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
case 'set!':
|
||||
if (steps.length !== 3) throw new Error(
|
||||
`Wrong number of arguments to \`set!\` predicate. Expected 2, got ${steps.length - 1}.`
|
||||
);
|
||||
if (steps[1].type !== 'string' || steps[2].type !== 'string') throw new Error(
|
||||
`Arguments to \`set!\` predicate must be a strings.".`
|
||||
);
|
||||
return {
|
||||
key: steps[1].value,
|
||||
value: steps[2].value,
|
||||
};
|
||||
|
||||
default:
|
||||
throw new Error(`Unknown query predicate \`${steps[0].value}\``);
|
||||
}
|
||||
}
|
||||
|
||||
function unmarshalCaptures(query, tree, address, result) {
|
||||
for (let i = 0, n = result.length; i < n; i++) {
|
||||
const captureIndex = getValue(address, 'i32');
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue