// The specification for a SwiftScript program
program  :
    (nsdecl)*        //namespace declaration
    (importStatement)*
    (topLevelStatement)*
    EOF ;

nsdecl : "namespace" (ID)? STRING_LITERAL SEMI  ;

importStatement : "import" STRING_LITERAL SEMI ;

typedecl : 
   "type" ID ( SEMI | (type SEMI) | structdecl ) ;

structdecl : 
    LCURLY
    (type ID (LBRACK RBRACK )* ( COMMA ID (LBRACK RBRACK )* )* SEMI)*
    RCURLY
    ( SEMI )?
    ;

topLevelStatement :
      typedecl
    | (predictProceduredecl) => proceduredecl
    | ("app") => appproceduredecl
    | innerStatement ;

innerStatement : 
    (predictVarDeclaration) => varDeclaration
    | nonDecInnerStatement ;

nonDecInnerStatement :
    ( ll1statement
   |  (procedureInvocationExpr) => procedureInvocationExpr SEMI
   |  (predictAssignStat) => assignStat
   |  (predictAppendStat) => appendStat ) 
   (procedurecallStatAssignManyReturnParam) => procedurecallStatAssignManyReturnParam
    ;

predictVarDeclaration : ("global") | (type ID) ;

varDeclaration : 
    ("global")? type declpart (COMMA declpart)* SEMI ;

declpart :
   ID (LBRACK (type )? RBRACK )*
   (LT (mappingdecl | STRING_LITERAL) GT)?
   (ASSIGN expression)? ;

arrayInitializer :   
   LBRACK (
     (expression COLON) => (expression COLON expression (COLON expression)?)
   | ( expression ( COMMA expression )* (COMMA)? )
    )?  RBRACK ;

mappingdecl: ID SEMI mapparamdecl ;

mapparamdecl : ( mapparam ( COMMA mapparam )* )? ;

mapparam : ID ASSIGN expression ;

predictProceduredecl : 
    ( ID LPAREN ( formals )? RPAREN LCURLY )
    | ( LPAREN formals RPAREN ID LPAREN ) ;

proceduredecl : 
    ( LPAREN formals  RPAREN )? ID LPAREN (formals)? RPAREN
    LCURLY (appSpec | innerStatement)* RCURLY ;

appproceduredecl : 
   "app" ( LPAREN formals  RPAREN )? ID  LPAREN (formals)? RPAREN
    LCURLY (appProfile)* ID (appArg)* SEMI RCURLY ;

appProfile : "profile" expression ASSIGN expression SEMI ;

appArg : expression | stdioArg ;

/* This is the deprecated format for app { } blocks */
appSpec : "app" LCURLY ID ( appArg )* SEMI RCURLY ;

formals : formalParameter ( COMMA formalParameter )*  ;

formalParameter : type ID (LBRACK RBRACK)* (ASSIGN constant)? ;

type : ID (typeSubscript)* ;

typeSubscript :	LBRACK (ID)? RBRACK ;

compoundStat : LCURLY ( innerStatement )*  RCURLY ;

ll1statement :
     ifStat | foreachStat | switchStat | iterateStat;

ifStat :  
    "if" LPAREN expression RPAREN compoundStat ( "else" compoundStat )?  ;

foreachStat : 
    "foreach" ID (COMMA ID)? "in" expression compoundStat ;

iterateStat :  
    "iterate" ID compoundStat "until" LPAREN expression RPAREN SEMI ;

switchStat :
    "switch" LPAREN expression RPAREN LCURLY (casesGroup)* RCURLY ;

casesGroup :  aCase caseSList ;

aCase :  ( "case" expression | "default" ) COLON ;

caseSList : (nonDecInnerStatement)*  ;

predictAssignStat : identifier ASSIGN ;

predictAppendStat : identifier APPEND ;

assignStat :
   identifier ASSIGN
   ((predictProcedurecallAssign) => procedureInvocationExpr | expression) 
   SEMI;

appendStat :
   identifier APPEND
   ((predictProcedurecallAssign) => procedureInvocationExpr | expression) 
   SEMI;

predictProcedurecallAssign : ID LPAREN ;

procedureInvocationExpr :
   ID LPAREN ( actualParameter (COMMA actualParameter)* )? RPAREN ;

actualParameter :
    ((ID ASSIGN) => (ID ASSIGN))? expression;

