package org.jastadd.tooling.aspect.lexer;

import com.intellij.lexer.FlexLexer;
import com.intellij.psi.tree.IElementType;
import org.jastadd.tooling.aspect.psi.AspectTypes;
import com.intellij.psi.TokenType;

%%

%class AspectLexer
%implements FlexLexer
%unicode
%function advance
%type IElementType
%eof{
%eof}

%{
  private int counter = 0;
  private int totalBraceLevel = 0;
  private java.util.Stack<Integer> modeStack = new java.util.Stack<>();

  private void enterState(int state) {
    modeStack.push(yystate());
    yybegin(state);
  }

  private void exitState() {
    yybegin(modeStack.pop());
  }
%}

WhiteSpace          = [ \t\n\r\f]

// TODO what is /**/ in Java? Is this caputered here?
SingleLineComment   = "//"  [^\n\r]*
FormalComment       = "/**" [^*]* [*]+([^*/][^*]*[*]+)*[/]
MultiLineComment    = "/*"  [^*]+ [*]+([^*/][^*]*[*]+)*[/]

// from jjt
DecimalLiteral    = [1-9] [0-9]*
HexLiteral        = 0 [xX] [0-9a-fA-F]+
OctalLiteral      = 0 [0-7]*
IntegerLiteral    = ( {DecimalLiteral} | {HexLiteral} | {OctalLiteral} ) [lL]?

Exponent             = [eE] [+-]? [0-9]+
FloatingPointLiteral = [0-9]+ "." [0-9]* {Exponent}? [fFdD]?
                     |        "." [0-9]+ {Exponent}? [fFdD]?
                     |            [0-9]+ {Exponent}  [fFdD]?
                     |            [0-9]+ {Exponent}? [fFdD]

