//TODO add ListLiteral
//TODO ExprTyped:  assign default(none) type

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

#define wprintf(format, ...) \
                char __buffer[100];    \
                wcstombs(__buffer, format, 100);   \
                fprintf(stderr, __buffer, __VA_ARGS__)
                
using namespace xreate;
using namespace std;
COMPILER Xreate

 xreate::AST root;  // current program unit 
 
 struct {
     std::stack<xreate::CodeScope*> scopesOld;
    xreate::CodeScope* scope = nullptr;
 } context;
 
 void pushContextScope(CodeScope* scope){
    context.scopesOld.push(context.scope);
    context.scope = scope;
 }
 
 void popContextScope(){
        context.scope = context.scopesOld.top();
        context.scopesOld.pop();
 }
     
int skipIdent()
{
  int kind = 0;
  scanner->ResetPeek();
  while ((kind = scanner->Peek()->kind) == _colon)
    if (scanner->Peek()->kind != _ident)
      return 0;
    
  return kind;  
}

bool checkParametersList()
{
  return la->kind == _ident && skipIdent() == _lparen;
}

bool checkInfix()
{
  return la->kind == _ident && skipIdent() == _ident;
}

bool checkIndex()
{
  return la->kind == _ident && skipIdent()  == _lbrack;
}

bool checkFuncDecl()
{
  if (la->kind != _ident) return false;
  
  int xkind = skipIdent();
  Token* y = scanner->Peek();
  return  xkind  == _assign && (y->kind == _function || y->kind == _pre);
}

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 } '"'.
  function = "function".
  pre =  "pre".  
  lparen = '('. 
  rparen = ')'.
  lbrack = '['.
  rbrack = ']'.
  lcurbrack = '{'.
  rcurbrack = '}'.
  equal = "==".
  assign = '='.
  implic = '-' '>'.
  colon = ':'.
  context = "context".
  tagcolon   = "::".
  lse = "<=".
  lss = "<".
  gte = ">=".
  gtr = ">".
  
  ne1 = "!=".
  ne2= "<>".
  

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

IGNORE cr + lf + tab

PRODUCTIONS

Xreate =    (. Function* function; .)
{ ( RuleDecl | InterfaceData | Imprt | ContextSection 
    | IF(checkFuncDecl()) FDecl<function>  (. root.add(function); .)
    | TDecl ) }.

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

FDecl<Function*& f> =       (. std::wstring fname; std::wstring argName; TypeAnnotation typIn; TypeAnnotation typOut; bool flagIsPrefunct = false; Expression binding; .)
Ident<fname> assign 
[pre            (. flagIsPrefunct = true; .)]
function 		(. f = new Function(fname); f->isPrefunction = flagIsPrefunct; CodeScope* entry = f->getEntryScope(); .)

['(' Ident<argName> tagcolon ExprAnnotations<binding>  (. f->addBinding(Atom<Identifier_t>(argName), move(binding)); .)
{',' Ident<argName> tagcolon ExprAnnotations<binding>  (. f->addBinding(Atom <Identifier_t>(argName), move(binding));.)
} ')']

[ tagcolon  
( IF(flagIsPrefunct) FnTag<f>
  |  Type<typOut>				(. f->setReturnType(typOut); .)
)
{';' FnTag<f> }]
BDecl<entry>    (. entry->getBody().bindType(move(typOut));.)
.

ContextSection<>=                       (. Expression context; Function* f; .)
"case" "context" tagcolon MetaSimpExpr<context>  
lcurbrack { FDecl<f>                    (. f->guardContext = context; root.add(f); .)
} rcurbrack.

  /**
   *                          TYPES  
   * 
   */
TypeTerm<TypeAtom& typ> =                       (. std::wstring tid; .)
  ("string" | "num" | "int" | "i8" | "i32" | "float" | "bool") (. typ = Atom<Type_t>(t->val); .)
.

