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

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

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

COMPILER Xreate

details::inconsistent::AST* root = nullptr;  // current program unit 
 
void SemErr(std::initializer_list<std::wstring> msgs){
  std::wstringstream output;
  for(const auto& msg: msgs){output << msg;}
  SemErr(output.str().c_str());
}

void ensureInitalizedAST(){
  if (root == nullptr) root = new details::inconsistent::AST();
}
 
 struct {
    std::stack<CodeScope*> scopesOld;
    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 nextToken()
{
  scanner->ResetPeek();
  return scanner->Peek()->kind;
}

bool checkTokenAfterIdent(int key){
  if (la->kind != _ident) return false;
  return  nextToken()  == key;
}

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

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

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

bool checkListIndex()
{
  return la->kind == _lcurbrack && nextToken()  == _lbrack;
}

bool checkFuncDecl()
{
  if (la->kind != _ident) return false;
  
  int token2 = nextToken();
  int token3 = scanner->Peek()->kind;
  
  return  token2  == _assign && token3 == _function;
}

bool checkAssignment()
{
  if (la->kind != _ident) return false;
  
  scanner->ResetPeek();
  int token2 = scanner->Peek()->kind;
  if (token2 == _lcurbrack) {
      scanner->Peek();
      int token3 = scanner->Peek()->kind;
      if (token3 != _rcurbrack) return false;
      
      int token4 = scanner->Peek()->kind;
      return token4 == _assign;
  }
  
  return  token2  == _assign;
}

void recognizeIdentifier(Expression& id, const std::wstring& hint){
  if (!context.scope)
    SemErr({L"Identifier found in undefined scope: ", hint});
  
  if (!context.scope->recognizeIdentifier(id)){       
      root->postponeIdentifier(context.scope, id);
  }
}

enum SwitchKind{SWITCH_NORMAL, SWITCH_META};

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

TOKENS
  ident = (letter ['-' letter] | '_') {letter ['-' letter] | digit | '_' }.
  number = digit{digit}.
  string = '"' { any } '"'.
  function = "function".
  comma = ','.
  period = '.'.
  lparen = '('. 
  rparen = ')'.
  lbrack = '['.
  rbrack = ']'.
  lcurbrack = '{'.
  rcurbrack = '}'.
  equal = "==".
  assign = '='.
  implic = '-' '>'.
  colon = ':'.
  tagcolon   = "::".
  lse = "<=".
  lss = "<".
  gte = ">=".
  gtr = ">".
  
  ne1 = "!=".
  ne2= "<>".
  

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

IGNORE cr + lf + tab

PRODUCTIONS

Xreate =    (. Function* function; ensureInitalizedAST(); .)
{(  //RuleDecl 
      InterfaceData | Imprt | GuardSection
    | IF(checkFuncDecl()) FDecl<function>  (. root->add(function); .)
    | TDecl 
    | SkipModulesSection
)}                                         (.  .)
.

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

// recognition 
IdentR<Expression &e> =                     (. std::wstring name; .)
  Ident<name>                               (. e = Expression(Atom<Identifier_t>(name)); .)
                                            (. recognizeIdentifier(e, name); .).

//versioning
IdentV<Expression& e>=                      (. std::wstring name; .)
  Ident<name>                               (. e = Expression(Atom<Identifier_t>(name)); .)
  [ Version<e> ].

//recognition + versioning
IdentVR<Expression& e>=                     (. std::wstring name; .)
  Ident<name>                               (. e = Expression(Atom<Identifier_t>(name)); .)
  [ Version<e> ]                            (. recognizeIdentifier(e, name); .)
.

Version<Expression &e>=
  lcurbrack (                              
    ident                                 (. SemErr({L"var version as ident is not implemented yet"}); .)
    | number                              (. Attachments::put<versions::VariableVersion>(e, Atom<Number_t>(t->val).get()); .)
  ) rcurbrack .
  

FDecl<Function*& f> =       (. std::wstring fname; std::wstring argName; TypeAnnotation typIn; TypeAnnotation typOut; Expression binding; .)
Ident<fname> assign 
function 		(. f = new Function(fname); CodeScope* entry = f->getEntryScope(); .)

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

tagcolon Type<typOut> {';' FnTag<f> }
BDecl<entry>    (. const_cast<Expression&>(entry->getBody()).bindType(move(typOut));.)
.

GuardSection<>=                         (. std::wstring arg, guardI; Expression guardE, guardBinding; Function* f; TypeAnnotation guardT; .)
  "guard" lparen 
    [Ident<arg>] tagcolon Ident<guardI> (. guardE = Expression(Operator::CALL, {Atom<Identifier_t>(guardI)}); bool res = root->recognizeVariantConstructor(guardE); .)
                                        (. if(!res) SemErr(coco_string_create("Can't recognize a guard"));.)
                                        (. if (!arg.empty()) guardE.addBindings({Atom<Identifier_t>(arg)}); .)
                                        (. guardBinding.type = TypeAnnotation(TypeOperator::GUARD, {guardE.type}); guardBinding.type.__valueCustom = Atom<Identifier_t>(guardI).get(); .)
  rparen lcurbrack { 
    FDecl<f>                            (. f->guard = guardE; if (!arg.empty()){f->addBinding(Atom<Identifier_t>(arg), Expression(guardBinding));} .)
                                        (. root->add(f); .)
  } rcurbrack
.    


  /**
   *                          TYPES  
   * 
   */
TypeTerm<TypePrimitive& typ> =                       (. std::wstring tid; .)
  ( "string" (. typ = TypePrimitive::String;.)
  | "int"    (. typ = TypePrimitive::Int;.)
  | "float"  (. typ = TypePrimitive::Float;.)
  | "bool"   (. typ = TypePrimitive::Bool; .)
  | "i8"     (. typ = TypePrimitive::I8; .)
  | "i32"    (. typ = TypePrimitive::I32; .)
  | "i64"    (. typ = TypePrimitive::I64; .)
  ).

Type<TypeAnnotation& typ> =  (. TypeAnnotation typ2; TypePrimitive typ3; std::wstring tid; std::string field; .)
( 
  TList<typ>   
| TRecord<typ>
| TVariant<typ>
| TPred<typ>
| TSlave<typ>
| TRef<typ>
| TypeTerm<typ3> (. typ = typ3; .) 
| IF (checkIndex()) Ident<tid> lbrack
    TypeIndex<field>        (. typ = TypeAnnotation(TypeOperator::ACCESS, {}); typ.__valueCustom = Atom<Identifier_t>(tid).get(); typ.fields.push_back(field); .)
    {comma TypeIndex<field> (.  typ.fields.push_back(field);  .)
    } rbrack
      
| Ident<tid>            (. typ = TypeAnnotation(TypeOperator::ALIAS, {}); typ.__valueCustom = Atom<Identifier_t>(tid).get(); .)
    [lparen Type<typ2>  (. typ.__operands.push_back(typ2); .)
    {comma Type<typ2>   (. typ.__operands.push_back(typ2); .)
    } rparen]
| '*'                   (.typ = TypeAnnotation(); .)
) .

TypeIndex<std::string& name> =
(
    number                (. name = Atom<Identifier_t>(t->val).get(); .)
  | string                (. name = Atom<String_t>(t->val).get(); .)
)                       
. 

TList<TypeAnnotation& typ> =            (.      TypeAnnotation ty; .)
    lbrack Type<ty> rbrack                    (. typ = TypeAnnotation(TypeOperator::ARRAY, {ty}); .)
.

TRecordBody<TypeAnnotation& typ> =
  (. TypeAnnotation t; std::wstring key; size_t keyCounter=0; 
     typ = TypeAnnotation(TypeOperator::RECORD, {});
  .)
{
  (
    IF(checkTokenAfterIdent(_tagcolon)) Ident<key> tagcolon 
    |                              (. key = to_wstring(keyCounter++); .)
  )
  Type<t> [comma]                  (. typ.__operands.push_back(t); .)
                                   (. typ.fields.push_back(Atom<Identifier_t>(key).get()); .)
}.

TRecord<TypeAnnotation& typ> = 
  lcurbrack TRecordBody<typ> rcurbrack 
    (. if(!typ.__operands.size()) SemErr(coco_string_create("Record type can't be empty.")); .)
.  

TVariantRec<TypeAnnotation& typ> = (. TypeAnnotation typVoid; .)
  lparen TRecordBody<typ> rparen
    (. if(typ.__operands.size()==0) typ = typVoid; .)
.    
  
TVariantBody<TypeAnnotation& typ> = (. TypeAnnotation t, typVoid; std::vector<TypeAnnotation> operands; std::vector<Atom<Identifier_t>> keys; std::wstring v; .)
lcurbrack
  {                             (. t = typVoid; .)
    Ident<v> [TVariantRec<t>]   (. keys.push_back(Atom<Identifier_t>(v)); operands.push_back(t); .)
    [comma]
  } 
rcurbrack                     
      (. typ = TypeAnnotation(TypeOperator::VARIANT, {}); 
        typ.__operands = operands;  
        typ.addFields(std::move(keys)); 
      .)
.

TVariant<TypeAnnotation& typ>=  
  "variant" TVariantBody<typ>
  (. if(!typ.__operands.size()) SemErr(coco_string_create("Variant type can't be empty.")); .)
.  

TPred<TypeAnnotation& typ>= 
  "predicate" TVariantBody<typ>
  (. if(!typ.__operands.size()) SemErr(coco_string_create("Predicate type can't be empty.")); .)
.  

TSlave<TypeAnnotation& typ>=    
    "slave"                     (. typ = TypeAnnotation(TypeOperator::SLAVE, {});    .)
    lparen 
      string                    (. typ.__valueCustom = Atom<String_t>(t->val).get(); .)
    rparen
.

TRef<TypeAnnotation& typ>=    (. TypeAnnotation typChild; .)
    "ref" lparen Type<typChild> rparen (. typ = TypeAnnotation(TypeOperator::REF, {typChild}); .)
.

TDecl =  (. TypeAnnotation t; std::wstring tname, arg; std::vector<Atom<Identifier_t>> args; .)
    Ident<tname> assign "type"
    [lparen  Ident<arg>        (. args.push_back(Atom<Identifier_t>(arg));   .)
    {comma Ident<arg>          (. args.push_back(Atom<Identifier_t>(arg));   .)
    } rparen]
    Type<t>[period]            (. t.addBindings(move(args)); root->add(move(t), Atom<Identifier_t>(tname)); .)
    .
  
ContextDecl<CodeScope * scope> =        (. Expression tag; .)
"context" tagcolon 
MetaSimpExpr<tag>                     (. scope->tags.push_back(tag); .)
{';' MetaSimpExpr<tag>                (. scope->tags.push_back(tag); .)
}.

VDecl<CodeScope* f> =                       (. Expression var, value;.)
    IdentV<var> assign ExprTyped<value>     (. Symbol identSymbol = f->addDefinition(move(var), move(value)); 
                                                Attachments::put<ExprAlias_A>(value, identSymbol);
                                            .)
.

BDecl<CodeScope* scope> =  lcurbrack      (. Expression body; pushContextScope(scope); bool flagBodyFound = false; .)
    {(IF(checkAssignment()) VDecl<scope> period
//    | RuleContextDecl<scope>  
    | ContextDecl<scope>period
    | ExprTyped<body>                     (. scope->setBody(body); flagBodyFound = true; Attachments::put<ExprAlias_A>(body, Symbol{ScopedSymbol::RetSymbol, scope});.)
    )}
    rcurbrack                             (. if(!flagBodyFound) SemErr(coco_string_create("Code block with an empty body!"));
                                             popContextScope(); 
                                          .) 
. 

IfDecl<Expression& e> =     
  (.  Expression cond(Operator::AND, {}), condPart; 
      ManagedScpPtr blockTrue = root->add(new CodeScope(context.scope)); 
      ManagedScpPtr blockFalse = root->add(new CodeScope(context.scope)); 
      e = Expression(Operator::IF, {});
  .)
"if" lparen Expr<condPart>                      (. cond.operands.push_back(condPart); .)
{ comma Expr<condPart>                          (. cond.operands.push_back(condPart); .)
} rparen                                        (. e.operands.push_back(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 CodeScope(context.scope)); 
                  block->trackExternalSymbs = true;
                .)
  "loop"
  (
    "map" lparen Expr<eIn> implic Ident<varEl>     
              (. e = Expression(Operator::MAP, {eIn}); .)
    tagcolon ExprAnnotations<tagsEl> rparen tagcolon ExprAnnotations<e> 
              (.
                e.addBindings({Atom<Identifier_t>(varEl)});
                block->addBinding(Atom<Identifier_t>(varEl), move(tagsEl)); 
              .)
  BDecl<&*block>
              (. e.addBlock(block);  .)

  |  "fold" lparen Expr<eIn> implic Ident<varEl> tagcolon ExprAnnotations<tagsEl> 
    ['|' Expr<eFilters> ] comma Expr<eAcc> implic Ident<varAcc>rparen
              (.
                e = Expression(Operator::FOLD, {eIn, eAcc});
                e.addBindings({Atom<Identifier_t>(varEl), Atom<Identifier_t>(varAcc)});
              .)
    tagcolon ExprAnnotations<e> 
              (.
                Expression varAccBindingE; varAccBindingE.type = e.type;
                block->addBinding(Atom<Identifier_t>(varEl), move(tagsEl));
                block->addBinding(Atom<Identifier_t>(varAcc), move(varAccBindingE));
              .)
    BDecl<&*block>
              (. e.addBlock(block); .)
  | lparen Expr<eAcc> implic Ident<varAcc> rparen 
              (.
                e = Expression(Operator::FOLD_INF, {eAcc});
                e.addBindings({Atom<Identifier_t>(varAcc)});
              .)        
  tagcolon ExprAnnotations<e> 
              (.
                Expression varAccBindingE; varAccBindingE.type = e.type;
                block->addBinding(Atom<Identifier_t>(varAcc), move(varAccBindingE));
              .)
  BDecl<&*block>
              (.  e.addBlock(block); .) 
  ).
   
//              Switches   
SwitchDecl<Expression& eSwitch, SwitchKind flagSwitchKind> = (. TypeAnnotation typ; eSwitch = Expression(Operator::SWITCH, {}); Expression eCondition; Expression tag;.)
"switch" 
    (
          SwitchVariantDecl<eSwitch>
        | SwitchLateDecl<eSwitch>
        | lparen ExprTyped<eCondition> rparen tagcolon  ExprAnnotations<eSwitch>  (. eSwitch.operands.push_back(eCondition);.)
          CaseDecl<eSwitch, flagSwitchKind> {CaseDecl<eSwitch, flagSwitchKind>}
    ) 
.
  
CaseDecl<Expression& outer, SwitchKind flagSwitchKind> = (. ManagedScpPtr scope = root->add(new CodeScope(context.scope)); Expression condition; .)
  "case" 
  ( IF(flagSwitchKind == SWITCH_META)
        lparen MetaSimpExpr<condition> rparen BDecl<&*scope>  (. Expression exprCase(Operator::CASE, {}); exprCase.addTags({condition}); exprCase.addBlock(scope); outer.addArg(move(exprCase));.)
        
    | "default" BDecl<&*scope>                    (. Expression exprCase(Operator::CASE_DEFAULT, {});       
                                                   exprCase.addBlock(scope); 
                                                   outer.operands.insert(++outer.operands.begin(), exprCase); .)
  
    | lparen CaseParams<&*scope> rparen         (. ManagedScpPtr scopeBody = root->add(new CodeScope(&*scope)); Expression exprCase(Operator::CASE, {}); .)
      BDecl<&*scopeBody>                                (. exprCase.addBlock(scope); exprCase.addBlock(scopeBody); outer.addArg(move(exprCase)); .)
 ).

CaseParams<CodeScope* scope> =           (. Expression condition; Expression guard(Operator::LOGIC_AND, {}); pushContextScope(scope); .)
 ExprTyped<condition>                  (. guard.addArg(Expression(condition)); .)
   {comma ExprTyped<condition>             (. guard.addArg(Expression(condition)); .)
   }                                     (. scope->setBody(guard); popContextScope(); .)
.

SwitchLateDecl<Expression& expr> =
            (.
                std::wstring aliasCondition; Expression exprCondition, aliasAnns;
                expr = Expression(Operator::SWITCH_LATE, {});
                ManagedScpPtr scope = root->add(new CodeScope(context.scope)); 
            .)
             
    "late" lparen Expr<exprCondition> [implic Ident<aliasCondition>] [tagcolon ExprAnnotations<aliasAnns>] rparen
    tagcolon ExprAnnotations<expr>  BDecl<&*scope>
            (. 
                expr.addArg(Expression(exprCondition));
                expr.addBlock(scope);
                std::string alias;
                if(aliasCondition.empty()){
                    if(exprCondition.__state != Expression::IDENT){
                        SemErr(coco_string_create("An identifier expected in the short form"));
                        return;
                    }

                    //Use exprCondition as  identifier
                    alias = exprCondition.getValueString();
                } else {
                    //Use aliasCondition
                    alias = Atom<Identifier_t>(move(aliasCondition)).get();
                }

                expr.addBindings({Atom<Identifier_t>(string(alias))});
                scope->addBinding(Atom<Identifier_t>(move(alias)), move(aliasAnns)); 
            .) 
.
    
SwitchVariantDecl<Expression& expr> =                                        
            (. Expression varTested; std::wstring varAlias; bool flagAliasFound = false; expr = Expression(Operator::SWITCH_VARIANT, {}); .)
                
    "variant" lparen Expr<varTested> [implic Ident<varAlias>
            (. flagAliasFound = true; .) 
            
    ] [tagcolon ExprAnnotations<varTested>] rparen tagcolon ExprAnnotations<expr>  
            (. expr.addArg(std::move(varTested)); 
                if (flagAliasFound) {
                    expr.addBindings({Atom<Identifier_t>(varAlias)}); 
                    
                } else {
                    if(varTested.__state == Expression::IDENT){
                        expr.addBindings({Atom<Identifier_t>(string(varTested.getValueString()))});
                    }
                }
            .)
                
    CaseVariantDecl<expr> {CaseVariantDecl<expr>}
.    

CaseVariantDecl<Expression& expr> =   (. ManagedScpPtr scope = root->add(new CodeScope(context.scope)); std::wstring key; scope->addBinding(Atom<Identifier_t>(string(expr.bindings.front())), Expression()); .)
    "case" lparen Ident<key> rparen   (. expr.addArg(root->recognizeVariantConstructor(Atom<Identifier_t>(std::move(key))));  .)
    BDecl<&*scope>                    (. expr.addBlock(scope); .)
.    
    
IntrinsicDecl<Expression& outer>=       (. std::wstring name; .)
    "intrinsic" 
(
    Ident< name>                        
      (. outer = Expression(Operator::CALL_INTRINSIC, {}); 
         outer.setValue(Atom<Identifier_t>(name)); 
         root->recognizeIntrinsic(outer); 
      .)
    lparen [CalleeParams<outer>] rparen 
                                        
    |   "query"                         (. outer = Expression(Operator::QUERY, {}); .)
        (
            "late" IntrinsicQueryLateDecl<outer>                     
            | lparen [CalleeParams<outer>] rparen       
        )                          
).

IntrinsicQueryLateDecl<Expression& expr> = 
        (.
            std::wstring predicateAlias; Expression predicateE, predicateAnns;
            expr = Expression(Operator::QUERY_LATE, {});
            ManagedScpPtr scope = root->add(new CodeScope(context.scope));         
        .)

    lparen Expr<predicateE> implic Ident<predicateAlias> tagcolon ExprAnnotations<predicateAnns> rparen
    tagcolon ExprAnnotations<expr>  BDecl<&*scope>
        (.
            expr.addArg(move(predicateE));
            expr.addBindings({Atom<Identifier_t>(wstring(predicateAlias))});
            scope->addBinding(Atom<Identifier_t>(move(predicateAlias)), move(predicateAnns));
            expr.addBlock(scope);
        .)
.

SequenceDecl<Expression& sequence> =    (. sequence = Expression(); sequence.setOp(Operator::SEQUENCE);  ManagedScpPtr scope = root->add(new CodeScope(context.scope)); .)
    "seq" BDecl<&*scope>                (. sequence.blocks.push_back(&*scope); scope = root->add(new CodeScope(&*scope)); .)  
    {                                   (. scope = root->add(new CodeScope(&*scope)); .) 
        BDecl<&*scope>                  (. sequence.blocks.push_back(&*scope);  .)
    }.
  
                              /*============================ INTERFACES ===============================*/
Imprt<> =                           
"import" "raw" lparen string      (. root->__rawImports.push_back(Atom<String_t>(t->val).get()); .) 
rparen period.

InterfaceData<> = "interface" lparen
  (   "dfa" rparen  InterfaceDFA
//    | "extern-c" rparen InterfaceExternC
    | "cfa" rparen InterfaceCFA
  ).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            

// InterfaceExternC<>  =   (. ExternData data; .)
//   lcurbrack {ExternHeadersDecl<data> | ExternAliasDecl<data> } rcurbrack
//       (. root->addExternData(move(data));  .)
// .
// 
// ExternPkgDecl<std::wstring& package> = 
//   "pkgconfig" lparen 
//   string                                (. package = t->val.)
//   rparen
// .  
// 
// ExternAliasDecl<ExternData& data> =       (. std::wstring alias, package; .)
//   Ident<alias> assign  "library" lparen  ExternPkgDecl<package> rparen period
//     (. data.addLibAlias(Atom<Identifier_t>(alias), Atom<String_t>(package)); .)
// . 
// 
// ExternHeadersDecl<ExternData& data> =   (. std::list<std::string> listInc; std::wstring& package; .)
//   "include" 
//   [lparen 
//   (
//     Ident<alias>                        (. data.requireLibAlias(Atom<Identifier_t>(alias)); .) 
//     | ExternPkgDecl<package>            (. data.requireLibPackage(Atom<String_t>(package)); .)
//   )
//   rparen] 
//   lcurbrack { string                    (. listInc.push_back(Atom<String_t>(t->val).get()); .)
//   [comma] } rcurbrack [period]          (. data.requireHeaders(listInc); .)
// .
  
InterfaceDFA<> = lcurbrack { InstructDecl } rcurbrack .

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

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<> = lcurbrack { InstructCFADecl } rcurbrack .

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); .)
  {
      comma MetaSimpExpr<tag>     (. tags.push_back(tag); .)
  }
] period                           (. 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; .)
// lparen Ident<arg> tagcolon Domain<typ>  	(. args.add(arg, typ); .)
// {comma Ident<arg> tagcolon Domain<typ>  	(. args.add(arg, typ); .)
// } rparen
// ["case" RGuard<guards> {comma RGuard<guards>}]
// lcurbrack RBody<args, guards> rcurbrack .    	

