dimanche 18 avril 2021

ANTLR parser for simple expression

 Hello,

Yes this is a very long time that I did not post anything... I recently had to make an interpreter (in swift) of quite simple expression. I thought of reusing it in some C# programs. My first thought were immediately to ANTLR (which I used in Java few years ago). My second thought was to use Lemon, in C, it would have worked with swift because of Objective-C but I could not have used it in C# (please no interop).

I really advise the plugin in Intellij, it was so easy to use: right click on the rule you want to test...


then you can enter an expression in the tool window, in the bottom.



the grammar was:

expr: '(' e=expr ')' # paren
| name=KEYWORD '(' expr (',' expr)* ')' # fun
| left=expr op=('*' | '/') right=expr # mul
| left=expr op=('+' | '-') right=expr # add
| VARIABLE # variable
| DECIMAL # decimal
| INT # int
| STRING # string
| # empty
;

fragment LETTER: [a-zA-Z];
fragment DIGIT: [0-9];

VARIABLE: '[' LETTER+(LETTER | DIGIT)* '.' LETTER+(LETTER | DIGIT)* ']';
STRING : '"' ~('"')* '"';
INT : DIGIT+;
DECIMAL : DIGIT*'.'DIGIT+;
KEYWORD : LETTER+(LETTER | DIGIT)*;


and the program to parse a string would be (in kotlin):

val expression = "=coucou(12,23,34,56)"
val stream: CharStream = CharStreams.fromString(expression)
val lexer = MobiExprLexer(stream)
val tokenStream: TokenStream = CommonTokenStream(lexer)
val parser = MyExprParser(tokenStream)
val tree: ParseTree = parser.prog()
val result: Int = MyInterpreter().visit(tree)

println(result)

An interesting thing is that we use a visitor to interpret the AST

and here below how we can retrieve all parameters of a function:

here the rule as reminder: name=KEYWORD '(' expr (',' expr)* ')' fun

class MyInterpreter : MyExprBaseVisitor<Int>() {


override fun visitFun(ctx: FunContext): Int {
// expr() will be a list of all expr nodes, meaning all parameters
val params = ctx.expr().map { visit(it) }
// once we visit each of them, we now have the values to interpret the function
...
}


}

For those who were wondering, I now think that it is better to discouple the grammar from the implementation, it is allowing the grammar to be reused in multiple languages.

I personally also enjoyed doing some kotlin again :-)

Hope it was useful for you.

See you