Type<TypeAnnotation& typ> =  (. TypeAnnotation typ2; TypeAtom typ3; std::wstring tid, field; .)
( 
  TList<typ>   
| TStruct<typ>
| TypeTerm<typ3> (. typ = TypeAnnotation(typ3); .) 
|IF (checkIndex()) Ident<tid> lbrack
      Ident<field>      (. typ = TypeAnnotation(TypeOperator::ACCESS, {}); typ.__valueCustom = Atom<Identifier_t>(tid).get(); typ.fields.push_back(Atom<Identifier_t>(field).get()); .)
      {',' Ident<field> (.  typ.fields.push_back(Atom<Identifier_t>(field).get());  .)
      } rbrack
      
| Ident<tid>            (. typ = TypeAnnotation(TypeOperator::CUSTOM, {}); typ.__valueCustom = Atom<Identifier_t>(tid).get(); .)
    ['(' Type<typ2>     (. typ.__operator = TypeOperator::CALL; typ.__operands.push_back(typ2); .)
    {',' Type<typ2>     (. typ.__operands.push_back(typ2); .)
    } ')']
) .

TList<TypeAnnotation& typ> =      (.      TypeAnnotation ty; .)
    '[' Type<ty>                         (. typ = TypeAnnotation(TypeOperator::ARRAY, {ty}); .)
    {',' Type<ty>                        (. typ.__operator = TypeOperator::TUPLE; typ.__operands.push_back(ty); .)
    }']'
  .
  
TStruct<TypeAnnotation& typ> = (. TypeAnnotation t; std::wstring field; .)
  '{'
    Ident<field> tagcolon Type<t>       (. typ = TypeAnnotation(TypeOperator::STRUCT, {t}); typ.fields.push_back(Atom<Identifier_t>(field).get()); .)
    {',' Ident<field> tagcolon Type<t>} (. typ.__operands.push_back(t); typ.fields.push_back(Atom<Identifier_t>(field).get()); .)
  '}'.
  
 TDecl =  (. std::wstring ttag; TypeAnnotation t, t1; std::wstring tname, arg; std::vector<Atom<Identifier_t>> args; .)
  Ident<tname> assign "type"
  (
    "alias" Type<t>             (. root.add(move(t), Atom<Identifier_t>(tname)); .)
    | "variant" lparen Ident<arg>  (. t = TypeAnnotation(TypeOperator::VARIANT, {}); args.push_back(Atom<Identifier_t>(arg)); .)
        {',' Ident<arg>         (. args.push_back(Atom<Identifier_t>(arg)); .)
        } rparen                (. t.addFields(move(args)); root.add(move(t), Atom<Identifier_t>(tname)); .)
    
    | Ident<ttag> 
      ['('  Ident<arg>        (. args.push_back(Atom<Identifier_t>(arg));   .)
      {',' Ident<arg>         (. args.push_back(Atom<Identifier_t>(arg));   .)
      } ')']
      Type<t>                   (. t.addBindings(move(args)); root.add(move(t), Atom<Identifier_t>(tname)); .)
  ) '.' 
  .
  
VDecl<CodeScope* f> =  (. std::wstring vname; Expression e;.)
Ident<vname> assign ExprTyped<e>      (. f->addDeclaration(Atom<Identifier_t>(vname), move(e)); .)
.

ContextDecl<CodeScope * scope> =        (. Expression tag; .)
context tagcolon 
MetaSimpExpr<tag>                     (. scope->tags.push_back(tag); .)
{';' MetaSimpExpr<tag>                (. scope->tags.push_back(tag); .)
}.

//TODO forbid multiple body declaration (ExprTyped) 
BDecl<CodeScope* scope> =  '{'      (. Expression body; pushContextScope(scope);
                                    .)
    { (RuleContextDecl<scope>  | ContextDecl<scope>    '.'
    | IF(checkAssignment()) VDecl<scope> '.'
    | ExprTyped<body>              (. scope->setBody(body); popContextScope();.)
)} '}'. 

IfDecl<Expression& e> =     (. Expression cond; ManagedScpPtr blockTrue = root.add(new xreate::CodeScope(context.scope)); ManagedScpPtr blockFalse = root.add(new xreate::CodeScope(context.scope)); .)
"if" '(' Expr<cond> ')'                         (. e = Expression(Operator::IF, {cond}); .)
tagcolon ExprAnnotations<e> 
BDecl<&*blockTrue> "else" BDecl<&*blockFalse>   (.  e.addBlock(blockTrue); e.addBlock(blockFalse); .)
.