/* - TODO use RGuard for guards-*/
// RuleContextDecl<CodeScope* scope> =     (.Expression eHead, eGuards, eBody; .)
// "rule" "context" tagcolon MetaSimpExpr<eHead>
// "case" lparen MetaSimpExpr<eGuards> rparen
// lcurbrack MetaSimpExpr<eBody> rcurbrack             (.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>=
// (
// lparen MetaExpr<e> rparen
// | 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))});
            if (!root->recognizeVariantConstructor(e))
              SemErr({L"Undefined predicate: ", i1});
        .)
    lparen [ MetaCalleeParams<e> ] rparen
    | 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));
        .)
    | IdentR<e>    
    | number                          (. e = Expression(Atom<Number_t>(t->val)); .)
  ).

MetaCalleeParams<Expression& e> = (. Expression e2; .)
  MetaSimpExpr<e2>	     	(. e.addArg(Expression(e2)); .)
  {comma MetaSimpExpr<e2>		(. e.addArg(Expression(e2)); .)
  }.

// 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; std::list<Expression> tags; Expression tag; e.tags.clear();.)
Type<typ>                    (. e.bindType(move(typ)); .)
  {';' MetaSimpExpr<tag>     (. tags.push_back(tag); .)
  }                          (. e.addTags(tags); .)
