#include "ast.h"
#include <string>

COMPILER Xreate

AST root;  // current program unit (procedure or main program)

bool checkParametersList()
{
  scanner->ResetPeek();
  Token* x = scanner->Peek();
  return la->kind == _ident && x->kind  == _lparen;
}

bool checkInfix()
{
    scanner->ResetPeek();
  Token* x = scanner->Peek();
  return la->kind == _ident && x->kind  == _ident;
}

CHARACTERS
  letter = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".
  any = ANY - '"'.
  digit = "0123456789".
  cr  = '\r'.
  lf  = '\n'.
  tab = '\t'.

TOKENS
  ident  = letter {letter | digit}.
  number = digit {digit}.
  string = '"' { any } '"'.
  lparen = '('. 
  rparen = ')'.

COMMENTS FROM "/*" TO "*/" NESTED
COMMENTS FROM "//" TO lf

IGNORE cr + lf + tab

PRODUCTIONS

Xreate = { ( FDecl | RuleDecl ) }.

Ident<std::wstring& name>
= ident                  (. name = t->val; .).


FDecl<> =  							(. std::wstring fname; Expression fbody; std::wstring varname; TypeAnnotation typIn; TypeAnnotation typOut; .)
Ident<fname> '=' "function" ':'		(. Function f = Function(fname); .)
( Type<typOut>  					(. f.setReturnType(typOut); .)
| '('Ident<varname> ':' Type<typIn>   (. f.addArg(varname, typIn); .)
{',' Ident<varname> ':' Type<typIn>  (. f.addArg(varname, typIn);.)
} ')' '-' '>' Type<typOut>				(. f.setReturnType(typOut); .)
{',' FnTag<f> }
) '{' {VDecl<f> ';' }
Expr<fbody> ';' '}' (. f.setBody(fbody); root.add(f); .)  .

TypeTerm<TypeAtom& typ> = ("string" | "int" | "num" | "float" | "bool") (. typ = Atom<Type_t>(t->val); .) .
Type<TypeAnnotation& typ> =  (. TypeAnnotation typ2; TypeAtom typ3; .)
('[' Type<typ2> ']'  (. typ = TypeAnnotation(TypeOperator::LIST, typ2); .)
| TypeTerm<typ3> (. typ = TypeAnnotation(typ3); .) 
) .

VDecl<Function& f> =  (. std::wstring vname; Expression e; TypeAnnotation typ; .)
Ident<vname> '=' 
(Expr<e> ':' Type<typ> (. f.addDeclaration(vname, typ, e); .)
|ListLiteral<e> ':' Type<typ>  (. f.addListDeclaration(vname, typ, e); .)
).
                              /*============================ METAPROGRAMMING ===============================*/
                              
FnTag<Function& f> = (.std::wstring tag; TagModifier mod = TagModifier::NONE; .)
Ident<tag>
['-' TagMod<mod>] (. f.addTag(tag, mod); .).

TagMod<TagModifier& mod> = 
(  "assert" (. mod = TagModifier::ASSERT; .)
 | "require" (. mod = TagModifier::REQUIRE; .)
).
 
RuleDecl<> =
"rule" ':' 				(. RuleArguments args; RuleGuards guards; DomainAnnotation typ; std::wstring arg; .)
'(' Ident<arg> ':' Domain<typ>  	(. args.add(arg, typ); .)
{',' Ident<arg> ':'Domain<typ>  	(. args.add(arg, typ); .)
} ')'
["case" RGuard<guards> {',' RGuard<guards>}]
                                        (.  .)
'{' RBody<args, guards> '}'     	.

Domain<DomainAnnotation& dom> = 
(
  "function"  				(. dom = DomainAnnotation::FUNCTION; .)
  | "variable"    			(. dom = DomainAnnotation::VARIABLE; .)
).

RGuard<RuleGuards& guards>=          (. Expression e; .)
MetaExpr<e>                       (. guards.add(std::move(e)); .).

MetaExpr<Expression& e>=            (.Operator op; Expression e2; .)
MetaExpr2<e>
[MetaOp<op> MetaExpr2<e2>           (. e = Expression(op, {e, e2}); .)
].

MetaExpr2<Expression& e>=      (. std::wstring i1, i2, infix; .)
( IF(checkParametersList()) Ident<i1>   (. e = Expression(Operator::CALL, {Expression(Atom<Identifier_t>(i1))}); .)
'(' [ CalleeParams<e> ] ')'
| IF(checkInfix()) Ident<i1> Ident<infix> Ident<i2>                                       
                                        (. e = Expression(Operator::CALL, {Expression(Atom<Identifier_t>(infix))});
                                           e.addArg(Expression(Atom<Identifier_t>(i1)));
                                           e.addArg(Expression(Atom<Identifier_t>(i2)));
                                        .)
| Ident<i1>                             (. e = Expression(Atom<Identifier_t>(i1)); .)
| '(' MetaExpr<e> ')'
).

RBody<const RuleArguments& args, const RuleGuards& guards> =
                                  (. Expression e; std::wstring msg; .)
 "warning" MetaExpr<e> ["message" string (. msg = t->val; .)
]                                 (. root.add(new RuleWarning(RuleArguments(args), RuleGuards(guards), std::move(e), Atom<String_t>(msg))); .)
 .

MetaOp< Operator& op> =
 '-' '>'                          (. op = Operator::IMPL; .)
.




/*============================ Expressions ===============================*/

// dublicates CalleeParams
ListLiteral<Expression& e> =  (. Expression e2; .)
'['  								(. e.setOp(Operator::LIST); .)
[ Expr<e2> 							(. e.addArg(std::move(e2)); .)
{',' Expr<e2> 						(. e.addArg(std::move(e2)); .)
}] ']'.


Expr< Expression& e>         (. Operator op; Expression e2; .)
= SimExpr< e>		 
  [ RelOp< op>
SimExpr< e2>      (. e = Expression(op, {e, e2});  .)
  ].

SimExpr< Expression& e>      (. Operator op; Expression e2; .)
= Term< e>
  { AddOp< op>
    Term< e2>         (. e = Expression(op, {e, e2});.)
	}.

Term< Expression& e>         (. Operator op; Expression e2; .)
= Factor< e>
  { MulOp< op>
            Factor< e2>        (. e = Expression(op, {e, e2}); .)
  }.

Factor< Expression& e>       (. std::wstring name;.)
=                        
  (IF (checkParametersList()) Ident< name> 
                                    (. e = Expression(Operator::CALL, {Atom<Identifier_t>(name)}); .)
  '(' [CalleeParams<e>] ')'
  | Ident< name>                    (. e = Expression(Atom<Identifier_t>(name)); .)

  | number                          (. e = Expression(Atom<Number_t>(t->val)); .)
  | '-' Factor< e>                  (. e = Expression(Operator::NEG, {e}); .)
  | '(' Expr<e> ')'  
  ).

CalleeParams<Expression& e> = (. Expression e2; .)
  Expr<e2>		(. e.addArg(std::move(e2)); .)
  {',' Expr<e2> 		(. e.addArg(std::move(e2)); .)
  }.

AddOp< Operator& op>
=                        (. op = Operator::ADD; .)
  ( '+'
  | '-'                  (. op = Operator::SUB; .)
  ).

MulOp< Operator& op>
=                        (. op = Operator::MUL; .)
  ( '*'
  | '/'                  (. op = Operator::DIV; .)
  ).

RelOp< Operator& op>
=                        (. op = Operator::EQU; .)
  ( '=' '='
  | '<'                  (. op = Operator::LSS; .)
  | '>'                  (. op = Operator::GTR; .)
  ).
  
END Xreate.
