header {
#include "antlr/CommonASTWithHiddenTokens.hpp"
#include "antlr/TokenStreamHiddenTokenFilter.hpp"
#include <iostream>
}

options {
	language="Cpp";
}

class InstrParser extends Parser;
options {
	buildAST = true;
	k=2;
}

tokens {
	CALL; // define imaginary token CALL
}

slist
	:	( stat )+
	;

stat:	LBRACE^ (stat)+ RBRACE
	|	"if"^ expr "then" stat ("else" stat)?
	|	ID ASSIGN^ expr SEMI
	|	call
	;

expr
	:	mexpr (PLUS^ mexpr)*
	;

mexpr
	:	atom (STAR^ atom)*
	;

atom:	INT
	|	ID
	;

call:	ID LPAREN (expr)? RPAREN SEMI
		{#call = #(#[CALL,"CALL"], #call);}
	;

{
#include "InstrParser.hpp"
}

class InstrLexer extends Lexer;
options {
	charVocabulary = '\3'..'\377';
}

WS_	:	(' '
		|	'\t'
		|	('\n'|'\r'('\n')) {newline();}
		)+
	;

// Single-line comments
SL_COMMENT
	:	"//"
		(~('\n'|'\r'))* ('\n'|'\r'('\n')?)
		{newline();}
	;

LBRACE:	'{'
	;

RBRACE:	'}'
	;

LPAREN:	'('
	;

RPAREN:	')'
	;

STAR:	'*'
	;

PLUS:	'+'
	;

SEMI:	';'
	;

ASSIGN
	:	'='
	;

protected
DIGIT
	:	'0'..'9'
	;

INT	:	(DIGIT)+
	;

ID	:	('a'..'z')+
	;

class InstrTreeWalker extends TreeParser;
{
public:
	void setFilter(ANTLR_USE_NAMESPACE(antlr)TokenStreamHiddenTokenFilter& filter);
private:
	ANTLR_USE_NAMESPACE(antlr)TokenStreamHiddenTokenFilter* filter;

	void dumpHidden(ANTLR_USE_NAMESPACE(antlr)RefToken t);
	void pr(ANTLR_USE_NAMESPACE(antlr)RefAST p);
}

slist
	:	{dumpHidden(filter->getInitialHiddenToken());}
		(stat)+
	;

stat:	#(LBRACE {pr(#LBRACE);} (stat)+ RBRACE {pr(#RBRACE);})
	|	#(i:"if" {pr(i);} expr t:"then" {pr(t);} stat (e:"else" {pr(e);} stat)?)
	|	#(ASSIGN ID {pr(#ID); pr(#ASSIGN);} expr SEMI {pr(#SEMI);} )
	|	call
	;

expr
	:	#(PLUS expr {pr(#PLUS);} expr)
	|	#(STAR expr {pr(#STAR);} expr)
	|	INT {pr(#INT);}
	|	ID  {pr(#ID);}
	;

call:	{
		// add instrumentation about call; manually call rule
		callDumpInstrumentation(#call);
		}
		#(CALL ID {pr(#ID);}
		  LPAREN {pr(#LPAREN);} (expr)? RPAREN {pr(#RPAREN);}
		  SEMI
		  {
		  // print SEMI manually; need '}' between it and whitespace
		  std::cout << #SEMI->getText();
		  std::cout << "}"; // close {...} of instrumentation
		  dumpHidden(
			(ANTLR_USE_NAMESPACE(antlr)RefCommonASTWithHiddenTokens(#SEMI))->getHiddenAfter()
		  );
		  }
		)
	;

/** Dump instrumentation for a call statement.
 *  The reference to rule expr prints out the arg
 *  and then at the end of this rule, we close the
 *  generated called to dbg.invoke().
 */
callDumpInstrumentation
	:	#(CALL id:ID
		  {std::cout << "{dbg.invoke(\"" << id->getText() << "\", \"";}
		  LPAREN (e:expr)? RPAREN SEMI
		  {std::cout << "\"); ";}
		)
	;

