5

I'd like to make a grammar that will allow curried function calls.

That is:

a() /// good
a()() /// good
a()()() /// good
a(a) /// good
a(a()()) /// good
/// etc

My first stab was this:

ID  :   ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*;

fncall  :   expr '(' (expr (',' expr)*)? ')';

expr    :   ID|fncall;

But that fails due to left recursion.

lakam99
  • 575
  • 4
  • 9
  • 24
Aaron Yodaiken
  • 19,163
  • 32
  • 103
  • 184

1 Answers1

3

Assuming (a)() would also be valid, here's a way to solve this:

grammar T;

options {
  output=AST;
}

tokens {
  EXPR_LIST;
  CALL;
  INDEX;
  LOOKUP;
}

parse
 : expr EOF -> expr
 ;

expr
 : add_expr
 ;

add_expr
 : mul_exp (('+' | '-')^ mul_exp)*
 ;

mul_exp 
 : atom (('*' | '/')^ atom)*
 ;

atom
 : fncall
 | NUM
 ;

fncall
 : (fncall_start -> fncall_start) ( '(' expr_list ')' -> ^(CALL $fncall expr_list)
                                  | '[' expr ']'      -> ^(INDEX $fncall expr)
                                  | '.' ID            -> ^(LOOKUP $fncall ID)
                                  )* 
 ;

fncall_start
 : ID
 | '(' expr ')' -> expr
 ;

expr_list
 : (expr (',' expr)*)? -> ^(EXPR_LIST expr*)
 ;

NUM : '0'..'9'+;
ID  :  ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*;

The parser generated from the grammar above would parse the input:

(foo.bar().array[i*2])(42)(1,2,3)

and construct the following AST:

enter image description here

Without the tree rewrite rules, the grammar would look like this:

grammar T;

parse
 : expr EOF
 ;

expr
 : add_expr
 ;

add_expr
 : mul_exp (('+' | '-') mul_exp)*
 ;

mul_exp 
 : atom (('*' | '/') atom)*
 ;

atom
 : fncall
 | NUM
 ;

fncall
 : fncall_start ( '(' expr_list ')' | '[' expr ']' | '.' ID )* 
 ;

fncall_start
 : ID
 | '(' expr ')'
 ;

expr_list
 : (expr (',' expr)*)?
 ;

NUM : '0'..'9'+;
ID  :  ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*;
Bart Kiers
  • 166,582
  • 36
  • 299
  • 288
  • Do you mind explaining how the predicate things work in this context? But thanks very much for the code. – Aaron Yodaiken May 09 '12 at 21:48
  • @luxun, I didn't use any predicates: it's just a (plain) grammar that creates an AST, so there are a couple of rewrite rules. I edited my answer to include the same grammar, but without the rewrite rules (needless to say, that grammar will not create an AST as in the image I posted...). – Bart Kiers May 09 '12 at 21:53