.
  
ExprTyped<Expression&e> = Expr<e> [tagcolon ExprAnnotations<e>].

Expr< Expression& e>         (. Expression e2; .)
= ExprLogicAnd<e>		 
  [ ("or" | "OR") Expr<e2>   (. e = Expression(Operator::OR, {e, e2});  .)
  ]
.
  
ExprLogicAnd< Expression& e> (. Expression e2; .)
= ExprRel<e>		 
  [ ("and" | "AND") ExprLogicAnd <e2>   (. e = Expression(Operator::AND, {e, e2});  .)
  ]
.

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

ExprArithmAdd< Expression& e>=      (. Operator op; Expression e2; .)
  ExprArithmMul< e>
  [ 
    AddOp<op>ExprArithmAdd< e2>     (. e = Expression(op, {e, e2});.)
  ].

ExprArithmMul< Expression& e>         (. Operator op; Expression e2; .)
= ExprUpdate<e>
  [ MulOp< op>
    ExprArithmMul< e2>        (. e = Expression(op, {e, e2}); .)
  ].
  
ExprUpdate<Expression& e>=      (. Expression e2; .)
  ExprPostfix< e>
  [
    colon
      (  IF(checkListIndex()) ListIndexLiteral<e2>
       | ListLiteral<e2>)      (. e = Expression(Operator::UPDATE, {e, e2}); .)
  ].


