From 7d52374fc01cf9c29cd9fe7a59b0d101b535886a Mon Sep 17 00:00:00 2001 From: Kenzie Togami Date: Sun, 13 Oct 2019 02:02:47 -0700 Subject: [PATCH] Add initial ANTLR setup + lex/parser --- worldedit-core/build.gradle.kts | 27 +++ .../com/sk89q/worldedit/antlr/Expression.g4 | 186 ++++++++++++++++++ 2 files changed, 213 insertions(+) create mode 100644 worldedit-core/src/main/antlr/com/sk89q/worldedit/antlr/Expression.g4 diff --git a/worldedit-core/build.gradle.kts b/worldedit-core/build.gradle.kts index a20b254f8..19dcecc05 100644 --- a/worldedit-core/build.gradle.kts +++ b/worldedit-core/build.gradle.kts @@ -1,7 +1,10 @@ +import org.gradle.plugins.ide.idea.model.IdeaModel + plugins { id("java-library") id("net.ltgt.apt-eclipse") id("net.ltgt.apt-idea") + id("antlr") } applyPlatformAndCoreConfiguration() @@ -23,6 +26,10 @@ dependencies { "compile"("org.slf4j:slf4j-api:1.7.26") "compile"("it.unimi.dsi:fastutil:8.2.1") + val antlrVersion = "4.7.2" + "antlr"("org.antlr:antlr4:$antlrVersion") + "implementation"("org.antlr:antlr4-runtime:$antlrVersion") + "compileOnly"(project(":worldedit-libs:core:ap")) "annotationProcessor"(project(":worldedit-libs:core:ap")) // ensure this is on the classpath for the AP @@ -36,6 +43,26 @@ tasks.withType().configureEach { options.compilerArgs.add("-Aarg.name.key.prefix=") } +tasks.named("generateGrammarSource").configure { + val pkg = "com.sk89q.worldedit.antlr" + outputDirectory = file("build/generated-src/antlr/main/${pkg.replace('.', '/')}") + arguments = listOf( + "-visitor", "-package", pkg, + "-Xexact-output-dir" + ) +} + +// Give intellij info about where ANTLR code comes from +plugins.withId("idea") { + configure { + afterEvaluate { + module.sourceDirs.add(file("src/main/antlr")) + module.sourceDirs.add(file("build/generated-src/antlr/main")) + module.generatedSourceDirs.add(file("build/generated-src/antlr/main")) + } + } +} + sourceSets { main { java { diff --git a/worldedit-core/src/main/antlr/com/sk89q/worldedit/antlr/Expression.g4 b/worldedit-core/src/main/antlr/com/sk89q/worldedit/antlr/Expression.g4 new file mode 100644 index 000000000..254e0bf46 --- /dev/null +++ b/worldedit-core/src/main/antlr/com/sk89q/worldedit/antlr/Expression.g4 @@ -0,0 +1,186 @@ +grammar Expression; + +// Lexer tokens: + +PLUS : '+' ; +MINUS : '-' ; +TIMES : '*' ; +DIVIDE : '/' ; +MODULO : '%' ; +POWER : '^' | '**' ; +LEFT_SHIFT : '<<' ; +RIGHT_SHIFT : '>>' ; +ASSIGN : '=' ; +COMPLEMENT : '~' ; + +PLUS_ASSIGN : '+=' ; +MINUS_ASSIGN : '-=' ; +TIMES_ASSIGN : '*=' ; +DIVIDE_ASSIGN : '/=' ; +MODULO_ASSIGN : '%=' ; +POWER_ASSIGN : '^=' ; + +EQUAL : '==' ; +NOT_EQUAL : '!=' ; +NEAR : '~=' ; +LESS_THAN : '<' ; +LESS_THAN_OR_EQUAL : '<=' ; +GREATER_THAN : '>' ; +GREATER_THAN_OR_EQUAL : '>=' ; +// SC = "Short Circuit" +// Non-SC variants not currently implemented. +AND_SC : '&&' ; +OR_SC : '||' ; + +INCREMENT : '++' ; +DECREMENT : '--' ; + +COMMA : ',' ; +OPEN_PAREN : '(' ; +CLOSE_PAREN : ')' ; +OPEN_BRACKET : '{' ; +CLOSE_BRACKET : '}' ; +SEMI_COLON : ';' ; +QUESTION_MARK : '?' ; +COLON : ':' ; +EXCLAMATION_MARK : '!' ; + +IF : 'if' ; +ELSE : 'else' ; +WHILE : 'while' ; +DO : 'do' ; +FOR : 'for' ; +BREAK : 'break' ; +CONTINUE : 'continue' ; +RETURN : 'return' ; +SWITCH : 'switch' ; +CASE : 'case' ; +DEFAULT : 'default' ; + +fragment DIGIT : [0-9] ; +fragment SIGN : [+-] ; +fragment EXP_CHAR : [eE] ; +fragment DECIMAL : '.' DIGIT+ ( EXP_CHAR SIGN? DIGIT+ )? ; + +// All numbers are treated the same. No int/dec divide. +NUMBER : SIGN? ( DIGIT+ DECIMAL? | DECIMAL ) ; + +ID : [A-Za-z] [0-9A-Za-z_]* ; + +WS : [ \t\r\n\u000C]+ -> skip ; + +// Parser rules: + +/** + * All statements parseable from the input. Forces consumption of EOF token. + */ +allStatements : statements EOF ; + +statements : statement+ ; + +statement + : block + | ifStatement + | whileStatement + | doStatement + | forStatement + | breakStatement + | continueStatement + | returnStatement + | switchStatement + | expressionStatement + | SEMI_COLON + ; + +block : '{' statements '}' ; + +ifStatement : IF '(' expression ')' statement ( ELSE statement ) ; + +whileStatement : WHILE '(' expression ')' statement ; + +doStatement : DO statement WHILE '(' expression ')' SEMI_COLON ; + +forStatement + : FOR '(' + // C-style for loop + ( expression ';' expression ';' expression + // Range for loop + | ID ASSIGN ID ',' ID + ) + ')' statement ; + +breakStatement : BREAK ; + +continueStatement : CONTINUE ; + +returnStatement : RETURN expression? ; + +switchStatement : SWITCH '(' expression ')' '{' (switchLabel ':' statements )+ '}' ; + +switchLabel + : CASE constantExpression + | DEFAULT + ; + +expressionStatement : expression SEMI_COLON ; + +expression + : constantExpression + | functionCall + | identifierExpression + | '(' expression ')' + | unaryOp expression + | identifierExpression binaryAssignOp expression + | expression binaryOp expression + | expression postUnaryOp + ; + +constantExpression : NUMBER ; + +functionCall : ID '(' (expression ( ',' expression )*)? ')' ; + +identifierExpression : ID ; + +unaryOp + : MINUS + | EXCLAMATION_MARK + | COMPLEMENT + | INCREMENT + | DECREMENT + ; + +postUnaryOp + : INCREMENT + | DECREMENT + | EXCLAMATION_MARK + ; + +binaryOp + : POWER + | TIMES + | DIVIDE + | MODULO + | PLUS + | MINUS + | LEFT_SHIFT + | RIGHT_SHIFT + | LESS_THAN + | GREATER_THAN + | LESS_THAN_OR_EQUAL + | GREATER_THAN_OR_EQUAL + | EQUAL + | NOT_EQUAL + | NEAR + | AND_SC + | OR_SC + ; + +binaryAssignOp + : ASSIGN + | PLUS_ASSIGN + | MINUS_ASSIGN + | TIMES_ASSIGN + | DIVIDE_ASSIGN + | MODULO_ASSIGN + | POWER_ASSIGN + ;