LoopDecl<Expression& e> = 
  (. Expression eIn, eAcc, eFilters; std::wstring varEl, varAcc, contextClass; Expression tagsEl;
    ManagedScpPtr block = root.add(new xreate::CodeScope(context.scope)); .)
   "loop"
   ("map" '(' Expr<eIn> implic Ident<varEl>     (. e = Expression(Operator::MAP, {eIn}); .)
    tagcolon ExprAnnotations<tagsEl> ')' tagcolon ExprAnnotations<e> BDecl<&*block>
      (.
         e.addBindings({Atom<Identifier_t>(varEl)});
         block->addBinding(Atom<Identifier_t>(varEl), move(tagsEl)); 
         e.addBlock(block); 
      .)

  |"fold" 
    ("inf" '(' Expr<eAcc> implic Ident<varAcc> ')' 
        (.
                e = Expression(Operator::FOLD_INF, {eAcc});
                e.addBindings({Atom<Identifier_t>(varAcc)});
        .)        
     tagcolon ExprAnnotations<e> BDecl<&*block>
        (.
                block->addBinding(Atom<Identifier_t>(varAcc), Expression());
                e.addBlock(block); 
        .)
  
     | '(' Expr<eIn> implic Ident<varEl> tagcolon ExprAnnotations<tagsEl> ['|' Expr<eFilters> ] ',' Expr<eAcc> implic Ident<varAcc>')'
            (.
                e = Expression(Operator::FOLD, {eIn, eAcc});
                e.addBindings({Atom<Identifier_t>(varEl), Atom<Identifier_t>(varAcc)});
            .)
        tagcolon ExprAnnotations<e> BDecl<&*block>
            (.  
                
                block->addBinding(Atom<Identifier_t>(varEl), move(tagsEl));
                block->addBinding(Atom<Identifier_t>(varAcc), Expression());
                e.addBlock(block); 
            .)
    )
  | "context" '(' string                          (. contextClass = t->val; .)
    ')' BDecl<&*block> 
        (. e = Expression(Operator::LOOP_CONTEXT, {Expression(Atom<String_t>(std::move(contextClass)))});
            e.addBlock(block);
         .)
).
   
SwitchDecl<Expression& eSwitch> =                  (. TypeAnnotation typ; eSwitch = Expression(Operator::SWITCH, {}); Expression eCondition; Expression tag;.)
["switch" 
    (   "ad" "hoc" lparen Expr<eCondition> tagcolon MetaSimpExpr<tag> rparen (. eSwitch.op = Operator::SWITCH_ADHOC; eSwitch.operands.push_back(eCondition); eSwitch.tags.emplace(tag.getValueString(), move(tag)); .)
        | lparen Expr<eCondition> rparen tagcolon  ExprAnnotations<eSwitch>  (. eSwitch.operands.push_back(eCondition);.)
    )
]
  CaseDecl<eSwitch> {CaseDecl<eSwitch>}
.
  
CaseDecl<Expression& outer> =                    (. ManagedScpPtr scope = root.add(new xreate::CodeScope(context.scope)); .)
  "case" 
  (   "default" BDecl<&*scope>                   (. Expression exprCase(Operator::CASE_DEFAULT, {});       
                                                   exprCase.addBlock(scope); 
                                                   outer.operands.insert(++outer.operands.begin(), exprCase); .)
  
    | CaseParams<&*scope>                 (. ManagedScpPtr scopeBody = root.add(new xreate::CodeScope(&*scope)); .)
      BDecl<&*scopeBody>                  (. Expression exprCase(Operator::CASE, {});
                                            exprCase.addBlock(scope); exprCase.addBlock(scopeBody); outer.addArg(move(exprCase)); .)
 ).

CaseParams<CodeScope* scope> =           (. Expression e; Expression guard(Operator::LOGIC_AND, {}); pushContextScope(scope); .)
   CaseParam<scope, guard>               
   {',' CaseParam<scope, guard>
  }                                      (. scope->setBody(guard); popContextScope(); .).
  