ExprPostfix<Expression& e> 
= Term<e> 
  [                                  (. e = Expression(Operator::INDEX, {e}); .)
    {lbrack CalleeParams<e> rbrack }
  ].


Term< Expression& e>       (. std::wstring name; e = Expression(); .)
=                        
  (IF (checkParametersList()) Ident< name> 
                                    (. e = Expression(Operator::CALL, {Atom<Identifier_t>(name)}); root->recognizeVariantConstructor(e); .)
  lparen [CalleeParams<e>] rparen
  | IdentVR<e>                      
  | ListLiteral<e>     
  | ListRangeLiteral<e>
  | LoopDecl<e>
  | IfDecl<e>   
  | SwitchDecl<e, SWITCH_NORMAL>
  | IntrinsicDecl<e>
  | SequenceDecl<e>
  | number                          (. e = Expression(Atom<Number_t>(t->val)); .)
  | string                          (. e = Expression(Atom<String_t>(t->val)); .)
  | "true"                          (. e = Expression(Atom<Number_t>(1)); e.bindType(TypePrimitive::Bool); .)
  | "false"                         (. e = Expression(Atom<Number_t>(0)); e.bindType(TypePrimitive::Bool); .)
  | "undef"                         (. e = Expression(Operator::UNDEF, {}); .)
  | '-' Term<e>                     (. e = Expression(Operator::NEG, {e}); .)
  | lparen ExprTyped<e> rparen  
  ).
  
