2020-07-25 12:17:42 -07:00
parser grammar EscriptParser;
options { tokenVocab=EscriptLexer; }
@header
{
}
@parser::members
{
2025-09-06 17:26:06 +02:00
bool inUninitFunction = false;
2020-07-25 12:17:42 -07:00
}
2025-09-06 17:26:06 +02:00
2020-07-25 12:17:42 -07:00
compilationUnit
2020-08-21 01:53:42 -07:00
: topLevelDeclaration* EOF
2020-07-25 12:17:42 -07:00
;
moduleUnit
: moduleDeclarationStatement* EOF
;
2024-01-28 15:49:55 +07:00
evaluateUnit
: expression EOF
;
2020-07-25 12:17:42 -07:00
moduleDeclarationStatement
: moduleFunctionDeclaration
| constStatement
;
moduleFunctionDeclaration
: IDENTIFIER '(' moduleFunctionParameterList? ')' ';'
;
moduleFunctionParameterList
: moduleFunctionParameter (',' moduleFunctionParameter)*
;
moduleFunctionParameter
: IDENTIFIER (':=' expression)?
;
topLevelDeclaration
: useDeclaration
| includeDeclaration
| programDeclaration
| functionDeclaration
2024-08-10 15:27:25 +02:00
| classDeclaration
2020-07-25 12:17:42 -07:00
| statement
;
2024-08-10 15:27:25 +02:00
classDeclaration
2024-08-18 11:03:01 +02:00
: CLASS IDENTIFIER classParameters classBody ENDCLASS
2024-08-10 15:27:25 +02:00
;
classParameters
: '(' classParameterList? ')'
;
classParameterList
: IDENTIFIER (',' IDENTIFIER)*
;
classBody
: classStatement*
;
classStatement
: functionDeclaration
| varStatement
2025-09-05 23:23:59 +02:00
| uninitFunctionDeclaration
;
uninitFunctionDeclaration
2025-09-06 17:26:06 +02:00
: UNINIT FUNCTION IDENTIFIER { inUninitFunction = true; } functionParameters ';'
2024-08-10 15:27:25 +02:00
;
2020-07-25 12:17:42 -07:00
functionDeclaration
2025-09-06 17:26:06 +02:00
: EXPORTED? FUNCTION IDENTIFIER { inUninitFunction = false; } functionParameters block ENDFUNCTION
2020-07-25 12:17:42 -07:00
;
stringIdentifier
: STRING_LITERAL
| IDENTIFIER
;
useDeclaration
: USE stringIdentifier ';'
;
includeDeclaration
: INCLUDE stringIdentifier ';'
;
programDeclaration
: PROGRAM IDENTIFIER programParameters block ENDPROGRAM
;
// Some ignored / to-be-handled things:
// - Labels can only come before DO, WHILE, FOR, FOREACH, REPEAT, and CASE statements.
// - Const expression must be optimizable
// TODO maybe split these all into individual statements?
statement
: ifStatement
| gotoStatement
| returnStatement
| constStatement
| varStatement
| doStatement
| whileStatement
| exitStatement
| breakStatement
| continueStatement
| forStatement
| foreachStatement
| repeatStatement
| caseStatement
| enumStatement
| SEMI
| statementExpression=expression ';'
;
statementLabel
: IDENTIFIER ':'
;
ifStatement
: IF parExpression THEN? block (ELSEIF parExpression block)* (ELSE block)? ENDIF
;
gotoStatement
: GOTO IDENTIFIER ';'
;
returnStatement
: RETURN expression? ';'
;
constStatement
2021-04-19 21:58:36 +02:00
: TOK_CONST constantDeclaration ';'
2020-07-25 12:17:42 -07:00
;
varStatement
: VAR variableDeclarationList ';'
;
doStatement
: statementLabel? DO block DOWHILE parExpression ';'
;
whileStatement
: statementLabel? WHILE parExpression block ENDWHILE
;
exitStatement
: EXIT ';'
;
breakStatement
: BREAK IDENTIFIER? ';'
;
continueStatement
: CONTINUE IDENTIFIER? ';'
;
forStatement
: statementLabel? FOR forGroup ENDFOR
;
2020-08-21 01:53:42 -07:00
foreachIterableExpression
: functionCall
| scopedFunctionCall
2024-08-10 15:27:25 +02:00
| scopedIdentifier
2020-08-21 01:53:42 -07:00
| IDENTIFIER
| parExpression
| bareArrayInitializer
| explicitArrayInitializer
;
2020-07-25 12:17:42 -07:00
foreachStatement
2020-08-21 01:53:42 -07:00
: statementLabel? FOREACH IDENTIFIER TOK_IN foreachIterableExpression block ENDFOREACH
2020-07-25 12:17:42 -07:00
;
repeatStatement
: statementLabel? REPEAT block UNTIL expression ';'
;
caseStatement
: statementLabel? CASE '(' expression ')' switchBlockStatementGroup+ ENDCASE
;
enumStatement
2025-08-18 20:50:18 +02:00
: ENUM CLASS? IDENTIFIER enumList ENDENUM
2020-07-25 12:17:42 -07:00
;
block
: statement*
;
variableDeclarationInitializer
: ':=' expression
2020-08-29 02:52:40 -07:00
| '=' expression { notifyErrorListeners("Unexpected token: '='. Did you mean := for assign?\n"); }
2020-07-25 12:17:42 -07:00
| ARRAY
;
enumList
: enumListEntry (',' enumListEntry)* ','?
;
enumListEntry
: IDENTIFIER (':=' expression)?
;
switchBlockStatementGroup
: switchLabel+ block
;
switchLabel
2025-08-19 09:07:02 +02:00
: (integerLiteral | boolLiteral | scopedIdentifier | UNINIT | IDENTIFIER | STRING_LITERAL) ':'
2020-07-25 12:17:42 -07:00
| DEFAULT ':'
;
forGroup
2021-04-19 21:58:36 +02:00
: cstyleForStatement
2020-07-25 12:17:42 -07:00
| basicForStatement
;
basicForStatement
2021-04-19 21:58:36 +02:00
: IDENTIFIER ':=' expression TO expression block
2020-07-25 12:17:42 -07:00
;
cstyleForStatement
: '(' expression ';' expression ';' expression ')' block
;
identifierList
: IDENTIFIER (',' identifierList)?
;
variableDeclarationList
: variableDeclaration (',' variableDeclaration)*
;
2021-04-19 21:58:36 +02:00
constantDeclaration
: IDENTIFIER variableDeclarationInitializer
;
2020-07-25 12:17:42 -07:00
variableDeclaration
: IDENTIFIER variableDeclarationInitializer?
2025-02-17 21:59:42 +01:00
| bindingDeclaration bindingDeclarationInitializer
;
bindingDeclaration
: LBRACK sequenceBindingList RBRACK
| LBRACE indexBindingList RBRACE
;
indexBindingList
: indexBinding (',' indexBinding)*
;
sequenceBindingList
: sequenceBinding (',' sequenceBinding)*
;
sequenceBinding
: IDENTIFIER ELLIPSIS?
| bindingDeclaration
;
indexBinding
: IDENTIFIER ELLIPSIS?
| IDENTIFIER binding?
| LBRACK expression RBRACK binding
;
binding
: ':' IDENTIFIER
| ':' bindingDeclaration
;
bindingDeclarationInitializer
: ':=' expression
| '=' expression { notifyErrorListeners("Unexpected token: '='. Did you mean := for assign?\n"); }
2020-07-25 12:17:42 -07:00
;
// PARAMETERS
programParameters
: '(' programParameterList? ')'
;
programParameterList
: programParameter (','? programParameter)*
;
programParameter
: UNUSED IDENTIFIER
| IDENTIFIER (':=' expression)?
;
functionParameters
: '(' functionParameterList? ')'
;
functionParameterList
: functionParameter (',' functionParameter)*
;
functionParameter
2025-09-06 17:26:06 +02:00
: BYREF?
( { if ( inUninitFunction ) notifyErrorListeners( "The 'unused' keyword may not be used here." ); } UNUSED)?
( { if ( !inUninitFunction ) notifyErrorListeners( "The 'default' keyword may not be used here." ); } DEFAULT)?
IDENTIFIER
ELLIPSIS?
( ':=' { if ( inUninitFunction ) notifyErrorListeners( "A parameter may not be given a default value here." ); } expression )?
2020-07-25 12:17:42 -07:00
;
// EXPRESSIONS
2024-07-28 20:21:23 +02:00
//
// Currently no module functions return a function reference, so no need to
// support eg. `uo::Foo( bar )( baz )`.
2020-08-21 01:53:42 -07:00
scopedFunctionCall
2024-08-11 23:01:38 +02:00
: IDENTIFIER? '::' functionCall
2020-08-21 01:53:42 -07:00
;
functionReference
2024-08-11 23:01:38 +02:00
: '@' (scope=IDENTIFIER? '::')? function=IDENTIFIER
2020-07-25 12:17:42 -07:00
;
expression
: primary
2020-08-21 01:53:42 -07:00
| expression expressionSuffix
2020-07-25 12:17:42 -07:00
| expression postfix=('++' | '--')
| prefix=('+'|'-'|'++'|'--') expression
| prefix=('~'|'!'|'not') expression
2020-09-15 06:03:05 -07:00
| expression bop=('*' | '/' | '%' | '<<' | '>>' | '&') expression
| expression bop=('+' | '-' | '|' | '^') expression
2020-08-21 01:53:42 -07:00
| expression bop='?:' expression
2020-09-15 06:48:36 -07:00
| expression bop='in' expression
2020-07-25 12:17:42 -07:00
| expression bop=('<=' | '>=' | '>' | '<') expression
2024-08-26 22:37:22 +02:00
| expression bop='is' expression
2020-08-21 01:53:42 -07:00
| expression bop='=' { notifyErrorListeners("Deprecated '=' found: did you mean '==' or ':='?\n"); } expression
2020-07-25 12:17:42 -07:00
| expression bop=('==' | '!=' | '<>') expression
| expression bop=('&&' | 'and') expression
| expression bop=('||' | 'or') expression
2021-03-14 23:20:20 +01:00
| expression '?' expression ':' expression
2020-07-25 12:17:42 -07:00
| <assoc=right> expression bop=('.+' | '.-' | '.?') expression
| <assoc=right> expression
bop=( ':=' | '+=' | '-=' | '*=' | '/=' | '%=')
expression
;
primary
2020-08-21 01:53:42 -07:00
: literal
| parExpression
| functionCall
| scopedFunctionCall
2024-08-10 15:27:25 +02:00
| scopedIdentifier
2020-07-25 12:17:42 -07:00
| IDENTIFIER
2020-08-21 01:53:42 -07:00
| functionReference
2024-07-29 22:30:52 +02:00
| functionExpression
2020-08-21 01:53:42 -07:00
| explicitArrayInitializer
| explicitStructInitializer
| explicitDictInitializer
| explicitErrorInitializer
| bareArrayInitializer
2021-03-11 22:48:16 +01:00
| interpolatedString
2020-08-21 01:53:42 -07:00
;
2024-08-10 15:27:25 +02:00
scopedIdentifier
2024-08-11 23:01:38 +02:00
: scope=IDENTIFIER? '::' identifier=IDENTIFIER;
2024-08-10 15:27:25 +02:00
2024-07-29 22:30:52 +02:00
functionExpression
2025-09-06 17:26:06 +02:00
: AT { inUninitFunction = false; } functionParameters? LBRACE block RBRACE
2024-07-29 22:30:52 +02:00
;
2020-08-21 01:53:42 -07:00
explicitArrayInitializer
: ARRAY arrayInitializer?
;
explicitStructInitializer
: STRUCT structInitializer?
;
explicitDictInitializer
: DICTIONARY dictInitializer?
;
explicitErrorInitializer
: TOK_ERROR structInitializer?
;
bareArrayInitializer
: LBRACE expressionList? RBRACE
| LBRACE expressionList? ',' RBRACE {notifyErrorListeners("Expected expression following comma before right-brace in array initializer list");}
2020-07-25 12:17:42 -07:00
;
parExpression
: '(' expression ')'
;
expressionList
2024-08-06 23:34:24 +02:00
: expressionListEntry (',' expressionListEntry)*
;
expressionListEntry
: expression ELLIPSIS?
2020-07-25 12:17:42 -07:00
;
2020-08-21 01:53:42 -07:00
expressionSuffix
: indexingSuffix
| methodCallSuffix
| navigationSuffix
2024-07-28 20:21:23 +02:00
| functionCallSuffix
2020-08-21 01:53:42 -07:00
;
indexingSuffix
2024-08-06 23:34:24 +02:00
: LBRACK indexList RBRACK
;
indexList
: expression (',' expression)*
2020-07-25 12:17:42 -07:00
;
2020-08-21 01:53:42 -07:00
navigationSuffix
2024-08-26 22:37:22 +02:00
: '.' ( IDENTIFIER | STRING_LITERAL | FUNCTION )
2020-07-25 12:17:42 -07:00
;
2020-08-21 01:53:42 -07:00
methodCallSuffix
: '.' IDENTIFIER LPAREN expressionList? RPAREN
2020-07-25 12:17:42 -07:00
;
2024-07-28 20:21:23 +02:00
functionCallSuffix
: '(' expressionList? ')'
;
2020-08-21 01:53:42 -07:00
functionCall
2020-07-25 12:17:42 -07:00
: IDENTIFIER '(' expressionList? ')'
;
structInitializerExpression
: IDENTIFIER (':=' expression)?
| STRING_LITERAL (':=' expression)?
2025-02-19 18:33:56 +01:00
| expression ELLIPSIS
2020-07-25 12:17:42 -07:00
;
structInitializerExpressionList
: structInitializerExpression (',' structInitializerExpression)*
;
structInitializer
: '{' structInitializerExpressionList? '}'
2020-08-29 02:52:40 -07:00
| '{' structInitializerExpressionList? ',' '}' {notifyErrorListeners("Expected expression following comma before right-brace in struct initializer list");}
2020-07-25 12:17:42 -07:00
;
dictInitializerExpression
: expression ('->' expression)?
2025-02-19 18:33:56 +01:00
| expression ELLIPSIS
2020-07-25 12:17:42 -07:00
;
dictInitializerExpressionList
: dictInitializerExpression (',' dictInitializerExpression)*
;
dictInitializer
: '{' dictInitializerExpressionList? '}'
2020-08-29 02:52:40 -07:00
| '{' dictInitializerExpressionList? ',' '}' {notifyErrorListeners("Expected expression following comma before right-brace in dictionary initializer list");}
2020-07-25 12:17:42 -07:00
;
arrayInitializer
: '{' expressionList? '}'
2020-08-29 02:52:40 -07:00
| '{' expressionList? ',' '}' {notifyErrorListeners("Expected expression following comma before right-brace in array initializer list");}
2020-07-25 12:17:42 -07:00
| '(' expressionList? ')'
2020-08-29 02:52:40 -07:00
| '(' expressionList? ',' ')' {notifyErrorListeners("Expected expression following comma before right-paren in array initializer list");}
2020-07-25 12:17:42 -07:00
;
// Literals
literal
: integerLiteral
| floatLiteral
2024-02-01 15:21:52 +07:00
| boolLiteral
2020-07-25 12:17:42 -07:00
| STRING_LITERAL
2024-02-01 15:21:52 +07:00
| UNINIT
2025-11-17 18:02:45 +01:00
| REGEXP_LITERAL
2020-07-25 12:17:42 -07:00
;
2021-03-11 22:48:16 +01:00
interpolatedString
: INTERPOLATED_STRING_START interpolatedStringPart* DOUBLE_QUOTE_INSIDE
;
interpolatedStringPart
: LBRACE_INSIDE expression (':' FORMAT_STRING)?
| LBRACE_INSIDE (':' FORMAT_STRING)? {notifyErrorListeners("Expected expression following interpolated string part start");}
| DOUBLE_LBRACE_INSIDE
| REGULAR_CHAR_INSIDE
| DOUBLE_RBRACE
| STRING_LITERAL_INSIDE
;
2020-07-25 12:17:42 -07:00
integerLiteral
: DECIMAL_LITERAL
| HEX_LITERAL
| OCT_LITERAL
| BINARY_LITERAL
;
floatLiteral
: FLOAT_LITERAL
| HEX_FLOAT_LITERAL
;
2024-01-20 12:38:25 +08:00
boolLiteral
: BOOL_TRUE
| BOOL_FALSE
;