bitmark-grammar
Version:
101 lines (87 loc) • 2.9 kB
text/typescript
/*!
* Copyright 2016 The ANTLR Project. All rights reserved.
* Licensed under the BSD-3-Clause license. See LICENSE file in the project root for license information.
*/
// ConvertTo-TS run at 2016-10-04T11:26:47.8252451-07:00
import { ParseTree } from "./ParseTree";
import { ParseTreeListener } from "./ParseTreeListener";
import { ErrorNode } from "./ErrorNode";
import { TerminalNode } from "./TerminalNode";
import { RuleNode } from "./RuleNode";
import { ParserRuleContext } from "../ParserRuleContext";
export class ParseTreeWalker {
public walk<T extends ParseTreeListener>(listener: T, t: ParseTree): void {
let nodeStack: ParseTree[] = [];
let indexStack: number[] = [];
let currentNode: ParseTree | undefined = t;
let currentIndex: number = 0;
while (currentNode) {
// pre-order visit
if (currentNode instanceof ErrorNode) {
if (listener.visitErrorNode) {
listener.visitErrorNode(currentNode);
}
} else if (currentNode instanceof TerminalNode) {
if (listener.visitTerminal) {
listener.visitTerminal(currentNode);
}
} else {
this.enterRule(listener, currentNode as RuleNode);
}
// Move down to first child, if exists
if (currentNode.childCount > 0) {
nodeStack.push(currentNode);
indexStack.push(currentIndex);
currentIndex = 0;
currentNode = currentNode.getChild(0);
continue;
}
// No child nodes, so walk tree
do {
// post-order visit
if (currentNode instanceof RuleNode) {
this.exitRule(listener, currentNode);
}
// No parent, so no siblings
if (nodeStack.length === 0) {
currentNode = undefined;
currentIndex = 0;
break;
}
// Move to next sibling if possible
let last = nodeStack[nodeStack.length - 1];
currentIndex++;
currentNode = currentIndex < last.childCount ? last.getChild(currentIndex) : undefined;
if (currentNode) {
break;
}
// No next sibling, so move up
currentNode = nodeStack.pop();
currentIndex = indexStack.pop()!;
} while (currentNode);
}
}
/**
* The discovery of a rule node, involves sending two events: the generic
* {@link ParseTreeListener#enterEveryRule} and a
* {@link RuleContext}-specific event. First we trigger the generic and then
* the rule specific. We to them in reverse order upon finishing the node.
*/
protected enterRule(listener: ParseTreeListener, r: RuleNode): void {
let ctx = r.ruleContext as ParserRuleContext;
if (listener.enterEveryRule) {
listener.enterEveryRule(ctx);
}
ctx.enterRule(listener);
}
protected exitRule(listener: ParseTreeListener, r: RuleNode): void {
let ctx = r.ruleContext as ParserRuleContext;
ctx.exitRule(listener);
if (listener.exitEveryRule) {
listener.exitEveryRule(ctx);
}
}
}
export namespace ParseTreeWalker {
export const DEFAULT: ParseTreeWalker = new ParseTreeWalker();
}