ListLiteral<Expression& e> =                     (. std::wstring key; 
                                                    Expression val; 
                                                    std::list<Atom<Identifier_t>> keys; 
                                                    e = Expression(Operator::LIST, {});
                                                 .)
  lcurbrack {
  ( IF(checkTokenAfterIdent(_assign)) Ident<key> assign
    |                                            (. key = L""; .)
  ) Expr<val>                                    (. keys.push_back(Atom<Identifier_t>(key)); 
                                                    e.operands.push_back(val);
                                                  .)
  [comma] } rcurbrack                            (. e.addBindings(keys.begin(), keys.end()); .)
.

ListIndexLiteral<Expression& e> =         (. e = Expression(Operator::LIST_INDEX, {});Expression valE;.)
  lcurbrack 
    {                                     (. Expression idxE(Operator::LIST, {});.)
      lbrack CalleeParams<idxE> rbrack      
      assign Expr<valE>[comma]            (. e.operands.push_back(idxE); e.operands.push_back(valE); .)
    }   
  rcurbrack
.
  
ListRangeLiteral<Expression& e> =                  (. Expression eFrom, eTo; .)
  lbrack Expr<eFrom> ".."  Expr<eTo>  rbrack       (. e = Expression(Operator::LIST_RANGE, {eFrom, eTo}); .)
.
   

CalleeParams<Expression& e> = (. Expression e2; .)
  ExprTyped<e2>		(. e.addArg(Expression(e2)); .)
  {comma ExprTyped<e2> 		(. e.addArg(Expression(e2)); .)
  }.

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

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

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; .)
  
  ).
  
SkipModulesSection = "module" {ANY} (lcurbrack {ANY} rcurbrack | '.').  
  
END Xreate.