CharacterLiteral     = '  ( [^'\\\n\r]  | ( \\ ( [ntbrf\\'\"] | [0-7][0-7?] | [0-3][0-7][0-7] ) ) ) '
StringLiteral        = \" ( [^\"\\\n\r] | ( \\ ( [ntbrf\\'\"] | [0-7][0-7?] | [0-3][0-7][0-7] ) ) )* \"

Identifier           = [:jletter:] [:jletterdigit:]*

%state IN_ASPECT
%state COLLECTION_DECL
%state ATTRIBUTE_DEFINITION

%%

<YYINITIAL,IN_ASPECT,COLLECTION_DECL,ATTRIBUTE_DEFINITION> {
  {WhiteSpace}+                  { return TokenType.WHITE_SPACE; }
  {SingleLineComment}            { return AspectTypes.SINGLE_LINE_COMMENT; }
  {FormalComment}                { return AspectTypes.FORMAL_COMMENT; }
  {MultiLineComment}             { return AspectTypes.MULTI_LINE_COMMENT; }
}

<YYINITIAL,IN_ASPECT,COLLECTION_DECL,ATTRIBUTE_DEFINITION> {
  "abstract"                    { return AspectTypes.ABSTRACT; }
  "assert"                      { return AspectTypes.ASSERT; }
  "boolean"                     { return AspectTypes.BOOLEAN; }
  "break"                       { return AspectTypes.BREAK; }
  "byte"                        { return AspectTypes.BYTE; }
  "case"                        { return AspectTypes.CASE; }
  "catch"                       { return AspectTypes.CATCH; }
  "char"                        { return AspectTypes.CHAR; }
  "class"                       { return AspectTypes.CLASS; }
  "const"                       { return AspectTypes.CONST; }
  "continue"                    { return AspectTypes.CONTINUE; }
  "default"                     { return AspectTypes.DEFAULT; }
  "do"                          { return AspectTypes.DO; }
  "double"                      { return AspectTypes.DOUBLE; }
  "else"                        { return AspectTypes.ELSE; }
  "enum"                        { return AspectTypes.ENUM; }
  "extends"                     { return AspectTypes.EXTENDS; }
  "false"                       { return AspectTypes.FALSE; }
  "final"                       { return AspectTypes.FINAL; }
  "finally"                     { return AspectTypes.FINALLY; }
  "float"                       { return AspectTypes.FLOAT; }
  "for"                         { return AspectTypes.FOR; }
  "goto"                        { return AspectTypes.GOTO; }
  "if"                          { return AspectTypes.IF; }
  "implements"                  { return AspectTypes.IMPLEMENTS; }
  "import"                      { return AspectTypes.IMPORT; }
  "instanceof"                  { return AspectTypes.INSTANCEOF; }
  "int"                         { return AspectTypes.INT; }
  "interface"                   { return AspectTypes.INTERFACE; }
  "long"                        { return AspectTypes.LONG; }
  "native"                      { return AspectTypes.NATIVE; }
  "new"                         { return AspectTypes.NEW; }
  "null"                        { return AspectTypes.NULL; }
  "package"                     { return AspectTypes.PACKAGE; }
  "private"                     { return AspectTypes.PRIVATE; }
  "protected"                   { return AspectTypes.PROTECTED; }
  "public"                      { return AspectTypes.PUBLIC; }
  "return"                      { return AspectTypes.RETURN; }
  "short"                       { return AspectTypes.SHORT; }
  "static"                      { return AspectTypes.STATIC; }
  "strictfp"                    { return AspectTypes.STRICTFP; }
  "super"                       { return AspectTypes.SUPER; }
  "switch"                      { return AspectTypes.SWITCH; }
  "synchronized"                { return AspectTypes.SYNCHRONIZED; }
  "this"                        { return AspectTypes.THIS; }
  "throw"                       { return AspectTypes.THROW; }
  "throws"                      { return AspectTypes.THROWS; }
  "transient"                   { return AspectTypes.TRANSIENT; }
  "true"                        { return AspectTypes.TRUE; }
  "try"                         { return AspectTypes.TRY; }
  "void"                        { return AspectTypes.VOID; }
  "volatile"                    { return AspectTypes.VOLATILE; }
  "while"                       { return AspectTypes.WHILE; }

  // the only jastadd-specific keyword here is aspect
  "aspect"                      { enterState(IN_ASPECT); return AspectTypes.ASPECT; }
}

<IN_ASPECT,COLLECTION_DECL,ATTRIBUTE_DEFINITION> {
  "inh"                         { enterState(ATTRIBUTE_DEFINITION); return AspectTypes.INH; }
  "syn"                         { enterState(ATTRIBUTE_DEFINITION); return AspectTypes.SYN; }
  "lazy"                        { return AspectTypes.LAZY; }
  "rewrite"                     { return AspectTypes.REWRITE; }
  "to"                          { return AspectTypes.TO; }
  "when"                        { return AspectTypes.WHEN; }
  "eq"                          { enterState(ATTRIBUTE_DEFINITION); return AspectTypes.EQUATION; }
  "circular"                    { return AspectTypes.CIRCULAR; }
  "refine"                      { return AspectTypes.REFINE; }
  "contributes"                 { return AspectTypes.CONTRIBUTES; }
  "each"                        { return AspectTypes.EACH; }
  "nta"                         { return AspectTypes.NTA; }
  "cache"                       { return AspectTypes.CACHE; }
  "uncache"                     { return AspectTypes.UNCACHE; }

  // TODO this is strangely split in another Token block, check semantics of JJTree file!
  "coll"                        { enterState(COLLECTION_DECL); return AspectTypes.COLL; }

}

<COLLECTION_DECL> {
  "with"                        { return AspectTypes.WITH; }
  "root"                        { return AspectTypes.ROOT; }
  "["                           { return AspectTypes.LBRACKET; }
}

<ATTRIBUTE_DEFINITION> {
  "{"                           { ++totalBraceLevel; return AspectTypes.LBRACE; }
}

// a semicolon exits the COLLECTION_DECL (because we ignore semicolons in expressions)
<COLLECTION_DECL, ATTRIBUTE_DEFINITION> {
  ";"                           { exitState(); return AspectTypes.SEMICOLON; }
}

<YYINITIAL,IN_ASPECT,COLLECTION_DECL,ATTRIBUTE_DEFINITION> {
  // LITERALS
  {IntegerLiteral}              { return AspectTypes.INTEGER_LITERAL; }
  {FloatingPointLiteral}        { return AspectTypes.FLOATING_POINT_LITERAL; }
  {CharacterLiteral}            { return AspectTypes.CHARACTER_LITERAL; }
  {StringLiteral}               { return AspectTypes.STRING_LITERAL; }

  // IDENTIFIERS
  {Identifier}                  { return AspectTypes.IDENTIFIER; }

  // SEPARATORS

  "("                           { return AspectTypes.LPAREN; }
  ")"                           { return AspectTypes.RPAREN; }
  "{"                           { ++totalBraceLevel; return AspectTypes.LBRACE; }
  "}"                           { --totalBraceLevel; if (totalBraceLevel == 0) { exitState(); } return AspectTypes.RBRACE; }
  "["                           { return AspectTypes.LBRACKET; }
  "]"                           { return AspectTypes.RBRACKET; }

  ";"                           { return AspectTypes.SEMICOLON; }
  ","                           { return AspectTypes.COMMA; }
  "."                           { return AspectTypes.DOT; }
  "@"                           { return AspectTypes.AT; }

  // OPERATORS
  "="                           { return AspectTypes.ASSIGN; }
  "<"                           { return AspectTypes.LT; }
  "!"                           { return AspectTypes.BANG; }
  "~"                           { return AspectTypes.TILDE; }
  "?"                           { return AspectTypes.QUESTIONMARK; }
  ":"                           { return AspectTypes.COLON; }
  "=="                          { return AspectTypes.EQ; }
  "<="                          { return AspectTypes.LE; }
  ">="                          { return AspectTypes.GE; }
  "!="                          { return AspectTypes.NE; }
  "||"                          { return AspectTypes.SC_OR; }
  "&&"                          { return AspectTypes.SC_AND; }
  "++"                          { return AspectTypes.INCR; }
  "--"                          { return AspectTypes.DECR; }
  "+"                           { return AspectTypes.PLUS; }
  "-"                           { return AspectTypes.MINUS; }
  "*"                           { return AspectTypes.STAR; }
  "/"                           { return AspectTypes.SLASH; }
  "&"                           { return AspectTypes.BIT_AND; }
  "|"                           { return AspectTypes.BIT_OR; }
  "^"                           { return AspectTypes.XOR; }
  "%"                           { return AspectTypes.REM; }
  "<<"                          { return AspectTypes.LSHIFT; }
  "+="                          { return AspectTypes.PLUSASSIGN; }
  "-="                          { return AspectTypes.MINUSASSIGN; }
  "*="                          { return AspectTypes.STARASSIGN; }
  "/="                          { return AspectTypes.SLASHASSIGN; }
  "&="                          { return AspectTypes.ANDASSIGN; }
  "|="                          { return AspectTypes.ORASSIGN; }
  "^="                          { return AspectTypes.XORASSIGN; }
  "%="                          { return AspectTypes.REMASSIGN; }
  "<<="                         { return AspectTypes.LSHIFTASSIGN; }
  ">>="                         { return AspectTypes.RSIGNEDSHIFTASSIGN; }
  ">>>="                        { return AspectTypes.RUNSIGNEDSHIFTASSIGN; }
  "..."                         { return AspectTypes.ELLIPSIS; }
  "->"                          { return AspectTypes.ARROW; }
  "::"                          { return AspectTypes.DOUBLECOLON; }

  // TODO RUNSIGNEDSHIFT >>> and RSIGNEDSHIFT >> are not parsed

  ">"                           { return AspectTypes.GT; }
}