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

using namespace xreate;
using namespace std;
COMPILER Xreate

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;
}

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

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

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 = ')'.
  lbrack = '['.
  rbrack = ']'.
  assign = '='.
  implic = '-' '>'.

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; std::wstring varname; TypeAnnotation typIn; TypeAnnotation typOut; .)
Ident<fname> assign "function" ':'		(. Function f = Function(fname); .)
( Type<typOut>  					(. f.setReturnType(typOut); .)
| '(' [Ident<varname> ':' Type<typIn>   (. f.addArg(std::move(varname), move(typIn)); .)
{',' Ident<varname> ':' Type<typIn>  (. f.addArg(std::move(varname), move(typIn));.)
}] ')' implic Type<typOut>				(. f.setReturnType(typOut); .)
{',' FnTag<f> }
) BDecl<f.getEntryScope()>              (. 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<CodeScope& f> =  (. std::wstring vname; Expression e; TypeAnnotation typ;  .)
Ident<vname> assign 
(ListLiteral<e> ':' Type<typ>  
| LoopDecl<e, typ> ';'              
| Expr<e> ':' Type<typ> 
)                              (. f.addDeclaration(move(vname), move(typ), move(e)); .).

BDecl<CodeScope& scope> =           (. Expression body; .)
'{' { (
    IF(checkAssignment()) VDecl<scope> ';' 
    | TagsDecl<scope> ';'
    | Expr<body> ';'             (. scope.setBody(body); .)
)}
'}'. 

LoopDecl<Expression& e, TypeAnnotation& typOut> = (. std::wstring varIn, varEl; TypeAnnotation typEl; CodeScope block; .)

  "loop" "map" '(' Ident<varIn> implic Ident<varEl> ':' Type<typEl> ')' ':' Type<typOut> BDecl<block>
      (. e = Expression(Operator::LOOP, {Expression(Atom<Identifier_t>(varIn))}); e.addBindings({Atom<Identifier_t>(varEl)});
        block.addArg(Atom<Identifier_t>(varEl), move(typEl)); e.addBlock(move(block)); .)
.

                              /*============================ METAPROGRAMMING ===============================*/

TagsDecl<CodeScope& f> = (. Expression tag; TagModifier mod = TagModifier::NONE; .)
':' { MetaSimpExpr<tag> ['-' TagMod<mod>] (. /*f.addTag(std::move(tag), mod); */ .)
}.

FnTag<Function& f> = (. Expression tag; TagModifier mod = TagModifier::NONE; .)
MetaSimpExpr<tag>
['-' TagMod<mod>] (. f.addTag(std::move(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>=
(
'(' MetaExpr<e> ')'
| MetaSimpExpr<e>
).

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

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> =
 implic                          (. 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>] ')'
  |IF (checkIndex()) Ident<name>    (. e = Expression(). e.setOp(Operator::INDEX); e.addArg(Atom<Identifier_t>(name)}); .)
    lbrack  CalleeParams<e> rbrack
  | 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.
