; FLINT GRAMMAR (RFC 7405)

; TOP LEVEL
topLevelModule = 1*(topLevelDeclaration CRLF);

topLevelDeclaration = contractDeclaration
                    / contractBehaviourDeclaration
                    / structDeclaration
                    / enumDeclaration
                    / traitDeclaration;

; CONTRACTS
contractDeclaration = %s"contract" SP identifier SP [identifierGroup] SP "{" *(WSP variableDeclaration CRLF) "}";

; VARIABLES
variableDeclaration = [*(modifier SP)] WSP (%s"var" / %s"let") SP identifier typeAnnotation [WSP "=" WSP expression];

; TYPES
typeAnnotation = ":" WSP type;

type = identifier ["<" type *("," WSP type) ">"]
     / basicType
     / solidityType
     / arrayType
     / fixedArrayType
     / dictType;

basicType = %s"Bool"
          / %s"Int"
          / %s"String"
          / %s"Address";

solidityType = %s"address"
             / %s"string"
             / %s"bool"
             / %s"int8"
             / %s"int16"
             / %s"int24"
             / %s"int32"
             / %s"int40"
             / %s"int48"
             / %s"int56"
             / %s"int64"
             / %s"int72"
             / %s"int80"
             / %s"int88"
             / %s"int96"
             / %s"int104"
             / %s"int112"
             / %s"int120"
             / %s"int128"
             / %s"int136"
             / %s"int144"
             / %s"int152"
             / %s"int160"
             / %s"int168"
             / %s"int176"
             / %s"int184"
             / %s"int192"
             / %s"int200"
             / %s"int208"
             / %s"int216"
             / %s"int224"
             / %s"int232"
             / %s"int240"
             / %s"int248"
             / %s"int256"
             / %s"uint8"
             / %s"uint16"
             / %s"uint24"
             / %s"uint32"
             / %s"uint40"
             / %s"uint48"
             / %s"uint56"
             / %s"uint64"
             / %s"uint72"
             / %s"uint80"
             / %s"uint88"
             / %s"uint96"
             / %s"uint104"
             / %s"uint112"
             / %s"uint120"
             / %s"uint128"
             / %s"uint136"
             / %s"uint144"
             / %s"uint152"
             / %s"uint160"
             / %s"uint168"
             / %s"uint176"
             / %s"uint184"
             / %s"uint192"
             / %s"uint200"
             / %s"uint208"
             / %s"uint216"
             / %s"uint224"
             / %s"uint232"
             / %s"uint240"
             / %s"uint248"
             / %s"uint256"

arrayType      = "[" type "]";
fixedArrayType = type "[" numericLiteral "]";
dictType       = "[" type ":" WSP type "]";

; ENUMS
enumDeclaration = %s"enum" SP identifier SP [typeAnnotation] SP "{" *(WSP enumCase CRLF) "}";
enumCase        = %s"case" SP identifier
                / %s"case" SP identifier WSP "=" WSP expression;

; TRAITS
traitDeclaration = %s"struct" SP %s"trait" SP identifier SP "{" *(WSP traitMember CRLF) "}"
                 / %s"contract" SP %s"trait" SP identifier SP "{" *(WSP traitMember CRLF) "}"
                 / %s"external" SP %s"trait" SP identifier SP "{" *(WSP traitMember CRLF) "}";

traitMember = functionDeclaration
            / functionSignatureDeclaration
            / initializerDeclaration
            / initializerSignatureDeclaration
            / contractBehaviourDeclaration
            / eventDeclaration;

; EVENTS
eventDeclaration = %s"event" identifer parameterList

; STRUCTS
structDeclaration = %s"struct" SP identifier [":" WSP identifierList ] SP "{" *(WSP structMember CRLF) "}";

structMember = variableDeclaration
             / functionDeclaration
             / initializerDeclaration;

; BEHAVIOUR
contractBehaviourDeclaration = identifier WSP [stateGroup] SP "::" WSP [callerBinding] callerProtectionGroup WSP "{" *(WSP contractBehaviourMember CRLF) "}";