procedurecallStatAssignManyReturnParam :
   LPAREN returnParameter ( COMMA returnParameter )* RPAREN
   ASSIGN procedureInvocationExpr SEMI;

returnParameter :
   (type identifier) | identifier ((ASSIGN ID) => (ASSIGN ID))? ;

stdioArg : ("stdin" | "stdout" | "stderr" ) ASSIGN expression ;

functionInvocation
    : AT 
    ((ID LPAREN) => (ID LPAREN (expression (COMMA expression)* )? RPAREN )
    | (identifier | (LPAREN identifier RPAREN) ) 
    );

expression : orExpr ;

orExpr : andExpr ( OR andExpr )* ;

andExpr : equalExpr ( AND equalExpr  )* ;

equalExpr : condExpr  ( {LT(1);} ( EQ | NE ) condExpr )? ;

condExpr : additiveExpr ( {LT(1);}  ( LT | LE | GT | GE ) additiveExpr )? ;

additiveExpr : multiExpr ( {LT(1);} ( PLUS | MINUS ) multiExpr )* ;

multiExpr : unaryExpr ( {LT(1);} ( STAR | IDIV | FDIV | MOD ) unaryExpr )* ;

unaryExpr : MINUS unaryExpr
    | PLUS unaryExpr 
    | NOT unaryExpr
    | primExpr ;

primExpr : (predictProcedureCallExpr) => procedureInvocationExpr
    | identifier
    | LPAREN orExpr RPAREN 
    | constant
    | functionInvocation ;

predictProcedureCallExpr : ID LPAREN ;

identifier : ID  (arrayIndex | memberName)* ;

arrayIndex : LBRACK (expression | STAR) RBRACK ;

memberName : DOT (ID | STAR) ;

constant : INT_LITERAL
    | FLOAT_LITERAL
    | STRING_LITERAL
    | "true"
    | "false"
    | arrayInitializer ;

class SwiftScriptLexer extends Lexer;

AT        :   "@" ;
PLUS    :   "+" ;
MINUS   :   '-' ;
FDIV        :   '/' ;
IDIV        :   "%/" ;
MOD        :   "%%" ;
EQ      :   "==" ;
NE        :   "!=" ;
LT      :   '<' ;
LE        :   "<=" ;
GT        :   ">" ;
GE        :   ">=";
APPEND    :   "<<";
ASSIGN  :   '=' ;
AND        :   "&&";
OR        :   "||";
NOT        :   "!";
LBRACK options { paraphrase = "'['"; }   :   '[' ;
RBRACK options { paraphrase = "']'"; }   :   ']' ;
LPAREN options { paraphrase = "'('"; } :   '(' ;
RPAREN options { paraphrase = "')'"; } :   ')' ;
LCURLY options { paraphrase = "'{'"; } :   '{' ;
RCURLY options { paraphrase = "'}'"; } :   '}' ;
SEMI options { paraphrase = "a semicolon"; } : ';' ;
COMMA   :   ',' ;
COLON    :   ':' ;
STAR    :   '*' ;

ID : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')* ;

STRING_LITERAL
    :    '"'! (ESC|~('"'|'\\'|'\n'|'\r'))* '"'!  ;

NUMBER
    :
    ( INTPART ('.' FLOATPART )? (EXPONENT )?) 
  | ( '.' ((FLOATPART) (EXPONENT)?)? );

protected INTPART : (ANYDIGIT)+;

protected ANYDIGIT : ('0'..'9');

protected FLOATPART : (ANYDIGIT)+;

protected EXPONENT : ('e'|'E') ('+'|'-')? (ANYDIGIT)+ ;

// white spaces
WS  :   (   ' '
        |   '\t'
        |   '\r'
        |   '\n' {newline();}
        )+  ;

// Single-line comments, c style
SL_CCOMMENT
    :    "//"
        (~('\n'|'\r'))* ('\n'|'\r'('\n')?) ;

// Single-line comments, shell style
SL_SCOMMENT
    :    "#" (~('\n'|'\r'))* ('\n'|'\r'('\n')?) ;

// multiple-line comments
ML_COMMENT
    :    "/*"
        (
        { LA(2)!='/' }? '*'
        | '\r' '\n' | '\r' | '\n' | ~('*'|'\n'|'\r')
        )*
        "*/"
        {$setType(Token.SKIP);}
    ;

// escape sequence
protected
ESC
    :    '\\'
    ('n' | 'r' | 't' | 'b' | 'f' | '"' | '\'' | '\\' );

