grammar Boppi;

program: expr EOF;

expr
    : singleExpr (COMPOUND singleExpr?)*
    ;

singleExpr
    : PAROPEN expr PARCLOSE #parens
    | BRAOPEN expr BRACLOSE #block
    | IN PAROPEN variable (LISTDELIM variable)* PARCLOSE #read
    | OUT PAROPEN expr (LISTDELIM expr)* PARCLOSE #write
    | variable PAROPEN (expr (LISTDELIM expr)*)? PARCLOSE #call
    | IFOPEN cond=expr IFTRUE onTrue=expr (IFFALSE onFalse=expr)? IFCLOSE #if
    | WHILEOPEN cond=expr WHILETRUE onTrue=expr WHILECLOSE #while
    | op=(PLUS|MINUS|NOT) singleExpr #prefix1
    | lhs=singleExpr op=(MULTIPLY|DIVIDE|MODULO) rhs=singleExpr #infix1
    | lhs=singleExpr op=(PLUS|MINUS) rhs=singleExpr #infix2
    | lhs=singleExpr op=(LT|LEQ|GTE|GT|EQ|NEQ) rhs=singleExpr #infix3
    | lhs=singleExpr AND rhs=singleExpr #infix4
    | lhs=singleExpr OR rhs=singleExpr #infix5
    | DECLARE type IDENTIFIER #declare
    | FUNCTION result=type name=IDENTIFIER PAROPEN (type IDENTIFIER (LISTDELIM type IDENTIFIER)*)? PARCLOSE body=singleExpr #funcDeclare
    | <assoc=right> variable ASSIGN singleExpr #assign
    | variable #var
    | LITERAL10 #number
    | CHAR #char
    | (TRUE|FALSE) #bool
    ;

type
	: type ARROW type #typeFunction
	| staticType=(INTTYPE | BOOLTYPE | CHARTYPE) #typeSimple
	| PAROPEN (type (LISTDELIM type )*)? PARCLOSE #typeTuple
	| variable #typeVariable
	;
variable: IDENTIFIER;

PAROPEN: '(';
PARCLOSE: ')';
BRAOPEN: '{';
BRACLOSE: '}';
IN: 'read';
OUT: 'print';
IFOPEN: 'if';
IFTRUE: 'then';
IFFALSE: 'else';
IFCLOSE: 'fi';
WHILEOPEN: 'while';
WHILETRUE: 'do';
WHILECLOSE: 'od';

PLUS: '+';
MINUS: '-';
NOT: '!';
MULTIPLY: '*';
DIVIDE: '/';
MODULO: '%';
LEQ: '<=';
GTE: '>=';
NEQ: '<>';
EQ: '==';
LT: '<';
GT: '>';
AND: '&&';
OR: '||';

DECLARE: 'var';
FUNCTION: 'function';
INTTYPE: 'int';
BOOLTYPE: 'bool';
CHARTYPE: 'char';
ARROW: '->';
ASSIGN: ':=';
COMPOUND: ';';
LISTDELIM: ',';

CHAR: '\'' . '\'';
TRUE: 'true';
FALSE: 'false';
IDENTIFIER: [A-Za-z_][A-Za-z0-9_]*;
LITERAL10: ('0' | [1-9] [0-9]*);

WHITESPACE: [\t\r\n ] -> skip;
LINECOMMENT: '//' ~[\r\n]* -> skip;
BLOCKCOMMENT: '/*' .*? '*/' -> skip;
