2020-05-10 11:58:07 +02:00
|
|
|
|
2020-05-30 11:29:08 +02:00
|
|
|
export class NodeVisitor {
|
2020-05-26 16:55:41 +02:00
|
|
|
visit(node) {
|
|
|
|
for (const child of node.preorder()) {
|
|
|
|
const key = `visit${kindToString(child.kind)}`;
|
|
|
|
if (this[key] !== undefined) {
|
|
|
|
this[key](child);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-30 11:29:08 +02:00
|
|
|
let nextNodeId = 1;
|
|
|
|
|
|
|
|
class SyntaxBase {
|
2020-05-26 16:55:41 +02:00
|
|
|
|
2020-05-30 11:29:08 +02:00
|
|
|
constructor(span) {
|
|
|
|
this.id = nextNodeId++;
|
|
|
|
this.errors = [];
|
|
|
|
this.span = span;
|
|
|
|
}
|
2020-05-10 11:58:07 +02:00
|
|
|
|
2020-05-23 14:18:20 +02:00
|
|
|
*getChildNodes() {
|
|
|
|
for (const key of Object.keys(this)) {
|
2020-05-29 18:44:58 +02:00
|
|
|
if (key === 'span' || key === 'parentNode' || key === 'type') {
|
2020-05-23 14:18:20 +02:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
const value = this[key];
|
|
|
|
if (Array.isArray(value)) {
|
|
|
|
for (const element of value) {
|
|
|
|
if (isSyntax(element)) {
|
|
|
|
yield element;
|
|
|
|
}
|
|
|
|
}
|
2020-05-27 09:11:59 +02:00
|
|
|
} else {
|
2020-05-23 14:18:20 +02:00
|
|
|
if (isSyntax(value)) {
|
|
|
|
yield value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-05-30 11:29:08 +02:00
|
|
|
}
|
2020-05-23 14:18:20 +02:00
|
|
|
|
2020-05-26 20:59:44 +02:00
|
|
|
visit(visitors) {
|
|
|
|
const stack = [this];
|
|
|
|
while (stack.length > 0) {
|
|
|
|
const node = stack.pop();
|
2020-05-28 14:08:49 +02:00
|
|
|
const kindName = kindToString(node.kind);
|
|
|
|
const kindNamesToVisit = [kindName, ...NODE_TYPES[kindName].parents];
|
2020-05-26 20:59:44 +02:00
|
|
|
for (const visitor of visitors) {
|
2020-05-28 14:08:49 +02:00
|
|
|
for (const kindName of kindNamesToVisit) {
|
|
|
|
const key = `visit${kindName}`
|
|
|
|
if (visitor[key] !== undefined) {
|
|
|
|
visitor[key](node);
|
|
|
|
}
|
|
|
|
}
|
2020-05-26 20:59:44 +02:00
|
|
|
}
|
|
|
|
for (const childNode of node.getChildNodes()) {
|
|
|
|
stack.push(childNode);
|
|
|
|
}
|
|
|
|
}
|
2020-05-30 11:29:08 +02:00
|
|
|
}
|
2020-05-26 20:59:44 +02:00
|
|
|
|
2020-05-23 14:18:20 +02:00
|
|
|
*preorder() {
|
|
|
|
const stack = [this];
|
|
|
|
while (stack.length > 0) {
|
|
|
|
const node = stack.pop();
|
|
|
|
yield node
|
|
|
|
for (const childNode of node.getChildNodes()) {
|
|
|
|
stack.push(childNode);
|
|
|
|
}
|
|
|
|
}
|
2020-05-30 11:29:08 +02:00
|
|
|
}
|
2020-05-23 14:18:20 +02:00
|
|
|
|
|
|
|
mayContainKind(kind) {
|
|
|
|
// TODO
|
|
|
|
return true;
|
2020-05-30 11:29:08 +02:00
|
|
|
}
|
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;
|
2020-05-30 11:29:08 +02:00
|
|
|
}
|
2020-05-23 14:18:20 +02:00
|
|
|
|
|
|
|
*findAllChildrenOfKind(kind) {
|
|
|
|
for (const node of this.preorder()) {
|
|
|
|
if (!node.mayContainKind(kind)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (node.kind === kind) {
|
|
|
|
yield node
|
|
|
|
}
|
|
|
|
}
|
2020-05-10 11:58:07 +02:00
|
|
|
}
|
2020-05-23 14:18:20 +02:00
|
|
|
|
2020-05-10 11:58:07 +02:00
|
|
|
}
|
|
|
|
|
2020-05-30 11:29:08 +02:00
|
|
|
export function isSyntax(value) {
|
2020-05-10 15:56:34 +02:00
|
|
|
return typeof value === 'object'
|
|
|
|
&& value !== null
|
|
|
|
&& value.__NODE_TYPE !== undefined;
|
|
|
|
}
|
|
|
|
|
2020-05-30 11:29:08 +02:00
|
|
|
export function setParents(node, parentNode = null) {
|
2020-05-10 23:50:42 +02:00
|
|
|
node.parentNode = parentNode;
|
2020-05-23 21:15:20 +02:00
|
|
|
for (const child of node.getChildNodes()) {
|
2020-05-10 23:50:42 +02:00
|
|
|
setParents(child, node)
|
|
|
|
}
|
|
|
|
}
|