CaseParam<CodeScope* scope, Expression& guard> =           (. Expression condition; .)
  (
    IF(checkAssignment()) VDecl<scope>
   | ExprTyped<condition>                                (. guard.addArg(move(condition)); .)
  ).
  
SequenceDecl<Expression& sequence> =
"sequence" ListLiteral<sequence>    (. sequence.setOp(Operator::SEQUENCE); .).

  
                              /*============================ INTERFACES ===============================*/
Imprt<> =                           
"import" "raw" lparen string      (. root.__rawImports.push_back(Atom<String_t>(t->val).get()); .) 
rparen .

InterfaceData<> = "interface" '('
  (   "dfa" ')'  InterfaceDFA
    | "extern-c" ')' InterfaceExternC
    | "cfa" ')' InterfaceCFA
    | "adhoc" ')' InterfaceAdhoc
    
  ).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            

InterfaceAdhoc<> = 
    '{' [ PrefunctionSchemeDecl ]  '}'.
     
PrefunctionSchemeDecl<> =         (. TypeAnnotation typReturn; std::wstring prefName; Expression exprCases; .)
    pre function Ident<prefName> tagcolon Type<typReturn>
    '{' SwitchDecl<exprCases>      '}'
                                  (. Expression prefData(Operator::CALL, {Atom<Identifier_t>(prefName), exprCases}); 
                                     prefData.type = typReturn; 
                                     root.addInterfaceData(Adhoc, move(prefData));  
                                  .).    


InterfaceExternC<>  =   (.xreate::ExternData data; .)
  '{' {IncludeExternDecl<data> | LibExternDecl<data> } '}'
      (. root.addExternData(move(data));  .)
.

LibExternDecl<xreate::ExternData& data> =       (. std::wstring pkgname, libname; .)
  Ident<libname> assign "library" tagcolon "pkgconfig" 
  '(' string            (. pkgname = t->val; .)
  ')' '.'               (. data.addLibrary(Atom<Identifier_t>(libname), Atom<String_t>(pkgname)); .)
. 

IncludeExternDecl<xreate::ExternData& data> =   (. Expression inc; .)
  "include" StructLiteral<inc> '.'              (. data.addIncludeDecl(move(inc)); .)
.
  
InterfaceDFA<> = '{' { InstructDecl } '}' .

InstructDecl =                        (.Operator op; Expression tag; 
                                  Expression scheme; 
                                  std::vector<Expression>& tags = scheme.operands; 
                                  tags.push_back(Expression()); /* return value */ .)
"operator" InstructAlias<op> tagcolon '('      (.scheme.setOp(op); .)
[
  MetaSimpExpr<tag>             (. tags.push_back(tag); .)
  {
      ',' MetaSimpExpr<tag>     (. tags.push_back(tag); .)
  }
] ')' [ implic MetaSimpExpr<tag>             (. tags[0] = tag; .) 
      ]                                      (. root.addDFAData(move(scheme)); .)
'.'.

InstructAlias<Operator& op> = 
(
   "map"  (. op = Operator::MAP; .)
  | "list_range" (. op = Operator::LIST_RANGE; .)
  | "list"       (. op = Operator::LIST; .)
  | "fold"       (. op = Operator::FOLD; .)
  | "index"      (. op = Operator::INDEX; .) 
). 


InterfaceCFA<> = '{' { InstructCFADecl } '}' .

InstructCFADecl<> =                          (.Operator op; Expression tag; 
                                                Expression scheme; 
                                                std::vector<Expression>& tags = scheme.operands;  .)

"operator" InstructAlias<op> tagcolon     (. scheme.setOp(op); .)
[
  MetaSimpExpr<tag>             (. tags.push_back(tag); .)
  {
      ',' MetaSimpExpr<tag>     (. tags.push_back(tag); .)
  }
] '.'                           (. root.addInterfaceData(CFA, move(scheme)); .).
    
                              /*============================ METAPROGRAMMING ===============================*/
