bolt/src/treegen/ast-template.js

179 lines
3.7 KiB
JavaScript
Raw Normal View History

const exported = {};
class NodeVisitor {
visit(node) {
for (const child of node.preorder()) {
const key = `visit${kindToString(child.kind)}`;
if (this[key] !== undefined) {
this[key](child);
}
}
}
}
exported.NodeVisitor = NodeVisitor;
const nodeProto = {
*getChildNodes() {
for (const key of Object.keys(this)) {
if (key === 'span' || key === 'parentNode') {
continue
}
const value = this[key];
if (Array.isArray(value)) {
for (const element of value) {
if (isSyntax(element)) {
yield element;
}
}
} else {
if (isSyntax(value)) {
yield value;
}
}
}
},
2020-05-26 20:59:44 +02:00
visit(visitors) {
const stack = [this];
while (stack.length > 0) {
const node = stack.pop();
const key = `visit${kindToString(node.kind)}`
for (const visitor of visitors) {
if (visitor[key] !== undefined) {
visitor[key](node);
}
}
for (const childNode of node.getChildNodes()) {
stack.push(childNode);
}
}
},
*preorder() {
const stack = [this];
while (stack.length > 0) {
const node = stack.pop();
yield node
for (const childNode of node.getChildNodes()) {
stack.push(childNode);
}
}
},
mayContainKind(kind) {
// TODO
return true;
2020-05-26 20:59:44 +02:00
},
getParentOfKind(kind) {
let currNode = this.parentNode;
while (currNode !== null) {
if (currNode.kind === kind) {
return currNode;
}
currNode = currNode.parentNode;
}
return null;
},
*findAllChildrenOfKind(kind) {
for (const node of this.preorder()) {
if (!node.mayContainKind(kind)) {
break;
}
if (node.kind === kind) {
yield node
}
}
}
}
2020-05-10 15:56:34 +02:00
function isSyntax(value) {
return typeof value === 'object'
&& value !== null
&& value.__NODE_TYPE !== undefined;
}
exported.isSyntax = isSyntax;
let nextNodeId = 1;
function createNode(nodeType) {
const obj = Object.create(nodeProto);
Object.defineProperty(obj, '__NODE_TYPE', {
enumerable: false,
writable: false,
configurable: true,
value: nodeType,
});
Object.defineProperty(obj, 'kind', {
enumerable: false,
configurable: true,
2020-05-10 15:56:34 +02:00
get() {
return this.__NODE_TYPE.index;
}
});
Object.defineProperty(obj, 'errors', {
enumerable: false,
configurable: true,
value: [],
})
Object.defineProperty(obj, 'id', {
enumerable: true,
configurable: true,
value: nextNodeId++,
})
obj.span = null;
return obj;
}
for (const nodeName of Object.keys(NODE_TYPES)) {
2020-05-10 15:56:34 +02:00
exported[`create${nodeName}`] = function (...args) {
const nodeType = NODE_TYPES[nodeName];
const node = createNode(nodeType);
let i = 0;
const iter = nodeType.fields[Symbol.iterator]();
for (; i < args.length; i++) {
const { done, value } = iter.next();
if (done) {
break;
}
const [fieldName, fieldType] = value;
node[fieldName] = args[i];
}
while (true) {
const { done, value } = iter.next();
if (done) {
break;
}
const [fieldName, fieldType] = value;
throw new Error(`No argument provided for field '${fieldName}'`);
}
if (i < args.length) {
node.span = args[i++];
}
if (i < args.length) {
throw new Error(`Too many arguments provided to function create${nodeName}`);
}
return node;
}
2020-05-10 15:56:34 +02:00
}
2020-05-10 23:50:42 +02:00
exported.setParents = function setParents(node, parentNode = null) {
node.parentNode = parentNode;
for (const child of node.getChildNodes()) {
2020-05-10 23:50:42 +02:00
setParents(child, node)
}
}
if (typeof module !== 'undefined') {
module.exports = exports;
}