Start work on wasm binding to query API
This commit is contained in:
parent
fe7c74e7aa
commit
52cda5f541
4 changed files with 198 additions and 12 deletions
|
|
@ -5,6 +5,7 @@ const SIZE_OF_NODE = 5 * SIZE_OF_INT;
|
|||
const SIZE_OF_POINT = 2 * SIZE_OF_INT;
|
||||
const SIZE_OF_RANGE = 2 * SIZE_OF_INT + 2 * SIZE_OF_POINT;
|
||||
const ZERO_POINT = {row: 0, column: 0};
|
||||
const QUERY_WORD_REGEX = /[\w-.]*/;
|
||||
|
||||
var VERSION;
|
||||
var MIN_COMPATIBLE_VERSION;
|
||||
|
|
@ -143,9 +144,7 @@ class Parser {
|
|||
|
||||
class Tree {
|
||||
constructor(internal, address, language, textCallback) {
|
||||
if (internal !== INTERNAL) {
|
||||
throw new Error('Illegal constructor')
|
||||
}
|
||||
assertInternal(internal);
|
||||
this[0] = address;
|
||||
this.language = language;
|
||||
this.textCallback = textCallback;
|
||||
|
|
@ -201,9 +200,7 @@ class Tree {
|
|||
|
||||
class Node {
|
||||
constructor(internal, tree) {
|
||||
if (internal !== INTERNAL) {
|
||||
throw new Error('Illegal constructor')
|
||||
}
|
||||
assertInternal(internal);
|
||||
this.tree = tree;
|
||||
}
|
||||
|
||||
|
|
@ -526,9 +523,7 @@ class Node {
|
|||
|
||||
class TreeCursor {
|
||||
constructor(internal, tree) {
|
||||
if (internal !== INTERNAL) {
|
||||
throw new Error('Illegal constructor')
|
||||
}
|
||||
assertInternal(internal);
|
||||
this.tree = tree;
|
||||
unmarshalTreeCursor(this);
|
||||
}
|
||||
|
|
@ -630,9 +625,7 @@ class TreeCursor {
|
|||
|
||||
class Language {
|
||||
constructor(internal, address) {
|
||||
if (internal !== INTERNAL) {
|
||||
throw new Error('Illegal constructor')
|
||||
}
|
||||
assertInternal(internal);
|
||||
this[0] = address;
|
||||
this.types = new Array(C._ts_language_symbol_count(this[0]));
|
||||
for (let i = 0, n = this.types.length; i < n; i++) {
|
||||
|
|
@ -672,6 +665,51 @@ class Language {
|
|||
return this.fields[fieldName] || null;
|
||||
}
|
||||
|
||||
query(source) {
|
||||
const sourceLength = lengthBytesUTF8(source);
|
||||
const sourceAddress = C._malloc(sourceLength + 1);
|
||||
stringToUTF8(source, sourceAddress, sourceLength + 1);
|
||||
const address = C._ts_query_new(
|
||||
this[0],
|
||||
sourceAddress,
|
||||
sourceLength,
|
||||
TRANSFER_BUFFER,
|
||||
TRANSFER_BUFFER + SIZE_OF_INT
|
||||
);
|
||||
if (address) {
|
||||
const contextAddress = C._ts_query_context_new(address);
|
||||
const captureCount = C._ts_query_capture_count(address);
|
||||
const captureNames = new Array(captureCount);
|
||||
for (let i = 0; i < captureCount; i++) {
|
||||
const nameAddress = C._ts_query_capture_name_for_id(
|
||||
address,
|
||||
i,
|
||||
TRANSFER_BUFFER
|
||||
);
|
||||
const nameLength = getValue(TRANSFER_BUFFER, 'i32');
|
||||
captureNames[i] = UTF8ToString(nameAddress, nameLength);
|
||||
}
|
||||
return new Query(INTERNAL, address, contextAddress, captureNames);
|
||||
} else {
|
||||
const errorId = getValue(TRANSFER_BUFFER + SIZE_OF_INT, 'i32');
|
||||
const utf8ErrorOffset = getValue(TRANSFER_BUFFER, 'i32');
|
||||
const errorOffset = UTF8ToString(sourceAddress, utf8ErrorOffset).length;
|
||||
C._free(sourceAddress);
|
||||
const suffix = source.slice(errorOffset, 100);
|
||||
switch (errorId) {
|
||||
case 2: throw new RangeError(
|
||||
`Bad node name '${suffix.match(QUERY_WORD_REGEX)[0]}'`
|
||||
);
|
||||
case 3: throw new RangeError(
|
||||
`Bad field name '${suffix.match(QUERY_WORD_REGEX)[0]}'`
|
||||
);
|
||||
default: throw new SyntaxError(
|
||||
`Bad syntax at offset ${errorOffset}: '${suffix}'...`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static load(url) {
|
||||
let bytes;
|
||||
if (
|
||||
|
|
@ -704,6 +742,55 @@ class Language {
|
|||
}
|
||||
}
|
||||
|
||||
class Query {
|
||||
constructor(internal, address, contextAddress, captureNames) {
|
||||
assertInternal(internal);
|
||||
this[0] = address;
|
||||
this[1] = contextAddress;
|
||||
this.captureNames = captureNames;
|
||||
}
|
||||
|
||||
delete() {
|
||||
C._ts_query_delete(this[0]);
|
||||
C._ts_query_context_delete(this[0]);
|
||||
}
|
||||
|
||||
exec(queryNode) {
|
||||
marshalNode(queryNode);
|
||||
|
||||
C._ts_query_exec_wasm(this[0], this[1], queryNode.tree[0]);
|
||||
|
||||
const matchCount = getValue(TRANSFER_BUFFER, 'i32');
|
||||
const nodesAddress = getValue(TRANSFER_BUFFER + SIZE_OF_INT, 'i32');
|
||||
const result = new Array(matchCount);
|
||||
|
||||
let address = nodesAddress;
|
||||
for (let i = 0; i < matchCount; i++) {
|
||||
const pattern = getValue(address, 'i32');
|
||||
address += SIZE_OF_INT;
|
||||
const captures = new Array(getValue(address, 'i32'));
|
||||
address += SIZE_OF_INT;
|
||||
for (let j = 0, n = captures.length; j < n; j++) {
|
||||
const captureIndex = getValue(address, 'i32');
|
||||
address += SIZE_OF_INT;
|
||||
const node = unmarshalNode(queryNode.tree, address);
|
||||
address += SIZE_OF_NODE;
|
||||
captures[j] = {name: this.captureNames[captureIndex], node};
|
||||
}
|
||||
result[i] = {pattern, captures};
|
||||
}
|
||||
|
||||
// Free the intermediate buffers
|
||||
C._free(nodesAddress);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
function assertInternal(x) {
|
||||
if (x !== INTERNAL) throw new Error('Illegal constructor')
|
||||
}
|
||||
|
||||
function isPoint(point) {
|
||||
return (
|
||||
point &&
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue