ParseTreeWalker doesn't understand the generated listeners
BurtHarris opened this issue · comments
In TypeScript we can declare listeners as interfaces which specify, and implement them using object literal syntax and lambda's. E.g:
const parser = new ExprParser(tokens);
parser.addParseListener({
enterProg: ctx => console.log("statments:"),
exitStat: ctx => console.log(" - " + ctx.text.trim()),
exitProg: ctx => console.log("\nStringTree:", ctx.toStringTree(parser))
}).prog();
This is much more compact that creating a listener class implementing interface ExprParseListener
, but just as typesafe. Typescript understands the correct type of each ctx
, in the lambda's without explicit typing. It also checks the implemented enter/exit function member names, and catches any misspellings.
This unfortunately we don't get these benefits when using class ParseTreeWalker
because it isn't customized by the code generation. A solution to this might be to implement a correctly typed walk(listener: ExprListener)
method on the generated parse context/tree classes, which under the covers just creates a ParseTreeWalker
and calls walk(listener, this)
on it. The difference would be that the context/tree class. Similar processing to above could happen after the parse using:
const parser = new ExprParser(tokens);
const tree = parser.prog();
tree.walk({
enterProg: ctx => console.log("statments:"),
exitStat: ctx => console.log(" - " + ctx.text.trim()),
exitProg: ctx => console.log("\nStringTree:", ctx.toStringTree(parser))
});
I found a good approach for this by making the ParseTreeListener
class as a template parameter to both Parser
and ParserRuleContext
. This way there is zero runtime overhead. The updated classes become:
export abstract class Parser<T extends ParseTreeListener = ParseTreeListener> ...
and
export class ParserRuleContext<T extends ParseTreeListener = ParseTreeListener>
extends RuleContext {
...
public walk(listener: T): this {
const walker = new ParseTreeWalker();
walker.walk(listener, this);
return this;
}
}
The generated code then substitutes the right listener type in when creating the custom-built ParserRuleContext
s.