contractBehaviourMember = functionDeclaration
                        / initializerDeclaration
                        / fallbackDeclaration
                        / initializerSignatureDeclaration
                        / functionSignatureDeclaration;

; ACCESS GROUPS
stateGroup            = "@" identifierGroup;
callerBinding         = identifier WSP "<-";
callerProtectionGroup = identifierGroup;
identifierGroup       = "(" identifierList ")";
identifierList        = identifier *("," WSP identifier)

; FUNCTIONS + INITIALIZER + FALLBACK
functionSignatureDeclaration    = functionHead SP identifier parameterList [returnType]
functionDeclaration             = functionSignatureDeclaration codeBlock;
initializerSignatureDeclaration = initializerHead parameterList
initializerDeclaration          = initializerSignatureDeclaration codeBlock;
fallbackDeclaration             = fallbackHead parameterList codeBlock;

functionHead    = [*(attribute SP)] [*(modifier SP)] %s"func";
initializerHead = [*(attribute SP)] [*(modifier SP)] %s"init";
fallbackHead    = [*(modifier SP)] %s"fallback";

attribute = "@" identifier;
modifier  = %s"public"
          / %s"mutating"
          / %s"visible";

returnType = "->" type;

parameterList = "()"
              / "(" parameter *("," parameter) ")";

parameter          = *(parameterModifiers SP) identifier typeAnnotation [WSP "=" WSP expression];
parameterModifiers = %s"inout" / %s"implicit"

; STATEMENTS
codeBlock = "{" [CRLF] *(WSP statement CRLF) WSP statement [CRLF]"}";
statement = expression
          / returnStatement
          / becomeStatement
          / emitStatement
          / forStatement
          / ifStatement
          / doCatchStatement;

returnStatement  = %s"return" SP expression
becomeStatement  = %s"become" SP expression
emitStatement    = %s"emit" SP functionCall
forStatement     = %s"for" SP variableDeclaration SP %s"in" SP expression SP codeBlock
doCatchStatement = %s"do" SP codeBlock SP %s"catch" SP "%s"is SP type SP codeBlock

; EXPRESSIONS
expression = identifier
           / inOutExpression
           / binaryExpression
           / functionCall
           / literal
           / arrayLiteral
           / dictionaryLiteral
           / self
           / variableDeclaration
           / bracketedExpression
           / subscriptExpression
           / rangeExpression
           / attemptExpression
           / externalCall;

inOutExpression = "&" expression;

binaryOp = "+" / "-" / "*" / "/" / "**"
         / "&+" / "&-" / "&*"
         / "="
         / "==" / "!="
         / "+=" / "-=" / "*=" / "/="
         / "||" / "&&"
         / ">" / "<" / "<=" / ">="
         / ".";

binaryExpression = expression WSP binaryOp WSP expression;

self = %s"self"

rangeExpression = "(" expression ( "..<" / "..." ) expression ")"

bracketedExpression = "(" expression ")";

subscriptExpression = subscriptExpression "[" expression "]";
                    / identifier "[" expression "]";

attemptExpression = try expression
try = %s"try" ( "!" / "?" )

; FUNCTION CALLS
functionCall = identifier "(" [expression] *( "," WSP expression ) ")";

; EXTERNAL CALLS
externalCall = %s"call" WSP [ "(" [expression] *( "," WSP expression ) ")" ] WSP [ "!" / "?" ] SP functionCall;

; CONDITIONALS
ifStatement = %s"if" SP expression SP codeBlock [elseClause];
elseClause  = %s"else" SP codeBlock;

; LITERALS
identifier = ( ALPHA / "_" ) *( ALPHA / DIGIT / "$" / "_" );
literal    = numericLiteral
           / stringLiteral
           / booleanLiteral
           / addressLiteral;

number         = 1*DIGIT;
numericLiteral = decimalLiteral;
decimalLiteral = number
               / number "." number;

addressLiteral = %s"0x" 40HEXDIG;

arrayLiteral      = "[]";
dictionaryLiteral = "[:]";

booleanLiteral = %s"true" / %s"false";
stringLiteral  = """ identifier """;