// TagsDecl<CodeScope* f> = (. Expression tag; TagModifier mod = TagModifier::NONE; .)
// ':' { MetaSimpExpr<tag> (. /*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" tagcolon         		(. RuleArguments args; RuleGuards guards; DomainAnnotation typ; std::wstring arg; .)
'(' Ident<arg> tagcolon Domain<typ>  	(. args.add(arg, typ); .)
{',' Ident<arg> tagcolon Domain<typ>  	(. args.add(arg, typ); .)
} ')'
["case" RGuard<guards> {',' RGuard<guards>}]
'{' RBody<args, guards> '}' .    	

/* - TODO use RGuard for guards-*/
RuleContextDecl<CodeScope* scope> =     (.Expression eHead, eGuards, eBody; .)
"rule" "context" tagcolon MetaSimpExpr<eHead>
"case" MetaSimpExpr<eGuards>
'{' MetaSimpExpr<eBody> '}'             (.scope->contextRules.push_back(Expression(Operator::CONTEXT_RULE, {eHead, eGuards, eBody})); .).

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 ===============================*/
ExprAnnotations<Expression& e> = (. TypeAnnotation typ; Expression tag; e.tags.clear();.)
Type<typ>                    (. e.bindType(move(typ)); .)
  {';' MetaSimpExpr<tag>     (. e.tags.emplace(tag.getValueString(), tag); .)
  }.
  
ExprTyped<Expression&e> = Expr<e> [tagcolon ExprAnnotations<e>].

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; e = Expression(); .)
=                        
  (IF (checkParametersList()) Ident< name> 
                                    (. e = Expression(Operator::CALL, {Atom<Identifier_t>(name)}); .)
  '(' [CalleeParams<e>] ')'
  |IF (checkIndex()) Ident<name>    
    lbrack  CalleeParams<e> rbrack  (. e.setOp(Operator::INDEX); e.setValue({Atom<Identifier_t>(name)}); .)
  | Ident< name>                    (. e = Expression(Atom<Identifier_t>(name)); root.recognizeVariantIdentifier(e); .)
  | ListLiteral<e>                  (. /* tuple */.)    
  | StructLiteral<e>                (. /* struct */.)
  | SequenceDecl<e> 
  | LoopDecl<e>
  | IfDecl<e>  
  | SwitchDecl<e>
  | AdhocDecl<e>

  | number                          (. e = Expression(Atom<Number_t>(t->val)); .)
  | string                          (. e = Expression(Atom<String_t>(t->val)); .)
  | '-' Factor< e>                  (. e = Expression(Operator::NEG, {e}); .)
  | '(' ExprTyped<e> ')'  
  ).
  
StructLiteral<Expression& e> =                     (. std::wstring key; Expression val; std::list<Atom<Identifier_t>> keys; .)
  '{' Ident<key> '=' Expr<val>                    (. keys.push_back(Atom<Identifier_t>(key)); e = Expression(Operator::LIST_NAMED, {val}); .)
  {',' Ident<key> '=' Expr<val>                 (.e.addArg(Expression(val)); keys.push_back(Atom<Identifier_t>(key)); .)
  } '}'                                         (. e.addBindings(keys.begin(), keys.end()); .)
  .
  
ListLiteral<Expression& e> =  (. Expression eFrom, eTo; .)
'['                                                             
[ Expr<eFrom>                                                   (. e.addArg(Expression(eFrom)); .)
(".."  Expr<eTo>                                                (. e.addArg(Expression(eTo)); e.setOp(Operator::LIST_RANGE); .)
 |{',' Expr<eFrom>                                              (. e.addArg(Expression(eFrom)); .)
}                                                               (. e.setOp(Operator::LIST); .) 
) ] ']'.

AdhocDecl<Expression& e> = (. Expression command; .)
"ad" "hoc" Expr<command>       (. e.setOp(Operator::ADHOC); e.addArg(std::move(command));  .).

CalleeParams<Expression& e> = (. Expression e2; .)
  ExprTyped<e2>		(. e.addArg(Expression(e2)); .)
  {',' ExprTyped<e2> 		(. e.addArg(Expression(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; .)
  ( equal
  | (ne1 | ne2)           (. op = Operator::NE; .)
  
  | lse                   (. op = Operator::LSE; .)
  | lss                   (. op = Operator::LSS; .)
  
  | gte                   (. op = Operator::GTE; .)
  | gtr                   (. op = Operator::GTR; .)
  
  ).
  
END Xreate.
