Affichage des articles dont le libellé est ANTLR. Afficher tous les articles
Affichage des articles dont le libellé est ANTLR. Afficher tous les articles

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