commit 96962f18148d5b4c0fe79775e0fdefd99fd5801a Author: reet15 Date: Sun May 30 17:54:41 2021 -0700 Initial commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..1533eb8 --- /dev/null +++ b/README.md @@ -0,0 +1,240 @@ +# Luraph_Deobfuscator +Automatically deobfuscate Luraph scripts. Tested on Luraph scripts between v11.5 and v11.8.1 (May 1, 2020 release) + +Requires Java 8 to run. + +# Binary Release (Version 1.0.0) +https://github.com/TheGreatSageEqualToHeaven/LuraphDeobfuscator/releases/download/v1.0.0/LuraphDevirtualizer.jar + +# Discord Group +Come discuss this project in my Lua discord + +https://discord.gg/cdhcryv + +Add me on discord + + ```The Great Sage Equal To Heaven#7780``` + +# Motivation +This project was created for intellectual purposes as a way to explore code deobfuscation techniques on Lua scripts obfuscated by Luraph, which is a tool used to obfuscate Lua scripts. This Lua obfuscator has been used by cybercriminals as a way of protecting malicious exploits they sell for Robolox or bots for World of Warcraft. This project may be of interest to game security developers to prevent cheating in their games by discovering how hackers are breaking their code. + +Luraph scripts are extremely complicated to tackle, especially in an automated matter. The process is essentially. + +- Lex and parse input Lua file to generate parse tree +- Convert parse tree into abstract syntax treee +- Optimize the abstract syntax tree +- Rename variables, populate symbol table. +- Detect and rename variables and function names into names such as ```get_float64```, etc +- Detect VM handlers +- Find all encryption information in Lua VM +- Load and decrypt bytecode using above information. +- Decrypt and populate chunks by symbolically executing Lua code (constants, instructions, prototypes). +- Process VM handler redirection and decryption (bytecode redirection). +- Remove antidecompiler tricks from optimized bytecode +- Remove junk code and further optimize bytecode using various techniques and algorithms. +- Generate luac file + +LuraphDeobfuscator provides a Lua deobfuscation engine which can be ported to other Lua obfuscators trivially. This source code can be used, with minor modifications, for a vast array of things related to Lua. I emplore the community to reuse my code and engine to create other Lua deobfuscators, as long as they state they are using my code and provide a link back to this project. + +# Simple usage +Generate a .luac file from an obfuscated Luraph VM script + + +```java -jar LuraphDeobfuscator.jar -i obfuscated.lua -o obfuscated.luac``` + +You may now use a Lua decompiler such as unluac to decompile the code back into Lua. I recommend unluac for decompilation and luadec for generating disassemblies with pseudocode. + +# Advanced Use: Introduction + +I have written the sample Lua code below: +```lua +local x = 0 + +local name = "name: " + +for i = 0, 10 do + x = x + i +end + +for i = 0, 10 do + name = name .. i .. "," +end + +print(x) +print(name) +``` + +Ive uploaded the obfuscated version of that Luraph script to pastebin here: https://pastebin.com/SubyTCBL + +Here is a screenshot of what the obfuscated code looks like: + +![Luraph VM](https://i.imgur.com/xZwYsOg.png) + +We are now going to explore all of Luraph Deobfuscator's features: + +## Advanced Use: Deobfuscation ## + +Download the above script as file.lua and run the deobfuscator + +```java -jar LuraphDeobfuscator.java -i file.lua -o file.luac``` + +Use unluac to decompile the generated bytecode and save it to file.lua + +```java -jar unluac.jar file.luac > file.lua``` + +Deobfuscated code using luadec + +```lua +local l_0_0 = 0 +local l_0_1 = "name: " +for l_0_5 = 0, 10 do + l_0_0 = l_0_0 + l_0_5 +end +for l_0_9 = 0, 10 do + l_0_1 = l_0_1 .. l_0_9 .. "," +end +print(l_0_0) +print(l_0_1) +``` + +Deobfuscated code using unluac + +```lua +local L0_0, L1_1, L2_2, L3_3, L4_4, L5_5 +L0_0 = 0 +L1_1 = "name: " +for L5_5 = 0, 10 do + L0_0 = L0_0 + L5_5 +end +for L5_5 = 0, 10 do + L1_1 = L1_1 .. L5_5 .. "," +end +L2_2(L3_3) +L2_2(L3_3) +``` + +Of course, we know that using a decompiler we can't always expect to get back a complete version of the source code. In this case luadec generates the complete source code but unluac misses assigning ```print``` to ```L2_2``` + +To get an accurate dissasembly listing, we can also use luadec's disassembler + +```luadec -dis file.lua > file.dis.txt``` + +```lua +-dis file.lua + +; Function: 0 +; Defined at line: 0 +; #Upvalues: 0 +; #Parameters: 0 +; Is_vararg: 2 +; Max Stack Size: 13 + + 0 [-]: LOADK R0 K0 ; R0 := 0 + 1 [-]: LOADK R1 K1 ; R1 := "name: " + 2 [-]: LOADK R6 K0 ; R6 := 0 + 3 [-]: LOADK R7 K2 ; R7 := 10 + 4 [-]: LOADK R8 K3 ; R8 := 1 + 5 [-]: FORPREP R6 1 ; R6 -= R8; pc += 1 (goto 7) + 6 [-]: ADD R0 R0 R9 ; R0 := R0 + R9 + 7 [-]: FORLOOP R6 -2 ; R6 += R8; if R6 <= R7 then R9 := R6; PC += -2 , goto 6 end + 8 [-]: LOADK R6 K0 ; R6 := 0 + 9 [-]: LOADK R7 K2 ; R7 := 10 + 10 [-]: LOADK R8 K3 ; R8 := 1 + 11 [-]: FORPREP R6 4 ; R6 -= R8; pc += 4 (goto 16) + 12 [-]: MOVE R10 R1 ; R10 := R1 + 13 [-]: MOVE R11 R9 ; R11 := R9 + 14 [-]: LOADK R12 K4 ; R12 := "," + 15 [-]: CONCAT R1 R10 R12 ; R1 := concat(R10 to R12) + 16 [-]: FORLOOP R6 -5 ; R6 += R8; if R6 <= R7 then R9 := R6; PC += -5 , goto 12 end + 17 [-]: MOVE R6 R2 ; R6 := R2 + 18 [-]: MOVE R7 R3 ; R7 := R3 + 19 [-]: CALL R6 2 1 ; := R6(R7) + 20 [-]: MOVE R6 R2 ; R6 := R2 + 21 [-]: MOVE R7 R3 ; R7 := R3 + 22 [-]: CALL R6 2 1 ; := R6(R7) + 23 [-]: RETURN R0 1 ; return +``` + +# Advanced Use: Inspecting Luraph VM + +If we are curious about manually inspecting the VM ourselves, we can use LuraphDeobfuscator to generate a pseudocode source listing of the Luraph VM either in psuedocode or with its graphical display + +To save the VM to a file + +```java -jar LuraphDeobfuscator -i file.lua -s > file.vm.lua``` + +To copy the VM to your clipboard (CTRL+C/CTRL+P) + +```java -jar LuraphDeobfuscator -i file.lua -c``` + +The contents of running that command on the script we are interested can be found here: + +https://pastebin.com/eK0Tu0QM + +And when we look at a screenshot we can now easily read the VM code. This process is used internally by LuraphDeobfuscator in order to fully deobfuscate Luraph code. + +We can see it even assigns names to functions and variables. Pretty neat! + +![Optimized VM](https://i.imgur.com/LuingqO.png) + +# Advanced Use: Viewing Abstract Syntax Trees + +We can also view the optimized VM graphically: + +```java -jar LuraphDeobfuscator -i file.lua -s``` + +The following window will be displayed: + +![Luraph AST](https://i.imgur.com/Hv3rTQ7.png) + +# Advanced Use: Dumping Bytecode + +It is normally better to use luadec to generate a disassembly of bytecode, but for whatever reason if that doesn't happen or you are curious about exploring LuraphDeobfuscator's features, you can also use it to dump optimized bytecode. + +```java -jar LuraphDeobfuscator -i file.lua -b > file.bytecode.txt``` + +```lua +DUMPING CHUNK +#UPVALUES = 0.0 +#PARAMS = 0.0 +#MAXSTACKSIZE = 9.0 +#CONSTANTS = 7 +#INSTRUCTIONS = 24 +#PROTOTYPES = 0 + +Constant 0 = 0.0 +Constant 1 = name: +Constant 2 = 10.0 +Constant 3 = 1.0 +Constant 4 = , +Constant 5 = print +Constant 6 = + +[0] LOADK 0.0 0.0 +[1] LOADK 1.0 1.0 +[2] LOADK 2.0 0.0 +[3] LOADK 3.0 2.0 +[4] LOADK 4.0 3.0 +[5] FORPREP 2.0 1.0 +[6] ADD 0.0 0.0 5.0 +[7] FORLOOP 2.0 -2.0 +[8] LOADK 2.0 0.0 +[9] LOADK 3.0 2.0 +[10] LOADK 4.0 3.0 +[11] FORPREP 2.0 4.0 +[12] MOVE 6.0 1.0 +[13] MOVE 7.0 5.0 +[14] LOADK 8.0 4.0 +[15] CONCAT 1.0 6.0 8.0 +[16] FORLOOP 2.0 -5.0 +[17] GETGLOBAL 2.0 5.0 +[18] MOVE 3.0 0.0 +[19] CALL 2.0 2.0 1.0 +[20] GETGLOBAL 2.0 5.0 +[21] MOVE 3.0 1.0 +[22] CALL 2.0 2.0 1.0 +[23] RETURN 0.0 1.0 + + +END CHUNK +``` diff --git a/grammar/Lua.g4 b/grammar/Lua.g4 new file mode 100644 index 0000000..7d69ed9 --- /dev/null +++ b/grammar/Lua.g4 @@ -0,0 +1,352 @@ +/* +BSD License + +Copyright (c) 2013, Kazunori Sakamoto +Copyright (c) 2016, Alexander Alexeev +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. Neither the NAME of Rainer Schuster nor the NAMEs of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This grammar file derived from: + + Lua 5.3 Reference Manual + http://www.lua.org/manual/5.3/manual.html + + Lua 5.2 Reference Manual + http://www.lua.org/manual/5.2/manual.html + + Lua 5.1 grammar written by Nicolai Mainiero + http://www.antlr3.org/grammar/1178608849736/Lua.g + +Tested by Kazunori Sakamoto with Test suite for Lua 5.2 (http://www.lua.org/tests/5.2/) + +Tested by Alexander Alexeev with Test suite for Lua 5.3 http://www.lua.org/tests/lua-5.3.2-tests.tar.gz +*/ + +grammar Lua; + +chunk + : block EOF + ; + +block + : stat* retstat? + ; + +stat + : ';' #stmtSemicolon + | varlist '=' explist #stmtAssign + | functioncall #stmtFuncCall + | label #stmtLabel + | 'break' #stmtBreak + | 'goto' NAME #stmtGoto + | 'do' block 'end' #stmtDo + | 'while' exp 'do' block 'end' #stmtWhile + | 'repeat' block 'until' exp #stmtRepeat + | ifstmt elseifstmt elsestmt 'end' #stmtIf + | 'for' NAME '=' exp ',' exp (',' exp)? 'do' block 'end' #stmtForStep + | 'for' namelist 'in' explist 'do' block 'end' #stmtForIn + | 'function' funcname funcbody #stmtFuncDef + | 'local' 'function' NAME funcbody #stmtLocalFuncDef + | 'local' namelist ('=' explist)? #stmtLocalDecl + ; + +ifstmt + : + 'if' exp 'then' block + ; + +elseifstmt + : + ('elseif' exp 'then' block)* + ; + +elsestmt + : + ('else' block)? + ; + +retstat + : 'return' explist? ';'? + ; + +label + : '::' NAME '::' + ; + +funcname + : NAME ('.' NAME)* (':' NAME)? + ; + +varlist + : var (',' var)* + ; + +namelist + : NAME (',' NAME)* + ; + +explist + : exp (',' exp)* + ; + +exp + : 'nil' #expNil + | 'false' #expFalse + | 'true' #expTrue + | number #expNumber + | string #expString + | '...' #expThreeDots + | functiondef #expFuncDef + | prefixexp #expPrefix + | tableconstructor #expTableCtor + | exp operatorPower exp #expPow + | operatorUnary exp #expUnary + | exp operatorMulDivMod exp #expMulDivMod + | exp operatorAddSub exp #expAddSub + | exp operatorStrcat exp #expStrcat + | exp operatorComparison exp #expCmp + | exp operatorAnd exp #expAnd + | exp operatorOr exp #expOr + | exp operatorBitwise exp #expBitwise + ; + +prefixexp + : varOrExp nameAndArgs* + ; + +functioncall + : varOrExp nameAndArgs+ + ; + +varOrExp + : var | '(' exp ')' + ; + +var + : (NAME | '(' exp ')' varSuffix) varSuffix* + ; + +varSuffix + : nameAndArgs* ('[' exp ']' | '.' NAME) + ; + +nameAndArgs + : (':' NAME)? args + ; + +/* +var + : NAME | prefixexp '[' exp ']' | prefixexp '.' NAME + ; + +prefixexp + : var | functioncall | '(' exp ')' + ; + +functioncall + : prefixexp args | prefixexp ':' NAME args + ; +*/ + +args + : '(' explist? ')' | tableconstructor | string + ; + +functiondef + : 'function' funcbody + ; + +funcbody + : '(' parlist? ')' block 'end' + ; + +parlist + : namelist (',' '...')? | '...' + ; + +tableconstructor + : '{' fieldlist? '}' + ; + +fieldlist + : field (fieldsep field)* fieldsep? + ; + +field + : '[' exp ']' '=' exp | NAME '=' exp | exp + ; + +fieldsep + : ',' | ';' + ; + +operatorOr + : 'or'; + +operatorAnd + : 'and'; + +operatorComparison + : '<' | '>' | '<=' | '>=' | '~=' | '=='; + +operatorStrcat + : '..'; + +operatorAddSub + : '+' | '-'; + +operatorMulDivMod + : '*' | '/' | '%' | '//'; + +operatorBitwise + : '&' | '|' | '~' | '<<' | '>>'; + +operatorUnary + : 'not' | '#' | '-' | '~'; + +operatorPower + : '^'; + +number + : INT | HEX | FLOAT | HEX_FLOAT + ; + +string + : NORMALSTRING | CHARSTRING | LONGSTRING + ; + +// LEXER + +NAME + : [a-zA-Z_][a-zA-Z_0-9]* + ; + +NORMALSTRING + : '"' ( EscapeSequence | ~('\\'|'"') )* '"' + ; + +CHARSTRING + : '\'' ( EscapeSequence | ~('\''|'\\') )* '\'' + ; + +LONGSTRING + : '[' NESTED_STR ']' + ; + +fragment +NESTED_STR + : '=' NESTED_STR '=' + | '[' .*? ']' + ; + +INT + : Digit+ + ; + +HEX + : '0' [xX] HexDigit+ + ; + +FLOAT + : Digit+ '.' Digit* ExponentPart? + | '.' Digit+ ExponentPart? + | Digit+ ExponentPart + ; + +HEX_FLOAT + : '0' [xX] HexDigit+ '.' HexDigit* HexExponentPart? + | '0' [xX] '.' HexDigit+ HexExponentPart? + | '0' [xX] HexDigit+ HexExponentPart + ; + +fragment +ExponentPart + : [eE] [+-]? Digit+ + ; + +fragment +HexExponentPart + : [pP] [+-]? Digit+ + ; + +fragment +EscapeSequence + : '\\' [abfnrtvz"'\\] + | '\\' '\r'? '\n' + | DecimalEscape + | HexEscape + | UtfEscape + ; + +fragment +DecimalEscape + : '\\' Digit + | '\\' Digit Digit + | '\\' [0-2] Digit Digit + ; + +fragment +HexEscape + : '\\' 'x' HexDigit HexDigit + ; + +fragment +UtfEscape + : '\\' 'u{' HexDigit+ '}' + ; + +fragment +Digit + : [0-9] + ; + +fragment +HexDigit + : [0-9a-fA-F] + ; + +COMMENT + : '--[' NESTED_STR ']' -> channel(HIDDEN) + ; + +LINE_COMMENT + : '--' + ( // -- + | '[' '='* // --[== + | '[' '='* ~('='|'['|'\r'|'\n') ~('\r'|'\n')* // --[==AA + | ~('['|'\r'|'\n') ~('\r'|'\n')* // --AAA + ) ('\r\n'|'\r'|'\n'|EOF) + -> channel(HIDDEN) + ; + +WS + : [ \t\u000C\r\n]+ -> skip + ; + +SHEBANG + : '#' '!' ~('\n'|'\r')* -> channel(HIDDEN) + ; diff --git a/lib/antlr-4.8-complete.jar b/lib/antlr-4.8-complete.jar new file mode 100644 index 0000000..89a0640 Binary files /dev/null and b/lib/antlr-4.8-complete.jar differ diff --git a/lib/commons-cli-1.4.jar b/lib/commons-cli-1.4.jar new file mode 100644 index 0000000..22deb30 Binary files /dev/null and b/lib/commons-cli-1.4.jar differ diff --git a/src/ASTBasicRenamer.java b/src/ASTBasicRenamer.java new file mode 100644 index 0000000..d96d66c --- /dev/null +++ b/src/ASTBasicRenamer.java @@ -0,0 +1,74 @@ +import ASTNodes.*; + +public class ASTBasicRenamer extends ASTOptimizerBase { + private int varCounter; + private int funcCounter; + + private String varName = "var"; + private String funcName = "func"; + + public ASTBasicRenamer() { + varCounter = 0; + funcCounter = 0; + } + + private void rename(String name) { + // dont rename keyword + if(name.equals("...")) { + return; + } + + SymbolTable.VarInfo info = mgr.symbols.getScopedVariable(name); + + if (info != null && info.rename == null) { + if (info.value instanceof Function) { + info.rename = funcName + funcCounter++; + } + else { + info.rename = varName + varCounter++; + } + } + } + + // Handle function and loop construct renaming + @Override + public Node visit(Block node) { + if (node.parent instanceof Function) { + Function fn = (Function)node.parent; + + for (LuaString arg : fn.params.names) { + rename(arg.symbol()); + } + } + else if (node.parent instanceof ForStep) { + ForStep stmt = (ForStep)node.parent; + + rename(stmt.iteratorName.value); + } + else if (node.parent instanceof ForIn) { + ForIn stmt = (ForIn)node.parent; + + for (LuaString name : stmt.names.names) { + rename(name.value); + } + } + + return node; + } + + @Override + public Node visit(Variable node) { + if (node.name instanceof LuaString) { + rename(node.symbol()); + } + + return node; + } + + @Override + public Node visit(LuaString node) { + rename(node.symbol()); + + return node; + } +} diff --git a/src/ASTConstantFolder.java b/src/ASTConstantFolder.java new file mode 100644 index 0000000..5e2ca67 --- /dev/null +++ b/src/ASTConstantFolder.java @@ -0,0 +1,295 @@ +import ASTNodes.*; +import ASTNodes.Number; + +public class ASTConstantFolder extends ASTOptimizerBase { + private Node foldTableLengthOptimization(UnaryExpression node) { + if (node.op == UnaryExpression.Operator.HASHTAG && (node.expr instanceof TableConstructor)) { + TableConstructor ctor = (TableConstructor)node.expr; + + boolean canOptimize = true; + + // all entries + for (Pair entry : ctor.entries) { + if (entry.first != null || !(entry.second instanceof Literal)) { + canOptimize = false; + break; + } + } + + if (canOptimize) { + return new Number(Integer.toString(ctor.entries.size())); + } + } + + return node; + } + + private Node foldNumberNegation(UnaryExpression node) { + if (node.op == UnaryExpression.Operator.MINUS && (node.expr instanceof Number)) { + return new Number(((Number) node.expr).value * -1); + } + + return node; + } + + private Node foldDoubleNegation(UnaryExpression node) { + if (node.op == UnaryExpression.Operator.TILDE && (node.expr instanceof UnaryExpression)) { + UnaryExpression child = (UnaryExpression)node.expr; + if (child.op == UnaryExpression.Operator.TILDE) { + return child.expr.clone(); + } + } + else if (node.op == UnaryExpression.Operator.NOT && (node.expr instanceof UnaryExpression)) { + UnaryExpression child = (UnaryExpression)node.expr; + if (child.op == UnaryExpression.Operator.NOT) { + return child.expr.clone(); + } + } + + return node; + } + + private void swap(BinaryExpression node) { + Expression temp = node.left; + node.left = node.right; + node.right = node.left; + } + + private boolean isVarBinExp(Expression _node) { + if (!(_node instanceof BinaryExpression)) { + return false; + } + + BinaryExpression node = (BinaryExpression)_node; + + return (!(node.left instanceof Number) && node.right instanceof Number) || + (node.left instanceof Number && !(node.right instanceof Number)); + } + + // c commutative, a associative + // (0) (A + 1) - 1 --> (A + 1) + (-1) + // (A - 1) + 1 --> (A + (-1)) + 1 + // (1) (1 c A) c 1 --> (A c 1) c 1 + // (2) (A a 1) a 1 --> A a (1 a 1) + + // precondition for rewrites: valid rewrite format VarBinExp op Const + + private BinaryExpression rewriteBinExpRule0(BinaryExpression node) { + if (isVarBinExp(node.left)) { + BinaryExpression left = (BinaryExpression)node.left; + if (left.operator == BinaryExpression.Operator.ADD && node.operator == BinaryExpression.Operator.SUB) { + node.operator = BinaryExpression.Operator.ADD; + node.right = new Number(((Number)node.right).value * (-1)); + // dont need to clone because (1) or (2) will + } + else if (left.operator == BinaryExpression.Operator.SUB && node.operator == BinaryExpression.Operator.ADD) { + left.operator = BinaryExpression.Operator.ADD; + left.right = new Number(((Number)left.right).value * (-1)); + // dont need to clone because (1) or (2) will + } + } + + return node; + } + + private BinaryExpression rewriteBinExpRule1(BinaryExpression node) { + if (isVarBinExp(node.left)) { + BinaryExpression left = (BinaryExpression)node.left; + if (left.left instanceof Number && left.isCommutative() && node.operator == left.operator) { + BinaryExpression cloned = node.clone(); + swap((BinaryExpression)cloned.left); + return cloned; + } + } + else { + BinaryExpression right = (BinaryExpression)node.right; + if (right.right instanceof Number && right.isCommutative() && node.operator == right.operator) { + BinaryExpression cloned = node.clone(); + swap((BinaryExpression)cloned.right); + return cloned; + } + } + + return node; + } + + private BinaryExpression rewriteBinExpRule2(BinaryExpression node) { + if (isVarBinExp(node.left)) { + BinaryExpression left = (BinaryExpression)node.left; + if (left.right instanceof Number && left.operator == node.operator) { + BinaryExpression cloned = node.clone(); + BinaryExpression clonedLeft = (BinaryExpression)cloned.left; + Expression temp = cloned.right; + cloned.right = clonedLeft.left; + clonedLeft.left = temp; + return cloned; + } + } + else { + BinaryExpression right = (BinaryExpression)node.right; + if (right.left instanceof Number && right.operator == node.operator) { + BinaryExpression cloned = node.clone(); + BinaryExpression clonedRight = (BinaryExpression)cloned.right; + Expression temp = cloned.left; + cloned.left = clonedRight.right; + clonedRight.right = temp; + return cloned; + } + } + + return node; + } + + private BinaryExpression rewriteBinExp(BinaryExpression node) { + if (isVarBinExp(node.left) && node.right instanceof Number || + isVarBinExp(node.right) && node.left instanceof Number) { + + // (0) + node = rewriteBinExpRule0(node); + + // (1) + node = rewriteBinExpRule1(node); + + // (2) + node = rewriteBinExpRule2(node); + + } + + return node; + } + + // are we going to do LOGICAL and BITWISE here? + private Node foldNumberBinaryExpression(BinaryExpression node) { + if (node.left instanceof Number && node.right instanceof Number) { + double left = ((Number) node.left).value; + double right = ((Number) node.right).value; + + switch (node.operator) { + default: + break; + case POW: + return new Number(Math.pow(left, right)); + case MUL: + return new Number(left * right); + case INTEGER_DIV: + return new Number(Math.floor(left / right)); + case REAL_DIV: + return new Number(left / right); + case MOD: + return new Number(left % right); + case ADD: + return new Number(left + right); + case SUB: + return new Number(left - right); + case LT: + return left < right ? new True() : new False(); + case GT: + return left > right ? new True() : new False(); + case LTE: + return left <= right ? new True() : new False(); + case GTE: + return left >= right ? new True() : new False(); + case NEQ: + return left != right ? new True() : new False(); + case EQ: + return left == right ? new True() : new False(); + } + } + + return node; + } + + private boolean isFoldableVariableName(String name) { + return ConstantData.isConstant(name); + } + + private Node foldVariableName(Variable var) { + if (var.name instanceof LuaString) { + if (var.suffixes.size() == 0 && isFoldableVariableName(var.line())) { + return new LuaString(var.line()); + } + else { + boolean tailCall = false; + + for (Suffix s : var.suffixes) { + if (s.nameAndArgs.size() != 0) { + tailCall = true; + break; + } + } + + if (!tailCall && isFoldableVariableName(var.line())) { + return new LuaString(var.line()); + } + } + } + + return var; + } + + private Node foldTableUnpack(TableConstructor ctor) { + if (ctor.entries.size() == 1) { + Pair p = ctor.entries.get(0); + + if (p.first == null && p.second instanceof FunctionCall) { + FunctionCall call = (FunctionCall)p.second; + + if (call.varOrExp.line().equals("unpack") & call.nameAndArgs.size() == 1 && + call.nameAndArgs.get(0).name == null && + call.nameAndArgs.get(0).args instanceof ExprList) { + + ExprList list = (ExprList)call.nameAndArgs.get(0).args; + + if (list.exprs.size() == 3 && list.exprs.get(0) instanceof TableConstructor) { + TableConstructor a1 = (TableConstructor)list.exprs.get(0); + + if (a1.entries.size() == 0) { + return new TableConstructor(); + } + } + } + } + } + + return ctor; + } + + @Override + public Node visit(Variable node) { + return foldVariableName(node); + } + + @Override + public Node visit(UnaryExpression node) { + Node replaced = foldTableLengthOptimization(node); + + if (node != replaced) { + return replaced; + } + + replaced = foldNumberNegation(node); + + if (node != replaced) { + return replaced; + } + + replaced = foldDoubleNegation(node); + + return replaced; + } + + @Override + public Node visit(BinaryExpression node) { + // rewriting is safe eg can continue optimizing after + node = rewriteBinExp(node); + + Node replaced = foldNumberBinaryExpression(node); + + return replaced; + } + + @Override + public Node visit(TableConstructor node) { + return foldTableUnpack(node); + } +} diff --git a/src/ASTConstantPropagator.java b/src/ASTConstantPropagator.java new file mode 100644 index 0000000..4626303 --- /dev/null +++ b/src/ASTConstantPropagator.java @@ -0,0 +1,144 @@ +import ASTNodes.*; + +public class ASTConstantPropagator extends ASTOptimizerBase { + private Node replaceVar(Variable node) { + SymbolTable.VarInfo info = mgr.symbols.getScopedVariable(node.symbol()); + + if (info != null && info.isConstant()) { + return info.value.clone(); + } + + return node; + } + + @Override + public Node visit(Assign node) { + Assign copy = null; + + for (int i = 0; i < node.exprs.exprs.size(); i++) { + Node expr = node.exprs.exprs.get(i); + if (expr instanceof Variable) { + Node replaced = replaceVar((Variable)expr); + + if (copy == null && replaced != expr) { + copy = node.clone(); + } + + if (replaced != expr) { + copy.exprs.exprs.set(i, (Expression)replaced); + } + } + } + + return copy != null ? copy : node; + } + + + @Override + public Node visit(LocalDeclare node) { + LocalDeclare copy = null; + + for (int i = 0; i < node.exprs.exprs.size(); i++) { + Node expr = node.exprs.exprs.get(i); + if (expr instanceof Variable) { + Node replaced = replaceVar((Variable)expr); + + if (copy == null && replaced != expr) { + copy = node.clone(); + } + + if (replaced != expr) { + copy.exprs.exprs.set(i, (Expression)replaced); + } + } + } + + return copy != null ? copy : node; + } + + @Override + public Node visit(BinaryExpression node) { + BinaryExpression copy = null; + + if (node.left instanceof Variable) { + Expression expr = (Expression)replaceVar((Variable)node.left); + + if (copy == null && expr != node.left) { + copy = node.clone(); + } + + if (expr != node.left) { + copy.left = expr; + } + } + + if (node.right instanceof Variable) { + Expression expr = (Expression)replaceVar((Variable)node.right); + + if (copy == null && expr != node.right) { + copy = node.clone(); + } + + if (expr != node.right) { + copy.right = expr; + } + } + + return copy != null ? copy : node; + } + + @Override + public Node visit(UnaryExpression node) { + UnaryExpression copy = null; + + if (node.expr instanceof Variable) { + Expression expr = (Expression)replaceVar((Variable)node.expr); + + if (copy == null && expr != node.expr) { + copy = node.clone(); + } + + if (expr != node.expr) { + copy.expr = expr; + } + } + + return copy != null ? copy : node; + } + + @Override + public Node visit(FunctionCall node) { + if (node.varOrExp instanceof Variable) { + Expression expr = (Expression)replaceVar((Variable)node.varOrExp); + if (expr != node.varOrExp) { + FunctionCall call = node.clone(); + call.varOrExp = expr; + return call; + } + } + + return node; + } + + @Override + public Node visit(ExprList node) { + ExprList copy = null; + + for (int i = 0; i < node.exprs.size(); i++) { + Node expr = node.exprs.get(i); + if (expr instanceof Variable) { + Node replaced = replaceVar((Variable)expr); + + if (copy == null && replaced != expr) { + copy = node.clone(); + } + + if (replaced != expr) { + copy.exprs.set(i, (Expression)replaced); + } + } + } + + return copy != null ? copy : node; + } +} diff --git a/src/ASTNodes/ASTVisitor.java b/src/ASTNodes/ASTVisitor.java new file mode 100644 index 0000000..c818bf5 --- /dev/null +++ b/src/ASTNodes/ASTVisitor.java @@ -0,0 +1,178 @@ +package ASTNodes; + +public class ASTVisitor implements IASTVisitor { + @Override + public Node visit(Assign node) { + return node; + } + + @Override + public Node visit(BinaryExpression node) { + return node; + } + + @Override + public Node visit(Block node) { + return node; + } + + @Override + public Node visit(Break node) { + return node; + } + + @Override + public Node visit(Do node) { + return node; + } + + @Override + public Node visit(Expression node) { + return node; + } + + @Override + public Node visit(ExprList node) { + return node; + } + + @Override + public Node visit(False node) { + return node; + } + + @Override + public Node visit(ForIn node) { + return node; + } + + @Override + public Node visit(ForStep node) { + return node; + } + + @Override + public Node visit(Function node) { + return node; + } + + @Override + public Node visit(FunctionCall node) { + return node; + } + + @Override + public Node visit(GoTo node) { + return node; + } + + @Override + public Node visit(If node) { + return node; + } + + @Override + public Node visit(Label node) { + return node; + } + + @Override + public Node visit(Literal node) { + return node; + } + + @Override + public Node visit(LocalDeclare node) { + return node; + } + + @Override + public Node visit(LuaString node) { + return node; + } + + @Override + public Node visit(NameAndArgs node) { + return node; + } + + @Override + public Node visit(NameList node) { + return node; + } + + @Override + public Node visit(Nil node) { + return node; + } + + @Override + public Node visit(Node node) { + return node; + } + + @Override + public Node visit(Number node) { + return node; + } + + @Override + public Node visit(Pair node) { + return node; + } + + @Override + public Node visit(Repeat node) { + return node; + } + + @Override + public Node visit(Return node) { + return node; + } + + @Override + public Node visit(Suffix node) { + return node; + } + + @Override + public Node visit(TableConstructor node) { + return node; + } + + @Override + public Node visit(True node) { + return node; + } + + @Override + public Node visit(UnaryExpression node) { + return node; + } + + @Override + public Node visit(Variable node) { + return node; + } + + @Override + public Node visit(VarList node) { + return node; + } + + @Override + public Node visit(While node) { + return node; + } + + @Override + public void enterBlock(Block block) { + + } + + @Override + public void exitBlock(Block block) { + + } +} diff --git a/src/ASTNodes/Assign.java b/src/ASTNodes/Assign.java new file mode 100644 index 0000000..6fbc279 --- /dev/null +++ b/src/ASTNodes/Assign.java @@ -0,0 +1,68 @@ +package ASTNodes; + +import java.util.ArrayList; +import java.util.List; + +public class Assign extends Statement { + public VarList vars = new VarList(); + public ExprList exprs = new ExprList(); + + @Override + public String line() { // doesnt work well with Function blocks + return vars.line() + " = " + exprs.line(); + } + + @Override + public List getChildren() { + List children = new ArrayList<>(); + + children.add(vars); + children.add(exprs); + + return children; + } + + @Override + public boolean matches(Node obj) { + if (super.matches(obj)) { + Assign assign = (Assign)obj; + return vars.matches(assign.vars) && exprs.matches(assign.exprs); + } + + return false; + } + + @Override + public Assign clone() { + Assign assign = new Assign(); + + assign.vars = vars.clone(); + assign.exprs = exprs.clone(); + + return assign; + } + + @Override + public String toString() { + return "="; + } + + @Override + public void acceptChildren(IASTVisitor visitor) { + vars = (VarList)vars.accept(visitor); + exprs = (ExprList)exprs.accept(visitor); + } + + @Override + public Node accept(IASTVisitor visitor) { + Node replacement = visitor.visit(this); + + if (replacement != this) { + return replacement; + } + + acceptChildren(visitor); + + return this; + } +} diff --git a/src/ASTNodes/BinaryExpression.java b/src/ASTNodes/BinaryExpression.java new file mode 100644 index 0000000..73aceef --- /dev/null +++ b/src/ASTNodes/BinaryExpression.java @@ -0,0 +1,152 @@ +package ASTNodes; + +import java.util.ArrayList; +import java.util.List; + +public class BinaryExpression extends Expression { + public enum Operator { + INVALID, + + POW, + MUL, + REAL_DIV, + INTEGER_DIV, + MOD, + ADD, + SUB, + STRCAT, + LT, + GT, + LTE, + GTE, + NEQ, + EQ, + LOGICAL_AND, + LOGICAL_OR, + BITWISE_AND, + BITWISE_OR, + BITWISE_NOT, + BITWISE_SHL, + BITWISE_SHR, + } + + public Operator operator; + public Expression left; + public Expression right; + + public BinaryExpression(Operator operator, Expression left, Expression right) { + this.operator = operator; + this.left = left; + this.right = right; + } + + @Override + public String line() { + return "(" + left.line() + " " + this.toString() + " " + right.line() + ")"; + } + + @Override + public List getChildren() { + List children = new ArrayList<>(); + + children.add(left); + children.add(right); + + return children; + } + + @Override + public boolean matches(Node obj) { + if (super.matches(obj)) { + BinaryExpression exp = (BinaryExpression)obj; + return exp.operator == operator && exp.right.matches(right) && exp.left.matches(left); + } + + return false; + } + + @Override + public BinaryExpression clone() { + return new BinaryExpression(operator, left.clone(), right.clone()); + } + + // not complete + public boolean isCommutative() { + return operator == Operator.ADD || operator == Operator.MUL; + } + + // not complete + public boolean isAssociative() { + return operator == Operator.ADD || operator == Operator.MUL; + } + + @Override + public String toString() { + switch(operator) { + default: + case INVALID: + return "INVALID"; + case POW: + return "^"; + case MUL: + return "*"; + case REAL_DIV: + return "/"; + case INTEGER_DIV: + return "//"; + case MOD: + return "%"; + case ADD: + return "+"; + case SUB: + return "-"; + case STRCAT: + return ".."; + case LT: + return "<"; + case GT: + return ">"; + case LTE: + return "<="; + case GTE: + return ">="; + case NEQ: + return "~="; + case EQ: + return "=="; + case LOGICAL_AND: + return "and"; + case LOGICAL_OR: + return "or"; + case BITWISE_AND: + return "&"; + case BITWISE_OR: + return "|"; + case BITWISE_NOT: + return "~"; + case BITWISE_SHL: + return "<<"; + case BITWISE_SHR: + return ">>"; + } + } + + @Override + public void acceptChildren(IASTVisitor visitor) { + left = (Expression)left.accept(visitor); + right = (Expression)right.accept(visitor); + } + + @Override + public Node accept(IASTVisitor visitor) { + Node replacement = visitor.visit(this); + + if (replacement != this) { + return replacement; + } + + acceptChildren(visitor); + + return this; + } +} diff --git a/src/ASTNodes/Block.java b/src/ASTNodes/Block.java new file mode 100644 index 0000000..ba94950 --- /dev/null +++ b/src/ASTNodes/Block.java @@ -0,0 +1,94 @@ +package ASTNodes; + +import javax.swing.plaf.nimbus.State; +import java.util.ArrayList; +import java.util.List; + +public class Block extends Node { + public List stmts = new ArrayList<>(); + public Return ret; + + // used for optimizing, code gen, etc + public Statement parent; // used for function, for in, for step because they can declare new variables + + @Override + public List getChildren() { + List children = new ArrayList<>(); + + for (Statement stmt : stmts) { + children.add(stmt); + } + + if (ret != null) { + children.add(ret); + } + + return children; + } + + @Override + public boolean matches(Node obj) { + if (super.matches(obj)) { + Block block = (Block)obj; + + if (stmts.size() != block.stmts.size()) { + return false; + } + + for (int i = 0; i < stmts.size(); i++) { + if (!stmts.get(i).matches(block.stmts.get(i))) { + return false; + } + } + + return ret.matches(block.ret); + } + + return false; + } + + @Override + public Block clone() { + Block block = new Block(); + + for (Statement stmt : stmts) { + block.stmts.add((Statement)stmt.clone()); + } + + if (ret != null) { + block.ret = (Return) ret.clone(); + } + + return block; + } + + @Override + public void acceptChildren(IASTVisitor visitor) { + for (int i = 0; i < stmts.size(); i++) { + Node node = stmts.get(i).accept(visitor); + + if (node != stmts.get(i)) { + stmts.set(i, (Statement)node); + } + } + + if (ret != null) { + ret = (Return) ret.accept(visitor); + } + } + + @Override + public Node accept(IASTVisitor visitor) { + visitor.enterBlock(this); + + Node replacement = visitor.visit(this); + + if (replacement == this) { + acceptChildren(visitor); + } + + visitor.exitBlock(this); + + return replacement; + } +} diff --git a/src/ASTNodes/Break.java b/src/ASTNodes/Break.java new file mode 100644 index 0000000..3507e24 --- /dev/null +++ b/src/ASTNodes/Break.java @@ -0,0 +1,20 @@ +package ASTNodes; + +public class Break extends Statement { + @Override + public String line() { + return "break"; + } + + @Override + public Node accept(IASTVisitor visitor) { + visitor.visit(this); + + return this; + } + + @Override + public Break clone() { + return new Break(); + } +} diff --git a/src/ASTNodes/Do.java b/src/ASTNodes/Do.java new file mode 100644 index 0000000..97e5181 --- /dev/null +++ b/src/ASTNodes/Do.java @@ -0,0 +1,56 @@ +package ASTNodes; + +import java.util.ArrayList; +import java.util.List; + +public class Do extends Statement { + public Block block; + + @Override + public String line() { + return "do"; + } + + @Override + public List getChildren() { + List children = new ArrayList<>(); + + children.add(block); + + return children; + } + + @Override + public boolean matches(Node obj) { + if (super.matches(obj)) { + return block.matches(((Do)obj).block); + } + + return false; + } + + @Override + public Do clone() { + Do stmt = new Do(); + stmt.block = block.clone(); + return stmt; + } + + @Override + public void acceptChildren(IASTVisitor visitor) { + block = (Block)block.accept(visitor); + } + + @Override + public Node accept(IASTVisitor visitor) { + Node replacement = visitor.visit(this); + + if (replacement != this) { + return replacement; + } + + acceptChildren(visitor); + + return this; + } +} diff --git a/src/ASTNodes/ExprList.java b/src/ASTNodes/ExprList.java new file mode 100644 index 0000000..3dc68bf --- /dev/null +++ b/src/ASTNodes/ExprList.java @@ -0,0 +1,90 @@ +package ASTNodes; + +import java.util.ArrayList; +import java.util.List; + +public class ExprList extends Expression { + public List exprs = new ArrayList<>(); + + @Override + public String line() { + String s = ""; + + for (int i = 0; i < exprs.size(); i++) { + s += exprs.get(i).line(); + + if (i < exprs.size() - 1) { + s += ", "; + } + } + + return s; + } + + @Override + public List getChildren() { + List children = new ArrayList<>(); + + for (Expression expr : exprs) { + children.add(expr); + } + + return children; + } + + @Override + public boolean matches(Node obj) { + if (super.matches(obj)) { + ExprList list = (ExprList)obj; + + if (exprs.size() != list.exprs.size()) { + return false; + } + + for (int i = 0; i < exprs.size(); i++) { + if (!exprs.get(i).matches(list.exprs.get(i))) { + return false; + } + } + + return true; + } + + return false; + } + + @Override + public ExprList clone() { + ExprList list = new ExprList(); + + for (Expression expr : exprs) { + list.exprs.add(expr.clone()); + } + + return list; + } + + @Override + public void acceptChildren(IASTVisitor visitor) { + for (int i = 0; i < exprs.size(); i++) { + Node node = exprs.get(i).accept(visitor); + + if (node != exprs.get(i)) { + exprs.set(i, (Expression)node); + } + } + } + + @Override + public Node accept(IASTVisitor visitor) { + Node replacement = visitor.visit(this); + + if (replacement != this) { + return replacement; + } + + acceptChildren(visitor); + + return this; + } +} diff --git a/src/ASTNodes/Expression.java b/src/ASTNodes/Expression.java new file mode 100644 index 0000000..97c21d1 --- /dev/null +++ b/src/ASTNodes/Expression.java @@ -0,0 +1,8 @@ +package ASTNodes; + +public class Expression extends Statement { + @Override + public Expression clone() { + return new Expression(); + } +} diff --git a/src/ASTNodes/False.java b/src/ASTNodes/False.java new file mode 100644 index 0000000..82f123a --- /dev/null +++ b/src/ASTNodes/False.java @@ -0,0 +1,18 @@ +package ASTNodes; + +public class False extends Literal { + @Override + public Node accept(IASTVisitor visitor) { + return visitor.visit(this); + } + + @Override + public String line() { + return "false"; + } + + @Override + public False clone() { + return new False(); + } +} diff --git a/src/ASTNodes/ForIn.java b/src/ASTNodes/ForIn.java new file mode 100644 index 0000000..f9d64ec --- /dev/null +++ b/src/ASTNodes/ForIn.java @@ -0,0 +1,68 @@ +package ASTNodes; + +import java.util.ArrayList; +import java.util.List; + +public class ForIn extends Statement { + public NameList names = new NameList(); + public ExprList exprs = new ExprList(); + public Block block; + + @Override + public List getChildren() { + List children = new ArrayList<>(); + + children.add(names); + children.add(exprs); + children.add(block); + + return children; + } + + @Override + public boolean matches(Node obj) { + if (super.matches(obj)) { + ForIn stmt = (ForIn)obj; + return names.matches(stmt.names) && exprs.matches(stmt.exprs) && block.matches(stmt.block); + } + + return false; + } + + @Override + public String line() { + return "for " + names.line() + " in " + exprs.line() + " do"; + } + + @Override + public ForIn clone() { + ForIn stmt = new ForIn(); + + stmt.names = names.clone(); + stmt.exprs = exprs.clone(); + stmt.block = block.clone(); + stmt.block.parent = stmt; + + return stmt; + } + + @Override + public void acceptChildren(IASTVisitor visitor) { + names = (NameList)names.accept(visitor); + exprs = (ExprList)exprs.accept(visitor); + block = (Block)block.accept(visitor); + } + + @Override + public Node accept(IASTVisitor visitor) { + Node replacement = visitor.visit(this); + + if (replacement != this) { + return replacement; + } + + acceptChildren(visitor); + + return this; + } +} diff --git a/src/ASTNodes/ForStep.java b/src/ASTNodes/ForStep.java new file mode 100644 index 0000000..6226493 --- /dev/null +++ b/src/ASTNodes/ForStep.java @@ -0,0 +1,111 @@ +package ASTNodes; + +import java.util.ArrayList; +import java.util.List; + +public class ForStep extends Statement{ + public LuaString iteratorName; + public Expression init; + public Expression condition; + public Expression step; // optional + public Block block; + + @Override + public List getChildren() { + List children = new ArrayList<>(); + + children.add(iteratorName); + children.add(init); + children.add(condition); + + if (step != null) { + children.add(step); + } + + children.add(block); + + return children; + } + + @Override + public boolean matches(Node obj) { + if (super.matches(obj)) { + ForStep stmt = (ForStep)obj; + + if (step == null && stmt.step != null || step != null && stmt.step == null) { + return false; + } + + if (step != null && !step.matches(stmt.step)) { + return false; + } + + return iteratorName.matches(stmt.iteratorName) && + init.matches(stmt.init) && + condition.matches(stmt.condition) && + block.matches(stmt.block); + + } + + return false; + } + + @Override + public String line() { + String s = "for "; + + s += iteratorName + " = " + init.line() + ", " + condition.line(); + + if (step != null) { + s += ", " + step.line(); + } + + s += " do"; + + return s; + } + + @Override + public ForStep clone() { + ForStep stmt = new ForStep(); + + stmt.iteratorName = iteratorName.clone(); + stmt.init = init.clone(); + stmt.condition = condition.clone(); + + if (step != null) { + stmt.step = step.clone(); + } + + stmt.block = block.clone(); + stmt.block.parent = stmt; + + return stmt; + } + + @Override + public void acceptChildren(IASTVisitor visitor) { + iteratorName = (LuaString)iteratorName.accept(visitor); + init = (Expression)init.accept(visitor); + condition = (Expression)condition.accept(visitor); + + if (step != null) { + step = (Expression)step.accept(visitor); + } + + block = (Block)block.accept(visitor); + } + + @Override + public Node accept(IASTVisitor visitor) { + Node replacement = visitor.visit(this); + + if (replacement != this) { + return replacement; + } + + acceptChildren(visitor); + + return this; + } +} diff --git a/src/ASTNodes/Function.java b/src/ASTNodes/Function.java new file mode 100644 index 0000000..4401263 --- /dev/null +++ b/src/ASTNodes/Function.java @@ -0,0 +1,120 @@ +package ASTNodes; + +import java.util.ArrayList; +import java.util.List; + +public class Function extends Expression { + public LuaString name; // function names are optional + public NameList params = new NameList(); + public Block block; + + public enum Type { + LOCAL, + GLOBAL, + EXPR, + } + + public final Type type; + + public Function(Type type) { + this.type = type; + } + + @Override + public List getChildren() { + List children = new ArrayList<>(); + + if (name != null) { + children.add(name); + } + + children.add(params); + children.add(block); + + return children; + } + + @Override + public boolean matches(Node obj) { + if (super.matches(obj)) { + Function fn = (Function)obj; + + if (type != fn.type) { + return false; + } + + if (name == null && fn.name != null || name != null && fn.name == null) { + return false; + } + + if (name != null && !name.matches(fn.name)) { + return false; + } + + return params.matches(fn.params) && block.matches(fn.block); + } + + return false; + } + + @Override + public String line() { + String s = ""; + + if (type == Type.LOCAL) { + s += "local "; + } + + s += "function "; + + if (name != null) { + s += name.line(); + } + + s += "(" + params.line() + ")"; + + return s; + } + + @Override + public Function clone() { + Function func = new Function(type); + + if (name != null) { + func.name = name.clone(); + } + + func.params = params.clone(); + func.block = block.clone(); + + func.block.parent = func; + + return func; + } + + @Override + public void acceptChildren(IASTVisitor visitor) { + Node node; + + if (name != null) { + name = (LuaString) name.accept(visitor); + } + + params = (NameList)params.accept(visitor); + + block = (Block)block.accept(visitor); + } + + @Override + public Node accept(IASTVisitor visitor) { + Node replacement = visitor.visit(this); + + if (replacement != this) { + return replacement; + } + + acceptChildren(visitor); + + return this; + } +} diff --git a/src/ASTNodes/FunctionCall.java b/src/ASTNodes/FunctionCall.java new file mode 100644 index 0000000..8ec22cc --- /dev/null +++ b/src/ASTNodes/FunctionCall.java @@ -0,0 +1,105 @@ +package ASTNodes; + +import java.util.ArrayList; +import java.util.List; + +// Does not include suffix function calls +// eg (0):test()[name] = 0 +public class FunctionCall extends Expression { + public Expression varOrExp; // may also be a literal + public List nameAndArgs = new ArrayList<>(); + + // if the function call occurred in a statement grammar + public boolean isStatement = false; + + @Override + public List getChildren() { + List children = new ArrayList<>(); + + children.add(varOrExp); + + for (NameAndArgs it : nameAndArgs) { + children.add(it); + } + + return children; + } + + @Override + public String line() { + String s = ""; + + s += varOrExp.line(); + + s += "("; + + for (NameAndArgs it : nameAndArgs) { + s += it.line(); + } + + return s + ")"; + } + + @Override + public String toString() { + return "call"; + } + + @Override + public boolean matches(Node obj) { + if (super.matches(obj)) { + FunctionCall call = (FunctionCall)obj; + + if (nameAndArgs.size() != call.nameAndArgs.size()) { + return false; + } + + for (int i = 0; i < nameAndArgs.size(); i++) { + if (!nameAndArgs.get(i).matches(call.nameAndArgs.get(i))) { + return false; + } + } + + return varOrExp.matches(call.varOrExp); + } + + return false; + } + + @Override + public FunctionCall clone() { + FunctionCall call = new FunctionCall(); + + call.varOrExp = varOrExp.clone(); + + for (NameAndArgs it : nameAndArgs) { + call.nameAndArgs.add(it.clone()); + } + + return call; + } + + @Override + public void acceptChildren(IASTVisitor visitor) { + varOrExp = (Expression)varOrExp.accept(visitor); + + for (int i = 0; i < nameAndArgs.size(); i++){ + Node node = nameAndArgs.get(i).accept(visitor); + + nameAndArgs.set(i, (NameAndArgs)node); + } + } + + @Override + public Node accept(IASTVisitor visitor) { + Node replacement = visitor.visit(this); + + if (replacement != this) { + return replacement; + } + + acceptChildren(visitor); + + return this; + } +} diff --git a/src/ASTNodes/GoTo.java b/src/ASTNodes/GoTo.java new file mode 100644 index 0000000..a1da51f --- /dev/null +++ b/src/ASTNodes/GoTo.java @@ -0,0 +1,58 @@ +package ASTNodes; + +import java.util.ArrayList; +import java.util.List; + +public class GoTo extends Statement { + public LuaString name; + + @Override + public List getChildren() { + List children = new ArrayList<>(); + + children.add(name); + + return children; + } + + @Override + public String line() { + return "goto " + name.line(); + } + + @Override + public boolean matches(Node obj) { + if (super.matches(obj)) { + return name.matches(((GoTo)obj).name); + } + + return false; + } + + @Override + public GoTo clone() { + GoTo stmt = new GoTo(); + + stmt.name = name.clone(); + + return stmt; + } + + @Override + public void acceptChildren(IASTVisitor visitor) { + name = (LuaString)name.accept(visitor); + } + + @Override + public Node accept(IASTVisitor visitor) { + Node replacement = visitor.visit(this); + + if (replacement != this) { + return replacement; + } + + acceptChildren(visitor); + + return this; + } +} diff --git a/src/ASTNodes/IASTVisitor.java b/src/ASTNodes/IASTVisitor.java new file mode 100644 index 0000000..82bf51e --- /dev/null +++ b/src/ASTNodes/IASTVisitor.java @@ -0,0 +1,73 @@ +package ASTNodes; + +public interface IASTVisitor { + public Node visit(Assign node); + + public Node visit(BinaryExpression node); + + public Node visit(Block node); + + public Node visit(Break node); + + public Node visit(Do node); + + public Node visit(Expression node); + + public Node visit(ExprList node); + + public Node visit(False node); + + public Node visit(ForIn node); + + public Node visit(ForStep node); + + public Node visit(Function node); + + public Node visit(FunctionCall node); + + public Node visit(GoTo node); + + public Node visit(If node); + + public Node visit(Label node); + + public Node visit(Literal node); + + public Node visit(LocalDeclare node); + + public Node visit(LuaString node); + + public Node visit(NameAndArgs node); + + public Node visit(NameList node); + + public Node visit(Nil node); + + public Node visit(Node node); + + public Node visit(Number node); + + public Node visit(Pair node); + + public Node visit(Repeat node); + + public Node visit(Return node); + + public Node visit(Suffix node); + + public Node visit(TableConstructor node); + + public Node visit(True node); + + public Node visit(UnaryExpression node); + + public Node visit(Variable node); + + public Node visit(VarList node); + + public Node visit(While node); + + public void enterBlock(Block block); + + public void exitBlock(Block block); +} diff --git a/src/ASTNodes/If.java b/src/ASTNodes/If.java new file mode 100644 index 0000000..860dab8 --- /dev/null +++ b/src/ASTNodes/If.java @@ -0,0 +1,109 @@ +package ASTNodes; + +import java.util.ArrayList; +import java.util.List; + +public class If extends Statement { + public Pair ifstmt; + public List> elseifstmt = new ArrayList<>(); + public Block elsestmt; + + // cannot be represented on a line, call line() on members + + @Override + public List getChildren() { + List children = new ArrayList<>(); + + children.add(ifstmt); + + for (Pair p : elseifstmt) { + children.add(p); + } + + if (elsestmt != null) { + children.add(elsestmt); + } + + return children; + } + + @Override + public boolean matches(Node obj) { + if (super.matches(obj)) { + If stmt = (If)obj; + + if (elsestmt == null && stmt.elsestmt != null || elsestmt != null && stmt.elsestmt == null) { + return false; + } + + if(elsestmt != null && !elsestmt.matches(stmt.elsestmt)) { + return false; + } + + if (!ifstmt.matches(stmt.ifstmt)) { + return false; + } + + if (elseifstmt.size() != stmt.elseifstmt.size()) { + return false; + } + + for (int i = 0; i < elseifstmt.size(); i++) { + if (!elseifstmt.get(i).matches(stmt.elseifstmt.get(i))) { + return false; + } + } + + return true; + } + + return false; + } + + @Override + public If clone() { + If stmt = new If(); + + stmt.ifstmt = ifstmt.clone(); + + for (Pair p : elseifstmt) { + stmt.elseifstmt.add(p.clone()); + } + + if (elsestmt != null) { + stmt.elsestmt = elsestmt.clone(); + } + + return stmt; + } + + @Override + public void acceptChildren(IASTVisitor visitor) { + ifstmt = (Pair)ifstmt.accept(visitor); + + for (int i = 0; i < elseifstmt.size(); i++) { + Node node = elseifstmt.get(i).accept(visitor); + + if (node != elseifstmt) { + elseifstmt.set(i, (Pair)node); + } + } + + if (elsestmt != null) { + elsestmt = (Block) elsestmt.accept(visitor); + } + } + + @Override + public Node accept(IASTVisitor visitor) { + Node replacement = visitor.visit(this); + + if (replacement != this) { + return replacement; + } + + acceptChildren(visitor); + + return this; + } + } diff --git a/src/ASTNodes/Label.java b/src/ASTNodes/Label.java new file mode 100644 index 0000000..a8b6cfc --- /dev/null +++ b/src/ASTNodes/Label.java @@ -0,0 +1,57 @@ +package ASTNodes; + +import java.util.ArrayList; +import java.util.List; + +public class Label extends Statement { + public LuaString name; + + @Override + public List getChildren() { + List children = new ArrayList<>(); + + children.add(name); + + return children; + } + + @Override + public String line() { + return "::" + name.line() + "::"; + } + + @Override + public boolean matches(Node obj) { + if (super.matches(obj)) { + return name.matches(((Label)obj).name); + } + + return false; + } + + @Override + public Label clone() { + Label label = new Label(); + label.name = name.clone(); + + return label; + } + + @Override + public void acceptChildren(IASTVisitor visitor) { + name = (LuaString)name.accept(visitor); + } + + @Override + public Node accept(IASTVisitor visitor) { + Node replacement = visitor.visit(this); + + if (replacement != this) { + return replacement; + } + + acceptChildren(visitor); + + return this; + } +} diff --git a/src/ASTNodes/Literal.java b/src/ASTNodes/Literal.java new file mode 100644 index 0000000..00ddfc2 --- /dev/null +++ b/src/ASTNodes/Literal.java @@ -0,0 +1,4 @@ +package ASTNodes; + +public class Literal extends Expression { +} diff --git a/src/ASTNodes/LocalDeclare.java b/src/ASTNodes/LocalDeclare.java new file mode 100644 index 0000000..68bd782 --- /dev/null +++ b/src/ASTNodes/LocalDeclare.java @@ -0,0 +1,76 @@ +package ASTNodes; + +import java.util.ArrayList; +import java.util.List; + +public class LocalDeclare extends Statement { + public NameList names = new NameList(); + public ExprList exprs = new ExprList(); + + @Override + public List getChildren() { + List children = new ArrayList<>(); + + children.add(names); + children.add(exprs); + + return children; + } + + public String toString() { + return "local"; + } + + @Override + public String line() { // doesnt work well with Function blocks + String s = "local "; + + s += names.line(); + + if (exprs.exprs.size() > 0) { + s += " = " + exprs.line(); + } + + return s; + } + + @Override + public boolean matches(Node obj) { + if (super.matches(obj)) { + LocalDeclare stmt = (LocalDeclare)obj; + + return names.matches(stmt.names) && exprs.matches(stmt.exprs); + } + + return false; + } + + @Override + public LocalDeclare clone() { + LocalDeclare stmt = new LocalDeclare(); + + stmt.names = names.clone(); + stmt.exprs = exprs.clone(); + + return stmt; + } + + @Override + public void acceptChildren(IASTVisitor visitor) { + names = (NameList)names.accept(visitor); + exprs = (ExprList)exprs.accept(visitor); + } + + @Override + public Node accept(IASTVisitor visitor) { + Node replacement = visitor.visit(this); + + if (replacement != this) { + return replacement; + } + + acceptChildren(visitor); + + return this; + } +} diff --git a/src/ASTNodes/LuaString.java b/src/ASTNodes/LuaString.java new file mode 100644 index 0000000..b888eff --- /dev/null +++ b/src/ASTNodes/LuaString.java @@ -0,0 +1,40 @@ +package ASTNodes; + +public class LuaString extends Literal { + public String value; + + public LuaString(java.lang.String literalValue) { + value = literalValue; + } + + public String toString() { + if (value.length() > 128) { + return "<>"; + } + return value; + } + + @Override + public String line() { + return value; + } + + @Override + public boolean matches(Node obj) { + if (super.matches(obj)) { + return value.equals(((LuaString)obj).value); + } + + return false; + } + + @Override + public LuaString clone() { + return new LuaString(value); + } + + @Override + public Node accept(IASTVisitor visitor) { + return visitor.visit(this); + } +} diff --git a/src/ASTNodes/NameAndArgs.java b/src/ASTNodes/NameAndArgs.java new file mode 100644 index 0000000..1207e69 --- /dev/null +++ b/src/ASTNodes/NameAndArgs.java @@ -0,0 +1,80 @@ +package ASTNodes; + +import java.util.ArrayList; +import java.util.List; + +public class NameAndArgs extends Node { + // if name and args are both null then that corresponds to () + public String name; + public Expression args; + + @Override + public List getChildren() { + List children = new ArrayList<>(); + + if (args != null) { + children.add(args); + } + + return children.size() > 0 ? children : null; + } + + @Override + public String line() { + String s = ""; + + if (name != null) { + s += ":" + name; + } + + if (args != null) { + s += args.line(); + } + + return s; + } + + @Override + public boolean matches(Node obj) { + if (super.matches(obj)) { + NameAndArgs node = (NameAndArgs)obj; + + return name.equals(node.name) && args.equals(node.args); + } + + return false; + } + + @Override + public NameAndArgs clone() { + NameAndArgs obj = new NameAndArgs(); + + obj.name = name; + + if (args != null) { + obj.args = args.clone(); + } + + return obj; + } + + @Override + public void acceptChildren(IASTVisitor visitor) { + if (args != null) { + args = (Expression) args.accept(visitor); + } + } + + @Override + public Node accept(IASTVisitor visitor) { + Node replacement = visitor.visit(this); + + if (replacement != this) { + return replacement; + } + + acceptChildren(visitor); + + return this; + } +} diff --git a/src/ASTNodes/NameList.java b/src/ASTNodes/NameList.java new file mode 100644 index 0000000..e537a4e --- /dev/null +++ b/src/ASTNodes/NameList.java @@ -0,0 +1,90 @@ +package ASTNodes; + +import java.util.ArrayList; +import java.util.List; + +public class NameList extends Expression { + public List names = new ArrayList<>(); + + @Override + public List getChildren() { + List children = new ArrayList<>(); + + for (LuaString name : names) { + children.add(name); + } + + return children; + } + + @Override + public String line() { + String s = ""; + + for (int i = 0; i < names.size(); i++) { + s += names.get(i).line(); + + if (i < names.size() - 1) { + s += ", "; + } + } + + return s; + } + + @Override + public boolean matches(Node obj) { + if (super.matches(obj)) { + NameList list = (NameList)obj; + + if (names.size() != list.names.size()) { + return false; + } + + for (int i = 0; i < names.size(); i++) { + if (!names.get(i).matches(list.names.get(i))) { + return false; + } + } + + return true; + } + + return false; + } + + @Override + public NameList clone() { + NameList list = new NameList(); + + for (LuaString name : names) { + list.names.add(name.clone()); + } + + return list; + } + + @Override + public void acceptChildren(IASTVisitor visitor) { + for (int i = 0; i < names.size(); i++) { + Node node = names.get(i).accept(visitor); + + if (node != names.get(i)) { + names.set(i, (LuaString)node); + } + } + } + + @Override + public Node accept(IASTVisitor visitor) { + Node replacement = visitor.visit(this); + + if (replacement != this) { + return replacement; + } + + acceptChildren(visitor); + + return this; + } +} diff --git a/src/ASTNodes/Nil.java b/src/ASTNodes/Nil.java new file mode 100644 index 0000000..61056ea --- /dev/null +++ b/src/ASTNodes/Nil.java @@ -0,0 +1,18 @@ +package ASTNodes; + +public class Nil extends Literal { + @Override + public Node accept(IASTVisitor visitor) { + return visitor.visit(this); + } + + @Override + public String line() { + return "nil"; + } + + @Override + public Nil clone() { + return new Nil(); + } +} diff --git a/src/ASTNodes/Node.java b/src/ASTNodes/Node.java new file mode 100644 index 0000000..4909209 --- /dev/null +++ b/src/ASTNodes/Node.java @@ -0,0 +1,121 @@ +package ASTNodes; + +import java.util.ArrayList; +import java.util.List; + +public class Node { + public String line() throws UnsupportedOperationException{ + throw new UnsupportedOperationException(); + } + + public List getChildren() { + return null; + } + + public boolean matches(Node node) { + return node.getClass().equals(this.getClass()); + } + + @Override + public Node clone() { + return new Node(); + } + + @Override + public String toString() { + return getClass().getSimpleName().toLowerCase(); + } + + public String symbol() { + return line(); + } + + public void acceptChildren(IASTVisitor visitor) { + } + + // caller responsible for doing replacement on top level node + public Node accept(IASTVisitor visitor) { + Node replacement = visitor.visit(this); + + if (replacement != this) { + return replacement; + } + + acceptChildren(visitor); + + return this; + } + + public int count(Class type) { + int count = 0; + + List children = getChildren(); + + if (children == null) { + return 0; + } + + for (Node node : children) { + if (type.isInstance(node)) { + ++count; + } + + count += node.count(type); + } + + return count; + } + + public List getChildren(Class type) { + List res = new ArrayList<>(); + + List children = getChildren(); + + if (children != null) { + for (Node child : children) { + if (type.isInstance(child)) { + res.add(child); + } + } + } + + return res; + } + + // only check children, no recursion + public Node child(Class type) { + List children = getChildren(); + + if (children != null) { + for (Node node : children) { + if (type.isInstance(node)) { + return node; + } + } + } + + return null; + } + + // does DFS + public Node first(Class type) { + List children = getChildren(); + + if (children != null) { + for (Node node : children) { + if (type.isInstance(node)) { + return node; + } + else { + Node ret = node.first(type); + + if (ret != null) { + return ret; + } + } + } + } + + return null; + } +} diff --git a/src/ASTNodes/Number.java b/src/ASTNodes/Number.java new file mode 100644 index 0000000..590b496 --- /dev/null +++ b/src/ASTNodes/Number.java @@ -0,0 +1,42 @@ +package ASTNodes; + +public class Number extends Literal { + public final double value; + + public Number(String literalValue) { + value = Double.parseDouble(literalValue); + } + + public Number(double literalValue) { + value = literalValue; + } + + @Override + public String line() { + return toString(); + } + + @Override + public String toString() { + return Double.toString(value); + } + + @Override + public boolean matches(Node obj) { + if (super.matches(obj)) { + return value == ((Number)obj).value; + } + + return false; + } + + @Override + public Number clone() { + return new Number(value); + } + + @Override + public Node accept(IASTVisitor visitor) { + return visitor.visit(this); + } +} diff --git a/src/ASTNodes/Pair.java b/src/ASTNodes/Pair.java new file mode 100644 index 0000000..e28a293 --- /dev/null +++ b/src/ASTNodes/Pair.java @@ -0,0 +1,71 @@ +package ASTNodes; + +import java.util.ArrayList; +import java.util.List; + +public class Pair extends Node { + public K first; + public V second; + + public Pair(K first, V second) { + this.first = first; + this.second = second; + } + + @Override + public boolean matches(Node obj) { + if (super.matches(obj)) { + Pair p = (Pair)obj; + + return first.matches(p.first) && second.matches(p.second); + } + + return false; + } + + @Override + public List getChildren() { + List children = new ArrayList<>(); + + if (first != null) { + children.add(first); + } + + if (second != null) { + children.add(second); + } + + return children; + } + + @Override + public Pair clone() { + K f = first == null ? null : (K)first.clone(); + V s = second == null ? null : (V)second.clone(); + return new Pair(f, s); + } + + @Override + public void acceptChildren(IASTVisitor visitor) { + if (first != null) { + first = (K) first.accept(visitor); + } + + if (second != null) { + second = (V) second.accept(visitor); + } + } + + @Override + public Node accept(IASTVisitor visitor) { + Node replacement = visitor.visit(this); + + if (replacement != this) { + return replacement; + } + + acceptChildren(visitor); + + return this; + } +} diff --git a/src/ASTNodes/Repeat.java b/src/ASTNodes/Repeat.java new file mode 100644 index 0000000..aabf6c2 --- /dev/null +++ b/src/ASTNodes/Repeat.java @@ -0,0 +1,64 @@ +package ASTNodes; + +import java.util.ArrayList; +import java.util.List; + +public class Repeat extends Statement { + public Block block; + public Expression expr; + + @Override + public String line() { + return "repeat"; + } + + @Override + public List getChildren() { + List children = new ArrayList<>(); + + children.add(block); + children.add(expr); + + return children; + } + + @Override + public boolean matches(Node obj) { + if (super.matches(obj)) { + Repeat stmt = (Repeat)obj; + + return block.matches(stmt.block) && expr.matches(stmt.expr); + } + + return false; + } + + @Override + public Repeat clone() { + Repeat stmt = new Repeat(); + + stmt.block = block.clone(); + stmt.expr = expr.clone(); + + return stmt; + } + + @Override + public void acceptChildren(IASTVisitor visitor) { + block = (Block)block.accept(visitor); + expr = (Expression)expr.accept(visitor); + } + + @Override + public Node accept(IASTVisitor visitor) { + Node replacement = visitor.visit(this); + + if (replacement != this) { + return replacement; + } + + acceptChildren(visitor); + + return this; + } +} diff --git a/src/ASTNodes/Return.java b/src/ASTNodes/Return.java new file mode 100644 index 0000000..6cef763 --- /dev/null +++ b/src/ASTNodes/Return.java @@ -0,0 +1,59 @@ +package ASTNodes; + +import java.util.ArrayList; +import java.util.List; + +public class Return extends Node { + // optional + public ExprList exprs = new ExprList(); + + @Override + public String line() { + return "return " + exprs.line(); + } + + @Override + public List getChildren() { + List children = new ArrayList<>(); + + children.add(exprs); + + return children.size() > 0 ? children : null; + } + + @Override + public boolean matches(Node obj) { + if (super.matches(obj)) { + return exprs.matches(((Return)obj).exprs); + } + + return false; + } + + @Override + public Return clone() { + Return ret = new Return(); + + ret.exprs = exprs.clone(); + + return ret; + } + + @Override + public void acceptChildren(IASTVisitor visitor) { + exprs = (ExprList)exprs.accept(visitor); + } + + @Override + public Node accept(IASTVisitor visitor) { + Node replacement = visitor.visit(this); + + if (replacement != this) { + return replacement; + } + + acceptChildren(visitor); + + return this; + } +} diff --git a/src/ASTNodes/Semicolon.java b/src/ASTNodes/Semicolon.java new file mode 100644 index 0000000..e314a60 --- /dev/null +++ b/src/ASTNodes/Semicolon.java @@ -0,0 +1,5 @@ +package ASTNodes; + +// This node isn't added to AST and is effectively ignored +public class Semicolon extends Statement { +} diff --git a/src/ASTNodes/Statement.java b/src/ASTNodes/Statement.java new file mode 100644 index 0000000..71c138a --- /dev/null +++ b/src/ASTNodes/Statement.java @@ -0,0 +1,4 @@ +package ASTNodes; + +public class Statement extends Node { +} diff --git a/src/ASTNodes/Suffix.java b/src/ASTNodes/Suffix.java new file mode 100644 index 0000000..4c2fb98 --- /dev/null +++ b/src/ASTNodes/Suffix.java @@ -0,0 +1,101 @@ +package ASTNodes; + +import java.util.ArrayList; +import java.util.List; + +// Possibly includes a function call +public class Suffix extends Expression { + public List nameAndArgs = new ArrayList<>(); + public Expression expOrName; + + @Override + public String line() { + String s = ""; + + for (NameAndArgs it : nameAndArgs) { + s += it.line(); + } + + if (expOrName instanceof LuaString) { + s += "." + ((LuaString) expOrName).value; + } + else { + s += "[" + expOrName.line() + "]"; + } + + return s; + } + + @Override + public List getChildren() { + List children = new ArrayList<>(); + + for (NameAndArgs it : nameAndArgs) { + children.add(it); + } + + children.add(expOrName); + + return children; + } + + @Override + public boolean matches(Node obj) { + if (super.matches(obj)) { + Suffix s = (Suffix)obj; + + if (nameAndArgs.size() != s.nameAndArgs.size()) { + return false; + } + + for (int i = 0; i < nameAndArgs.size(); i++) { + if (!nameAndArgs.get(i).matches(s.nameAndArgs.get(i))) { + return false; + } + } + + return expOrName.matches(s.expOrName); + } + + return false; + } + + @Override + public Suffix clone() { + Suffix s = new Suffix(); + + for (NameAndArgs it : nameAndArgs) { + s.nameAndArgs.add(it.clone()); + } + + s.expOrName = expOrName.clone(); + + return s; + } + + @Override + public void acceptChildren(IASTVisitor visitor) { + for (int i = 0; i < nameAndArgs.size(); i++) { + Node node = nameAndArgs.get(i).accept(visitor); + + if (node != nameAndArgs.get(i)) { + nameAndArgs.set(i, (NameAndArgs)node); + } + } + + expOrName = (Expression)expOrName.accept(visitor); + } + + @Override + public Node accept(IASTVisitor visitor) { + Node replacement = visitor.visit(this); + + if (replacement != this) { + return replacement; + } + + acceptChildren(visitor); + + return this; + } +} diff --git a/src/ASTNodes/TableConstructor.java b/src/ASTNodes/TableConstructor.java new file mode 100644 index 0000000..88c23da --- /dev/null +++ b/src/ASTNodes/TableConstructor.java @@ -0,0 +1,97 @@ +package ASTNodes; + +import java.util.ArrayList; +import java.util.List; + +public class TableConstructor extends Expression { + public List> entries = new ArrayList<>(); + + @Override + public String line() { + String s = "{ "; + + for (int i = 0; i < entries.size(); i++) { + Pair entry = entries.get(i); + + if (entry.first != null) { + s += "[" + entry.first.line() + "] = "; + } + if (entry.second != null) { + s += entry.second.line(); + } + + if (i < entries.size() - 1) { + s += ", "; + } + } + + return s + " }"; + } + + @Override + public List getChildren() { + List children = new ArrayList<>(); + + for (Pair p : entries) { + children.add(p); + } + + return children; + } + + @Override + public boolean matches(Node obj) { + if (super.matches(obj)) { + TableConstructor ctor = (TableConstructor)obj; + + if (entries.size() != ctor.entries.size()) { + return false; + } + + for (int i = 0; i < entries.size(); i++) { + if (!entries.get(i).matches(ctor.entries.get(i))) { + return false; + } + } + + return true; + } + + return false; + } + + @Override + public TableConstructor clone() { + TableConstructor ctor = new TableConstructor(); + + for (Pair p : entries) { + ctor.entries.add(p.clone()); + } + + return ctor; + } + + @Override + public void acceptChildren(IASTVisitor visitor) { + for (int i = 0; i < entries.size(); i++) { + Node node = entries.get(i).accept(visitor); + + if (node != entries.get(i)) { + entries.set(i, (Pair)node); + } + } + } + + @Override + public Node accept(IASTVisitor visitor) { + Node replacement = visitor.visit(this); + + if (replacement != this) { + return replacement; + } + + acceptChildren(visitor); + + return this; + } +} diff --git a/src/ASTNodes/True.java b/src/ASTNodes/True.java new file mode 100644 index 0000000..7381818 --- /dev/null +++ b/src/ASTNodes/True.java @@ -0,0 +1,18 @@ +package ASTNodes; + +public class True extends Literal { + @Override + public String line() { + return "true"; + } + + @Override + public True clone() { + return new True(); + } + + @Override + public Node accept(IASTVisitor visitor) { + return visitor.visit(this); + } +} diff --git a/src/ASTNodes/UnaryExpression.java b/src/ASTNodes/UnaryExpression.java new file mode 100644 index 0000000..e4bf67a --- /dev/null +++ b/src/ASTNodes/UnaryExpression.java @@ -0,0 +1,87 @@ +package ASTNodes; + +import java.util.ArrayList; +import java.util.List; + +public class UnaryExpression extends Expression { + public enum Operator { + INVALID, + + NOT, + HASHTAG, + MINUS, + TILDE, + } + + public Operator op; + public Expression expr; + + public UnaryExpression(Operator op, Expression expr) { + this.op = op; + this.expr = expr; + } + + @Override + public String line() { + return toString() + expr.line(); + } + + @Override + public List getChildren() { + List children = new ArrayList<>(); + + children.add(expr); + + return children; + } + + public String toString() { + switch(op) { + default: + case INVALID: + return "INVALID"; + case NOT: + return "not "; + case HASHTAG: + return "#"; + case MINUS: + return "-"; + case TILDE: + return "~"; + } + } + + @Override + public boolean matches(Node obj) { + if (super.matches(obj)) { + UnaryExpression exp = (UnaryExpression)obj; + + return op == exp.op && expr.matches(exp.expr); + } + + return false; + } + + @Override + public UnaryExpression clone() { + return new UnaryExpression(op, expr.clone()); + } + + @Override + public void acceptChildren(IASTVisitor visitor) { + expr = (Expression)expr.accept(visitor); + } + + @Override + public Node accept(IASTVisitor visitor) { + Node replacement = visitor.visit(this); + + if (replacement != this) { + return replacement; + } + + acceptChildren(visitor); + + return this; + } +} diff --git a/src/ASTNodes/VarList.java b/src/ASTNodes/VarList.java new file mode 100644 index 0000000..dbecde3 --- /dev/null +++ b/src/ASTNodes/VarList.java @@ -0,0 +1,90 @@ +package ASTNodes; + +import java.util.ArrayList; +import java.util.List; + +public class VarList extends Expression { + public List vars = new ArrayList<>(); + + @Override + public List getChildren() { + List children = new ArrayList<>(); + + for (Expression expr : vars) { + children.add(expr); + } + + return children; + } + + @Override + public String line() { + String s = ""; + + for (int i = 0; i < vars.size(); i++) { + s += vars.get(i).line(); + + if (i < vars.size() - 1) { + s += ", "; + } + } + + return s; + } + + @Override + public boolean matches(Node obj) { + if (super.matches(obj)) { + VarList list = (VarList)obj; + + if (vars.size() != list.vars.size()) { + return false; + } + + for (int i = 0; i < vars.size(); i++) { + if (!vars.get(i).matches(list.vars.get(i))) { + return false; + } + } + + return true; + } + + return false; + } + + @Override + public VarList clone() { + VarList list = new VarList(); + + for (Expression expr : vars) { + list.vars.add(expr.clone()); + } + + return list; + } + + @Override + public void acceptChildren(IASTVisitor visitor) { + for (int i = 0; i < vars.size(); i++) { + Node node = vars.get(i).accept(visitor); + + if (node != vars.get(i)) { + vars.set(i, (Expression)node); + } + } + } + + @Override + public Node accept(IASTVisitor visitor) { + Node replacement = visitor.visit(this); + + if (replacement != this) { + return replacement; + } + + acceptChildren(visitor); + + return this; + } +} diff --git a/src/ASTNodes/Variable.java b/src/ASTNodes/Variable.java new file mode 100644 index 0000000..f5517d1 --- /dev/null +++ b/src/ASTNodes/Variable.java @@ -0,0 +1,109 @@ +package ASTNodes; + +import java.util.ArrayList; +import java.util.List; + +public class Variable extends Expression { + public Expression name; + public List suffixes = new ArrayList<>(); + + @Override + public String symbol() { + if (name instanceof LuaString) { + return name.line(); + } + + return line(); + } + + @Override + public String line() { + String s = ""; + + if (name instanceof LuaString) { + s += name.line(); + } + else { + s += "(" + name.line() + ")"; + } + + for (int i = 0; i < suffixes.size(); i++) { + s += suffixes.get(i).line(); + } + + return s; + } + + @Override + public List getChildren() { + List children = new ArrayList<>(); + + children.add(name); + + for (Suffix suffix : suffixes) { + children.add(suffix); + } + + return children; + } + + @Override + public boolean matches(Node obj) { + if (super.matches(obj)) { + Variable var = (Variable)obj; + + if (suffixes.size() != var.suffixes.size()) { + return false; + } + + for (int i = 0; i < suffixes.size(); i++) { + if (!suffixes.get(i).matches(var.suffixes.get(i))) { + return false; + } + } + + return name.matches(var.name); + } + + return false; + } + + @Override + public Variable clone() { + Variable var = new Variable(); + + var.name = name.clone(); + + for (Suffix suffix : suffixes) { + var.suffixes.add(suffix.clone()); + } + + return var; + } + + @Override + public void acceptChildren(IASTVisitor visitor) { + name = (Expression)name.accept(visitor); + + for (int i = 0; i < suffixes.size(); i++) { + Node node = suffixes.get(i).accept(visitor); + + if (node != suffixes.get(i)) { + suffixes.set(i, (Suffix)node); + } + } + } + + @Override + public Node accept(IASTVisitor visitor) { + Node replacement = visitor.visit(this); + + if (replacement != this) { + return replacement; + } + + acceptChildren(visitor); + + return this; + } +} diff --git a/src/ASTNodes/While.java b/src/ASTNodes/While.java new file mode 100644 index 0000000..8f79dca --- /dev/null +++ b/src/ASTNodes/While.java @@ -0,0 +1,64 @@ +package ASTNodes; + +import java.util.ArrayList; +import java.util.List; + +public class While extends Statement { + public Expression expr; + public Block block; + + @Override + public String line() { + return "while " + expr.line() + " do"; + } + + @Override + public List getChildren() { + List children = new ArrayList<>(); + + children.add(expr); + children.add(block); + + return children; + } + + @Override + public boolean matches(Node obj) { + if (super.matches(obj)) { + While stmt = (While)obj; + + return expr.matches(stmt.expr) && block.matches(stmt.block); + } + + return false; + } + + @Override + public While clone() { + While stmt = new While(); + + stmt.expr = expr.clone(); + stmt.block = block.clone(); + + return stmt; + } + + @Override + public void acceptChildren(IASTVisitor visitor) { + expr = (Expression)expr.accept(visitor); + block = (Block)block.accept(visitor); + } + + @Override + public Node accept(IASTVisitor visitor) { + Node replacement = visitor.visit(this); + + if (replacement != this) { + return replacement; + } + + acceptChildren(visitor); + + return this; + } +} diff --git a/src/ASTOptimizerBase.java b/src/ASTOptimizerBase.java new file mode 100644 index 0000000..b302222 --- /dev/null +++ b/src/ASTOptimizerBase.java @@ -0,0 +1,9 @@ +import ASTNodes.ASTVisitor; + +public class ASTOptimizerBase extends ASTVisitor { + protected ASTOptimizerMgr mgr; + + public void setMgr(ASTOptimizerMgr mgr) { + this.mgr = mgr; + } +} diff --git a/src/ASTOptimizerMgr.java b/src/ASTOptimizerMgr.java new file mode 100644 index 0000000..be2fa69 --- /dev/null +++ b/src/ASTOptimizerMgr.java @@ -0,0 +1,671 @@ +import ASTNodes.*; +import ASTNodes.Number; + +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + +// WARNING: Replacing blocks will mess up symbol tables for renamers. +// Do not replace blocks for renamer routines; + +// Optimizations may result in incorrect code +// their only purpose is to simplify LUA for structural matching purposes + +public class ASTOptimizerMgr { + private List optimizers = new ArrayList<>(); + private boolean renamer; + private Block root; + private int optimizationCount; + + public SymbolTable symbols; + + public ASTOptimizerMgr(Node root) { + renamer = false; + this.root = (Block)root; + } + + public void addOptimizer(ASTOptimizerBase optimizer) { + optimizers.add(optimizer); + optimizer.setMgr(this); + } + + public void addRenamer(ASTOptimizerBase renamer) { + optimizers.add(renamer); + renamer.setMgr(this); + + this.renamer = true; + } + + // returns number of optimizations applied + public int optimizeOneIter() { + optimizationCount = 0; + + // Root symbol table is not associated with any block and should be empty + symbols = new SymbolTable(null); + + // run optimizations + root.accept(new Visitor()); + + // run renamer + if (renamer) { + root.accept(new Renamer()); + } + + return optimizationCount; + } + + // fixed point algorithm + public void optimize() { + while (optimizeOneIter() > 0); + } + + // Rename symbols + private class Renamer extends ASTVisitor { + @Override + public void enterBlock(Block block) { + symbols = symbols.getScope(block); + } + + @Override + public void exitBlock(Block block) { + symbols = symbols.parent; + } + + @Override + public Node visit(Block node) { + if (node.parent instanceof Function) { + Function fn = (Function)node.parent; + + for (LuaString arg : fn.params.names) { + SymbolTable.VarInfo info = symbols.getScopedVariable(arg.symbol()); + + if (info != null && info.rename != null) { + arg.value = info.rename; + } + } + } + else if (node.parent instanceof ForStep) { + ForStep stmt = (ForStep)node.parent; + + SymbolTable.VarInfo info = symbols.getScopedVariable(stmt.iteratorName.symbol()); + + if (info != null && info.rename != null) { + stmt.iteratorName.value = info.rename; + } + } + else if (node.parent instanceof ForIn) { + ForIn stmt = (ForIn)node.parent; + + for (LuaString name : stmt.names.names) { + SymbolTable.VarInfo info = symbols.getScopedVariable(name.symbol()); + + if (info != null && info.rename != null) { + name.value = info.rename; + } + } + } + + return node; + } + + @Override + public Node visit(Variable node) { + if (node.name instanceof LuaString) { + SymbolTable.VarInfo info = symbols.getScopedVariable(node.symbol()); + + if (info != null && info.rename != null) { + ((LuaString)node.name).value = info.rename; + } + } + + return node; + } + + @Override + public Node visit(LuaString node) { + SymbolTable.VarInfo info = symbols.getScopedVariable(node.symbol()); + + if (info != null && info.rename != null) { + node.value = info.rename; + } + + return node; + } + } + + private class Visitor extends ASTVisitor { + private void setSymbol(String name, Expression value) { + symbols.getScopedVariable(name).setValue(value); + } + + @Override + public void enterBlock(Block block) { + symbols = symbols.createChild(block); + } + + @Override + public void exitBlock(Block block) { + symbols = symbols.parent; + } + + @Override + public Node visit(Assign node) { + // Invalidate constants + for (int i = 0; i < node.vars.vars.size(); i++) { + Node n = node.vars.vars.get(i); + + SymbolTable.VarInfo info = symbols.getScopedVariable(n.symbol()); + + if (info != null) { + setSymbol(n.symbol(), null); + } + } + + for (ASTOptimizerBase optimizer : optimizers) { + Node replaced = optimizer.visit(node); + + if (replaced != node) { + ++optimizationCount; + return replaced; + } + } + + for (int i = 0; i < node.vars.vars.size(); i++) { + Node n = node.vars.vars.get(i); + + SymbolTable.VarInfo info = symbols.getScopedVariable(n.symbol()); + + if (info == null) { + symbols.addGlobal(n.symbol(), null); + } + + if (i < node.exprs.exprs.size()) { + setSymbol(n.symbol(), null); + } + } + + return node; + } + + @Override + public Node visit(BinaryExpression node) { + for (ASTOptimizerBase optimizer : optimizers) { + Node replaced = optimizer.visit(node); + + if (replaced != node) { + ++optimizationCount; + return replaced; + } + } + + return node; + } + + @Override + public Node visit(Block node) { + for (ASTOptimizerBase optimizer : optimizers) { + Node replaced = optimizer.visit(node); + + if (replaced != node) { + ++optimizationCount; + return replaced; + } + } + + if (node.parent instanceof Function) { + Function fn = (Function)node.parent; + + for (LuaString arg : fn.params.names) { + symbols.add(arg.symbol(), null); + } + } + else if (node.parent instanceof ForStep) { + ForStep stmt = (ForStep)node.parent; + + symbols.add(stmt.iteratorName.value, null); + } + else if (node.parent instanceof ForIn) { + ForIn stmt = (ForIn)node.parent; + + for (LuaString name : stmt.names.names) { + symbols.add(name.value, null); + } + } + + return node; + } + + @Override + public Node visit(Break node) { + for (ASTOptimizerBase optimizer : optimizers) { + Node replaced = optimizer.visit(node); + + if (replaced != node) { + ++optimizationCount; + return replaced; + } + } + + return node; + } + + @Override + public Node visit(Do node) { + for (ASTOptimizerBase optimizer : optimizers) { + Node replaced = optimizer.visit(node); + + if (replaced != node) { + ++optimizationCount; + return replaced; + } + } + + return node; + } + + @Override + public Node visit(Expression node) { + for (ASTOptimizerBase optimizer : optimizers) { + Node replaced = optimizer.visit(node); + + if (replaced != node) { + ++optimizationCount; + return replaced; + } + } + + return node; + } + + @Override + public Node visit(ExprList node) { + for (ASTOptimizerBase optimizer : optimizers) { + Node replaced = optimizer.visit(node); + + if (replaced != node) { + ++optimizationCount; + return replaced; + } + } + + return node; + } + + @Override + public Node visit(False node) { + for (ASTOptimizerBase optimizer : optimizers) { + Node replaced = optimizer.visit(node); + + if (replaced != node) { + ++optimizationCount; + return replaced; + } + } + + return node; + } + + @Override + public Node visit(ForIn node) { + for (ASTOptimizerBase optimizer : optimizers) { + Node replaced = optimizer.visit(node); + + if (replaced != node) { + ++optimizationCount; + return replaced; + } + } + + return node; + } + + @Override + public Node visit(ForStep node) { + for (ASTOptimizerBase optimizer : optimizers) { + Node replaced = optimizer.visit(node); + + if (replaced != node) { + ++optimizationCount; + return replaced; + } + } + + return node; + } + + @Override + public Node visit(Function node) { + for (ASTOptimizerBase optimizer : optimizers) { + Node replaced = optimizer.visit(node); + + if (replaced != node) { + ++optimizationCount; + return replaced; + } + } + + if (node.name != null) { + symbols.add(node.name.value, node); + } + + return node; + } + + @Override + public Node visit(FunctionCall node) { + for (ASTOptimizerBase optimizer : optimizers) { + Node replaced = optimizer.visit(node); + + if (replaced != node) { + ++optimizationCount; + return replaced; + } + } + + return node; + } + + @Override + public Node visit(GoTo node) { + for (ASTOptimizerBase optimizer : optimizers) { + Node replaced = optimizer.visit(node); + + if (replaced != node) { + ++optimizationCount; + return replaced; + } + } + + return node; + } + + @Override + public Node visit(If node) { + for (ASTOptimizerBase optimizer : optimizers) { + Node replaced = optimizer.visit(node); + + if (replaced != node) { + ++optimizationCount; + return replaced; + } + } + + return node; + } + + @Override + public Node visit(Label node) { + for (ASTOptimizerBase optimizer : optimizers) { + Node replaced = optimizer.visit(node); + + if (replaced != node) { + ++optimizationCount; + return replaced; + } + } + + return node; + } + + @Override + public Node visit(Literal node) { + for (ASTOptimizerBase optimizer : optimizers) { + Node replaced = optimizer.visit(node); + + if (replaced != node) { + ++optimizationCount; + return replaced; + } + } + + return node; + } + + @Override + public Node visit(LocalDeclare node) { + for (ASTOptimizerBase optimizer : optimizers) { + Node replaced = optimizer.visit(node); + + if (replaced != node) { + ++optimizationCount; + return replaced; + } + } + + for (int i = 0; i < node.names.names.size(); i++) { + symbols.add(node.names.names.get(i).symbol(), null); + + if (i < node.exprs.exprs.size()) { + setSymbol(node.names.names.get(i).symbol(), node.exprs.exprs.get(i)); + } + } + + return node; + } + + @Override + public Node visit(LuaString node) { + for (ASTOptimizerBase optimizer : optimizers) { + Node replaced = optimizer.visit(node); + + if (replaced != node) { + ++optimizationCount; + return replaced; + } + } + + return node; + } + + @Override + public Node visit(NameAndArgs node) { + for (ASTOptimizerBase optimizer : optimizers) { + Node replaced = optimizer.visit(node); + + if (replaced != node) { + ++optimizationCount; + return replaced; + } + } + + return node; + } + + @Override + public Node visit(NameList node) { + for (ASTOptimizerBase optimizer : optimizers) { + Node replaced = optimizer.visit(node); + + if (replaced != node) { + ++optimizationCount; + return replaced; + } + } + + return node; + } + + @Override + public Node visit(Nil node) { + for (ASTOptimizerBase optimizer : optimizers) { + Node replaced = optimizer.visit(node); + + if (replaced != node) { + ++optimizationCount; + return replaced; + } + } + + return node; + } + + @Override + public Node visit(Node node) { + for (ASTOptimizerBase optimizer : optimizers) { + Node replaced = optimizer.visit(node); + + if (replaced != node) { + ++optimizationCount; + return replaced; + } + } + + return node; + } + + @Override + public Node visit(Number node) { + for (ASTOptimizerBase optimizer : optimizers) { + Node replaced = optimizer.visit(node); + + if (replaced != node) { + ++optimizationCount; + return replaced; + } + } + + return node; + } + + @Override + public Node visit(Pair node) { + for (ASTOptimizerBase optimizer : optimizers) { + Node replaced = optimizer.visit(node); + + if (replaced != node) { + ++optimizationCount; + return replaced; + } + } + + return node; + } + + @Override + public Node visit(Repeat node) { + for (ASTOptimizerBase optimizer : optimizers) { + Node replaced = optimizer.visit(node); + + if (replaced != node) { + ++optimizationCount; + return replaced; + } + } + + return node; + } + + @Override + public Node visit(Return node) { + for (ASTOptimizerBase optimizer : optimizers) { + Node replaced = optimizer.visit(node); + + if (replaced != node) { + ++optimizationCount; + return replaced; + } + } + + return node; + } + + @Override + public Node visit(Suffix node) { + for (ASTOptimizerBase optimizer : optimizers) { + Node replaced = optimizer.visit(node); + + if (replaced != node) { + ++optimizationCount; + return replaced; + } + } + + return node; + } + + @Override + public Node visit(TableConstructor node) { + for (ASTOptimizerBase optimizer : optimizers) { + Node replaced = optimizer.visit(node); + + if (replaced != node) { + ++optimizationCount; + return replaced; + } + } + + return node; + } + + @Override + public Node visit(True node) { + for (ASTOptimizerBase optimizer : optimizers) { + Node replaced = optimizer.visit(node); + + if (replaced != node) { + ++optimizationCount; + return replaced; + } + } + + return node; + } + + @Override + public Node visit(UnaryExpression node) { + for (ASTOptimizerBase optimizer : optimizers) { + Node replaced = optimizer.visit(node); + + if (replaced != node) { + ++optimizationCount; + return replaced; + } + } + + return node; + } + + @Override + public Node visit(Variable node) { + for (ASTOptimizerBase optimizer : optimizers) { + Node replaced = optimizer.visit(node); + + if (replaced != node) { + ++optimizationCount; + return replaced; + } + } + + return node; + } + + @Override + public Node visit(VarList node) { + for (ASTOptimizerBase optimizer : optimizers) { + Node replaced = optimizer.visit(node); + + if (replaced != node) { + ++optimizationCount; + return replaced; + } + } + + return node; + } + + @Override + public Node visit(While node) { + for (ASTOptimizerBase optimizer : optimizers) { + Node replaced = optimizer.visit(node); + + if (replaced != node) { + ++optimizationCount; + return replaced; + } + } + + return node; + } + } + +} diff --git a/src/ASTSourceGenerator.java b/src/ASTSourceGenerator.java new file mode 100644 index 0000000..bee0ee3 --- /dev/null +++ b/src/ASTSourceGenerator.java @@ -0,0 +1,215 @@ +import ASTNodes.*; +import ASTNodes.Number; + +import java.util.Stack; + +// beautifier at https://goonlinetools.com/lua-beautifier/ + +public class ASTSourceGenerator { + private Block root; + private StringBuilder sb; + private int depth; + + public ASTSourceGenerator(Node root) { + this.root = (Block)root; + } + + public String generate() { + sb = new StringBuilder(); + + depth = 0; + + visitBlock((Block)root); + + return sb.toString(); + } + + private void indent() { + for (int i = 0; i < depth; i++) { + sb.append(" "); + } + } + + private void newline() { + sb.append(System.lineSeparator()); + } + + private void visitBlock(Block block) { + for (Statement stmt : block.stmts) { + buildSource(stmt); + } + } + + private void gen(String s) { + indent(); + sb.append(s); + newline(); + } + + private void genBlock(Block block, boolean end) { + newline(); + ++depth; + visitBlock(block); + if (block.ret != null) { + gen(block.ret.line()); + } + --depth; + if (end) { + gen("end"); + newline(); + } + } + + private void genExprList(ExprList list) { + for (int i = 0; i < list.exprs.size(); i++) { + Expression expr = list.exprs.get(i); + + if (expr instanceof Function) { + sb.append(expr.line()); + genBlock(((Function)expr).block, true); + } + else if (expr instanceof FunctionCall) { + FunctionCall call = (FunctionCall)expr; + + sb.append(call.varOrExp.line()); + + sb.append("("); + + boolean nested = false; + + for (NameAndArgs it : call.nameAndArgs) { + if (it.name == null && it.args instanceof ExprList) { + nested = true; + genExprList((ExprList)it.args); + } + } + + sb.append(")"); + } + else if (expr instanceof TableConstructor) { + TableConstructor ctor = (TableConstructor)expr; + + sb.append("{ "); + + for (int j = 0; j < ctor.entries.size(); j++) { + Pair entry = ctor.entries.get(j); + + if (entry.first != null) { + sb.append("[" + entry.first.line() + "] = "); + } + + if (entry.second != null) { + if (entry.second instanceof Function) { + Function fn = (Function)entry.second; + ExprList temp = new ExprList(); + temp.exprs.add(fn); + genExprList(temp); + } + else { + sb.append(entry.second.line()); + } + } + + if (j < ctor.entries.size() - 1) { + sb.append(", "); + } + } + + sb.append(" }"); + } + else { + sb.append(expr.line()); + } + + if (i < list.exprs.size() - 1) { + sb.append(", "); + } + } + } + + private void genConditional(String token, Pair p) { + indent(); + sb.append(token + " "); + sb.append(p.first.line()); + sb.append(" then"); + genBlock(p.second, false); + } + + private void buildSource(Node node) { + if (node instanceof Assign) { + Assign stmt = (Assign)node; + + indent(); + sb.append(stmt.vars.line() + " = "); + + genExprList(stmt.exprs); + } + else if (node instanceof LocalDeclare) { + LocalDeclare stmt = (LocalDeclare)node; + + indent(); + sb.append("local " + stmt.names.line()); + + if (stmt.exprs.exprs.size() > 0) { + sb.append(" = "); + genExprList(stmt.exprs); + } + } + else if (node instanceof Do) { + gen(node.line()); + genBlock(((Do)node).block, true); + } + else if (node instanceof While) { + gen(node.line()); + genBlock(((While)node).block, true); + } + else if (node instanceof Repeat) { + indent(); + sb.append(node.line()); + genBlock(((Repeat)node).block, false); + gen("until " + ((Repeat)node).expr.line()); + } + else if (node instanceof If) { + If stmt = (If)node; + genConditional("if", stmt.ifstmt); + + for (Pair p : stmt.elseifstmt) { + genConditional("elseif", p); + } + + if (stmt.elsestmt != null) { + indent(); + sb.append("else"); + genBlock(stmt.elsestmt, false); + } + + gen("end"); + } + else if (node instanceof ForStep) { + ForStep stmt = (ForStep) node; + gen(stmt.line()); + genBlock(stmt.block, true); + + } + else if (node instanceof ForIn) { + ForIn stmt = (ForIn) node; + gen(stmt.line()); + genBlock(stmt.block, true); + + } + else if (node instanceof Function) { + Function stmt = (Function) node; + gen(stmt.line()); + genBlock(stmt.block, true); + } + else { + indent(); + sb.append(node.line()); + newline(); + } + + newline(); + + } + +} diff --git a/src/ASTTree.java b/src/ASTTree.java new file mode 100644 index 0000000..ffecef1 --- /dev/null +++ b/src/ASTTree.java @@ -0,0 +1,36 @@ +import org.antlr.v4.runtime.tree.Tree; +import ASTNodes.*; + +import java.util.ArrayList; +import java.util.List; + +public class ASTTree implements Tree { + public Tree parent; + public List children = new ArrayList<>(); + public Node node; + + @Override + public Tree getChild(int i) { + return children.get(i); + } + + @Override + public int getChildCount() { + return children.size(); + } + + @Override + public Tree getParent() { + return parent; + } + + @Override + public Node getPayload() { + return node; + } + + @Override + public String toStringTree() { + return ""; + } +} diff --git a/src/ASTTreeBuilder.java b/src/ASTTreeBuilder.java new file mode 100644 index 0000000..36375a6 --- /dev/null +++ b/src/ASTTreeBuilder.java @@ -0,0 +1,33 @@ +import ASTNodes.*; + +import java.util.List; + +public class ASTTreeBuilder { + + private static ASTTree createASTTree(Node node, ASTTree parent) { + ASTTree tree = new ASTTree(); + + tree.node = node; + tree.parent = parent; + + List children = node.getChildren(); + + if (children != null) { + for (Node childNode : children) { + if (childNode == null) { + System.out.println("WARNING: Encountered null node in AST"); + continue; + } + ASTTree childTree = createASTTree(childNode, tree); + + tree.children.add(childTree); + } + } + + return tree; + } + + public static ASTTree create(Node node) { + return createASTTree(node, null); + } +} diff --git a/src/ASTTreeViewer.java b/src/ASTTreeViewer.java new file mode 100644 index 0000000..9cfe02c --- /dev/null +++ b/src/ASTTreeViewer.java @@ -0,0 +1,33 @@ +import org.antlr.v4.gui.TreeTextProvider; +import org.antlr.v4.gui.TreeViewer; +import org.antlr.v4.runtime.tree.Tree; + +import javax.swing.*; + +public class ASTTreeViewer { + private class ASTTreeTextProvider implements TreeTextProvider { + private final ASTTree tree; + + public ASTTreeTextProvider(ASTTree tree) { + this.tree = tree; + } + + @Override + public String getText(Tree tree) { + return ((ASTTree)tree).node.toString(); + } + } + + private final TreeViewer treeViewer; + + public ASTTreeViewer(ASTTree tree) { + ASTTreeTextProvider provider = new ASTTreeTextProvider(tree); + treeViewer = new TreeViewer(null, tree); + treeViewer.setTreeTextProvider(provider); + treeViewer.setScale(1.5); + } + + public void open() { + treeViewer.open(); + } +} diff --git a/src/BuildASTVisitor.java b/src/BuildASTVisitor.java new file mode 100644 index 0000000..3bf4503 --- /dev/null +++ b/src/BuildASTVisitor.java @@ -0,0 +1,652 @@ +import ASTNodes.*; +import ASTNodes.Number; +import ASTNodes.LuaString; +import org.antlr.v4.runtime.tree.TerminalNode; + +public class BuildASTVisitor extends LuaBaseVisitor { + + @Override + public Node visitChunk(LuaParser.ChunkContext ctx) { + return visitBlock(ctx.block()); + } + + @Override + public Block visitBlock(LuaParser.BlockContext ctx) { + Block block = new Block(); + + for (LuaParser.StatContext stat : ctx.stat()) { + Node node = visit(stat); + if (node instanceof Semicolon) { + continue; + } + block.stmts.add((Statement)node); + } + + if (ctx.retstat() != null) { + block.ret = visitRetstat(ctx.retstat()); + } + + return block; + } + + // Statements + + + @Override + public Semicolon visitStmtSemicolon(LuaParser.StmtSemicolonContext ctx) { + return new Semicolon(); + } + + @Override + public Assign visitStmtAssign(LuaParser.StmtAssignContext ctx) { + Assign assign = new Assign(); + + assign.vars = GetVarList(ctx.varlist()); + assign.exprs = GetExprList(ctx.explist()); + + return assign; + } + + @Override + public FunctionCall visitStmtFuncCall(LuaParser.StmtFuncCallContext ctx) { + FunctionCall call = visitFunctioncall(ctx.functioncall()); + call.isStatement = true; + return call; + } + + @Override + public Label visitStmtLabel(LuaParser.StmtLabelContext ctx) { + Label label = new Label(); + + label.name = new LuaString(ctx.label().NAME().getText()); + + return label; + } + + @Override + public Break visitStmtBreak(LuaParser.StmtBreakContext ctx) { + return new Break(); + } + + @Override + public GoTo visitStmtGoto(LuaParser.StmtGotoContext ctx) { + GoTo stmt = new GoTo(); + stmt.name = new LuaString(ctx.NAME().getText()); + return stmt; + } + + @Override + public Do visitStmtDo(LuaParser.StmtDoContext ctx) { + Do stmt = new Do(); + + stmt.block = visitBlock(ctx.block()); + + return stmt; + } + + @Override + public While visitStmtWhile(LuaParser.StmtWhileContext ctx) { + While stmt = new While(); + + stmt.expr = (Expression)visit(ctx.exp()); + stmt.block = visitBlock(ctx.block()); + + return stmt; + } + + @Override + public Repeat visitStmtRepeat(LuaParser.StmtRepeatContext ctx) { + Repeat stmt = new Repeat(); + + stmt.block = visitBlock(ctx.block()); + stmt.expr = (Expression)visit(ctx.exp()); + + return stmt; + } + + @Override + public If visitStmtIf(LuaParser.StmtIfContext ctx) { + If stmt = new If(); + + Expression expr = (Expression)visit(ctx.ifstmt().exp()); + Block block = visitBlock(ctx.ifstmt().block()); + + stmt.ifstmt = new Pair<>(expr, block); + + int i = 0; + + while (true) { + + if (ctx.elseifstmt().exp(i) == null) { + break; + } + + expr = (Expression)visit(ctx.elseifstmt().exp(i)); + block = visitBlock(ctx.elseifstmt().block(i)); + + stmt.elseifstmt.add(new Pair<>(expr, block)); + + ++i; + } + + + if (ctx.elsestmt().block() != null) { + stmt.elsestmt = visitBlock(ctx.elsestmt().block()); + } + + return stmt; + } + + @Override + public ForStep visitStmtForStep(LuaParser.StmtForStepContext ctx) { + ForStep stmt = new ForStep(); + + stmt.iteratorName = new LuaString(ctx.NAME().getText()); + stmt.init = (Expression)visit(ctx.exp(0)); + stmt.condition = (Expression)visit(ctx.exp(1)); + + if (ctx.exp(2) != null) { + stmt.step = (Expression)visit(ctx.exp(2)); + } + + stmt.block = visitBlock(ctx.block()); + stmt.block.parent = stmt; + + return stmt; + } + + @Override + public ForIn visitStmtForIn(LuaParser.StmtForInContext ctx) { + ForIn stmt = new ForIn(); + + stmt.names = GetNameList(ctx.namelist()); + stmt.exprs = GetExprList(ctx.explist()); + stmt.block = visitBlock(ctx.block()); + stmt.block.parent = stmt; + + return stmt; + } + + @Override + public Function visitStmtFuncDef(LuaParser.StmtFuncDefContext ctx) { + Function func = GetFunctionBody(ctx.funcbody(), Function.Type.GLOBAL); + + func.name = new LuaString(ctx.funcname().getText()); + + return func; + } + + @Override + public Function visitStmtLocalFuncDef(LuaParser.StmtLocalFuncDefContext ctx) { + Function fn = GetFunctionBody(ctx.funcbody(), Function.Type.LOCAL); + + fn.name = new LuaString(ctx.NAME().getText()); + + return fn; + } + + @Override + public LocalDeclare visitStmtLocalDecl(LuaParser.StmtLocalDeclContext ctx) { + LocalDeclare stmt = new LocalDeclare(); + + stmt.names = GetNameList(ctx.namelist()); + + if (ctx.explist() != null) { + stmt.exprs = GetExprList(ctx.explist()); + } + + return stmt; + } + + @Override + public Return visitRetstat(LuaParser.RetstatContext ctx) { + Return ret = new Return(); + + if (ctx.explist() != null) { + ret.exprs = GetExprList(ctx.explist()); + } + + return ret; + } + + // Expressions + + @Override + public Nil visitExpNil(LuaParser.ExpNilContext ctx) { + return new Nil(); + } + + @Override + public False visitExpFalse(LuaParser.ExpFalseContext ctx) { + return new False(); + } + + @Override + public True visitExpTrue(LuaParser.ExpTrueContext ctx) { + return new True(); + } + + @Override + public Number visitExpNumber(LuaParser.ExpNumberContext ctx) { + return new Number(ctx.getText()); + } + + @Override + public LuaString visitString(LuaParser.StringContext ctx) { + return new LuaString(ctx.getText()); + } + + @Override + public LuaString visitExpThreeDots(LuaParser.ExpThreeDotsContext ctx) { + return new LuaString("..."); + } + + @Override + public Function visitExpFuncDef(LuaParser.ExpFuncDefContext ctx) { + return GetFunctionBody(ctx.functiondef().funcbody(), Function.Type.EXPR); + } + + @Override + public Expression visitPrefixexp(LuaParser.PrefixexpContext ctx) { + + // rewrite prefix + // - varOrExp + if (ctx.nameAndArgs().size() == 0) { + if (ctx.varOrExp().var() != null) { + return visitVar(ctx.varOrExp().var()); + } + else if (ctx.varOrExp().exp() != null) { + return (Expression)visit(ctx.varOrExp().exp()); + } + } + + // return call + + FunctionCall prefix = new FunctionCall(); + + prefix.varOrExp = visitVarOrExp(ctx.varOrExp()); + + for (LuaParser.NameAndArgsContext namesAndArgsContext : ctx.nameAndArgs()) { + prefix.nameAndArgs.add(visitNameAndArgs(namesAndArgsContext)); + } + + return prefix; + } + + @Override + public TableConstructor visitTableconstructor(LuaParser.TableconstructorContext ctx) { + return GetTableConstructor(ctx); + } + + @Override + public BinaryExpression visitExpPow(LuaParser.ExpPowContext ctx) { + return new BinaryExpression( + BinaryExpression.Operator.POW, + (Expression)visit(ctx.exp(0)), + (Expression)visit(ctx.exp(1))); + } + + @Override + public UnaryExpression visitExpUnary(LuaParser.ExpUnaryContext ctx) { + String token = ctx.operatorUnary().getText(); + + UnaryExpression.Operator op; + + switch(token) { + case "not": + op = UnaryExpression.Operator.NOT; + break; + case "#": + op = UnaryExpression.Operator.HASHTAG; + break; + case "-": + op = UnaryExpression.Operator.MINUS; + break; + case "~": + op = UnaryExpression.Operator.TILDE; + break; + default: + op = UnaryExpression.Operator.INVALID; + break; + } + + return new UnaryExpression( + op, + (Expression)visit(ctx.exp())); + } + + @Override + public Node visitExpMulDivMod(LuaParser.ExpMulDivModContext ctx) { + String token = ctx.operatorMulDivMod().getText(); + + BinaryExpression.Operator op; + + switch(token) { + case "*": + op = BinaryExpression.Operator.MUL; + break; + case "/": + op = BinaryExpression.Operator.REAL_DIV; + break; + case "%": + op = BinaryExpression.Operator.MOD; + break; + case "//": + op = BinaryExpression.Operator.INTEGER_DIV; + break; + default: + op = BinaryExpression.Operator.INVALID; + break; + } + + return new BinaryExpression( + op, + (Expression)visit(ctx.exp(0)), + (Expression)visit(ctx.exp(1))); + } + + @Override + public BinaryExpression visitExpAddSub(LuaParser.ExpAddSubContext ctx) { + String token = ctx.operatorAddSub().getText(); + + BinaryExpression.Operator op = BinaryExpression.Operator.INVALID; + + if (token.equals("+")) { + op = BinaryExpression.Operator.ADD; + } + else if (token.equals("-")) { + op = BinaryExpression.Operator.SUB; + } + + return new BinaryExpression( + op, + (Expression)visit(ctx.exp(0)), + (Expression)visit(ctx.exp(1))); + } + + @Override + public BinaryExpression visitExpStrcat(LuaParser.ExpStrcatContext ctx) { + return new BinaryExpression( + BinaryExpression.Operator.STRCAT, + (Expression)visit(ctx.exp(0)), + (Expression)visit(ctx.exp(1))); + } + + @Override + public Node visitExpCmp(LuaParser.ExpCmpContext ctx) { + String token = ctx.operatorComparison().getText(); + + BinaryExpression.Operator op; + + switch (token) { + case "<": + op = BinaryExpression.Operator.LT; + break; + case ">": + op = BinaryExpression.Operator.GT; + break; + case "<=": + op = BinaryExpression.Operator.LTE; + break; + case ">=": + op = BinaryExpression.Operator.GTE; + break; + case "~=": + op = BinaryExpression.Operator.NEQ; + break; + case "==": + op = BinaryExpression.Operator.EQ; + break; + default: + op = BinaryExpression.Operator.INVALID; + break; + } + + return new BinaryExpression( + op, + (Expression)visit(ctx.exp(0)), + (Expression)visit(ctx.exp(1))); + } + + @Override + public BinaryExpression visitExpAnd(LuaParser.ExpAndContext ctx) { + return new BinaryExpression( + BinaryExpression.Operator.LOGICAL_AND, + (Expression)visit(ctx.exp(0)), + (Expression)visit(ctx.exp(1))); + } + + @Override + public BinaryExpression visitExpOr(LuaParser.ExpOrContext ctx) { + return new BinaryExpression( + BinaryExpression.Operator.LOGICAL_OR, + (Expression)visit(ctx.exp(0)), + (Expression)visit(ctx.exp(1))); + } + + @Override + public BinaryExpression visitExpBitwise(LuaParser.ExpBitwiseContext ctx) { + String token = ctx.operatorBitwise().getText(); + + BinaryExpression.Operator op; + + switch (token) { + case "&": + op = BinaryExpression.Operator.BITWISE_AND; + break; + case "|": + op = BinaryExpression.Operator.BITWISE_OR; + break; + case "~": + op = BinaryExpression.Operator.BITWISE_NOT; + break; + case "<<": + op = BinaryExpression.Operator.BITWISE_SHL; + break; + case ">>": + op = BinaryExpression.Operator.BITWISE_SHR; + break; + default: + op = BinaryExpression.Operator.INVALID; + break; + } + + return new BinaryExpression( + op, + (Expression)visit(ctx.exp(0)), + (Expression)visit(ctx.exp(1))); + } + + // Misc + + @Override + public FunctionCall visitFunctioncall(LuaParser.FunctioncallContext ctx) { + FunctionCall call = new FunctionCall(); + + call.varOrExp = visitVarOrExp(ctx.varOrExp()); + + for (LuaParser.NameAndArgsContext namesAndArgsContext : ctx.nameAndArgs()) { + call.nameAndArgs.add(visitNameAndArgs(namesAndArgsContext)); + } + + return call; + } + + @Override + public Expression visitVarOrExp(LuaParser.VarOrExpContext ctx) { + Expression exp; + + if (ctx.exp() != null) { + // (exp) + exp = (Expression)visit(ctx.exp()); + } + else { + // var + exp = (Expression)visit(ctx.var()); + } + + return exp; + } + + @Override + public Variable visitVar(LuaParser.VarContext ctx) { + Variable var = new Variable(); + + if (ctx.NAME() != null) { + var.name = new LuaString(ctx.NAME().getText()); + } + else { + var.name = (Expression)visit(ctx.exp()); + } + + for (LuaParser.VarSuffixContext suffixContext : ctx.varSuffix()) { + var.suffixes.add((Suffix)visit(suffixContext)); + } + + return var; + } + + @Override + public Suffix visitVarSuffix(LuaParser.VarSuffixContext ctx) { + Suffix suffix = new Suffix(); + + for (LuaParser.NameAndArgsContext context : ctx.nameAndArgs()) { + suffix.nameAndArgs.add((NameAndArgs)visit(context)); + } + + if (ctx.exp() != null) { + suffix.expOrName = (Expression)visit(ctx.exp()); + } + else { + suffix.expOrName = new LuaString(ctx.NAME().getText()); + } + + return suffix; + } + + @Override + public NameAndArgs visitNameAndArgs(LuaParser.NameAndArgsContext ctx) { + NameAndArgs nameAndArgs = new NameAndArgs(); + + if (ctx.NAME() != null) { + nameAndArgs.name = ctx.NAME().getText(); + } + + nameAndArgs.args = visitArgs(ctx.args()); + + return nameAndArgs; + } + + @Override + public Expression visitArgs(LuaParser.ArgsContext ctx) { + Expression expr = null; + + if (ctx.string() != null) { + expr = new LuaString(ctx.string().getText()); + } + else if (ctx.tableconstructor() != null) { + expr = GetTableConstructor(ctx.tableconstructor()); + } + else if(ctx.explist() != null) { + expr = GetExprList(ctx.explist()); + } + + return expr; + } + + // Helpers + + private VarList GetVarList(LuaParser.VarlistContext ctx) { + VarList list = new VarList(); + + for (LuaParser.VarContext varCtx : ctx.var()) { + list.vars.add((Variable)visit(varCtx)); + } + + return list; + } + + private NameList GetNameList(LuaParser.NamelistContext ctx) { + NameList list = new NameList(); + + for (TerminalNode node : ctx.NAME()) { + list.names.add(new LuaString(node.getText())); + } + + return list; + } + + private ExprList GetExprList(LuaParser.ExplistContext ctx) { + ExprList list = new ExprList(); + + for (LuaParser.ExpContext context : ctx.exp()) { + Node node = visit(context); + list.exprs.add((Expression)node); + } + + return list; + } + + private TableConstructor GetTableConstructor(LuaParser.TableconstructorContext ctx) { + TableConstructor ctor = new TableConstructor(); + + LuaParser.FieldlistContext fieldListCtx = ctx.fieldlist(); + + if (fieldListCtx != null) { + for (LuaParser.FieldContext fieldCtx : fieldListCtx.field()) { + if (fieldCtx.exp(1) != null) { + // [exp] = exp + Expression first = (Expression)visit(fieldCtx.exp(0)); + Expression second = (Expression)visit(fieldCtx.exp(1)); + ctor.entries.add(new Pair<>(first, second)); + } + else if (fieldCtx.NAME() != null) { + // NAME = exp + LuaString first = new LuaString(fieldCtx.NAME().getText()); + Expression second = (Expression)visit(fieldCtx.exp(0)); + ctor.entries.add(new Pair<>(first, second)); + } + else { + // exp + Expression second = (Expression)visit(fieldCtx.exp(0)); + ctor.entries.add(new Pair<>(null, second)); + } + } + } + + return ctor; + } + + private Function GetFunctionBody(LuaParser.FuncbodyContext ctx, Function.Type type) { + Function func = new Function(type); + + if (ctx.parlist() != null) { + + // namelist (',' '...')? | '...' + if (ctx.parlist().namelist() == null) { + func.params.names.add(new LuaString("...")); + } + else { + LuaParser.NamelistContext nameListCtx = ctx.parlist().namelist(); + + int i = 0; + + while (true) { + TerminalNode node = nameListCtx.NAME(i); + if (node != null) { + func.params.names.add(new LuaString(node.toString())); + } else { + break; + } + + ++i; + } + } + } + + func.block = (Block)visit(ctx.block()); + func.block.parent = func; + + return func; + } +} diff --git a/src/CFGOrderer.java b/src/CFGOrderer.java new file mode 100644 index 0000000..18b86b6 --- /dev/null +++ b/src/CFGOrderer.java @@ -0,0 +1,141 @@ +import LuaVM.VMControlFlowGraph; +import LuaVM.VMOp; + +import java.util.*; + +/* + Using Tarjan's algorithm + DFS + Heuristic to rearrange basic blocks correctly + */ + +public class CFGOrderer { + private List cfg; + private VMControlFlowGraph root; + + private Stack stack; + private List order; + private HashMap index; + private HashMap low; + private int dfsNum; + + private enum State { + TO_BE_DONE, + DONE, + NUMBER, + } + + private class IndexState { + public State state; + public int val; + + public IndexState(State state, int val) { + this.state = state; + this.val = val; + } + } + + public CFGOrderer(List cfg, VMControlFlowGraph root) { + this.cfg = cfg; + this.root = root; + reset(); + } + + private void reset() { + this.stack = new Stack<>(); + this.order = new ArrayList<>(); + this.index = new HashMap<>(); + this.low = new HashMap<>(); + this.dfsNum = 0; + } + + // heuristic to order loop constructs correctly + private VMControlFlowGraph selectFromSCC(List scc) { + if (scc.get(0).getLastInstruction().insn.opcode == VMOp.TFORLOOP) { + if (scc.contains(scc.get(0).next)) { + return scc.get(0).next; + } + } + else if (scc.get(0).getLastInstruction().insn.opcode == VMOp.FORLOOP) { + if (scc.contains(scc.get(0).target)) { + return scc.get(0).target; + } + } + + return scc.get(0); + } + + public void visit(VMControlFlowGraph v) { + index.put(v, new IndexState(State.NUMBER, dfsNum)); + low.put(v, dfsNum); + ++dfsNum; + stack.push(v); + + for (VMControlFlowGraph w : v.getChildren()) { + if (!cfg.contains(w)) { + continue; + } + + if (index.get(w).state == State.TO_BE_DONE) { + visit(w); + final int a = low.get(v); + final int b = low.get(w); + low.put(v, Math.min(a, b)); + } + else if (index.get(w).state == State.DONE) { + // Do nothing + } + else { + final int a = low.get(v); + final int b = index.get(w).val; + low.put(v, Math.min(a, b)); + } + } + + final int a = low.get(v); + final int b = index.get(v).val; + + if (a == b) { + VMControlFlowGraph w; + List scc = new ArrayList<>(); + + do { + w = stack.pop(); + scc.add(w); + index.get(w).state = State.DONE; + } while(w != v); + + Collections.reverse(scc); + + if (scc.size() == 1) { + order.add(0, v); + } + else { + CFGOrderer orderer = new CFGOrderer(scc, selectFromSCC(scc)); + List suborder = orderer.getOrder(); + suborder.addAll(order); + + order = suborder; + } + } + + } + + public List getOrder() { + reset(); + + for (VMControlFlowGraph v : cfg) { + index.put(v, new IndexState(State.TO_BE_DONE, -1)); + } + + index.put(root, new IndexState(State.DONE, -1)); + + for (VMControlFlowGraph v : root.getChildren()) { + if (cfg.contains(v) && index.get(v).state == State.TO_BE_DONE) { + visit(v); + } + } + + order.add(0, root); + + return order; + } +} diff --git a/src/ClipboardUtils.java b/src/ClipboardUtils.java new file mode 100644 index 0000000..e264132 --- /dev/null +++ b/src/ClipboardUtils.java @@ -0,0 +1,11 @@ +import java.awt.datatransfer.StringSelection; +import java.awt.Toolkit; +import java.awt.datatransfer.Clipboard; + +public class ClipboardUtils { + public static void set(String s) { + StringSelection stringSelection = new StringSelection(s); + Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); + clipboard.setContents(stringSelection, null); + } +} diff --git a/src/ConstantData.java b/src/ConstantData.java new file mode 100644 index 0000000..01a2516 --- /dev/null +++ b/src/ConstantData.java @@ -0,0 +1,27 @@ +import java.util.Arrays; + +public class ConstantData { + public static boolean isConstant(String name) { + String[] foldableNames = { + "assert", + "select", + "tonumber", + "unpack", + "pcall", + "setfenv", + "setmetatable", + "type", + "getfenv", + "tostring", + "error", + "string.sub", + "string.byte", + "string.char", + "string.rep", + "string.gsub", + "string.match" + }; + + return Arrays.asList(foldableNames).contains(name); + } +} diff --git a/src/LuaBaseListener.java b/src/LuaBaseListener.java new file mode 100644 index 0000000..58f2332 --- /dev/null +++ b/src/LuaBaseListener.java @@ -0,0 +1,864 @@ +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.tree.ErrorNode; +import org.antlr.v4.runtime.tree.TerminalNode; + +/** + * This class provides an empty implementation of {@link LuaListener}, + * which can be extended to create a listener which only needs to handle a subset + * of the available methods. + */ +public class LuaBaseListener implements LuaListener { + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterChunk(LuaParser.ChunkContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitChunk(LuaParser.ChunkContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterBlock(LuaParser.BlockContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitBlock(LuaParser.BlockContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterStmtSemicolon(LuaParser.StmtSemicolonContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitStmtSemicolon(LuaParser.StmtSemicolonContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterStmtAssign(LuaParser.StmtAssignContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitStmtAssign(LuaParser.StmtAssignContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterStmtFuncCall(LuaParser.StmtFuncCallContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitStmtFuncCall(LuaParser.StmtFuncCallContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterStmtLabel(LuaParser.StmtLabelContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitStmtLabel(LuaParser.StmtLabelContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterStmtBreak(LuaParser.StmtBreakContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitStmtBreak(LuaParser.StmtBreakContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterStmtGoto(LuaParser.StmtGotoContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitStmtGoto(LuaParser.StmtGotoContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterStmtDo(LuaParser.StmtDoContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitStmtDo(LuaParser.StmtDoContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterStmtWhile(LuaParser.StmtWhileContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitStmtWhile(LuaParser.StmtWhileContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterStmtRepeat(LuaParser.StmtRepeatContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitStmtRepeat(LuaParser.StmtRepeatContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterStmtIf(LuaParser.StmtIfContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitStmtIf(LuaParser.StmtIfContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterStmtForStep(LuaParser.StmtForStepContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitStmtForStep(LuaParser.StmtForStepContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterStmtForIn(LuaParser.StmtForInContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitStmtForIn(LuaParser.StmtForInContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterStmtFuncDef(LuaParser.StmtFuncDefContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitStmtFuncDef(LuaParser.StmtFuncDefContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterStmtLocalFuncDef(LuaParser.StmtLocalFuncDefContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitStmtLocalFuncDef(LuaParser.StmtLocalFuncDefContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterStmtLocalDecl(LuaParser.StmtLocalDeclContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitStmtLocalDecl(LuaParser.StmtLocalDeclContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterIfstmt(LuaParser.IfstmtContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitIfstmt(LuaParser.IfstmtContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterElseifstmt(LuaParser.ElseifstmtContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitElseifstmt(LuaParser.ElseifstmtContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterElsestmt(LuaParser.ElsestmtContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitElsestmt(LuaParser.ElsestmtContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterRetstat(LuaParser.RetstatContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitRetstat(LuaParser.RetstatContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterLabel(LuaParser.LabelContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitLabel(LuaParser.LabelContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterFuncname(LuaParser.FuncnameContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitFuncname(LuaParser.FuncnameContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterVarlist(LuaParser.VarlistContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitVarlist(LuaParser.VarlistContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterNamelist(LuaParser.NamelistContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitNamelist(LuaParser.NamelistContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterExplist(LuaParser.ExplistContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitExplist(LuaParser.ExplistContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterExpCmp(LuaParser.ExpCmpContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitExpCmp(LuaParser.ExpCmpContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterExpNumber(LuaParser.ExpNumberContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitExpNumber(LuaParser.ExpNumberContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterExpThreeDots(LuaParser.ExpThreeDotsContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitExpThreeDots(LuaParser.ExpThreeDotsContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterExpStrcat(LuaParser.ExpStrcatContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitExpStrcat(LuaParser.ExpStrcatContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterExpTrue(LuaParser.ExpTrueContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitExpTrue(LuaParser.ExpTrueContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterExpOr(LuaParser.ExpOrContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitExpOr(LuaParser.ExpOrContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterExpBitwise(LuaParser.ExpBitwiseContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitExpBitwise(LuaParser.ExpBitwiseContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterExpTableCtor(LuaParser.ExpTableCtorContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitExpTableCtor(LuaParser.ExpTableCtorContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterExpMulDivMod(LuaParser.ExpMulDivModContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitExpMulDivMod(LuaParser.ExpMulDivModContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterExpFuncDef(LuaParser.ExpFuncDefContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitExpFuncDef(LuaParser.ExpFuncDefContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterExpFalse(LuaParser.ExpFalseContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitExpFalse(LuaParser.ExpFalseContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterExpString(LuaParser.ExpStringContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitExpString(LuaParser.ExpStringContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterExpPrefix(LuaParser.ExpPrefixContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitExpPrefix(LuaParser.ExpPrefixContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterExpUnary(LuaParser.ExpUnaryContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitExpUnary(LuaParser.ExpUnaryContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterExpAnd(LuaParser.ExpAndContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitExpAnd(LuaParser.ExpAndContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterExpPow(LuaParser.ExpPowContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitExpPow(LuaParser.ExpPowContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterExpNil(LuaParser.ExpNilContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitExpNil(LuaParser.ExpNilContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterExpAddSub(LuaParser.ExpAddSubContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitExpAddSub(LuaParser.ExpAddSubContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterPrefixexp(LuaParser.PrefixexpContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitPrefixexp(LuaParser.PrefixexpContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterFunctioncall(LuaParser.FunctioncallContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitFunctioncall(LuaParser.FunctioncallContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterVarOrExp(LuaParser.VarOrExpContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitVarOrExp(LuaParser.VarOrExpContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterVar(LuaParser.VarContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitVar(LuaParser.VarContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterVarSuffix(LuaParser.VarSuffixContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitVarSuffix(LuaParser.VarSuffixContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterNameAndArgs(LuaParser.NameAndArgsContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitNameAndArgs(LuaParser.NameAndArgsContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterArgs(LuaParser.ArgsContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitArgs(LuaParser.ArgsContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterFunctiondef(LuaParser.FunctiondefContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitFunctiondef(LuaParser.FunctiondefContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterFuncbody(LuaParser.FuncbodyContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitFuncbody(LuaParser.FuncbodyContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterParlist(LuaParser.ParlistContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitParlist(LuaParser.ParlistContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterTableconstructor(LuaParser.TableconstructorContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitTableconstructor(LuaParser.TableconstructorContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterFieldlist(LuaParser.FieldlistContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitFieldlist(LuaParser.FieldlistContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterField(LuaParser.FieldContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitField(LuaParser.FieldContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterFieldsep(LuaParser.FieldsepContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitFieldsep(LuaParser.FieldsepContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterOperatorOr(LuaParser.OperatorOrContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitOperatorOr(LuaParser.OperatorOrContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterOperatorAnd(LuaParser.OperatorAndContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitOperatorAnd(LuaParser.OperatorAndContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterOperatorComparison(LuaParser.OperatorComparisonContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitOperatorComparison(LuaParser.OperatorComparisonContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterOperatorStrcat(LuaParser.OperatorStrcatContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitOperatorStrcat(LuaParser.OperatorStrcatContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterOperatorAddSub(LuaParser.OperatorAddSubContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitOperatorAddSub(LuaParser.OperatorAddSubContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterOperatorMulDivMod(LuaParser.OperatorMulDivModContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitOperatorMulDivMod(LuaParser.OperatorMulDivModContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterOperatorBitwise(LuaParser.OperatorBitwiseContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitOperatorBitwise(LuaParser.OperatorBitwiseContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterOperatorUnary(LuaParser.OperatorUnaryContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitOperatorUnary(LuaParser.OperatorUnaryContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterOperatorPower(LuaParser.OperatorPowerContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitOperatorPower(LuaParser.OperatorPowerContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterNumber(LuaParser.NumberContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitNumber(LuaParser.NumberContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterString(LuaParser.StringContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitString(LuaParser.StringContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterEveryRule(ParserRuleContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitEveryRule(ParserRuleContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void visitTerminal(TerminalNode node) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void visitErrorNode(ErrorNode node) { } +} \ No newline at end of file diff --git a/src/LuaBaseVisitor.java b/src/LuaBaseVisitor.java new file mode 100644 index 0000000..0b514fd --- /dev/null +++ b/src/LuaBaseVisitor.java @@ -0,0 +1,495 @@ +import org.antlr.v4.runtime.tree.AbstractParseTreeVisitor; + +/** + * This class provides an empty implementation of {@link LuaVisitor}, + * which can be extended to create a visitor which only needs to handle a subset + * of the available methods. + * + * @param The return type of the visit operation. Use {@link Void} for + * operations with no return type. + */ +public class LuaBaseVisitor extends AbstractParseTreeVisitor implements LuaVisitor { + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitChunk(LuaParser.ChunkContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitBlock(LuaParser.BlockContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitStmtSemicolon(LuaParser.StmtSemicolonContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitStmtAssign(LuaParser.StmtAssignContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitStmtFuncCall(LuaParser.StmtFuncCallContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitStmtLabel(LuaParser.StmtLabelContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitStmtBreak(LuaParser.StmtBreakContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitStmtGoto(LuaParser.StmtGotoContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitStmtDo(LuaParser.StmtDoContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitStmtWhile(LuaParser.StmtWhileContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitStmtRepeat(LuaParser.StmtRepeatContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitStmtIf(LuaParser.StmtIfContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitStmtForStep(LuaParser.StmtForStepContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitStmtForIn(LuaParser.StmtForInContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitStmtFuncDef(LuaParser.StmtFuncDefContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitStmtLocalFuncDef(LuaParser.StmtLocalFuncDefContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitStmtLocalDecl(LuaParser.StmtLocalDeclContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitIfstmt(LuaParser.IfstmtContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitElseifstmt(LuaParser.ElseifstmtContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitElsestmt(LuaParser.ElsestmtContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitRetstat(LuaParser.RetstatContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitLabel(LuaParser.LabelContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitFuncname(LuaParser.FuncnameContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitVarlist(LuaParser.VarlistContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitNamelist(LuaParser.NamelistContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitExplist(LuaParser.ExplistContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitExpCmp(LuaParser.ExpCmpContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitExpNumber(LuaParser.ExpNumberContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitExpThreeDots(LuaParser.ExpThreeDotsContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitExpStrcat(LuaParser.ExpStrcatContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitExpTrue(LuaParser.ExpTrueContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitExpOr(LuaParser.ExpOrContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitExpBitwise(LuaParser.ExpBitwiseContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitExpTableCtor(LuaParser.ExpTableCtorContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitExpMulDivMod(LuaParser.ExpMulDivModContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitExpFuncDef(LuaParser.ExpFuncDefContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitExpFalse(LuaParser.ExpFalseContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitExpString(LuaParser.ExpStringContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitExpPrefix(LuaParser.ExpPrefixContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitExpUnary(LuaParser.ExpUnaryContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitExpAnd(LuaParser.ExpAndContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitExpPow(LuaParser.ExpPowContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitExpNil(LuaParser.ExpNilContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitExpAddSub(LuaParser.ExpAddSubContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitPrefixexp(LuaParser.PrefixexpContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitFunctioncall(LuaParser.FunctioncallContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitVarOrExp(LuaParser.VarOrExpContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitVar(LuaParser.VarContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitVarSuffix(LuaParser.VarSuffixContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitNameAndArgs(LuaParser.NameAndArgsContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitArgs(LuaParser.ArgsContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitFunctiondef(LuaParser.FunctiondefContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitFuncbody(LuaParser.FuncbodyContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitParlist(LuaParser.ParlistContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitTableconstructor(LuaParser.TableconstructorContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitFieldlist(LuaParser.FieldlistContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitField(LuaParser.FieldContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitFieldsep(LuaParser.FieldsepContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitOperatorOr(LuaParser.OperatorOrContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitOperatorAnd(LuaParser.OperatorAndContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitOperatorComparison(LuaParser.OperatorComparisonContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitOperatorStrcat(LuaParser.OperatorStrcatContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitOperatorAddSub(LuaParser.OperatorAddSubContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitOperatorMulDivMod(LuaParser.OperatorMulDivModContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitOperatorBitwise(LuaParser.OperatorBitwiseContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitOperatorUnary(LuaParser.OperatorUnaryContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitOperatorPower(LuaParser.OperatorPowerContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitNumber(LuaParser.NumberContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitString(LuaParser.StringContext ctx) { return visitChildren(ctx); } +} \ No newline at end of file diff --git a/src/LuaChunkOptimizer.java b/src/LuaChunkOptimizer.java new file mode 100644 index 0000000..9581dbf --- /dev/null +++ b/src/LuaChunkOptimizer.java @@ -0,0 +1,234 @@ +import LuaVM.*; + +import java.util.*; + +public class LuaChunkOptimizer { + private int line; + + public LuaChunkOptimizer() { + line = 0; + } + + private static List generateFromCfgWithFallthroughJumps(List orderedCfg) { + // Get instructions from CFG and append jumps to handle fallthrough (will be optimized out later if not necessary) + List insns = new ArrayList<>(); + + for (VMControlFlowGraph vertex : orderedCfg) { + for (VMInstructionWrapper wrapper : vertex.block.insns) { + insns.add(wrapper); + } + + VMInstructionWrapper last = vertex.block.insns.get(vertex.block.insns.size() - 1); + + if (vertex.next != null && last.insn.opcode != VMOp.JUMP && last.insn.opcode != VMOp.FORPREP) { + VMInstructionWrapper forcedJump = new VMInstructionWrapper(); + forcedJump.insn = new VMInstruction(); + forcedJump.insn.opcode = VMOp.JUMP; + forcedJump.ref = vertex.next.block; + insns.add(forcedJump); + } + } + + return insns; + } + + private static List generateInstructionRefWrappers(List insns) { + // Convert refs from basic blocks to refs to instructions + List output = new ArrayList<>(); + + for (VMInstructionWrapper wrapper : insns) { + VMInstructionRefWrapper refWrapper = new VMInstructionRefWrapper(); + refWrapper.insn = wrapper.insn; + if (wrapper.ref != null) { + refWrapper.ref = wrapper.ref.insns.get(0).insn; + } + output.add(refWrapper); + } + + return output; + } + + private static HashMap getInstructionPCs(List insns) { + int PC = 0; + + HashMap insnPCs = new HashMap<>(); + + for (VMInstructionRefWrapper insn : insns) { + insnPCs.put(PC, insn); + ++PC; + } + + return insnPCs; + } + + private static HashMap getPCInstructions(List insns) { + int PC = 0; + + HashMap pcInsns = new HashMap<>(); + + for (VMInstructionRefWrapper insn : insns) { + pcInsns.put(insn.insn, PC); + ++PC; + } + + return pcInsns; + } + + private static void fixBranchTargets(List insns) { + int PC = 0; + + HashMap pcInsns = getPCInstructions(insns); + + for (VMInstructionRefWrapper insn : insns) { + ++PC; + + if (insn.insn.isVariableTargetBranch()) { + final int targetPC = pcInsns.get(insn.ref) - PC; + insn.insn.sBx = targetPC; + } + } + } + + // fixed point algorithm + private static int removeZeroBranches(List insns) { + // remove JUMP 0.0's + int numRemoved = 0; + + List fallthroughInsns = Arrays.asList(new VMOp[] { VMOp.EQ, VMOp.LT, VMOp.LTE, VMOp.TEST, VMOp.TESTSET }); + + for (int i = 0; i < insns.size(); i++) { + VMInstructionRefWrapper wrapper = insns.get(i); + + if (wrapper.insn.opcode == VMOp.JUMP && wrapper.insn.sBx == 0) { + + // Don't remove conditional fallthrough branches + if (i > 0 && fallthroughInsns.contains(insns.get(i - 1).insn.opcode)) { + continue; + } + + // set anything referencing this instruction to reference the instruction after it + for (int j = 0; j < insns.size(); j++) { + if (i == j) { + continue; + } + VMInstructionRefWrapper w = insns.get(j); + + if (w.ref == wrapper.insn) { + w.ref = insns.get(i + 1).insn; + } + } + + insns.remove(i); + ++numRemoved; + --i; + } + } + + return numRemoved; + } + + private static List generateInstructionsFromCfg(List orderedCfg) { + List insnsWithFallthroughJumps = generateFromCfgWithFallthroughJumps(orderedCfg); + List insnsWithRefs = generateInstructionRefWrappers(insnsWithFallthroughJumps); + + do { + fixBranchTargets(insnsWithRefs); + } while (removeZeroBranches(insnsWithRefs) > 0); + + List insns = new ArrayList<>(); + + for (VMInstructionRefWrapper wrapper : insnsWithRefs) { + insns.add(wrapper.insn); + } + + return insns; + } + + public static LuaChunk removeClosureAntiSymbExecTrick(LuaChunk chunk) { + // this trick is used to make symbolic execution fail when Lua checks code before execution + // remove it + + if (chunk.prototypes.size() != 1 || chunk.constants.size() != 1) { + return chunk; + } + + VMOp[] matchOps = { VMOp.CLOSURE, VMOp.SETGLOBAL, VMOp.GETGLOBAL, VMOp.CALL, VMOp.RETURN }; + + if (chunk.insns.size() == matchOps.length) { + for (int i = 0; i < matchOps.length; i++) { + if (chunk.insns.get(i).opcode != matchOps[i]) { + return chunk; + } + } + } + + // matched + return chunk.prototypes.get(0); + } + + private void setVarArgFlag(LuaChunk chunk) { + // set to 3 if vararg found + // otherwise set to 2 + + boolean found = false; + + for (VMInstruction insn : chunk.insns) { + if (insn.opcode == VMOp.VARARG) { + found = true; + break; + } + } + + if (found) { + chunk.isVarArgFlag = 3; + } + else { + chunk.isVarArgFlag = 2; + } + } + + private void renameGlobalIntegerRefs(LuaChunk chunk) { + // SetGlobal and GetGlobal require referenced constant string + // luraph replaces that string with a number to index into environment + // Lua symbexec requires the reference be a string, so just find and + // rename such tricks + + for (VMInstruction insn : chunk.insns) { + if (insn.opcode == VMOp.GETGLOBAL || insn.opcode == VMOp.SETGLOBAL) { + int constantIdx = (int)insn.Bx; + + LuaValue ref = chunk.constants.get(constantIdx); + + if (ref.type == LuaValue.Type.NUMBER) { + // change to string, as required by lua + String name = "global_" + Integer.toString((int)ref._nv); + ref.type = LuaValue.Type.STRING; + ref._sv = name; + } + } + } + } + + public void optimize(LuaChunk chunk) { + // note: could propagate debug lines with instructions if we wanted + // but no point as decompiler doesnt need them + chunk.debugLines.clear(); + + List blocks = VMBasicBlock.generateBasicBlocks(chunk.insns); + List cfg = VMControlFlowGraph.generateControlFlowGraph(blocks); + List orderedCfg = new CFGOrderer(cfg, cfg.get(0)).getOrder(); + + List insns = generateInstructionsFromCfg(orderedCfg); + + chunk.insns = insns; + chunk.lineDefined = line++; + + setVarArgFlag(chunk); + + renameGlobalIntegerRefs(chunk); + + for (LuaChunk child : chunk.prototypes) { + optimize(child); + } + } +} diff --git a/src/LuaLexer.java b/src/LuaLexer.java new file mode 100644 index 0000000..d659648 --- /dev/null +++ b/src/LuaLexer.java @@ -0,0 +1,357 @@ +import org.antlr.v4.runtime.Lexer; +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.TokenStream; +import org.antlr.v4.runtime.*; +import org.antlr.v4.runtime.atn.*; +import org.antlr.v4.runtime.dfa.DFA; +import org.antlr.v4.runtime.misc.*; + +@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast"}) +public class LuaLexer extends Lexer { + static { RuntimeMetaData.checkVersion("4.8", RuntimeMetaData.VERSION); } + + protected static final DFA[] _decisionToDFA; + protected static final PredictionContextCache _sharedContextCache = + new PredictionContextCache(); + public static final int + T__0=1, T__1=2, T__2=3, T__3=4, T__4=5, T__5=6, T__6=7, T__7=8, T__8=9, + T__9=10, T__10=11, T__11=12, T__12=13, T__13=14, T__14=15, T__15=16, T__16=17, + T__17=18, T__18=19, T__19=20, T__20=21, T__21=22, T__22=23, T__23=24, + T__24=25, T__25=26, T__26=27, T__27=28, T__28=29, T__29=30, T__30=31, + T__31=32, T__32=33, T__33=34, T__34=35, T__35=36, T__36=37, T__37=38, + T__38=39, T__39=40, T__40=41, T__41=42, T__42=43, T__43=44, T__44=45, + T__45=46, T__46=47, T__47=48, T__48=49, T__49=50, T__50=51, T__51=52, + T__52=53, T__53=54, T__54=55, NAME=56, NORMALSTRING=57, CHARSTRING=58, + LONGSTRING=59, INT=60, HEX=61, FLOAT=62, HEX_FLOAT=63, COMMENT=64, LINE_COMMENT=65, + WS=66, SHEBANG=67; + public static String[] channelNames = { + "DEFAULT_TOKEN_CHANNEL", "HIDDEN" + }; + + public static String[] modeNames = { + "DEFAULT_MODE" + }; + + private static String[] makeRuleNames() { + return new String[] { + "T__0", "T__1", "T__2", "T__3", "T__4", "T__5", "T__6", "T__7", "T__8", + "T__9", "T__10", "T__11", "T__12", "T__13", "T__14", "T__15", "T__16", + "T__17", "T__18", "T__19", "T__20", "T__21", "T__22", "T__23", "T__24", + "T__25", "T__26", "T__27", "T__28", "T__29", "T__30", "T__31", "T__32", + "T__33", "T__34", "T__35", "T__36", "T__37", "T__38", "T__39", "T__40", + "T__41", "T__42", "T__43", "T__44", "T__45", "T__46", "T__47", "T__48", + "T__49", "T__50", "T__51", "T__52", "T__53", "T__54", "NAME", "NORMALSTRING", + "CHARSTRING", "LONGSTRING", "NESTED_STR", "INT", "HEX", "FLOAT", "HEX_FLOAT", + "ExponentPart", "HexExponentPart", "EscapeSequence", "DecimalEscape", + "HexEscape", "UtfEscape", "Digit", "HexDigit", "COMMENT", "LINE_COMMENT", + "WS", "SHEBANG" + }; + } + public static final String[] ruleNames = makeRuleNames(); + + private static String[] makeLiteralNames() { + return new String[] { + null, "';'", "'='", "'break'", "'goto'", "'do'", "'end'", "'while'", + "'repeat'", "'until'", "'for'", "','", "'in'", "'function'", "'local'", + "'if'", "'then'", "'elseif'", "'else'", "'return'", "'::'", "'.'", "':'", + "'nil'", "'false'", "'true'", "'...'", "'('", "')'", "'['", "']'", "'{'", + "'}'", "'or'", "'and'", "'<'", "'>'", "'<='", "'>='", "'~='", "'=='", + "'..'", "'+'", "'-'", "'*'", "'/'", "'%'", "'//'", "'&'", "'|'", "'~'", + "'<<'", "'>>'", "'not'", "'#'", "'^'" + }; + } + private static final String[] _LITERAL_NAMES = makeLiteralNames(); + private static String[] makeSymbolicNames() { + return new String[] { + null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, "NAME", "NORMALSTRING", + "CHARSTRING", "LONGSTRING", "INT", "HEX", "FLOAT", "HEX_FLOAT", "COMMENT", + "LINE_COMMENT", "WS", "SHEBANG" + }; + } + private static final String[] _SYMBOLIC_NAMES = makeSymbolicNames(); + public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES); + + /** + * @deprecated Use {@link #VOCABULARY} instead. + */ + @Deprecated + public static final String[] tokenNames; + static { + tokenNames = new String[_SYMBOLIC_NAMES.length]; + for (int i = 0; i < tokenNames.length; i++) { + tokenNames[i] = VOCABULARY.getLiteralName(i); + if (tokenNames[i] == null) { + tokenNames[i] = VOCABULARY.getSymbolicName(i); + } + + if (tokenNames[i] == null) { + tokenNames[i] = ""; + } + } + } + + @Override + @Deprecated + public String[] getTokenNames() { + return tokenNames; + } + + @Override + + public Vocabulary getVocabulary() { + return VOCABULARY; + } + + + public LuaLexer(CharStream input) { + super(input); + _interp = new LexerATNSimulator(this,_ATN,_decisionToDFA,_sharedContextCache); + } + + @Override + public String getGrammarFileName() { return "Lua.g4"; } + + @Override + public String[] getRuleNames() { return ruleNames; } + + @Override + public String getSerializedATN() { return _serializedATN; } + + @Override + public String[] getChannelNames() { return channelNames; } + + @Override + public String[] getModeNames() { return modeNames; } + + @Override + public ATN getATN() { return _ATN; } + + public static final String _serializedATN = + "\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\2E\u025b\b\1\4\2\t"+ + "\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13"+ + "\t\13\4\f\t\f\4\r\t\r\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22\t\22"+ + "\4\23\t\23\4\24\t\24\4\25\t\25\4\26\t\26\4\27\t\27\4\30\t\30\4\31\t\31"+ + "\4\32\t\32\4\33\t\33\4\34\t\34\4\35\t\35\4\36\t\36\4\37\t\37\4 \t \4!"+ + "\t!\4\"\t\"\4#\t#\4$\t$\4%\t%\4&\t&\4\'\t\'\4(\t(\4)\t)\4*\t*\4+\t+\4"+ + ",\t,\4-\t-\4.\t.\4/\t/\4\60\t\60\4\61\t\61\4\62\t\62\4\63\t\63\4\64\t"+ + "\64\4\65\t\65\4\66\t\66\4\67\t\67\48\t8\49\t9\4:\t:\4;\t;\4<\t<\4=\t="+ + "\4>\t>\4?\t?\4@\t@\4A\tA\4B\tB\4C\tC\4D\tD\4E\tE\4F\tF\4G\tG\4H\tH\4I"+ + "\tI\4J\tJ\4K\tK\4L\tL\4M\tM\3\2\3\2\3\3\3\3\3\4\3\4\3\4\3\4\3\4\3\4\3"+ + "\5\3\5\3\5\3\5\3\5\3\6\3\6\3\6\3\7\3\7\3\7\3\7\3\b\3\b\3\b\3\b\3\b\3\b"+ + "\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\n\3\n\3\n\3\n\3\n\3\n\3\13\3\13\3\13\3"+ + "\13\3\f\3\f\3\r\3\r\3\r\3\16\3\16\3\16\3\16\3\16\3\16\3\16\3\16\3\16\3"+ + "\17\3\17\3\17\3\17\3\17\3\17\3\20\3\20\3\20\3\21\3\21\3\21\3\21\3\21\3"+ + "\22\3\22\3\22\3\22\3\22\3\22\3\22\3\23\3\23\3\23\3\23\3\23\3\24\3\24\3"+ + "\24\3\24\3\24\3\24\3\24\3\25\3\25\3\25\3\26\3\26\3\27\3\27\3\30\3\30\3"+ + "\30\3\30\3\31\3\31\3\31\3\31\3\31\3\31\3\32\3\32\3\32\3\32\3\32\3\33\3"+ + "\33\3\33\3\33\3\34\3\34\3\35\3\35\3\36\3\36\3\37\3\37\3 \3 \3!\3!\3\""+ + "\3\"\3\"\3#\3#\3#\3#\3$\3$\3%\3%\3&\3&\3&\3\'\3\'\3\'\3(\3(\3(\3)\3)\3"+ + ")\3*\3*\3*\3+\3+\3,\3,\3-\3-\3.\3.\3/\3/\3\60\3\60\3\60\3\61\3\61\3\62"+ + "\3\62\3\63\3\63\3\64\3\64\3\64\3\65\3\65\3\65\3\66\3\66\3\66\3\66\3\67"+ + "\3\67\38\38\39\39\79\u015b\n9\f9\169\u015e\139\3:\3:\3:\7:\u0163\n:\f"+ + ":\16:\u0166\13:\3:\3:\3;\3;\3;\7;\u016d\n;\f;\16;\u0170\13;\3;\3;\3<\3"+ + "<\3<\3<\3=\3=\3=\3=\3=\3=\7=\u017e\n=\f=\16=\u0181\13=\3=\5=\u0184\n="+ + "\3>\6>\u0187\n>\r>\16>\u0188\3?\3?\3?\6?\u018e\n?\r?\16?\u018f\3@\6@\u0193"+ + "\n@\r@\16@\u0194\3@\3@\7@\u0199\n@\f@\16@\u019c\13@\3@\5@\u019f\n@\3@"+ + "\3@\6@\u01a3\n@\r@\16@\u01a4\3@\5@\u01a8\n@\3@\6@\u01ab\n@\r@\16@\u01ac"+ + "\3@\3@\5@\u01b1\n@\3A\3A\3A\6A\u01b6\nA\rA\16A\u01b7\3A\3A\7A\u01bc\n"+ + "A\fA\16A\u01bf\13A\3A\5A\u01c2\nA\3A\3A\3A\3A\6A\u01c8\nA\rA\16A\u01c9"+ + "\3A\5A\u01cd\nA\3A\3A\3A\6A\u01d2\nA\rA\16A\u01d3\3A\3A\5A\u01d8\nA\3"+ + "B\3B\5B\u01dc\nB\3B\6B\u01df\nB\rB\16B\u01e0\3C\3C\5C\u01e5\nC\3C\6C\u01e8"+ + "\nC\rC\16C\u01e9\3D\3D\3D\3D\5D\u01f0\nD\3D\3D\3D\3D\5D\u01f6\nD\3E\3"+ + "E\3E\3E\3E\3E\3E\3E\3E\3E\3E\5E\u0203\nE\3F\3F\3F\3F\3F\3G\3G\3G\3G\3"+ + "G\6G\u020f\nG\rG\16G\u0210\3G\3G\3H\3H\3I\3I\3J\3J\3J\3J\3J\3J\3J\3J\3"+ + "J\3K\3K\3K\3K\3K\3K\7K\u0228\nK\fK\16K\u022b\13K\3K\3K\7K\u022f\nK\fK"+ + "\16K\u0232\13K\3K\3K\7K\u0236\nK\fK\16K\u0239\13K\3K\3K\7K\u023d\nK\f"+ + "K\16K\u0240\13K\5K\u0242\nK\3K\3K\3K\5K\u0247\nK\3K\3K\3L\6L\u024c\nL"+ + "\rL\16L\u024d\3L\3L\3M\3M\3M\7M\u0255\nM\fM\16M\u0258\13M\3M\3M\3\u017f"+ + "\2N\3\3\5\4\7\5\t\6\13\7\r\b\17\t\21\n\23\13\25\f\27\r\31\16\33\17\35"+ + "\20\37\21!\22#\23%\24\'\25)\26+\27-\30/\31\61\32\63\33\65\34\67\359\36"+ + ";\37= ?!A\"C#E$G%I&K\'M(O)Q*S+U,W-Y.[/]\60_\61a\62c\63e\64g\65i\66k\67"+ + "m8o9q:s;u}?\177@\u0081A\u0083\2\u0085\2\u0087\2\u0089\2\u008b"+ + "\2\u008d\2\u008f\2\u0091\2\u0093B\u0095C\u0097D\u0099E\3\2\23\5\2C\\a"+ + "ac|\6\2\62;C\\aac|\4\2$$^^\4\2))^^\4\2ZZzz\4\2GGgg\4\2--//\4\2RRrr\f\2"+ + "$$))^^cdhhppttvvxx||\3\2\62\64\3\2\62;\5\2\62;CHch\6\2\f\f\17\17??]]\4"+ + "\2\f\f\17\17\5\2\f\f\17\17]]\4\3\f\f\17\17\5\2\13\f\16\17\"\"\2\u0280"+ + "\2\3\3\2\2\2\2\5\3\2\2\2\2\7\3\2\2\2\2\t\3\2\2\2\2\13\3\2\2\2\2\r\3\2"+ + "\2\2\2\17\3\2\2\2\2\21\3\2\2\2\2\23\3\2\2\2\2\25\3\2\2\2\2\27\3\2\2\2"+ + "\2\31\3\2\2\2\2\33\3\2\2\2\2\35\3\2\2\2\2\37\3\2\2\2\2!\3\2\2\2\2#\3\2"+ + "\2\2\2%\3\2\2\2\2\'\3\2\2\2\2)\3\2\2\2\2+\3\2\2\2\2-\3\2\2\2\2/\3\2\2"+ + "\2\2\61\3\2\2\2\2\63\3\2\2\2\2\65\3\2\2\2\2\67\3\2\2\2\29\3\2\2\2\2;\3"+ + "\2\2\2\2=\3\2\2\2\2?\3\2\2\2\2A\3\2\2\2\2C\3\2\2\2\2E\3\2\2\2\2G\3\2\2"+ + "\2\2I\3\2\2\2\2K\3\2\2\2\2M\3\2\2\2\2O\3\2\2\2\2Q\3\2\2\2\2S\3\2\2\2\2"+ + "U\3\2\2\2\2W\3\2\2\2\2Y\3\2\2\2\2[\3\2\2\2\2]\3\2\2\2\2_\3\2\2\2\2a\3"+ + "\2\2\2\2c\3\2\2\2\2e\3\2\2\2\2g\3\2\2\2\2i\3\2\2\2\2k\3\2\2\2\2m\3\2\2"+ + "\2\2o\3\2\2\2\2q\3\2\2\2\2s\3\2\2\2\2u\3\2\2\2\2w\3\2\2\2\2{\3\2\2\2\2"+ + "}\3\2\2\2\2\177\3\2\2\2\2\u0081\3\2\2\2\2\u0093\3\2\2\2\2\u0095\3\2\2"+ + "\2\2\u0097\3\2\2\2\2\u0099\3\2\2\2\3\u009b\3\2\2\2\5\u009d\3\2\2\2\7\u009f"+ + "\3\2\2\2\t\u00a5\3\2\2\2\13\u00aa\3\2\2\2\r\u00ad\3\2\2\2\17\u00b1\3\2"+ + "\2\2\21\u00b7\3\2\2\2\23\u00be\3\2\2\2\25\u00c4\3\2\2\2\27\u00c8\3\2\2"+ + "\2\31\u00ca\3\2\2\2\33\u00cd\3\2\2\2\35\u00d6\3\2\2\2\37\u00dc\3\2\2\2"+ + "!\u00df\3\2\2\2#\u00e4\3\2\2\2%\u00eb\3\2\2\2\'\u00f0\3\2\2\2)\u00f7\3"+ + "\2\2\2+\u00fa\3\2\2\2-\u00fc\3\2\2\2/\u00fe\3\2\2\2\61\u0102\3\2\2\2\63"+ + "\u0108\3\2\2\2\65\u010d\3\2\2\2\67\u0111\3\2\2\29\u0113\3\2\2\2;\u0115"+ + "\3\2\2\2=\u0117\3\2\2\2?\u0119\3\2\2\2A\u011b\3\2\2\2C\u011d\3\2\2\2E"+ + "\u0120\3\2\2\2G\u0124\3\2\2\2I\u0126\3\2\2\2K\u0128\3\2\2\2M\u012b\3\2"+ + "\2\2O\u012e\3\2\2\2Q\u0131\3\2\2\2S\u0134\3\2\2\2U\u0137\3\2\2\2W\u0139"+ + "\3\2\2\2Y\u013b\3\2\2\2[\u013d\3\2\2\2]\u013f\3\2\2\2_\u0141\3\2\2\2a"+ + "\u0144\3\2\2\2c\u0146\3\2\2\2e\u0148\3\2\2\2g\u014a\3\2\2\2i\u014d\3\2"+ + "\2\2k\u0150\3\2\2\2m\u0154\3\2\2\2o\u0156\3\2\2\2q\u0158\3\2\2\2s\u015f"+ + "\3\2\2\2u\u0169\3\2\2\2w\u0173\3\2\2\2y\u0183\3\2\2\2{\u0186\3\2\2\2}"+ + "\u018a\3\2\2\2\177\u01b0\3\2\2\2\u0081\u01d7\3\2\2\2\u0083\u01d9\3\2\2"+ + "\2\u0085\u01e2\3\2\2\2\u0087\u01f5\3\2\2\2\u0089\u0202\3\2\2\2\u008b\u0204"+ + "\3\2\2\2\u008d\u0209\3\2\2\2\u008f\u0214\3\2\2\2\u0091\u0216\3\2\2\2\u0093"+ + "\u0218\3\2\2\2\u0095\u0221\3\2\2\2\u0097\u024b\3\2\2\2\u0099\u0251\3\2"+ + "\2\2\u009b\u009c\7=\2\2\u009c\4\3\2\2\2\u009d\u009e\7?\2\2\u009e\6\3\2"+ + "\2\2\u009f\u00a0\7d\2\2\u00a0\u00a1\7t\2\2\u00a1\u00a2\7g\2\2\u00a2\u00a3"+ + "\7c\2\2\u00a3\u00a4\7m\2\2\u00a4\b\3\2\2\2\u00a5\u00a6\7i\2\2\u00a6\u00a7"+ + "\7q\2\2\u00a7\u00a8\7v\2\2\u00a8\u00a9\7q\2\2\u00a9\n\3\2\2\2\u00aa\u00ab"+ + "\7f\2\2\u00ab\u00ac\7q\2\2\u00ac\f\3\2\2\2\u00ad\u00ae\7g\2\2\u00ae\u00af"+ + "\7p\2\2\u00af\u00b0\7f\2\2\u00b0\16\3\2\2\2\u00b1\u00b2\7y\2\2\u00b2\u00b3"+ + "\7j\2\2\u00b3\u00b4\7k\2\2\u00b4\u00b5\7n\2\2\u00b5\u00b6\7g\2\2\u00b6"+ + "\20\3\2\2\2\u00b7\u00b8\7t\2\2\u00b8\u00b9\7g\2\2\u00b9\u00ba\7r\2\2\u00ba"+ + "\u00bb\7g\2\2\u00bb\u00bc\7c\2\2\u00bc\u00bd\7v\2\2\u00bd\22\3\2\2\2\u00be"+ + "\u00bf\7w\2\2\u00bf\u00c0\7p\2\2\u00c0\u00c1\7v\2\2\u00c1\u00c2\7k\2\2"+ + "\u00c2\u00c3\7n\2\2\u00c3\24\3\2\2\2\u00c4\u00c5\7h\2\2\u00c5\u00c6\7"+ + "q\2\2\u00c6\u00c7\7t\2\2\u00c7\26\3\2\2\2\u00c8\u00c9\7.\2\2\u00c9\30"+ + "\3\2\2\2\u00ca\u00cb\7k\2\2\u00cb\u00cc\7p\2\2\u00cc\32\3\2\2\2\u00cd"+ + "\u00ce\7h\2\2\u00ce\u00cf\7w\2\2\u00cf\u00d0\7p\2\2\u00d0\u00d1\7e\2\2"+ + "\u00d1\u00d2\7v\2\2\u00d2\u00d3\7k\2\2\u00d3\u00d4\7q\2\2\u00d4\u00d5"+ + "\7p\2\2\u00d5\34\3\2\2\2\u00d6\u00d7\7n\2\2\u00d7\u00d8\7q\2\2\u00d8\u00d9"+ + "\7e\2\2\u00d9\u00da\7c\2\2\u00da\u00db\7n\2\2\u00db\36\3\2\2\2\u00dc\u00dd"+ + "\7k\2\2\u00dd\u00de\7h\2\2\u00de \3\2\2\2\u00df\u00e0\7v\2\2\u00e0\u00e1"+ + "\7j\2\2\u00e1\u00e2\7g\2\2\u00e2\u00e3\7p\2\2\u00e3\"\3\2\2\2\u00e4\u00e5"+ + "\7g\2\2\u00e5\u00e6\7n\2\2\u00e6\u00e7\7u\2\2\u00e7\u00e8\7g\2\2\u00e8"+ + "\u00e9\7k\2\2\u00e9\u00ea\7h\2\2\u00ea$\3\2\2\2\u00eb\u00ec\7g\2\2\u00ec"+ + "\u00ed\7n\2\2\u00ed\u00ee\7u\2\2\u00ee\u00ef\7g\2\2\u00ef&\3\2\2\2\u00f0"+ + "\u00f1\7t\2\2\u00f1\u00f2\7g\2\2\u00f2\u00f3\7v\2\2\u00f3\u00f4\7w\2\2"+ + "\u00f4\u00f5\7t\2\2\u00f5\u00f6\7p\2\2\u00f6(\3\2\2\2\u00f7\u00f8\7<\2"+ + "\2\u00f8\u00f9\7<\2\2\u00f9*\3\2\2\2\u00fa\u00fb\7\60\2\2\u00fb,\3\2\2"+ + "\2\u00fc\u00fd\7<\2\2\u00fd.\3\2\2\2\u00fe\u00ff\7p\2\2\u00ff\u0100\7"+ + "k\2\2\u0100\u0101\7n\2\2\u0101\60\3\2\2\2\u0102\u0103\7h\2\2\u0103\u0104"+ + "\7c\2\2\u0104\u0105\7n\2\2\u0105\u0106\7u\2\2\u0106\u0107\7g\2\2\u0107"+ + "\62\3\2\2\2\u0108\u0109\7v\2\2\u0109\u010a\7t\2\2\u010a\u010b\7w\2\2\u010b"+ + "\u010c\7g\2\2\u010c\64\3\2\2\2\u010d\u010e\7\60\2\2\u010e\u010f\7\60\2"+ + "\2\u010f\u0110\7\60\2\2\u0110\66\3\2\2\2\u0111\u0112\7*\2\2\u01128\3\2"+ + "\2\2\u0113\u0114\7+\2\2\u0114:\3\2\2\2\u0115\u0116\7]\2\2\u0116<\3\2\2"+ + "\2\u0117\u0118\7_\2\2\u0118>\3\2\2\2\u0119\u011a\7}\2\2\u011a@\3\2\2\2"+ + "\u011b\u011c\7\177\2\2\u011cB\3\2\2\2\u011d\u011e\7q\2\2\u011e\u011f\7"+ + "t\2\2\u011fD\3\2\2\2\u0120\u0121\7c\2\2\u0121\u0122\7p\2\2\u0122\u0123"+ + "\7f\2\2\u0123F\3\2\2\2\u0124\u0125\7>\2\2\u0125H\3\2\2\2\u0126\u0127\7"+ + "@\2\2\u0127J\3\2\2\2\u0128\u0129\7>\2\2\u0129\u012a\7?\2\2\u012aL\3\2"+ + "\2\2\u012b\u012c\7@\2\2\u012c\u012d\7?\2\2\u012dN\3\2\2\2\u012e\u012f"+ + "\7\u0080\2\2\u012f\u0130\7?\2\2\u0130P\3\2\2\2\u0131\u0132\7?\2\2\u0132"+ + "\u0133\7?\2\2\u0133R\3\2\2\2\u0134\u0135\7\60\2\2\u0135\u0136\7\60\2\2"+ + "\u0136T\3\2\2\2\u0137\u0138\7-\2\2\u0138V\3\2\2\2\u0139\u013a\7/\2\2\u013a"+ + "X\3\2\2\2\u013b\u013c\7,\2\2\u013cZ\3\2\2\2\u013d\u013e\7\61\2\2\u013e"+ + "\\\3\2\2\2\u013f\u0140\7\'\2\2\u0140^\3\2\2\2\u0141\u0142\7\61\2\2\u0142"+ + "\u0143\7\61\2\2\u0143`\3\2\2\2\u0144\u0145\7(\2\2\u0145b\3\2\2\2\u0146"+ + "\u0147\7~\2\2\u0147d\3\2\2\2\u0148\u0149\7\u0080\2\2\u0149f\3\2\2\2\u014a"+ + "\u014b\7>\2\2\u014b\u014c\7>\2\2\u014ch\3\2\2\2\u014d\u014e\7@\2\2\u014e"+ + "\u014f\7@\2\2\u014fj\3\2\2\2\u0150\u0151\7p\2\2\u0151\u0152\7q\2\2\u0152"+ + "\u0153\7v\2\2\u0153l\3\2\2\2\u0154\u0155\7%\2\2\u0155n\3\2\2\2\u0156\u0157"+ + "\7`\2\2\u0157p\3\2\2\2\u0158\u015c\t\2\2\2\u0159\u015b\t\3\2\2\u015a\u0159"+ + "\3\2\2\2\u015b\u015e\3\2\2\2\u015c\u015a\3\2\2\2\u015c\u015d\3\2\2\2\u015d"+ + "r\3\2\2\2\u015e\u015c\3\2\2\2\u015f\u0164\7$\2\2\u0160\u0163\5\u0087D"+ + "\2\u0161\u0163\n\4\2\2\u0162\u0160\3\2\2\2\u0162\u0161\3\2\2\2\u0163\u0166"+ + "\3\2\2\2\u0164\u0162\3\2\2\2\u0164\u0165\3\2\2\2\u0165\u0167\3\2\2\2\u0166"+ + "\u0164\3\2\2\2\u0167\u0168\7$\2\2\u0168t\3\2\2\2\u0169\u016e\7)\2\2\u016a"+ + "\u016d\5\u0087D\2\u016b\u016d\n\5\2\2\u016c\u016a\3\2\2\2\u016c\u016b"+ + "\3\2\2\2\u016d\u0170\3\2\2\2\u016e\u016c\3\2\2\2\u016e\u016f\3\2\2\2\u016f"+ + "\u0171\3\2\2\2\u0170\u016e\3\2\2\2\u0171\u0172\7)\2\2\u0172v\3\2\2\2\u0173"+ + "\u0174\7]\2\2\u0174\u0175\5y=\2\u0175\u0176\7_\2\2\u0176x\3\2\2\2\u0177"+ + "\u0178\7?\2\2\u0178\u0179\5y=\2\u0179\u017a\7?\2\2\u017a\u0184\3\2\2\2"+ + "\u017b\u017f\7]\2\2\u017c\u017e\13\2\2\2\u017d\u017c\3\2\2\2\u017e\u0181"+ + "\3\2\2\2\u017f\u0180\3\2\2\2\u017f\u017d\3\2\2\2\u0180\u0182\3\2\2\2\u0181"+ + "\u017f\3\2\2\2\u0182\u0184\7_\2\2\u0183\u0177\3\2\2\2\u0183\u017b\3\2"+ + "\2\2\u0184z\3\2\2\2\u0185\u0187\5\u008fH\2\u0186\u0185\3\2\2\2\u0187\u0188"+ + "\3\2\2\2\u0188\u0186\3\2\2\2\u0188\u0189\3\2\2\2\u0189|\3\2\2\2\u018a"+ + "\u018b\7\62\2\2\u018b\u018d\t\6\2\2\u018c\u018e\5\u0091I\2\u018d\u018c"+ + "\3\2\2\2\u018e\u018f\3\2\2\2\u018f\u018d\3\2\2\2\u018f\u0190\3\2\2\2\u0190"+ + "~\3\2\2\2\u0191\u0193\5\u008fH\2\u0192\u0191\3\2\2\2\u0193\u0194\3\2\2"+ + "\2\u0194\u0192\3\2\2\2\u0194\u0195\3\2\2\2\u0195\u0196\3\2\2\2\u0196\u019a"+ + "\7\60\2\2\u0197\u0199\5\u008fH\2\u0198\u0197\3\2\2\2\u0199\u019c\3\2\2"+ + "\2\u019a\u0198\3\2\2\2\u019a\u019b\3\2\2\2\u019b\u019e\3\2\2\2\u019c\u019a"+ + "\3\2\2\2\u019d\u019f\5\u0083B\2\u019e\u019d\3\2\2\2\u019e\u019f\3\2\2"+ + "\2\u019f\u01b1\3\2\2\2\u01a0\u01a2\7\60\2\2\u01a1\u01a3\5\u008fH\2\u01a2"+ + "\u01a1\3\2\2\2\u01a3\u01a4\3\2\2\2\u01a4\u01a2\3\2\2\2\u01a4\u01a5\3\2"+ + "\2\2\u01a5\u01a7\3\2\2\2\u01a6\u01a8\5\u0083B\2\u01a7\u01a6\3\2\2\2\u01a7"+ + "\u01a8\3\2\2\2\u01a8\u01b1\3\2\2\2\u01a9\u01ab\5\u008fH\2\u01aa\u01a9"+ + "\3\2\2\2\u01ab\u01ac\3\2\2\2\u01ac\u01aa\3\2\2\2\u01ac\u01ad\3\2\2\2\u01ad"+ + "\u01ae\3\2\2\2\u01ae\u01af\5\u0083B\2\u01af\u01b1\3\2\2\2\u01b0\u0192"+ + "\3\2\2\2\u01b0\u01a0\3\2\2\2\u01b0\u01aa\3\2\2\2\u01b1\u0080\3\2\2\2\u01b2"+ + "\u01b3\7\62\2\2\u01b3\u01b5\t\6\2\2\u01b4\u01b6\5\u0091I\2\u01b5\u01b4"+ + "\3\2\2\2\u01b6\u01b7\3\2\2\2\u01b7\u01b5\3\2\2\2\u01b7\u01b8\3\2\2\2\u01b8"+ + "\u01b9\3\2\2\2\u01b9\u01bd\7\60\2\2\u01ba\u01bc\5\u0091I\2\u01bb\u01ba"+ + "\3\2\2\2\u01bc\u01bf\3\2\2\2\u01bd\u01bb\3\2\2\2\u01bd\u01be\3\2\2\2\u01be"+ + "\u01c1\3\2\2\2\u01bf\u01bd\3\2\2\2\u01c0\u01c2\5\u0085C\2\u01c1\u01c0"+ + "\3\2\2\2\u01c1\u01c2\3\2\2\2\u01c2\u01d8\3\2\2\2\u01c3\u01c4\7\62\2\2"+ + "\u01c4\u01c5\t\6\2\2\u01c5\u01c7\7\60\2\2\u01c6\u01c8\5\u0091I\2\u01c7"+ + "\u01c6\3\2\2\2\u01c8\u01c9\3\2\2\2\u01c9\u01c7\3\2\2\2\u01c9\u01ca\3\2"+ + "\2\2\u01ca\u01cc\3\2\2\2\u01cb\u01cd\5\u0085C\2\u01cc\u01cb\3\2\2\2\u01cc"+ + "\u01cd\3\2\2\2\u01cd\u01d8\3\2\2\2\u01ce\u01cf\7\62\2\2\u01cf\u01d1\t"+ + "\6\2\2\u01d0\u01d2\5\u0091I\2\u01d1\u01d0\3\2\2\2\u01d2\u01d3\3\2\2\2"+ + "\u01d3\u01d1\3\2\2\2\u01d3\u01d4\3\2\2\2\u01d4\u01d5\3\2\2\2\u01d5\u01d6"+ + "\5\u0085C\2\u01d6\u01d8\3\2\2\2\u01d7\u01b2\3\2\2\2\u01d7\u01c3\3\2\2"+ + "\2\u01d7\u01ce\3\2\2\2\u01d8\u0082\3\2\2\2\u01d9\u01db\t\7\2\2\u01da\u01dc"+ + "\t\b\2\2\u01db\u01da\3\2\2\2\u01db\u01dc\3\2\2\2\u01dc\u01de\3\2\2\2\u01dd"+ + "\u01df\5\u008fH\2\u01de\u01dd\3\2\2\2\u01df\u01e0\3\2\2\2\u01e0\u01de"+ + "\3\2\2\2\u01e0\u01e1\3\2\2\2\u01e1\u0084\3\2\2\2\u01e2\u01e4\t\t\2\2\u01e3"+ + "\u01e5\t\b\2\2\u01e4\u01e3\3\2\2\2\u01e4\u01e5\3\2\2\2\u01e5\u01e7\3\2"+ + "\2\2\u01e6\u01e8\5\u008fH\2\u01e7\u01e6\3\2\2\2\u01e8\u01e9\3\2\2\2\u01e9"+ + "\u01e7\3\2\2\2\u01e9\u01ea\3\2\2\2\u01ea\u0086\3\2\2\2\u01eb\u01ec\7^"+ + "\2\2\u01ec\u01f6\t\n\2\2\u01ed\u01ef\7^\2\2\u01ee\u01f0\7\17\2\2\u01ef"+ + "\u01ee\3\2\2\2\u01ef\u01f0\3\2\2\2\u01f0\u01f1\3\2\2\2\u01f1\u01f6\7\f"+ + "\2\2\u01f2\u01f6\5\u0089E\2\u01f3\u01f6\5\u008bF\2\u01f4\u01f6\5\u008d"+ + "G\2\u01f5\u01eb\3\2\2\2\u01f5\u01ed\3\2\2\2\u01f5\u01f2\3\2\2\2\u01f5"+ + "\u01f3\3\2\2\2\u01f5\u01f4\3\2\2\2\u01f6\u0088\3\2\2\2\u01f7\u01f8\7^"+ + "\2\2\u01f8\u0203\5\u008fH\2\u01f9\u01fa\7^\2\2\u01fa\u01fb\5\u008fH\2"+ + "\u01fb\u01fc\5\u008fH\2\u01fc\u0203\3\2\2\2\u01fd\u01fe\7^\2\2\u01fe\u01ff"+ + "\t\13\2\2\u01ff\u0200\5\u008fH\2\u0200\u0201\5\u008fH\2\u0201\u0203\3"+ + "\2\2\2\u0202\u01f7\3\2\2\2\u0202\u01f9\3\2\2\2\u0202\u01fd\3\2\2\2\u0203"+ + "\u008a\3\2\2\2\u0204\u0205\7^\2\2\u0205\u0206\7z\2\2\u0206\u0207\5\u0091"+ + "I\2\u0207\u0208\5\u0091I\2\u0208\u008c\3\2\2\2\u0209\u020a\7^\2\2\u020a"+ + "\u020b\7w\2\2\u020b\u020c\7}\2\2\u020c\u020e\3\2\2\2\u020d\u020f\5\u0091"+ + "I\2\u020e\u020d\3\2\2\2\u020f\u0210\3\2\2\2\u0210\u020e\3\2\2\2\u0210"+ + "\u0211\3\2\2\2\u0211\u0212\3\2\2\2\u0212\u0213\7\177\2\2\u0213\u008e\3"+ + "\2\2\2\u0214\u0215\t\f\2\2\u0215\u0090\3\2\2\2\u0216\u0217\t\r\2\2\u0217"+ + "\u0092\3\2\2\2\u0218\u0219\7/\2\2\u0219\u021a\7/\2\2\u021a\u021b\7]\2"+ + "\2\u021b\u021c\3\2\2\2\u021c\u021d\5y=\2\u021d\u021e\7_\2\2\u021e\u021f"+ + "\3\2\2\2\u021f\u0220\bJ\2\2\u0220\u0094\3\2\2\2\u0221\u0222\7/\2\2\u0222"+ + "\u0223\7/\2\2\u0223\u0241\3\2\2\2\u0224\u0242\3\2\2\2\u0225\u0229\7]\2"+ + "\2\u0226\u0228\7?\2\2\u0227\u0226\3\2\2\2\u0228\u022b\3\2\2\2\u0229\u0227"+ + "\3\2\2\2\u0229\u022a\3\2\2\2\u022a\u0242\3\2\2\2\u022b\u0229\3\2\2\2\u022c"+ + "\u0230\7]\2\2\u022d\u022f\7?\2\2\u022e\u022d\3\2\2\2\u022f\u0232\3\2\2"+ + "\2\u0230\u022e\3\2\2\2\u0230\u0231\3\2\2\2\u0231\u0233\3\2\2\2\u0232\u0230"+ + "\3\2\2\2\u0233\u0237\n\16\2\2\u0234\u0236\n\17\2\2\u0235\u0234\3\2\2\2"+ + "\u0236\u0239\3\2\2\2\u0237\u0235\3\2\2\2\u0237\u0238\3\2\2\2\u0238\u0242"+ + "\3\2\2\2\u0239\u0237\3\2\2\2\u023a\u023e\n\20\2\2\u023b\u023d\n\17\2\2"+ + "\u023c\u023b\3\2\2\2\u023d\u0240\3\2\2\2\u023e\u023c\3\2\2\2\u023e\u023f"+ + "\3\2\2\2\u023f\u0242\3\2\2\2\u0240\u023e\3\2\2\2\u0241\u0224\3\2\2\2\u0241"+ + "\u0225\3\2\2\2\u0241\u022c\3\2\2\2\u0241\u023a\3\2\2\2\u0242\u0246\3\2"+ + "\2\2\u0243\u0244\7\17\2\2\u0244\u0247\7\f\2\2\u0245\u0247\t\21\2\2\u0246"+ + "\u0243\3\2\2\2\u0246\u0245\3\2\2\2\u0247\u0248\3\2\2\2\u0248\u0249\bK"+ + "\2\2\u0249\u0096\3\2\2\2\u024a\u024c\t\22\2\2\u024b\u024a\3\2\2\2\u024c"+ + "\u024d\3\2\2\2\u024d\u024b\3\2\2\2\u024d\u024e\3\2\2\2\u024e\u024f\3\2"+ + "\2\2\u024f\u0250\bL\3\2\u0250\u0098\3\2\2\2\u0251\u0252\7%\2\2\u0252\u0256"+ + "\7#\2\2\u0253\u0255\n\17\2\2\u0254\u0253\3\2\2\2\u0255\u0258\3\2\2\2\u0256"+ + "\u0254\3\2\2\2\u0256\u0257\3\2\2\2\u0257\u0259\3\2\2\2\u0258\u0256\3\2"+ + "\2\2\u0259\u025a\bM\2\2\u025a\u009a\3\2\2\2*\2\u015c\u0162\u0164\u016c"+ + "\u016e\u017f\u0183\u0188\u018f\u0194\u019a\u019e\u01a4\u01a7\u01ac\u01b0"+ + "\u01b7\u01bd\u01c1\u01c9\u01cc\u01d3\u01d7\u01db\u01e0\u01e4\u01e9\u01ef"+ + "\u01f5\u0202\u0210\u0229\u0230\u0237\u023e\u0241\u0246\u024d\u0256\4\2"+ + "\3\2\b\2\2"; + public static final ATN _ATN = + new ATNDeserializer().deserialize(_serializedATN.toCharArray()); + static { + _decisionToDFA = new DFA[_ATN.getNumberOfDecisions()]; + for (int i = 0; i < _ATN.getNumberOfDecisions(); i++) { + _decisionToDFA[i] = new DFA(_ATN.getDecisionState(i), i); + } + } +} \ No newline at end of file diff --git a/src/LuaListener.java b/src/LuaListener.java new file mode 100644 index 0000000..54cc6bf --- /dev/null +++ b/src/LuaListener.java @@ -0,0 +1,764 @@ +import org.antlr.v4.runtime.tree.ParseTreeListener; + +/** + * This interface defines a complete listener for a parse tree produced by + * {@link LuaParser}. + */ +public interface LuaListener extends ParseTreeListener { + /** + * Enter a parse tree produced by {@link LuaParser#chunk}. + * @param ctx the parse tree + */ + void enterChunk(LuaParser.ChunkContext ctx); + /** + * Exit a parse tree produced by {@link LuaParser#chunk}. + * @param ctx the parse tree + */ + void exitChunk(LuaParser.ChunkContext ctx); + /** + * Enter a parse tree produced by {@link LuaParser#block}. + * @param ctx the parse tree + */ + void enterBlock(LuaParser.BlockContext ctx); + /** + * Exit a parse tree produced by {@link LuaParser#block}. + * @param ctx the parse tree + */ + void exitBlock(LuaParser.BlockContext ctx); + /** + * Enter a parse tree produced by the {@code stmtSemicolon} + * labeled alternative in {@link LuaParser#stat}. + * @param ctx the parse tree + */ + void enterStmtSemicolon(LuaParser.StmtSemicolonContext ctx); + /** + * Exit a parse tree produced by the {@code stmtSemicolon} + * labeled alternative in {@link LuaParser#stat}. + * @param ctx the parse tree + */ + void exitStmtSemicolon(LuaParser.StmtSemicolonContext ctx); + /** + * Enter a parse tree produced by the {@code stmtAssign} + * labeled alternative in {@link LuaParser#stat}. + * @param ctx the parse tree + */ + void enterStmtAssign(LuaParser.StmtAssignContext ctx); + /** + * Exit a parse tree produced by the {@code stmtAssign} + * labeled alternative in {@link LuaParser#stat}. + * @param ctx the parse tree + */ + void exitStmtAssign(LuaParser.StmtAssignContext ctx); + /** + * Enter a parse tree produced by the {@code stmtFuncCall} + * labeled alternative in {@link LuaParser#stat}. + * @param ctx the parse tree + */ + void enterStmtFuncCall(LuaParser.StmtFuncCallContext ctx); + /** + * Exit a parse tree produced by the {@code stmtFuncCall} + * labeled alternative in {@link LuaParser#stat}. + * @param ctx the parse tree + */ + void exitStmtFuncCall(LuaParser.StmtFuncCallContext ctx); + /** + * Enter a parse tree produced by the {@code stmtLabel} + * labeled alternative in {@link LuaParser#stat}. + * @param ctx the parse tree + */ + void enterStmtLabel(LuaParser.StmtLabelContext ctx); + /** + * Exit a parse tree produced by the {@code stmtLabel} + * labeled alternative in {@link LuaParser#stat}. + * @param ctx the parse tree + */ + void exitStmtLabel(LuaParser.StmtLabelContext ctx); + /** + * Enter a parse tree produced by the {@code stmtBreak} + * labeled alternative in {@link LuaParser#stat}. + * @param ctx the parse tree + */ + void enterStmtBreak(LuaParser.StmtBreakContext ctx); + /** + * Exit a parse tree produced by the {@code stmtBreak} + * labeled alternative in {@link LuaParser#stat}. + * @param ctx the parse tree + */ + void exitStmtBreak(LuaParser.StmtBreakContext ctx); + /** + * Enter a parse tree produced by the {@code stmtGoto} + * labeled alternative in {@link LuaParser#stat}. + * @param ctx the parse tree + */ + void enterStmtGoto(LuaParser.StmtGotoContext ctx); + /** + * Exit a parse tree produced by the {@code stmtGoto} + * labeled alternative in {@link LuaParser#stat}. + * @param ctx the parse tree + */ + void exitStmtGoto(LuaParser.StmtGotoContext ctx); + /** + * Enter a parse tree produced by the {@code stmtDo} + * labeled alternative in {@link LuaParser#stat}. + * @param ctx the parse tree + */ + void enterStmtDo(LuaParser.StmtDoContext ctx); + /** + * Exit a parse tree produced by the {@code stmtDo} + * labeled alternative in {@link LuaParser#stat}. + * @param ctx the parse tree + */ + void exitStmtDo(LuaParser.StmtDoContext ctx); + /** + * Enter a parse tree produced by the {@code stmtWhile} + * labeled alternative in {@link LuaParser#stat}. + * @param ctx the parse tree + */ + void enterStmtWhile(LuaParser.StmtWhileContext ctx); + /** + * Exit a parse tree produced by the {@code stmtWhile} + * labeled alternative in {@link LuaParser#stat}. + * @param ctx the parse tree + */ + void exitStmtWhile(LuaParser.StmtWhileContext ctx); + /** + * Enter a parse tree produced by the {@code stmtRepeat} + * labeled alternative in {@link LuaParser#stat}. + * @param ctx the parse tree + */ + void enterStmtRepeat(LuaParser.StmtRepeatContext ctx); + /** + * Exit a parse tree produced by the {@code stmtRepeat} + * labeled alternative in {@link LuaParser#stat}. + * @param ctx the parse tree + */ + void exitStmtRepeat(LuaParser.StmtRepeatContext ctx); + /** + * Enter a parse tree produced by the {@code stmtIf} + * labeled alternative in {@link LuaParser#stat}. + * @param ctx the parse tree + */ + void enterStmtIf(LuaParser.StmtIfContext ctx); + /** + * Exit a parse tree produced by the {@code stmtIf} + * labeled alternative in {@link LuaParser#stat}. + * @param ctx the parse tree + */ + void exitStmtIf(LuaParser.StmtIfContext ctx); + /** + * Enter a parse tree produced by the {@code stmtForStep} + * labeled alternative in {@link LuaParser#stat}. + * @param ctx the parse tree + */ + void enterStmtForStep(LuaParser.StmtForStepContext ctx); + /** + * Exit a parse tree produced by the {@code stmtForStep} + * labeled alternative in {@link LuaParser#stat}. + * @param ctx the parse tree + */ + void exitStmtForStep(LuaParser.StmtForStepContext ctx); + /** + * Enter a parse tree produced by the {@code stmtForIn} + * labeled alternative in {@link LuaParser#stat}. + * @param ctx the parse tree + */ + void enterStmtForIn(LuaParser.StmtForInContext ctx); + /** + * Exit a parse tree produced by the {@code stmtForIn} + * labeled alternative in {@link LuaParser#stat}. + * @param ctx the parse tree + */ + void exitStmtForIn(LuaParser.StmtForInContext ctx); + /** + * Enter a parse tree produced by the {@code stmtFuncDef} + * labeled alternative in {@link LuaParser#stat}. + * @param ctx the parse tree + */ + void enterStmtFuncDef(LuaParser.StmtFuncDefContext ctx); + /** + * Exit a parse tree produced by the {@code stmtFuncDef} + * labeled alternative in {@link LuaParser#stat}. + * @param ctx the parse tree + */ + void exitStmtFuncDef(LuaParser.StmtFuncDefContext ctx); + /** + * Enter a parse tree produced by the {@code stmtLocalFuncDef} + * labeled alternative in {@link LuaParser#stat}. + * @param ctx the parse tree + */ + void enterStmtLocalFuncDef(LuaParser.StmtLocalFuncDefContext ctx); + /** + * Exit a parse tree produced by the {@code stmtLocalFuncDef} + * labeled alternative in {@link LuaParser#stat}. + * @param ctx the parse tree + */ + void exitStmtLocalFuncDef(LuaParser.StmtLocalFuncDefContext ctx); + /** + * Enter a parse tree produced by the {@code stmtLocalDecl} + * labeled alternative in {@link LuaParser#stat}. + * @param ctx the parse tree + */ + void enterStmtLocalDecl(LuaParser.StmtLocalDeclContext ctx); + /** + * Exit a parse tree produced by the {@code stmtLocalDecl} + * labeled alternative in {@link LuaParser#stat}. + * @param ctx the parse tree + */ + void exitStmtLocalDecl(LuaParser.StmtLocalDeclContext ctx); + /** + * Enter a parse tree produced by {@link LuaParser#ifstmt}. + * @param ctx the parse tree + */ + void enterIfstmt(LuaParser.IfstmtContext ctx); + /** + * Exit a parse tree produced by {@link LuaParser#ifstmt}. + * @param ctx the parse tree + */ + void exitIfstmt(LuaParser.IfstmtContext ctx); + /** + * Enter a parse tree produced by {@link LuaParser#elseifstmt}. + * @param ctx the parse tree + */ + void enterElseifstmt(LuaParser.ElseifstmtContext ctx); + /** + * Exit a parse tree produced by {@link LuaParser#elseifstmt}. + * @param ctx the parse tree + */ + void exitElseifstmt(LuaParser.ElseifstmtContext ctx); + /** + * Enter a parse tree produced by {@link LuaParser#elsestmt}. + * @param ctx the parse tree + */ + void enterElsestmt(LuaParser.ElsestmtContext ctx); + /** + * Exit a parse tree produced by {@link LuaParser#elsestmt}. + * @param ctx the parse tree + */ + void exitElsestmt(LuaParser.ElsestmtContext ctx); + /** + * Enter a parse tree produced by {@link LuaParser#retstat}. + * @param ctx the parse tree + */ + void enterRetstat(LuaParser.RetstatContext ctx); + /** + * Exit a parse tree produced by {@link LuaParser#retstat}. + * @param ctx the parse tree + */ + void exitRetstat(LuaParser.RetstatContext ctx); + /** + * Enter a parse tree produced by {@link LuaParser#label}. + * @param ctx the parse tree + */ + void enterLabel(LuaParser.LabelContext ctx); + /** + * Exit a parse tree produced by {@link LuaParser#label}. + * @param ctx the parse tree + */ + void exitLabel(LuaParser.LabelContext ctx); + /** + * Enter a parse tree produced by {@link LuaParser#funcname}. + * @param ctx the parse tree + */ + void enterFuncname(LuaParser.FuncnameContext ctx); + /** + * Exit a parse tree produced by {@link LuaParser#funcname}. + * @param ctx the parse tree + */ + void exitFuncname(LuaParser.FuncnameContext ctx); + /** + * Enter a parse tree produced by {@link LuaParser#varlist}. + * @param ctx the parse tree + */ + void enterVarlist(LuaParser.VarlistContext ctx); + /** + * Exit a parse tree produced by {@link LuaParser#varlist}. + * @param ctx the parse tree + */ + void exitVarlist(LuaParser.VarlistContext ctx); + /** + * Enter a parse tree produced by {@link LuaParser#namelist}. + * @param ctx the parse tree + */ + void enterNamelist(LuaParser.NamelistContext ctx); + /** + * Exit a parse tree produced by {@link LuaParser#namelist}. + * @param ctx the parse tree + */ + void exitNamelist(LuaParser.NamelistContext ctx); + /** + * Enter a parse tree produced by {@link LuaParser#explist}. + * @param ctx the parse tree + */ + void enterExplist(LuaParser.ExplistContext ctx); + /** + * Exit a parse tree produced by {@link LuaParser#explist}. + * @param ctx the parse tree + */ + void exitExplist(LuaParser.ExplistContext ctx); + /** + * Enter a parse tree produced by the {@code expCmp} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + */ + void enterExpCmp(LuaParser.ExpCmpContext ctx); + /** + * Exit a parse tree produced by the {@code expCmp} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + */ + void exitExpCmp(LuaParser.ExpCmpContext ctx); + /** + * Enter a parse tree produced by the {@code expNumber} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + */ + void enterExpNumber(LuaParser.ExpNumberContext ctx); + /** + * Exit a parse tree produced by the {@code expNumber} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + */ + void exitExpNumber(LuaParser.ExpNumberContext ctx); + /** + * Enter a parse tree produced by the {@code expThreeDots} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + */ + void enterExpThreeDots(LuaParser.ExpThreeDotsContext ctx); + /** + * Exit a parse tree produced by the {@code expThreeDots} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + */ + void exitExpThreeDots(LuaParser.ExpThreeDotsContext ctx); + /** + * Enter a parse tree produced by the {@code expStrcat} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + */ + void enterExpStrcat(LuaParser.ExpStrcatContext ctx); + /** + * Exit a parse tree produced by the {@code expStrcat} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + */ + void exitExpStrcat(LuaParser.ExpStrcatContext ctx); + /** + * Enter a parse tree produced by the {@code expTrue} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + */ + void enterExpTrue(LuaParser.ExpTrueContext ctx); + /** + * Exit a parse tree produced by the {@code expTrue} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + */ + void exitExpTrue(LuaParser.ExpTrueContext ctx); + /** + * Enter a parse tree produced by the {@code expOr} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + */ + void enterExpOr(LuaParser.ExpOrContext ctx); + /** + * Exit a parse tree produced by the {@code expOr} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + */ + void exitExpOr(LuaParser.ExpOrContext ctx); + /** + * Enter a parse tree produced by the {@code expBitwise} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + */ + void enterExpBitwise(LuaParser.ExpBitwiseContext ctx); + /** + * Exit a parse tree produced by the {@code expBitwise} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + */ + void exitExpBitwise(LuaParser.ExpBitwiseContext ctx); + /** + * Enter a parse tree produced by the {@code expTableCtor} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + */ + void enterExpTableCtor(LuaParser.ExpTableCtorContext ctx); + /** + * Exit a parse tree produced by the {@code expTableCtor} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + */ + void exitExpTableCtor(LuaParser.ExpTableCtorContext ctx); + /** + * Enter a parse tree produced by the {@code expMulDivMod} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + */ + void enterExpMulDivMod(LuaParser.ExpMulDivModContext ctx); + /** + * Exit a parse tree produced by the {@code expMulDivMod} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + */ + void exitExpMulDivMod(LuaParser.ExpMulDivModContext ctx); + /** + * Enter a parse tree produced by the {@code expFuncDef} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + */ + void enterExpFuncDef(LuaParser.ExpFuncDefContext ctx); + /** + * Exit a parse tree produced by the {@code expFuncDef} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + */ + void exitExpFuncDef(LuaParser.ExpFuncDefContext ctx); + /** + * Enter a parse tree produced by the {@code expFalse} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + */ + void enterExpFalse(LuaParser.ExpFalseContext ctx); + /** + * Exit a parse tree produced by the {@code expFalse} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + */ + void exitExpFalse(LuaParser.ExpFalseContext ctx); + /** + * Enter a parse tree produced by the {@code expString} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + */ + void enterExpString(LuaParser.ExpStringContext ctx); + /** + * Exit a parse tree produced by the {@code expString} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + */ + void exitExpString(LuaParser.ExpStringContext ctx); + /** + * Enter a parse tree produced by the {@code expPrefix} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + */ + void enterExpPrefix(LuaParser.ExpPrefixContext ctx); + /** + * Exit a parse tree produced by the {@code expPrefix} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + */ + void exitExpPrefix(LuaParser.ExpPrefixContext ctx); + /** + * Enter a parse tree produced by the {@code expUnary} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + */ + void enterExpUnary(LuaParser.ExpUnaryContext ctx); + /** + * Exit a parse tree produced by the {@code expUnary} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + */ + void exitExpUnary(LuaParser.ExpUnaryContext ctx); + /** + * Enter a parse tree produced by the {@code expAnd} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + */ + void enterExpAnd(LuaParser.ExpAndContext ctx); + /** + * Exit a parse tree produced by the {@code expAnd} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + */ + void exitExpAnd(LuaParser.ExpAndContext ctx); + /** + * Enter a parse tree produced by the {@code expPow} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + */ + void enterExpPow(LuaParser.ExpPowContext ctx); + /** + * Exit a parse tree produced by the {@code expPow} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + */ + void exitExpPow(LuaParser.ExpPowContext ctx); + /** + * Enter a parse tree produced by the {@code expNil} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + */ + void enterExpNil(LuaParser.ExpNilContext ctx); + /** + * Exit a parse tree produced by the {@code expNil} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + */ + void exitExpNil(LuaParser.ExpNilContext ctx); + /** + * Enter a parse tree produced by the {@code expAddSub} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + */ + void enterExpAddSub(LuaParser.ExpAddSubContext ctx); + /** + * Exit a parse tree produced by the {@code expAddSub} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + */ + void exitExpAddSub(LuaParser.ExpAddSubContext ctx); + /** + * Enter a parse tree produced by {@link LuaParser#prefixexp}. + * @param ctx the parse tree + */ + void enterPrefixexp(LuaParser.PrefixexpContext ctx); + /** + * Exit a parse tree produced by {@link LuaParser#prefixexp}. + * @param ctx the parse tree + */ + void exitPrefixexp(LuaParser.PrefixexpContext ctx); + /** + * Enter a parse tree produced by {@link LuaParser#functioncall}. + * @param ctx the parse tree + */ + void enterFunctioncall(LuaParser.FunctioncallContext ctx); + /** + * Exit a parse tree produced by {@link LuaParser#functioncall}. + * @param ctx the parse tree + */ + void exitFunctioncall(LuaParser.FunctioncallContext ctx); + /** + * Enter a parse tree produced by {@link LuaParser#varOrExp}. + * @param ctx the parse tree + */ + void enterVarOrExp(LuaParser.VarOrExpContext ctx); + /** + * Exit a parse tree produced by {@link LuaParser#varOrExp}. + * @param ctx the parse tree + */ + void exitVarOrExp(LuaParser.VarOrExpContext ctx); + /** + * Enter a parse tree produced by {@link LuaParser#var}. + * @param ctx the parse tree + */ + void enterVar(LuaParser.VarContext ctx); + /** + * Exit a parse tree produced by {@link LuaParser#var}. + * @param ctx the parse tree + */ + void exitVar(LuaParser.VarContext ctx); + /** + * Enter a parse tree produced by {@link LuaParser#varSuffix}. + * @param ctx the parse tree + */ + void enterVarSuffix(LuaParser.VarSuffixContext ctx); + /** + * Exit a parse tree produced by {@link LuaParser#varSuffix}. + * @param ctx the parse tree + */ + void exitVarSuffix(LuaParser.VarSuffixContext ctx); + /** + * Enter a parse tree produced by {@link LuaParser#nameAndArgs}. + * @param ctx the parse tree + */ + void enterNameAndArgs(LuaParser.NameAndArgsContext ctx); + /** + * Exit a parse tree produced by {@link LuaParser#nameAndArgs}. + * @param ctx the parse tree + */ + void exitNameAndArgs(LuaParser.NameAndArgsContext ctx); + /** + * Enter a parse tree produced by {@link LuaParser#args}. + * @param ctx the parse tree + */ + void enterArgs(LuaParser.ArgsContext ctx); + /** + * Exit a parse tree produced by {@link LuaParser#args}. + * @param ctx the parse tree + */ + void exitArgs(LuaParser.ArgsContext ctx); + /** + * Enter a parse tree produced by {@link LuaParser#functiondef}. + * @param ctx the parse tree + */ + void enterFunctiondef(LuaParser.FunctiondefContext ctx); + /** + * Exit a parse tree produced by {@link LuaParser#functiondef}. + * @param ctx the parse tree + */ + void exitFunctiondef(LuaParser.FunctiondefContext ctx); + /** + * Enter a parse tree produced by {@link LuaParser#funcbody}. + * @param ctx the parse tree + */ + void enterFuncbody(LuaParser.FuncbodyContext ctx); + /** + * Exit a parse tree produced by {@link LuaParser#funcbody}. + * @param ctx the parse tree + */ + void exitFuncbody(LuaParser.FuncbodyContext ctx); + /** + * Enter a parse tree produced by {@link LuaParser#parlist}. + * @param ctx the parse tree + */ + void enterParlist(LuaParser.ParlistContext ctx); + /** + * Exit a parse tree produced by {@link LuaParser#parlist}. + * @param ctx the parse tree + */ + void exitParlist(LuaParser.ParlistContext ctx); + /** + * Enter a parse tree produced by {@link LuaParser#tableconstructor}. + * @param ctx the parse tree + */ + void enterTableconstructor(LuaParser.TableconstructorContext ctx); + /** + * Exit a parse tree produced by {@link LuaParser#tableconstructor}. + * @param ctx the parse tree + */ + void exitTableconstructor(LuaParser.TableconstructorContext ctx); + /** + * Enter a parse tree produced by {@link LuaParser#fieldlist}. + * @param ctx the parse tree + */ + void enterFieldlist(LuaParser.FieldlistContext ctx); + /** + * Exit a parse tree produced by {@link LuaParser#fieldlist}. + * @param ctx the parse tree + */ + void exitFieldlist(LuaParser.FieldlistContext ctx); + /** + * Enter a parse tree produced by {@link LuaParser#field}. + * @param ctx the parse tree + */ + void enterField(LuaParser.FieldContext ctx); + /** + * Exit a parse tree produced by {@link LuaParser#field}. + * @param ctx the parse tree + */ + void exitField(LuaParser.FieldContext ctx); + /** + * Enter a parse tree produced by {@link LuaParser#fieldsep}. + * @param ctx the parse tree + */ + void enterFieldsep(LuaParser.FieldsepContext ctx); + /** + * Exit a parse tree produced by {@link LuaParser#fieldsep}. + * @param ctx the parse tree + */ + void exitFieldsep(LuaParser.FieldsepContext ctx); + /** + * Enter a parse tree produced by {@link LuaParser#operatorOr}. + * @param ctx the parse tree + */ + void enterOperatorOr(LuaParser.OperatorOrContext ctx); + /** + * Exit a parse tree produced by {@link LuaParser#operatorOr}. + * @param ctx the parse tree + */ + void exitOperatorOr(LuaParser.OperatorOrContext ctx); + /** + * Enter a parse tree produced by {@link LuaParser#operatorAnd}. + * @param ctx the parse tree + */ + void enterOperatorAnd(LuaParser.OperatorAndContext ctx); + /** + * Exit a parse tree produced by {@link LuaParser#operatorAnd}. + * @param ctx the parse tree + */ + void exitOperatorAnd(LuaParser.OperatorAndContext ctx); + /** + * Enter a parse tree produced by {@link LuaParser#operatorComparison}. + * @param ctx the parse tree + */ + void enterOperatorComparison(LuaParser.OperatorComparisonContext ctx); + /** + * Exit a parse tree produced by {@link LuaParser#operatorComparison}. + * @param ctx the parse tree + */ + void exitOperatorComparison(LuaParser.OperatorComparisonContext ctx); + /** + * Enter a parse tree produced by {@link LuaParser#operatorStrcat}. + * @param ctx the parse tree + */ + void enterOperatorStrcat(LuaParser.OperatorStrcatContext ctx); + /** + * Exit a parse tree produced by {@link LuaParser#operatorStrcat}. + * @param ctx the parse tree + */ + void exitOperatorStrcat(LuaParser.OperatorStrcatContext ctx); + /** + * Enter a parse tree produced by {@link LuaParser#operatorAddSub}. + * @param ctx the parse tree + */ + void enterOperatorAddSub(LuaParser.OperatorAddSubContext ctx); + /** + * Exit a parse tree produced by {@link LuaParser#operatorAddSub}. + * @param ctx the parse tree + */ + void exitOperatorAddSub(LuaParser.OperatorAddSubContext ctx); + /** + * Enter a parse tree produced by {@link LuaParser#operatorMulDivMod}. + * @param ctx the parse tree + */ + void enterOperatorMulDivMod(LuaParser.OperatorMulDivModContext ctx); + /** + * Exit a parse tree produced by {@link LuaParser#operatorMulDivMod}. + * @param ctx the parse tree + */ + void exitOperatorMulDivMod(LuaParser.OperatorMulDivModContext ctx); + /** + * Enter a parse tree produced by {@link LuaParser#operatorBitwise}. + * @param ctx the parse tree + */ + void enterOperatorBitwise(LuaParser.OperatorBitwiseContext ctx); + /** + * Exit a parse tree produced by {@link LuaParser#operatorBitwise}. + * @param ctx the parse tree + */ + void exitOperatorBitwise(LuaParser.OperatorBitwiseContext ctx); + /** + * Enter a parse tree produced by {@link LuaParser#operatorUnary}. + * @param ctx the parse tree + */ + void enterOperatorUnary(LuaParser.OperatorUnaryContext ctx); + /** + * Exit a parse tree produced by {@link LuaParser#operatorUnary}. + * @param ctx the parse tree + */ + void exitOperatorUnary(LuaParser.OperatorUnaryContext ctx); + /** + * Enter a parse tree produced by {@link LuaParser#operatorPower}. + * @param ctx the parse tree + */ + void enterOperatorPower(LuaParser.OperatorPowerContext ctx); + /** + * Exit a parse tree produced by {@link LuaParser#operatorPower}. + * @param ctx the parse tree + */ + void exitOperatorPower(LuaParser.OperatorPowerContext ctx); + /** + * Enter a parse tree produced by {@link LuaParser#number}. + * @param ctx the parse tree + */ + void enterNumber(LuaParser.NumberContext ctx); + /** + * Exit a parse tree produced by {@link LuaParser#number}. + * @param ctx the parse tree + */ + void exitNumber(LuaParser.NumberContext ctx); + /** + * Enter a parse tree produced by {@link LuaParser#string}. + * @param ctx the parse tree + */ + void enterString(LuaParser.StringContext ctx); + /** + * Exit a parse tree produced by {@link LuaParser#string}. + * @param ctx the parse tree + */ + void exitString(LuaParser.StringContext ctx); +} \ No newline at end of file diff --git a/src/LuaParser.java b/src/LuaParser.java new file mode 100644 index 0000000..1ebc519 --- /dev/null +++ b/src/LuaParser.java @@ -0,0 +1,3691 @@ +import org.antlr.v4.runtime.atn.*; +import org.antlr.v4.runtime.dfa.DFA; +import org.antlr.v4.runtime.*; +import org.antlr.v4.runtime.misc.*; +import org.antlr.v4.runtime.tree.*; +import java.util.List; +import java.util.Iterator; +import java.util.ArrayList; + +@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast"}) +public class LuaParser extends Parser { + static { RuntimeMetaData.checkVersion("4.8", RuntimeMetaData.VERSION); } + + protected static final DFA[] _decisionToDFA; + protected static final PredictionContextCache _sharedContextCache = + new PredictionContextCache(); + public static final int + T__0=1, T__1=2, T__2=3, T__3=4, T__4=5, T__5=6, T__6=7, T__7=8, T__8=9, + T__9=10, T__10=11, T__11=12, T__12=13, T__13=14, T__14=15, T__15=16, T__16=17, + T__17=18, T__18=19, T__19=20, T__20=21, T__21=22, T__22=23, T__23=24, + T__24=25, T__25=26, T__26=27, T__27=28, T__28=29, T__29=30, T__30=31, + T__31=32, T__32=33, T__33=34, T__34=35, T__35=36, T__36=37, T__37=38, + T__38=39, T__39=40, T__40=41, T__41=42, T__42=43, T__43=44, T__44=45, + T__45=46, T__46=47, T__47=48, T__48=49, T__49=50, T__50=51, T__51=52, + T__52=53, T__53=54, T__54=55, NAME=56, NORMALSTRING=57, CHARSTRING=58, + LONGSTRING=59, INT=60, HEX=61, FLOAT=62, HEX_FLOAT=63, COMMENT=64, LINE_COMMENT=65, + WS=66, SHEBANG=67; + public static final int + RULE_chunk = 0, RULE_block = 1, RULE_stat = 2, RULE_ifstmt = 3, RULE_elseifstmt = 4, + RULE_elsestmt = 5, RULE_retstat = 6, RULE_label = 7, RULE_funcname = 8, + RULE_varlist = 9, RULE_namelist = 10, RULE_explist = 11, RULE_exp = 12, + RULE_prefixexp = 13, RULE_functioncall = 14, RULE_varOrExp = 15, RULE_var = 16, + RULE_varSuffix = 17, RULE_nameAndArgs = 18, RULE_args = 19, RULE_functiondef = 20, + RULE_funcbody = 21, RULE_parlist = 22, RULE_tableconstructor = 23, RULE_fieldlist = 24, + RULE_field = 25, RULE_fieldsep = 26, RULE_operatorOr = 27, RULE_operatorAnd = 28, + RULE_operatorComparison = 29, RULE_operatorStrcat = 30, RULE_operatorAddSub = 31, + RULE_operatorMulDivMod = 32, RULE_operatorBitwise = 33, RULE_operatorUnary = 34, + RULE_operatorPower = 35, RULE_number = 36, RULE_string = 37; + private static String[] makeRuleNames() { + return new String[] { + "chunk", "block", "stat", "ifstmt", "elseifstmt", "elsestmt", "retstat", + "label", "funcname", "varlist", "namelist", "explist", "exp", "prefixexp", + "functioncall", "varOrExp", "var", "varSuffix", "nameAndArgs", "args", + "functiondef", "funcbody", "parlist", "tableconstructor", "fieldlist", + "field", "fieldsep", "operatorOr", "operatorAnd", "operatorComparison", + "operatorStrcat", "operatorAddSub", "operatorMulDivMod", "operatorBitwise", + "operatorUnary", "operatorPower", "number", "string" + }; + } + public static final String[] ruleNames = makeRuleNames(); + + private static String[] makeLiteralNames() { + return new String[] { + null, "';'", "'='", "'break'", "'goto'", "'do'", "'end'", "'while'", + "'repeat'", "'until'", "'for'", "','", "'in'", "'function'", "'local'", + "'if'", "'then'", "'elseif'", "'else'", "'return'", "'::'", "'.'", "':'", + "'nil'", "'false'", "'true'", "'...'", "'('", "')'", "'['", "']'", "'{'", + "'}'", "'or'", "'and'", "'<'", "'>'", "'<='", "'>='", "'~='", "'=='", + "'..'", "'+'", "'-'", "'*'", "'/'", "'%'", "'//'", "'&'", "'|'", "'~'", + "'<<'", "'>>'", "'not'", "'#'", "'^'" + }; + } + private static final String[] _LITERAL_NAMES = makeLiteralNames(); + private static String[] makeSymbolicNames() { + return new String[] { + null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, "NAME", "NORMALSTRING", + "CHARSTRING", "LONGSTRING", "INT", "HEX", "FLOAT", "HEX_FLOAT", "COMMENT", + "LINE_COMMENT", "WS", "SHEBANG" + }; + } + private static final String[] _SYMBOLIC_NAMES = makeSymbolicNames(); + public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES); + + /** + * @deprecated Use {@link #VOCABULARY} instead. + */ + @Deprecated + public static final String[] tokenNames; + static { + tokenNames = new String[_SYMBOLIC_NAMES.length]; + for (int i = 0; i < tokenNames.length; i++) { + tokenNames[i] = VOCABULARY.getLiteralName(i); + if (tokenNames[i] == null) { + tokenNames[i] = VOCABULARY.getSymbolicName(i); + } + + if (tokenNames[i] == null) { + tokenNames[i] = ""; + } + } + } + + @Override + @Deprecated + public String[] getTokenNames() { + return tokenNames; + } + + @Override + + public Vocabulary getVocabulary() { + return VOCABULARY; + } + + @Override + public String getGrammarFileName() { return "Lua.g4"; } + + @Override + public String[] getRuleNames() { return ruleNames; } + + @Override + public String getSerializedATN() { return _serializedATN; } + + @Override + public ATN getATN() { return _ATN; } + + public LuaParser(TokenStream input) { + super(input); + _interp = new ParserATNSimulator(this,_ATN,_decisionToDFA,_sharedContextCache); + } + + public static class ChunkContext extends ParserRuleContext { + public BlockContext block() { + return getRuleContext(BlockContext.class,0); + } + public TerminalNode EOF() { return getToken(LuaParser.EOF, 0); } + public ChunkContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_chunk; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterChunk(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitChunk(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitChunk(this); + else return visitor.visitChildren(this); + } + } + + public final ChunkContext chunk() throws RecognitionException { + ChunkContext _localctx = new ChunkContext(_ctx, getState()); + enterRule(_localctx, 0, RULE_chunk); + try { + enterOuterAlt(_localctx, 1); + { + setState(76); + block(); + setState(77); + match(EOF); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class BlockContext extends ParserRuleContext { + public List stat() { + return getRuleContexts(StatContext.class); + } + public StatContext stat(int i) { + return getRuleContext(StatContext.class,i); + } + public RetstatContext retstat() { + return getRuleContext(RetstatContext.class,0); + } + public BlockContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_block; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterBlock(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitBlock(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitBlock(this); + else return visitor.visitChildren(this); + } + } + + public final BlockContext block() throws RecognitionException { + BlockContext _localctx = new BlockContext(_ctx, getState()); + enterRule(_localctx, 2, RULE_block); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(82); + _errHandler.sync(this); + _la = _input.LA(1); + while ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << T__0) | (1L << T__2) | (1L << T__3) | (1L << T__4) | (1L << T__6) | (1L << T__7) | (1L << T__9) | (1L << T__12) | (1L << T__13) | (1L << T__14) | (1L << T__19) | (1L << T__26) | (1L << NAME))) != 0)) { + { + { + setState(79); + stat(); + } + } + setState(84); + _errHandler.sync(this); + _la = _input.LA(1); + } + setState(86); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==T__18) { + { + setState(85); + retstat(); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class StatContext extends ParserRuleContext { + public StatContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_stat; } + + public StatContext() { } + public void copyFrom(StatContext ctx) { + super.copyFrom(ctx); + } + } + public static class StmtDoContext extends StatContext { + public BlockContext block() { + return getRuleContext(BlockContext.class,0); + } + public StmtDoContext(StatContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterStmtDo(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitStmtDo(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitStmtDo(this); + else return visitor.visitChildren(this); + } + } + public static class StmtBreakContext extends StatContext { + public StmtBreakContext(StatContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterStmtBreak(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitStmtBreak(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitStmtBreak(this); + else return visitor.visitChildren(this); + } + } + public static class StmtFuncCallContext extends StatContext { + public FunctioncallContext functioncall() { + return getRuleContext(FunctioncallContext.class,0); + } + public StmtFuncCallContext(StatContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterStmtFuncCall(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitStmtFuncCall(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitStmtFuncCall(this); + else return visitor.visitChildren(this); + } + } + public static class StmtRepeatContext extends StatContext { + public BlockContext block() { + return getRuleContext(BlockContext.class,0); + } + public ExpContext exp() { + return getRuleContext(ExpContext.class,0); + } + public StmtRepeatContext(StatContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterStmtRepeat(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitStmtRepeat(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitStmtRepeat(this); + else return visitor.visitChildren(this); + } + } + public static class StmtForInContext extends StatContext { + public NamelistContext namelist() { + return getRuleContext(NamelistContext.class,0); + } + public ExplistContext explist() { + return getRuleContext(ExplistContext.class,0); + } + public BlockContext block() { + return getRuleContext(BlockContext.class,0); + } + public StmtForInContext(StatContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterStmtForIn(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitStmtForIn(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitStmtForIn(this); + else return visitor.visitChildren(this); + } + } + public static class StmtLocalFuncDefContext extends StatContext { + public TerminalNode NAME() { return getToken(LuaParser.NAME, 0); } + public FuncbodyContext funcbody() { + return getRuleContext(FuncbodyContext.class,0); + } + public StmtLocalFuncDefContext(StatContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterStmtLocalFuncDef(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitStmtLocalFuncDef(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitStmtLocalFuncDef(this); + else return visitor.visitChildren(this); + } + } + public static class StmtSemicolonContext extends StatContext { + public StmtSemicolonContext(StatContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterStmtSemicolon(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitStmtSemicolon(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitStmtSemicolon(this); + else return visitor.visitChildren(this); + } + } + public static class StmtLabelContext extends StatContext { + public LabelContext label() { + return getRuleContext(LabelContext.class,0); + } + public StmtLabelContext(StatContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterStmtLabel(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitStmtLabel(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitStmtLabel(this); + else return visitor.visitChildren(this); + } + } + public static class StmtWhileContext extends StatContext { + public ExpContext exp() { + return getRuleContext(ExpContext.class,0); + } + public BlockContext block() { + return getRuleContext(BlockContext.class,0); + } + public StmtWhileContext(StatContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterStmtWhile(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitStmtWhile(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitStmtWhile(this); + else return visitor.visitChildren(this); + } + } + public static class StmtGotoContext extends StatContext { + public TerminalNode NAME() { return getToken(LuaParser.NAME, 0); } + public StmtGotoContext(StatContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterStmtGoto(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitStmtGoto(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitStmtGoto(this); + else return visitor.visitChildren(this); + } + } + public static class StmtIfContext extends StatContext { + public IfstmtContext ifstmt() { + return getRuleContext(IfstmtContext.class,0); + } + public ElseifstmtContext elseifstmt() { + return getRuleContext(ElseifstmtContext.class,0); + } + public ElsestmtContext elsestmt() { + return getRuleContext(ElsestmtContext.class,0); + } + public StmtIfContext(StatContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterStmtIf(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitStmtIf(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitStmtIf(this); + else return visitor.visitChildren(this); + } + } + public static class StmtForStepContext extends StatContext { + public TerminalNode NAME() { return getToken(LuaParser.NAME, 0); } + public List exp() { + return getRuleContexts(ExpContext.class); + } + public ExpContext exp(int i) { + return getRuleContext(ExpContext.class,i); + } + public BlockContext block() { + return getRuleContext(BlockContext.class,0); + } + public StmtForStepContext(StatContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterStmtForStep(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitStmtForStep(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitStmtForStep(this); + else return visitor.visitChildren(this); + } + } + public static class StmtLocalDeclContext extends StatContext { + public NamelistContext namelist() { + return getRuleContext(NamelistContext.class,0); + } + public ExplistContext explist() { + return getRuleContext(ExplistContext.class,0); + } + public StmtLocalDeclContext(StatContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterStmtLocalDecl(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitStmtLocalDecl(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitStmtLocalDecl(this); + else return visitor.visitChildren(this); + } + } + public static class StmtAssignContext extends StatContext { + public VarlistContext varlist() { + return getRuleContext(VarlistContext.class,0); + } + public ExplistContext explist() { + return getRuleContext(ExplistContext.class,0); + } + public StmtAssignContext(StatContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterStmtAssign(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitStmtAssign(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitStmtAssign(this); + else return visitor.visitChildren(this); + } + } + public static class StmtFuncDefContext extends StatContext { + public FuncnameContext funcname() { + return getRuleContext(FuncnameContext.class,0); + } + public FuncbodyContext funcbody() { + return getRuleContext(FuncbodyContext.class,0); + } + public StmtFuncDefContext(StatContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterStmtFuncDef(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitStmtFuncDef(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitStmtFuncDef(this); + else return visitor.visitChildren(this); + } + } + + public final StatContext stat() throws RecognitionException { + StatContext _localctx = new StatContext(_ctx, getState()); + enterRule(_localctx, 4, RULE_stat); + int _la; + try { + setState(154); + _errHandler.sync(this); + switch ( getInterpreter().adaptivePredict(_input,4,_ctx) ) { + case 1: + _localctx = new StmtSemicolonContext(_localctx); + enterOuterAlt(_localctx, 1); + { + setState(88); + match(T__0); + } + break; + case 2: + _localctx = new StmtAssignContext(_localctx); + enterOuterAlt(_localctx, 2); + { + setState(89); + varlist(); + setState(90); + match(T__1); + setState(91); + explist(); + } + break; + case 3: + _localctx = new StmtFuncCallContext(_localctx); + enterOuterAlt(_localctx, 3); + { + setState(93); + functioncall(); + } + break; + case 4: + _localctx = new StmtLabelContext(_localctx); + enterOuterAlt(_localctx, 4); + { + setState(94); + label(); + } + break; + case 5: + _localctx = new StmtBreakContext(_localctx); + enterOuterAlt(_localctx, 5); + { + setState(95); + match(T__2); + } + break; + case 6: + _localctx = new StmtGotoContext(_localctx); + enterOuterAlt(_localctx, 6); + { + setState(96); + match(T__3); + setState(97); + match(NAME); + } + break; + case 7: + _localctx = new StmtDoContext(_localctx); + enterOuterAlt(_localctx, 7); + { + setState(98); + match(T__4); + setState(99); + block(); + setState(100); + match(T__5); + } + break; + case 8: + _localctx = new StmtWhileContext(_localctx); + enterOuterAlt(_localctx, 8); + { + setState(102); + match(T__6); + setState(103); + exp(0); + setState(104); + match(T__4); + setState(105); + block(); + setState(106); + match(T__5); + } + break; + case 9: + _localctx = new StmtRepeatContext(_localctx); + enterOuterAlt(_localctx, 9); + { + setState(108); + match(T__7); + setState(109); + block(); + setState(110); + match(T__8); + setState(111); + exp(0); + } + break; + case 10: + _localctx = new StmtIfContext(_localctx); + enterOuterAlt(_localctx, 10); + { + setState(113); + ifstmt(); + setState(114); + elseifstmt(); + setState(115); + elsestmt(); + setState(116); + match(T__5); + } + break; + case 11: + _localctx = new StmtForStepContext(_localctx); + enterOuterAlt(_localctx, 11); + { + setState(118); + match(T__9); + setState(119); + match(NAME); + setState(120); + match(T__1); + setState(121); + exp(0); + setState(122); + match(T__10); + setState(123); + exp(0); + setState(126); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==T__10) { + { + setState(124); + match(T__10); + setState(125); + exp(0); + } + } + + setState(128); + match(T__4); + setState(129); + block(); + setState(130); + match(T__5); + } + break; + case 12: + _localctx = new StmtForInContext(_localctx); + enterOuterAlt(_localctx, 12); + { + setState(132); + match(T__9); + setState(133); + namelist(); + setState(134); + match(T__11); + setState(135); + explist(); + setState(136); + match(T__4); + setState(137); + block(); + setState(138); + match(T__5); + } + break; + case 13: + _localctx = new StmtFuncDefContext(_localctx); + enterOuterAlt(_localctx, 13); + { + setState(140); + match(T__12); + setState(141); + funcname(); + setState(142); + funcbody(); + } + break; + case 14: + _localctx = new StmtLocalFuncDefContext(_localctx); + enterOuterAlt(_localctx, 14); + { + setState(144); + match(T__13); + setState(145); + match(T__12); + setState(146); + match(NAME); + setState(147); + funcbody(); + } + break; + case 15: + _localctx = new StmtLocalDeclContext(_localctx); + enterOuterAlt(_localctx, 15); + { + setState(148); + match(T__13); + setState(149); + namelist(); + setState(152); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==T__1) { + { + setState(150); + match(T__1); + setState(151); + explist(); + } + } + + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class IfstmtContext extends ParserRuleContext { + public ExpContext exp() { + return getRuleContext(ExpContext.class,0); + } + public BlockContext block() { + return getRuleContext(BlockContext.class,0); + } + public IfstmtContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_ifstmt; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterIfstmt(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitIfstmt(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitIfstmt(this); + else return visitor.visitChildren(this); + } + } + + public final IfstmtContext ifstmt() throws RecognitionException { + IfstmtContext _localctx = new IfstmtContext(_ctx, getState()); + enterRule(_localctx, 6, RULE_ifstmt); + try { + enterOuterAlt(_localctx, 1); + { + setState(156); + match(T__14); + setState(157); + exp(0); + setState(158); + match(T__15); + setState(159); + block(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class ElseifstmtContext extends ParserRuleContext { + public List exp() { + return getRuleContexts(ExpContext.class); + } + public ExpContext exp(int i) { + return getRuleContext(ExpContext.class,i); + } + public List block() { + return getRuleContexts(BlockContext.class); + } + public BlockContext block(int i) { + return getRuleContext(BlockContext.class,i); + } + public ElseifstmtContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_elseifstmt; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterElseifstmt(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitElseifstmt(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitElseifstmt(this); + else return visitor.visitChildren(this); + } + } + + public final ElseifstmtContext elseifstmt() throws RecognitionException { + ElseifstmtContext _localctx = new ElseifstmtContext(_ctx, getState()); + enterRule(_localctx, 8, RULE_elseifstmt); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(168); + _errHandler.sync(this); + _la = _input.LA(1); + while (_la==T__16) { + { + { + setState(161); + match(T__16); + setState(162); + exp(0); + setState(163); + match(T__15); + setState(164); + block(); + } + } + setState(170); + _errHandler.sync(this); + _la = _input.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class ElsestmtContext extends ParserRuleContext { + public BlockContext block() { + return getRuleContext(BlockContext.class,0); + } + public ElsestmtContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_elsestmt; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterElsestmt(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitElsestmt(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitElsestmt(this); + else return visitor.visitChildren(this); + } + } + + public final ElsestmtContext elsestmt() throws RecognitionException { + ElsestmtContext _localctx = new ElsestmtContext(_ctx, getState()); + enterRule(_localctx, 10, RULE_elsestmt); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(173); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==T__17) { + { + setState(171); + match(T__17); + setState(172); + block(); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class RetstatContext extends ParserRuleContext { + public ExplistContext explist() { + return getRuleContext(ExplistContext.class,0); + } + public RetstatContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_retstat; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterRetstat(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitRetstat(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitRetstat(this); + else return visitor.visitChildren(this); + } + } + + public final RetstatContext retstat() throws RecognitionException { + RetstatContext _localctx = new RetstatContext(_ctx, getState()); + enterRule(_localctx, 12, RULE_retstat); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(175); + match(T__18); + setState(177); + _errHandler.sync(this); + _la = _input.LA(1); + if ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << T__12) | (1L << T__22) | (1L << T__23) | (1L << T__24) | (1L << T__25) | (1L << T__26) | (1L << T__30) | (1L << T__42) | (1L << T__49) | (1L << T__52) | (1L << T__53) | (1L << NAME) | (1L << NORMALSTRING) | (1L << CHARSTRING) | (1L << LONGSTRING) | (1L << INT) | (1L << HEX) | (1L << FLOAT) | (1L << HEX_FLOAT))) != 0)) { + { + setState(176); + explist(); + } + } + + setState(180); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==T__0) { + { + setState(179); + match(T__0); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class LabelContext extends ParserRuleContext { + public TerminalNode NAME() { return getToken(LuaParser.NAME, 0); } + public LabelContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_label; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterLabel(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitLabel(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitLabel(this); + else return visitor.visitChildren(this); + } + } + + public final LabelContext label() throws RecognitionException { + LabelContext _localctx = new LabelContext(_ctx, getState()); + enterRule(_localctx, 14, RULE_label); + try { + enterOuterAlt(_localctx, 1); + { + setState(182); + match(T__19); + setState(183); + match(NAME); + setState(184); + match(T__19); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class FuncnameContext extends ParserRuleContext { + public List NAME() { return getTokens(LuaParser.NAME); } + public TerminalNode NAME(int i) { + return getToken(LuaParser.NAME, i); + } + public FuncnameContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_funcname; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterFuncname(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitFuncname(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitFuncname(this); + else return visitor.visitChildren(this); + } + } + + public final FuncnameContext funcname() throws RecognitionException { + FuncnameContext _localctx = new FuncnameContext(_ctx, getState()); + enterRule(_localctx, 16, RULE_funcname); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(186); + match(NAME); + setState(191); + _errHandler.sync(this); + _la = _input.LA(1); + while (_la==T__20) { + { + { + setState(187); + match(T__20); + setState(188); + match(NAME); + } + } + setState(193); + _errHandler.sync(this); + _la = _input.LA(1); + } + setState(196); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==T__21) { + { + setState(194); + match(T__21); + setState(195); + match(NAME); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class VarlistContext extends ParserRuleContext { + public List var() { + return getRuleContexts(VarContext.class); + } + public VarContext var(int i) { + return getRuleContext(VarContext.class,i); + } + public VarlistContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_varlist; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterVarlist(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitVarlist(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitVarlist(this); + else return visitor.visitChildren(this); + } + } + + public final VarlistContext varlist() throws RecognitionException { + VarlistContext _localctx = new VarlistContext(_ctx, getState()); + enterRule(_localctx, 18, RULE_varlist); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(198); + var(); + setState(203); + _errHandler.sync(this); + _la = _input.LA(1); + while (_la==T__10) { + { + { + setState(199); + match(T__10); + setState(200); + var(); + } + } + setState(205); + _errHandler.sync(this); + _la = _input.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class NamelistContext extends ParserRuleContext { + public List NAME() { return getTokens(LuaParser.NAME); } + public TerminalNode NAME(int i) { + return getToken(LuaParser.NAME, i); + } + public NamelistContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_namelist; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterNamelist(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitNamelist(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitNamelist(this); + else return visitor.visitChildren(this); + } + } + + public final NamelistContext namelist() throws RecognitionException { + NamelistContext _localctx = new NamelistContext(_ctx, getState()); + enterRule(_localctx, 20, RULE_namelist); + try { + int _alt; + enterOuterAlt(_localctx, 1); + { + setState(206); + match(NAME); + setState(211); + _errHandler.sync(this); + _alt = getInterpreter().adaptivePredict(_input,12,_ctx); + while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) { + if ( _alt==1 ) { + { + { + setState(207); + match(T__10); + setState(208); + match(NAME); + } + } + } + setState(213); + _errHandler.sync(this); + _alt = getInterpreter().adaptivePredict(_input,12,_ctx); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class ExplistContext extends ParserRuleContext { + public List exp() { + return getRuleContexts(ExpContext.class); + } + public ExpContext exp(int i) { + return getRuleContext(ExpContext.class,i); + } + public ExplistContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_explist; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterExplist(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitExplist(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitExplist(this); + else return visitor.visitChildren(this); + } + } + + public final ExplistContext explist() throws RecognitionException { + ExplistContext _localctx = new ExplistContext(_ctx, getState()); + enterRule(_localctx, 22, RULE_explist); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(214); + exp(0); + setState(219); + _errHandler.sync(this); + _la = _input.LA(1); + while (_la==T__10) { + { + { + setState(215); + match(T__10); + setState(216); + exp(0); + } + } + setState(221); + _errHandler.sync(this); + _la = _input.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class ExpContext extends ParserRuleContext { + public ExpContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_exp; } + + public ExpContext() { } + public void copyFrom(ExpContext ctx) { + super.copyFrom(ctx); + } + } + public static class ExpCmpContext extends ExpContext { + public List exp() { + return getRuleContexts(ExpContext.class); + } + public ExpContext exp(int i) { + return getRuleContext(ExpContext.class,i); + } + public OperatorComparisonContext operatorComparison() { + return getRuleContext(OperatorComparisonContext.class,0); + } + public ExpCmpContext(ExpContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterExpCmp(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitExpCmp(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitExpCmp(this); + else return visitor.visitChildren(this); + } + } + public static class ExpNumberContext extends ExpContext { + public NumberContext number() { + return getRuleContext(NumberContext.class,0); + } + public ExpNumberContext(ExpContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterExpNumber(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitExpNumber(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitExpNumber(this); + else return visitor.visitChildren(this); + } + } + public static class ExpThreeDotsContext extends ExpContext { + public ExpThreeDotsContext(ExpContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterExpThreeDots(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitExpThreeDots(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitExpThreeDots(this); + else return visitor.visitChildren(this); + } + } + public static class ExpStrcatContext extends ExpContext { + public List exp() { + return getRuleContexts(ExpContext.class); + } + public ExpContext exp(int i) { + return getRuleContext(ExpContext.class,i); + } + public OperatorStrcatContext operatorStrcat() { + return getRuleContext(OperatorStrcatContext.class,0); + } + public ExpStrcatContext(ExpContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterExpStrcat(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitExpStrcat(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitExpStrcat(this); + else return visitor.visitChildren(this); + } + } + public static class ExpTrueContext extends ExpContext { + public ExpTrueContext(ExpContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterExpTrue(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitExpTrue(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitExpTrue(this); + else return visitor.visitChildren(this); + } + } + public static class ExpOrContext extends ExpContext { + public List exp() { + return getRuleContexts(ExpContext.class); + } + public ExpContext exp(int i) { + return getRuleContext(ExpContext.class,i); + } + public OperatorOrContext operatorOr() { + return getRuleContext(OperatorOrContext.class,0); + } + public ExpOrContext(ExpContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterExpOr(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitExpOr(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitExpOr(this); + else return visitor.visitChildren(this); + } + } + public static class ExpBitwiseContext extends ExpContext { + public List exp() { + return getRuleContexts(ExpContext.class); + } + public ExpContext exp(int i) { + return getRuleContext(ExpContext.class,i); + } + public OperatorBitwiseContext operatorBitwise() { + return getRuleContext(OperatorBitwiseContext.class,0); + } + public ExpBitwiseContext(ExpContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterExpBitwise(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitExpBitwise(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitExpBitwise(this); + else return visitor.visitChildren(this); + } + } + public static class ExpTableCtorContext extends ExpContext { + public TableconstructorContext tableconstructor() { + return getRuleContext(TableconstructorContext.class,0); + } + public ExpTableCtorContext(ExpContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterExpTableCtor(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitExpTableCtor(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitExpTableCtor(this); + else return visitor.visitChildren(this); + } + } + public static class ExpMulDivModContext extends ExpContext { + public List exp() { + return getRuleContexts(ExpContext.class); + } + public ExpContext exp(int i) { + return getRuleContext(ExpContext.class,i); + } + public OperatorMulDivModContext operatorMulDivMod() { + return getRuleContext(OperatorMulDivModContext.class,0); + } + public ExpMulDivModContext(ExpContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterExpMulDivMod(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitExpMulDivMod(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitExpMulDivMod(this); + else return visitor.visitChildren(this); + } + } + public static class ExpFuncDefContext extends ExpContext { + public FunctiondefContext functiondef() { + return getRuleContext(FunctiondefContext.class,0); + } + public ExpFuncDefContext(ExpContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterExpFuncDef(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitExpFuncDef(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitExpFuncDef(this); + else return visitor.visitChildren(this); + } + } + public static class ExpFalseContext extends ExpContext { + public ExpFalseContext(ExpContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterExpFalse(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitExpFalse(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitExpFalse(this); + else return visitor.visitChildren(this); + } + } + public static class ExpStringContext extends ExpContext { + public StringContext string() { + return getRuleContext(StringContext.class,0); + } + public ExpStringContext(ExpContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterExpString(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitExpString(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitExpString(this); + else return visitor.visitChildren(this); + } + } + public static class ExpPrefixContext extends ExpContext { + public PrefixexpContext prefixexp() { + return getRuleContext(PrefixexpContext.class,0); + } + public ExpPrefixContext(ExpContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterExpPrefix(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitExpPrefix(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitExpPrefix(this); + else return visitor.visitChildren(this); + } + } + public static class ExpUnaryContext extends ExpContext { + public OperatorUnaryContext operatorUnary() { + return getRuleContext(OperatorUnaryContext.class,0); + } + public ExpContext exp() { + return getRuleContext(ExpContext.class,0); + } + public ExpUnaryContext(ExpContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterExpUnary(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitExpUnary(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitExpUnary(this); + else return visitor.visitChildren(this); + } + } + public static class ExpAndContext extends ExpContext { + public List exp() { + return getRuleContexts(ExpContext.class); + } + public ExpContext exp(int i) { + return getRuleContext(ExpContext.class,i); + } + public OperatorAndContext operatorAnd() { + return getRuleContext(OperatorAndContext.class,0); + } + public ExpAndContext(ExpContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterExpAnd(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitExpAnd(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitExpAnd(this); + else return visitor.visitChildren(this); + } + } + public static class ExpPowContext extends ExpContext { + public List exp() { + return getRuleContexts(ExpContext.class); + } + public ExpContext exp(int i) { + return getRuleContext(ExpContext.class,i); + } + public OperatorPowerContext operatorPower() { + return getRuleContext(OperatorPowerContext.class,0); + } + public ExpPowContext(ExpContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterExpPow(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitExpPow(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitExpPow(this); + else return visitor.visitChildren(this); + } + } + public static class ExpNilContext extends ExpContext { + public ExpNilContext(ExpContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterExpNil(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitExpNil(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitExpNil(this); + else return visitor.visitChildren(this); + } + } + public static class ExpAddSubContext extends ExpContext { + public List exp() { + return getRuleContexts(ExpContext.class); + } + public ExpContext exp(int i) { + return getRuleContext(ExpContext.class,i); + } + public OperatorAddSubContext operatorAddSub() { + return getRuleContext(OperatorAddSubContext.class,0); + } + public ExpAddSubContext(ExpContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterExpAddSub(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitExpAddSub(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitExpAddSub(this); + else return visitor.visitChildren(this); + } + } + + public final ExpContext exp() throws RecognitionException { + return exp(0); + } + + private ExpContext exp(int _p) throws RecognitionException { + ParserRuleContext _parentctx = _ctx; + int _parentState = getState(); + ExpContext _localctx = new ExpContext(_ctx, _parentState); + ExpContext _prevctx = _localctx; + int _startState = 24; + enterRecursionRule(_localctx, 24, RULE_exp, _p); + try { + int _alt; + enterOuterAlt(_localctx, 1); + { + setState(235); + _errHandler.sync(this); + switch (_input.LA(1)) { + case T__22: + { + _localctx = new ExpNilContext(_localctx); + _ctx = _localctx; + _prevctx = _localctx; + + setState(223); + match(T__22); + } + break; + case T__23: + { + _localctx = new ExpFalseContext(_localctx); + _ctx = _localctx; + _prevctx = _localctx; + setState(224); + match(T__23); + } + break; + case T__24: + { + _localctx = new ExpTrueContext(_localctx); + _ctx = _localctx; + _prevctx = _localctx; + setState(225); + match(T__24); + } + break; + case INT: + case HEX: + case FLOAT: + case HEX_FLOAT: + { + _localctx = new ExpNumberContext(_localctx); + _ctx = _localctx; + _prevctx = _localctx; + setState(226); + number(); + } + break; + case NORMALSTRING: + case CHARSTRING: + case LONGSTRING: + { + _localctx = new ExpStringContext(_localctx); + _ctx = _localctx; + _prevctx = _localctx; + setState(227); + string(); + } + break; + case T__25: + { + _localctx = new ExpThreeDotsContext(_localctx); + _ctx = _localctx; + _prevctx = _localctx; + setState(228); + match(T__25); + } + break; + case T__12: + { + _localctx = new ExpFuncDefContext(_localctx); + _ctx = _localctx; + _prevctx = _localctx; + setState(229); + functiondef(); + } + break; + case T__26: + case NAME: + { + _localctx = new ExpPrefixContext(_localctx); + _ctx = _localctx; + _prevctx = _localctx; + setState(230); + prefixexp(); + } + break; + case T__30: + { + _localctx = new ExpTableCtorContext(_localctx); + _ctx = _localctx; + _prevctx = _localctx; + setState(231); + tableconstructor(); + } + break; + case T__42: + case T__49: + case T__52: + case T__53: + { + _localctx = new ExpUnaryContext(_localctx); + _ctx = _localctx; + _prevctx = _localctx; + setState(232); + operatorUnary(); + setState(233); + exp(8); + } + break; + default: + throw new NoViableAltException(this); + } + _ctx.stop = _input.LT(-1); + setState(271); + _errHandler.sync(this); + _alt = getInterpreter().adaptivePredict(_input,16,_ctx); + while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) { + if ( _alt==1 ) { + if ( _parseListeners!=null ) triggerExitRuleEvent(); + _prevctx = _localctx; + { + setState(269); + _errHandler.sync(this); + switch ( getInterpreter().adaptivePredict(_input,15,_ctx) ) { + case 1: + { + _localctx = new ExpPowContext(new ExpContext(_parentctx, _parentState)); + pushNewRecursionContext(_localctx, _startState, RULE_exp); + setState(237); + if (!(precpred(_ctx, 9))) throw new FailedPredicateException(this, "precpred(_ctx, 9)"); + setState(238); + operatorPower(); + setState(239); + exp(9); + } + break; + case 2: + { + _localctx = new ExpMulDivModContext(new ExpContext(_parentctx, _parentState)); + pushNewRecursionContext(_localctx, _startState, RULE_exp); + setState(241); + if (!(precpred(_ctx, 7))) throw new FailedPredicateException(this, "precpred(_ctx, 7)"); + setState(242); + operatorMulDivMod(); + setState(243); + exp(8); + } + break; + case 3: + { + _localctx = new ExpAddSubContext(new ExpContext(_parentctx, _parentState)); + pushNewRecursionContext(_localctx, _startState, RULE_exp); + setState(245); + if (!(precpred(_ctx, 6))) throw new FailedPredicateException(this, "precpred(_ctx, 6)"); + setState(246); + operatorAddSub(); + setState(247); + exp(7); + } + break; + case 4: + { + _localctx = new ExpStrcatContext(new ExpContext(_parentctx, _parentState)); + pushNewRecursionContext(_localctx, _startState, RULE_exp); + setState(249); + if (!(precpred(_ctx, 5))) throw new FailedPredicateException(this, "precpred(_ctx, 5)"); + setState(250); + operatorStrcat(); + setState(251); + exp(5); + } + break; + case 5: + { + _localctx = new ExpCmpContext(new ExpContext(_parentctx, _parentState)); + pushNewRecursionContext(_localctx, _startState, RULE_exp); + setState(253); + if (!(precpred(_ctx, 4))) throw new FailedPredicateException(this, "precpred(_ctx, 4)"); + setState(254); + operatorComparison(); + setState(255); + exp(5); + } + break; + case 6: + { + _localctx = new ExpAndContext(new ExpContext(_parentctx, _parentState)); + pushNewRecursionContext(_localctx, _startState, RULE_exp); + setState(257); + if (!(precpred(_ctx, 3))) throw new FailedPredicateException(this, "precpred(_ctx, 3)"); + setState(258); + operatorAnd(); + setState(259); + exp(4); + } + break; + case 7: + { + _localctx = new ExpOrContext(new ExpContext(_parentctx, _parentState)); + pushNewRecursionContext(_localctx, _startState, RULE_exp); + setState(261); + if (!(precpred(_ctx, 2))) throw new FailedPredicateException(this, "precpred(_ctx, 2)"); + setState(262); + operatorOr(); + setState(263); + exp(3); + } + break; + case 8: + { + _localctx = new ExpBitwiseContext(new ExpContext(_parentctx, _parentState)); + pushNewRecursionContext(_localctx, _startState, RULE_exp); + setState(265); + if (!(precpred(_ctx, 1))) throw new FailedPredicateException(this, "precpred(_ctx, 1)"); + setState(266); + operatorBitwise(); + setState(267); + exp(2); + } + break; + } + } + } + setState(273); + _errHandler.sync(this); + _alt = getInterpreter().adaptivePredict(_input,16,_ctx); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + unrollRecursionContexts(_parentctx); + } + return _localctx; + } + + public static class PrefixexpContext extends ParserRuleContext { + public VarOrExpContext varOrExp() { + return getRuleContext(VarOrExpContext.class,0); + } + public List nameAndArgs() { + return getRuleContexts(NameAndArgsContext.class); + } + public NameAndArgsContext nameAndArgs(int i) { + return getRuleContext(NameAndArgsContext.class,i); + } + public PrefixexpContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_prefixexp; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterPrefixexp(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitPrefixexp(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitPrefixexp(this); + else return visitor.visitChildren(this); + } + } + + public final PrefixexpContext prefixexp() throws RecognitionException { + PrefixexpContext _localctx = new PrefixexpContext(_ctx, getState()); + enterRule(_localctx, 26, RULE_prefixexp); + try { + int _alt; + enterOuterAlt(_localctx, 1); + { + setState(274); + varOrExp(); + setState(278); + _errHandler.sync(this); + _alt = getInterpreter().adaptivePredict(_input,17,_ctx); + while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) { + if ( _alt==1 ) { + { + { + setState(275); + nameAndArgs(); + } + } + } + setState(280); + _errHandler.sync(this); + _alt = getInterpreter().adaptivePredict(_input,17,_ctx); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class FunctioncallContext extends ParserRuleContext { + public VarOrExpContext varOrExp() { + return getRuleContext(VarOrExpContext.class,0); + } + public List nameAndArgs() { + return getRuleContexts(NameAndArgsContext.class); + } + public NameAndArgsContext nameAndArgs(int i) { + return getRuleContext(NameAndArgsContext.class,i); + } + public FunctioncallContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_functioncall; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterFunctioncall(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitFunctioncall(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitFunctioncall(this); + else return visitor.visitChildren(this); + } + } + + public final FunctioncallContext functioncall() throws RecognitionException { + FunctioncallContext _localctx = new FunctioncallContext(_ctx, getState()); + enterRule(_localctx, 28, RULE_functioncall); + try { + int _alt; + enterOuterAlt(_localctx, 1); + { + setState(281); + varOrExp(); + setState(283); + _errHandler.sync(this); + _alt = 1; + do { + switch (_alt) { + case 1: + { + { + setState(282); + nameAndArgs(); + } + } + break; + default: + throw new NoViableAltException(this); + } + setState(285); + _errHandler.sync(this); + _alt = getInterpreter().adaptivePredict(_input,18,_ctx); + } while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class VarOrExpContext extends ParserRuleContext { + public VarContext var() { + return getRuleContext(VarContext.class,0); + } + public ExpContext exp() { + return getRuleContext(ExpContext.class,0); + } + public VarOrExpContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_varOrExp; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterVarOrExp(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitVarOrExp(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitVarOrExp(this); + else return visitor.visitChildren(this); + } + } + + public final VarOrExpContext varOrExp() throws RecognitionException { + VarOrExpContext _localctx = new VarOrExpContext(_ctx, getState()); + enterRule(_localctx, 30, RULE_varOrExp); + try { + setState(292); + _errHandler.sync(this); + switch ( getInterpreter().adaptivePredict(_input,19,_ctx) ) { + case 1: + enterOuterAlt(_localctx, 1); + { + setState(287); + var(); + } + break; + case 2: + enterOuterAlt(_localctx, 2); + { + setState(288); + match(T__26); + setState(289); + exp(0); + setState(290); + match(T__27); + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class VarContext extends ParserRuleContext { + public TerminalNode NAME() { return getToken(LuaParser.NAME, 0); } + public ExpContext exp() { + return getRuleContext(ExpContext.class,0); + } + public List varSuffix() { + return getRuleContexts(VarSuffixContext.class); + } + public VarSuffixContext varSuffix(int i) { + return getRuleContext(VarSuffixContext.class,i); + } + public VarContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_var; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterVar(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitVar(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitVar(this); + else return visitor.visitChildren(this); + } + } + + public final VarContext var() throws RecognitionException { + VarContext _localctx = new VarContext(_ctx, getState()); + enterRule(_localctx, 32, RULE_var); + try { + int _alt; + enterOuterAlt(_localctx, 1); + { + setState(300); + _errHandler.sync(this); + switch (_input.LA(1)) { + case NAME: + { + setState(294); + match(NAME); + } + break; + case T__26: + { + setState(295); + match(T__26); + setState(296); + exp(0); + setState(297); + match(T__27); + setState(298); + varSuffix(); + } + break; + default: + throw new NoViableAltException(this); + } + setState(305); + _errHandler.sync(this); + _alt = getInterpreter().adaptivePredict(_input,21,_ctx); + while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) { + if ( _alt==1 ) { + { + { + setState(302); + varSuffix(); + } + } + } + setState(307); + _errHandler.sync(this); + _alt = getInterpreter().adaptivePredict(_input,21,_ctx); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class VarSuffixContext extends ParserRuleContext { + public ExpContext exp() { + return getRuleContext(ExpContext.class,0); + } + public TerminalNode NAME() { return getToken(LuaParser.NAME, 0); } + public List nameAndArgs() { + return getRuleContexts(NameAndArgsContext.class); + } + public NameAndArgsContext nameAndArgs(int i) { + return getRuleContext(NameAndArgsContext.class,i); + } + public VarSuffixContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_varSuffix; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterVarSuffix(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitVarSuffix(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitVarSuffix(this); + else return visitor.visitChildren(this); + } + } + + public final VarSuffixContext varSuffix() throws RecognitionException { + VarSuffixContext _localctx = new VarSuffixContext(_ctx, getState()); + enterRule(_localctx, 34, RULE_varSuffix); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(311); + _errHandler.sync(this); + _la = _input.LA(1); + while ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << T__21) | (1L << T__26) | (1L << T__30) | (1L << NORMALSTRING) | (1L << CHARSTRING) | (1L << LONGSTRING))) != 0)) { + { + { + setState(308); + nameAndArgs(); + } + } + setState(313); + _errHandler.sync(this); + _la = _input.LA(1); + } + setState(320); + _errHandler.sync(this); + switch (_input.LA(1)) { + case T__28: + { + setState(314); + match(T__28); + setState(315); + exp(0); + setState(316); + match(T__29); + } + break; + case T__20: + { + setState(318); + match(T__20); + setState(319); + match(NAME); + } + break; + default: + throw new NoViableAltException(this); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class NameAndArgsContext extends ParserRuleContext { + public ArgsContext args() { + return getRuleContext(ArgsContext.class,0); + } + public TerminalNode NAME() { return getToken(LuaParser.NAME, 0); } + public NameAndArgsContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_nameAndArgs; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterNameAndArgs(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitNameAndArgs(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitNameAndArgs(this); + else return visitor.visitChildren(this); + } + } + + public final NameAndArgsContext nameAndArgs() throws RecognitionException { + NameAndArgsContext _localctx = new NameAndArgsContext(_ctx, getState()); + enterRule(_localctx, 36, RULE_nameAndArgs); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(324); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==T__21) { + { + setState(322); + match(T__21); + setState(323); + match(NAME); + } + } + + setState(326); + args(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class ArgsContext extends ParserRuleContext { + public ExplistContext explist() { + return getRuleContext(ExplistContext.class,0); + } + public TableconstructorContext tableconstructor() { + return getRuleContext(TableconstructorContext.class,0); + } + public StringContext string() { + return getRuleContext(StringContext.class,0); + } + public ArgsContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_args; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterArgs(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitArgs(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitArgs(this); + else return visitor.visitChildren(this); + } + } + + public final ArgsContext args() throws RecognitionException { + ArgsContext _localctx = new ArgsContext(_ctx, getState()); + enterRule(_localctx, 38, RULE_args); + int _la; + try { + setState(335); + _errHandler.sync(this); + switch (_input.LA(1)) { + case T__26: + enterOuterAlt(_localctx, 1); + { + setState(328); + match(T__26); + setState(330); + _errHandler.sync(this); + _la = _input.LA(1); + if ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << T__12) | (1L << T__22) | (1L << T__23) | (1L << T__24) | (1L << T__25) | (1L << T__26) | (1L << T__30) | (1L << T__42) | (1L << T__49) | (1L << T__52) | (1L << T__53) | (1L << NAME) | (1L << NORMALSTRING) | (1L << CHARSTRING) | (1L << LONGSTRING) | (1L << INT) | (1L << HEX) | (1L << FLOAT) | (1L << HEX_FLOAT))) != 0)) { + { + setState(329); + explist(); + } + } + + setState(332); + match(T__27); + } + break; + case T__30: + enterOuterAlt(_localctx, 2); + { + setState(333); + tableconstructor(); + } + break; + case NORMALSTRING: + case CHARSTRING: + case LONGSTRING: + enterOuterAlt(_localctx, 3); + { + setState(334); + string(); + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class FunctiondefContext extends ParserRuleContext { + public FuncbodyContext funcbody() { + return getRuleContext(FuncbodyContext.class,0); + } + public FunctiondefContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_functiondef; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterFunctiondef(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitFunctiondef(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitFunctiondef(this); + else return visitor.visitChildren(this); + } + } + + public final FunctiondefContext functiondef() throws RecognitionException { + FunctiondefContext _localctx = new FunctiondefContext(_ctx, getState()); + enterRule(_localctx, 40, RULE_functiondef); + try { + enterOuterAlt(_localctx, 1); + { + setState(337); + match(T__12); + setState(338); + funcbody(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class FuncbodyContext extends ParserRuleContext { + public BlockContext block() { + return getRuleContext(BlockContext.class,0); + } + public ParlistContext parlist() { + return getRuleContext(ParlistContext.class,0); + } + public FuncbodyContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_funcbody; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterFuncbody(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitFuncbody(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitFuncbody(this); + else return visitor.visitChildren(this); + } + } + + public final FuncbodyContext funcbody() throws RecognitionException { + FuncbodyContext _localctx = new FuncbodyContext(_ctx, getState()); + enterRule(_localctx, 42, RULE_funcbody); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(340); + match(T__26); + setState(342); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==T__25 || _la==NAME) { + { + setState(341); + parlist(); + } + } + + setState(344); + match(T__27); + setState(345); + block(); + setState(346); + match(T__5); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class ParlistContext extends ParserRuleContext { + public NamelistContext namelist() { + return getRuleContext(NamelistContext.class,0); + } + public ParlistContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_parlist; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterParlist(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitParlist(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitParlist(this); + else return visitor.visitChildren(this); + } + } + + public final ParlistContext parlist() throws RecognitionException { + ParlistContext _localctx = new ParlistContext(_ctx, getState()); + enterRule(_localctx, 44, RULE_parlist); + int _la; + try { + setState(354); + _errHandler.sync(this); + switch (_input.LA(1)) { + case NAME: + enterOuterAlt(_localctx, 1); + { + setState(348); + namelist(); + setState(351); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==T__10) { + { + setState(349); + match(T__10); + setState(350); + match(T__25); + } + } + + } + break; + case T__25: + enterOuterAlt(_localctx, 2); + { + setState(353); + match(T__25); + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class TableconstructorContext extends ParserRuleContext { + public FieldlistContext fieldlist() { + return getRuleContext(FieldlistContext.class,0); + } + public TableconstructorContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_tableconstructor; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterTableconstructor(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitTableconstructor(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitTableconstructor(this); + else return visitor.visitChildren(this); + } + } + + public final TableconstructorContext tableconstructor() throws RecognitionException { + TableconstructorContext _localctx = new TableconstructorContext(_ctx, getState()); + enterRule(_localctx, 46, RULE_tableconstructor); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(356); + match(T__30); + setState(358); + _errHandler.sync(this); + _la = _input.LA(1); + if ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << T__12) | (1L << T__22) | (1L << T__23) | (1L << T__24) | (1L << T__25) | (1L << T__26) | (1L << T__28) | (1L << T__30) | (1L << T__42) | (1L << T__49) | (1L << T__52) | (1L << T__53) | (1L << NAME) | (1L << NORMALSTRING) | (1L << CHARSTRING) | (1L << LONGSTRING) | (1L << INT) | (1L << HEX) | (1L << FLOAT) | (1L << HEX_FLOAT))) != 0)) { + { + setState(357); + fieldlist(); + } + } + + setState(360); + match(T__31); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class FieldlistContext extends ParserRuleContext { + public List field() { + return getRuleContexts(FieldContext.class); + } + public FieldContext field(int i) { + return getRuleContext(FieldContext.class,i); + } + public List fieldsep() { + return getRuleContexts(FieldsepContext.class); + } + public FieldsepContext fieldsep(int i) { + return getRuleContext(FieldsepContext.class,i); + } + public FieldlistContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_fieldlist; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterFieldlist(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitFieldlist(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitFieldlist(this); + else return visitor.visitChildren(this); + } + } + + public final FieldlistContext fieldlist() throws RecognitionException { + FieldlistContext _localctx = new FieldlistContext(_ctx, getState()); + enterRule(_localctx, 48, RULE_fieldlist); + int _la; + try { + int _alt; + enterOuterAlt(_localctx, 1); + { + setState(362); + field(); + setState(368); + _errHandler.sync(this); + _alt = getInterpreter().adaptivePredict(_input,31,_ctx); + while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) { + if ( _alt==1 ) { + { + { + setState(363); + fieldsep(); + setState(364); + field(); + } + } + } + setState(370); + _errHandler.sync(this); + _alt = getInterpreter().adaptivePredict(_input,31,_ctx); + } + setState(372); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==T__0 || _la==T__10) { + { + setState(371); + fieldsep(); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class FieldContext extends ParserRuleContext { + public List exp() { + return getRuleContexts(ExpContext.class); + } + public ExpContext exp(int i) { + return getRuleContext(ExpContext.class,i); + } + public TerminalNode NAME() { return getToken(LuaParser.NAME, 0); } + public FieldContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_field; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterField(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitField(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitField(this); + else return visitor.visitChildren(this); + } + } + + public final FieldContext field() throws RecognitionException { + FieldContext _localctx = new FieldContext(_ctx, getState()); + enterRule(_localctx, 50, RULE_field); + try { + setState(384); + _errHandler.sync(this); + switch ( getInterpreter().adaptivePredict(_input,33,_ctx) ) { + case 1: + enterOuterAlt(_localctx, 1); + { + setState(374); + match(T__28); + setState(375); + exp(0); + setState(376); + match(T__29); + setState(377); + match(T__1); + setState(378); + exp(0); + } + break; + case 2: + enterOuterAlt(_localctx, 2); + { + setState(380); + match(NAME); + setState(381); + match(T__1); + setState(382); + exp(0); + } + break; + case 3: + enterOuterAlt(_localctx, 3); + { + setState(383); + exp(0); + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class FieldsepContext extends ParserRuleContext { + public FieldsepContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_fieldsep; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterFieldsep(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitFieldsep(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitFieldsep(this); + else return visitor.visitChildren(this); + } + } + + public final FieldsepContext fieldsep() throws RecognitionException { + FieldsepContext _localctx = new FieldsepContext(_ctx, getState()); + enterRule(_localctx, 52, RULE_fieldsep); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(386); + _la = _input.LA(1); + if ( !(_la==T__0 || _la==T__10) ) { + _errHandler.recoverInline(this); + } + else { + if ( _input.LA(1)==Token.EOF ) matchedEOF = true; + _errHandler.reportMatch(this); + consume(); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class OperatorOrContext extends ParserRuleContext { + public OperatorOrContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_operatorOr; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterOperatorOr(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitOperatorOr(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitOperatorOr(this); + else return visitor.visitChildren(this); + } + } + + public final OperatorOrContext operatorOr() throws RecognitionException { + OperatorOrContext _localctx = new OperatorOrContext(_ctx, getState()); + enterRule(_localctx, 54, RULE_operatorOr); + try { + enterOuterAlt(_localctx, 1); + { + setState(388); + match(T__32); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class OperatorAndContext extends ParserRuleContext { + public OperatorAndContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_operatorAnd; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterOperatorAnd(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitOperatorAnd(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitOperatorAnd(this); + else return visitor.visitChildren(this); + } + } + + public final OperatorAndContext operatorAnd() throws RecognitionException { + OperatorAndContext _localctx = new OperatorAndContext(_ctx, getState()); + enterRule(_localctx, 56, RULE_operatorAnd); + try { + enterOuterAlt(_localctx, 1); + { + setState(390); + match(T__33); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class OperatorComparisonContext extends ParserRuleContext { + public OperatorComparisonContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_operatorComparison; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterOperatorComparison(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitOperatorComparison(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitOperatorComparison(this); + else return visitor.visitChildren(this); + } + } + + public final OperatorComparisonContext operatorComparison() throws RecognitionException { + OperatorComparisonContext _localctx = new OperatorComparisonContext(_ctx, getState()); + enterRule(_localctx, 58, RULE_operatorComparison); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(392); + _la = _input.LA(1); + if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << T__34) | (1L << T__35) | (1L << T__36) | (1L << T__37) | (1L << T__38) | (1L << T__39))) != 0)) ) { + _errHandler.recoverInline(this); + } + else { + if ( _input.LA(1)==Token.EOF ) matchedEOF = true; + _errHandler.reportMatch(this); + consume(); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class OperatorStrcatContext extends ParserRuleContext { + public OperatorStrcatContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_operatorStrcat; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterOperatorStrcat(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitOperatorStrcat(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitOperatorStrcat(this); + else return visitor.visitChildren(this); + } + } + + public final OperatorStrcatContext operatorStrcat() throws RecognitionException { + OperatorStrcatContext _localctx = new OperatorStrcatContext(_ctx, getState()); + enterRule(_localctx, 60, RULE_operatorStrcat); + try { + enterOuterAlt(_localctx, 1); + { + setState(394); + match(T__40); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class OperatorAddSubContext extends ParserRuleContext { + public OperatorAddSubContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_operatorAddSub; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterOperatorAddSub(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitOperatorAddSub(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitOperatorAddSub(this); + else return visitor.visitChildren(this); + } + } + + public final OperatorAddSubContext operatorAddSub() throws RecognitionException { + OperatorAddSubContext _localctx = new OperatorAddSubContext(_ctx, getState()); + enterRule(_localctx, 62, RULE_operatorAddSub); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(396); + _la = _input.LA(1); + if ( !(_la==T__41 || _la==T__42) ) { + _errHandler.recoverInline(this); + } + else { + if ( _input.LA(1)==Token.EOF ) matchedEOF = true; + _errHandler.reportMatch(this); + consume(); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class OperatorMulDivModContext extends ParserRuleContext { + public OperatorMulDivModContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_operatorMulDivMod; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterOperatorMulDivMod(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitOperatorMulDivMod(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitOperatorMulDivMod(this); + else return visitor.visitChildren(this); + } + } + + public final OperatorMulDivModContext operatorMulDivMod() throws RecognitionException { + OperatorMulDivModContext _localctx = new OperatorMulDivModContext(_ctx, getState()); + enterRule(_localctx, 64, RULE_operatorMulDivMod); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(398); + _la = _input.LA(1); + if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << T__43) | (1L << T__44) | (1L << T__45) | (1L << T__46))) != 0)) ) { + _errHandler.recoverInline(this); + } + else { + if ( _input.LA(1)==Token.EOF ) matchedEOF = true; + _errHandler.reportMatch(this); + consume(); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class OperatorBitwiseContext extends ParserRuleContext { + public OperatorBitwiseContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_operatorBitwise; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterOperatorBitwise(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitOperatorBitwise(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitOperatorBitwise(this); + else return visitor.visitChildren(this); + } + } + + public final OperatorBitwiseContext operatorBitwise() throws RecognitionException { + OperatorBitwiseContext _localctx = new OperatorBitwiseContext(_ctx, getState()); + enterRule(_localctx, 66, RULE_operatorBitwise); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(400); + _la = _input.LA(1); + if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << T__47) | (1L << T__48) | (1L << T__49) | (1L << T__50) | (1L << T__51))) != 0)) ) { + _errHandler.recoverInline(this); + } + else { + if ( _input.LA(1)==Token.EOF ) matchedEOF = true; + _errHandler.reportMatch(this); + consume(); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class OperatorUnaryContext extends ParserRuleContext { + public OperatorUnaryContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_operatorUnary; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterOperatorUnary(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitOperatorUnary(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitOperatorUnary(this); + else return visitor.visitChildren(this); + } + } + + public final OperatorUnaryContext operatorUnary() throws RecognitionException { + OperatorUnaryContext _localctx = new OperatorUnaryContext(_ctx, getState()); + enterRule(_localctx, 68, RULE_operatorUnary); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(402); + _la = _input.LA(1); + if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << T__42) | (1L << T__49) | (1L << T__52) | (1L << T__53))) != 0)) ) { + _errHandler.recoverInline(this); + } + else { + if ( _input.LA(1)==Token.EOF ) matchedEOF = true; + _errHandler.reportMatch(this); + consume(); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class OperatorPowerContext extends ParserRuleContext { + public OperatorPowerContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_operatorPower; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterOperatorPower(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitOperatorPower(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitOperatorPower(this); + else return visitor.visitChildren(this); + } + } + + public final OperatorPowerContext operatorPower() throws RecognitionException { + OperatorPowerContext _localctx = new OperatorPowerContext(_ctx, getState()); + enterRule(_localctx, 70, RULE_operatorPower); + try { + enterOuterAlt(_localctx, 1); + { + setState(404); + match(T__54); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class NumberContext extends ParserRuleContext { + public TerminalNode INT() { return getToken(LuaParser.INT, 0); } + public TerminalNode HEX() { return getToken(LuaParser.HEX, 0); } + public TerminalNode FLOAT() { return getToken(LuaParser.FLOAT, 0); } + public TerminalNode HEX_FLOAT() { return getToken(LuaParser.HEX_FLOAT, 0); } + public NumberContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_number; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterNumber(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitNumber(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitNumber(this); + else return visitor.visitChildren(this); + } + } + + public final NumberContext number() throws RecognitionException { + NumberContext _localctx = new NumberContext(_ctx, getState()); + enterRule(_localctx, 72, RULE_number); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(406); + _la = _input.LA(1); + if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << INT) | (1L << HEX) | (1L << FLOAT) | (1L << HEX_FLOAT))) != 0)) ) { + _errHandler.recoverInline(this); + } + else { + if ( _input.LA(1)==Token.EOF ) matchedEOF = true; + _errHandler.reportMatch(this); + consume(); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class StringContext extends ParserRuleContext { + public TerminalNode NORMALSTRING() { return getToken(LuaParser.NORMALSTRING, 0); } + public TerminalNode CHARSTRING() { return getToken(LuaParser.CHARSTRING, 0); } + public TerminalNode LONGSTRING() { return getToken(LuaParser.LONGSTRING, 0); } + public StringContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_string; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).enterString(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof LuaListener ) ((LuaListener)listener).exitString(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof LuaVisitor ) return ((LuaVisitor)visitor).visitString(this); + else return visitor.visitChildren(this); + } + } + + public final StringContext string() throws RecognitionException { + StringContext _localctx = new StringContext(_ctx, getState()); + enterRule(_localctx, 74, RULE_string); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(408); + _la = _input.LA(1); + if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << NORMALSTRING) | (1L << CHARSTRING) | (1L << LONGSTRING))) != 0)) ) { + _errHandler.recoverInline(this); + } + else { + if ( _input.LA(1)==Token.EOF ) matchedEOF = true; + _errHandler.reportMatch(this); + consume(); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public boolean sempred(RuleContext _localctx, int ruleIndex, int predIndex) { + switch (ruleIndex) { + case 12: + return exp_sempred((ExpContext)_localctx, predIndex); + } + return true; + } + private boolean exp_sempred(ExpContext _localctx, int predIndex) { + switch (predIndex) { + case 0: + return precpred(_ctx, 9); + case 1: + return precpred(_ctx, 7); + case 2: + return precpred(_ctx, 6); + case 3: + return precpred(_ctx, 5); + case 4: + return precpred(_ctx, 4); + case 5: + return precpred(_ctx, 3); + case 6: + return precpred(_ctx, 2); + case 7: + return precpred(_ctx, 1); + } + return true; + } + + public static final String _serializedATN = + "\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\3E\u019d\4\2\t\2\4"+ + "\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13\t"+ + "\13\4\f\t\f\4\r\t\r\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22\t\22"+ + "\4\23\t\23\4\24\t\24\4\25\t\25\4\26\t\26\4\27\t\27\4\30\t\30\4\31\t\31"+ + "\4\32\t\32\4\33\t\33\4\34\t\34\4\35\t\35\4\36\t\36\4\37\t\37\4 \t \4!"+ + "\t!\4\"\t\"\4#\t#\4$\t$\4%\t%\4&\t&\4\'\t\'\3\2\3\2\3\2\3\3\7\3S\n\3\f"+ + "\3\16\3V\13\3\3\3\5\3Y\n\3\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4"+ + "\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3"+ + "\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\5\4\u0081\n\4\3\4\3\4\3\4\3\4\3"+ + "\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4"+ + "\3\4\3\4\5\4\u009b\n\4\5\4\u009d\n\4\3\5\3\5\3\5\3\5\3\5\3\6\3\6\3\6\3"+ + "\6\3\6\7\6\u00a9\n\6\f\6\16\6\u00ac\13\6\3\7\3\7\5\7\u00b0\n\7\3\b\3\b"+ + "\5\b\u00b4\n\b\3\b\5\b\u00b7\n\b\3\t\3\t\3\t\3\t\3\n\3\n\3\n\7\n\u00c0"+ + "\n\n\f\n\16\n\u00c3\13\n\3\n\3\n\5\n\u00c7\n\n\3\13\3\13\3\13\7\13\u00cc"+ + "\n\13\f\13\16\13\u00cf\13\13\3\f\3\f\3\f\7\f\u00d4\n\f\f\f\16\f\u00d7"+ + "\13\f\3\r\3\r\3\r\7\r\u00dc\n\r\f\r\16\r\u00df\13\r\3\16\3\16\3\16\3\16"+ + "\3\16\3\16\3\16\3\16\3\16\3\16\3\16\3\16\3\16\5\16\u00ee\n\16\3\16\3\16"+ + "\3\16\3\16\3\16\3\16\3\16\3\16\3\16\3\16\3\16\3\16\3\16\3\16\3\16\3\16"+ + "\3\16\3\16\3\16\3\16\3\16\3\16\3\16\3\16\3\16\3\16\3\16\3\16\3\16\3\16"+ + "\3\16\3\16\7\16\u0110\n\16\f\16\16\16\u0113\13\16\3\17\3\17\7\17\u0117"+ + "\n\17\f\17\16\17\u011a\13\17\3\20\3\20\6\20\u011e\n\20\r\20\16\20\u011f"+ + "\3\21\3\21\3\21\3\21\3\21\5\21\u0127\n\21\3\22\3\22\3\22\3\22\3\22\3\22"+ + "\5\22\u012f\n\22\3\22\7\22\u0132\n\22\f\22\16\22\u0135\13\22\3\23\7\23"+ + "\u0138\n\23\f\23\16\23\u013b\13\23\3\23\3\23\3\23\3\23\3\23\3\23\5\23"+ + "\u0143\n\23\3\24\3\24\5\24\u0147\n\24\3\24\3\24\3\25\3\25\5\25\u014d\n"+ + "\25\3\25\3\25\3\25\5\25\u0152\n\25\3\26\3\26\3\26\3\27\3\27\5\27\u0159"+ + "\n\27\3\27\3\27\3\27\3\27\3\30\3\30\3\30\5\30\u0162\n\30\3\30\5\30\u0165"+ + "\n\30\3\31\3\31\5\31\u0169\n\31\3\31\3\31\3\32\3\32\3\32\3\32\7\32\u0171"+ + "\n\32\f\32\16\32\u0174\13\32\3\32\5\32\u0177\n\32\3\33\3\33\3\33\3\33"+ + "\3\33\3\33\3\33\3\33\3\33\3\33\5\33\u0183\n\33\3\34\3\34\3\35\3\35\3\36"+ + "\3\36\3\37\3\37\3 \3 \3!\3!\3\"\3\"\3#\3#\3$\3$\3%\3%\3&\3&\3\'\3\'\3"+ + "\'\2\3\32(\2\4\6\b\n\f\16\20\22\24\26\30\32\34\36 \"$&(*,.\60\62\64\66"+ + "8:<>@BDFHJL\2\n\4\2\3\3\r\r\3\2%*\3\2,-\3\2.\61\3\2\62\66\5\2--\64\64"+ + "\678\3\2>A\3\2;=\2\u01b5\2N\3\2\2\2\4T\3\2\2\2\6\u009c\3\2\2\2\b\u009e"+ + "\3\2\2\2\n\u00aa\3\2\2\2\f\u00af\3\2\2\2\16\u00b1\3\2\2\2\20\u00b8\3\2"+ + "\2\2\22\u00bc\3\2\2\2\24\u00c8\3\2\2\2\26\u00d0\3\2\2\2\30\u00d8\3\2\2"+ + "\2\32\u00ed\3\2\2\2\34\u0114\3\2\2\2\36\u011b\3\2\2\2 \u0126\3\2\2\2\""+ + "\u012e\3\2\2\2$\u0139\3\2\2\2&\u0146\3\2\2\2(\u0151\3\2\2\2*\u0153\3\2"+ + "\2\2,\u0156\3\2\2\2.\u0164\3\2\2\2\60\u0166\3\2\2\2\62\u016c\3\2\2\2\64"+ + "\u0182\3\2\2\2\66\u0184\3\2\2\28\u0186\3\2\2\2:\u0188\3\2\2\2<\u018a\3"+ + "\2\2\2>\u018c\3\2\2\2@\u018e\3\2\2\2B\u0190\3\2\2\2D\u0192\3\2\2\2F\u0194"+ + "\3\2\2\2H\u0196\3\2\2\2J\u0198\3\2\2\2L\u019a\3\2\2\2NO\5\4\3\2OP\7\2"+ + "\2\3P\3\3\2\2\2QS\5\6\4\2RQ\3\2\2\2SV\3\2\2\2TR\3\2\2\2TU\3\2\2\2UX\3"+ + "\2\2\2VT\3\2\2\2WY\5\16\b\2XW\3\2\2\2XY\3\2\2\2Y\5\3\2\2\2Z\u009d\7\3"+ + "\2\2[\\\5\24\13\2\\]\7\4\2\2]^\5\30\r\2^\u009d\3\2\2\2_\u009d\5\36\20"+ + "\2`\u009d\5\20\t\2a\u009d\7\5\2\2bc\7\6\2\2c\u009d\7:\2\2de\7\7\2\2ef"+ + "\5\4\3\2fg\7\b\2\2g\u009d\3\2\2\2hi\7\t\2\2ij\5\32\16\2jk\7\7\2\2kl\5"+ + "\4\3\2lm\7\b\2\2m\u009d\3\2\2\2no\7\n\2\2op\5\4\3\2pq\7\13\2\2qr\5\32"+ + "\16\2r\u009d\3\2\2\2st\5\b\5\2tu\5\n\6\2uv\5\f\7\2vw\7\b\2\2w\u009d\3"+ + "\2\2\2xy\7\f\2\2yz\7:\2\2z{\7\4\2\2{|\5\32\16\2|}\7\r\2\2}\u0080\5\32"+ + "\16\2~\177\7\r\2\2\177\u0081\5\32\16\2\u0080~\3\2\2\2\u0080\u0081\3\2"+ + "\2\2\u0081\u0082\3\2\2\2\u0082\u0083\7\7\2\2\u0083\u0084\5\4\3\2\u0084"+ + "\u0085\7\b\2\2\u0085\u009d\3\2\2\2\u0086\u0087\7\f\2\2\u0087\u0088\5\26"+ + "\f\2\u0088\u0089\7\16\2\2\u0089\u008a\5\30\r\2\u008a\u008b\7\7\2\2\u008b"+ + "\u008c\5\4\3\2\u008c\u008d\7\b\2\2\u008d\u009d\3\2\2\2\u008e\u008f\7\17"+ + "\2\2\u008f\u0090\5\22\n\2\u0090\u0091\5,\27\2\u0091\u009d\3\2\2\2\u0092"+ + "\u0093\7\20\2\2\u0093\u0094\7\17\2\2\u0094\u0095\7:\2\2\u0095\u009d\5"+ + ",\27\2\u0096\u0097\7\20\2\2\u0097\u009a\5\26\f\2\u0098\u0099\7\4\2\2\u0099"+ + "\u009b\5\30\r\2\u009a\u0098\3\2\2\2\u009a\u009b\3\2\2\2\u009b\u009d\3"+ + "\2\2\2\u009cZ\3\2\2\2\u009c[\3\2\2\2\u009c_\3\2\2\2\u009c`\3\2\2\2\u009c"+ + "a\3\2\2\2\u009cb\3\2\2\2\u009cd\3\2\2\2\u009ch\3\2\2\2\u009cn\3\2\2\2"+ + "\u009cs\3\2\2\2\u009cx\3\2\2\2\u009c\u0086\3\2\2\2\u009c\u008e\3\2\2\2"+ + "\u009c\u0092\3\2\2\2\u009c\u0096\3\2\2\2\u009d\7\3\2\2\2\u009e\u009f\7"+ + "\21\2\2\u009f\u00a0\5\32\16\2\u00a0\u00a1\7\22\2\2\u00a1\u00a2\5\4\3\2"+ + "\u00a2\t\3\2\2\2\u00a3\u00a4\7\23\2\2\u00a4\u00a5\5\32\16\2\u00a5\u00a6"+ + "\7\22\2\2\u00a6\u00a7\5\4\3\2\u00a7\u00a9\3\2\2\2\u00a8\u00a3\3\2\2\2"+ + "\u00a9\u00ac\3\2\2\2\u00aa\u00a8\3\2\2\2\u00aa\u00ab\3\2\2\2\u00ab\13"+ + "\3\2\2\2\u00ac\u00aa\3\2\2\2\u00ad\u00ae\7\24\2\2\u00ae\u00b0\5\4\3\2"+ + "\u00af\u00ad\3\2\2\2\u00af\u00b0\3\2\2\2\u00b0\r\3\2\2\2\u00b1\u00b3\7"+ + "\25\2\2\u00b2\u00b4\5\30\r\2\u00b3\u00b2\3\2\2\2\u00b3\u00b4\3\2\2\2\u00b4"+ + "\u00b6\3\2\2\2\u00b5\u00b7\7\3\2\2\u00b6\u00b5\3\2\2\2\u00b6\u00b7\3\2"+ + "\2\2\u00b7\17\3\2\2\2\u00b8\u00b9\7\26\2\2\u00b9\u00ba\7:\2\2\u00ba\u00bb"+ + "\7\26\2\2\u00bb\21\3\2\2\2\u00bc\u00c1\7:\2\2\u00bd\u00be\7\27\2\2\u00be"+ + "\u00c0\7:\2\2\u00bf\u00bd\3\2\2\2\u00c0\u00c3\3\2\2\2\u00c1\u00bf\3\2"+ + "\2\2\u00c1\u00c2\3\2\2\2\u00c2\u00c6\3\2\2\2\u00c3\u00c1\3\2\2\2\u00c4"+ + "\u00c5\7\30\2\2\u00c5\u00c7\7:\2\2\u00c6\u00c4\3\2\2\2\u00c6\u00c7\3\2"+ + "\2\2\u00c7\23\3\2\2\2\u00c8\u00cd\5\"\22\2\u00c9\u00ca\7\r\2\2\u00ca\u00cc"+ + "\5\"\22\2\u00cb\u00c9\3\2\2\2\u00cc\u00cf\3\2\2\2\u00cd\u00cb\3\2\2\2"+ + "\u00cd\u00ce\3\2\2\2\u00ce\25\3\2\2\2\u00cf\u00cd\3\2\2\2\u00d0\u00d5"+ + "\7:\2\2\u00d1\u00d2\7\r\2\2\u00d2\u00d4\7:\2\2\u00d3\u00d1\3\2\2\2\u00d4"+ + "\u00d7\3\2\2\2\u00d5\u00d3\3\2\2\2\u00d5\u00d6\3\2\2\2\u00d6\27\3\2\2"+ + "\2\u00d7\u00d5\3\2\2\2\u00d8\u00dd\5\32\16\2\u00d9\u00da\7\r\2\2\u00da"+ + "\u00dc\5\32\16\2\u00db\u00d9\3\2\2\2\u00dc\u00df\3\2\2\2\u00dd\u00db\3"+ + "\2\2\2\u00dd\u00de\3\2\2\2\u00de\31\3\2\2\2\u00df\u00dd\3\2\2\2\u00e0"+ + "\u00e1\b\16\1\2\u00e1\u00ee\7\31\2\2\u00e2\u00ee\7\32\2\2\u00e3\u00ee"+ + "\7\33\2\2\u00e4\u00ee\5J&\2\u00e5\u00ee\5L\'\2\u00e6\u00ee\7\34\2\2\u00e7"+ + "\u00ee\5*\26\2\u00e8\u00ee\5\34\17\2\u00e9\u00ee\5\60\31\2\u00ea\u00eb"+ + "\5F$\2\u00eb\u00ec\5\32\16\n\u00ec\u00ee\3\2\2\2\u00ed\u00e0\3\2\2\2\u00ed"+ + "\u00e2\3\2\2\2\u00ed\u00e3\3\2\2\2\u00ed\u00e4\3\2\2\2\u00ed\u00e5\3\2"+ + "\2\2\u00ed\u00e6\3\2\2\2\u00ed\u00e7\3\2\2\2\u00ed\u00e8\3\2\2\2\u00ed"+ + "\u00e9\3\2\2\2\u00ed\u00ea\3\2\2\2\u00ee\u0111\3\2\2\2\u00ef\u00f0\f\13"+ + "\2\2\u00f0\u00f1\5H%\2\u00f1\u00f2\5\32\16\13\u00f2\u0110\3\2\2\2\u00f3"+ + "\u00f4\f\t\2\2\u00f4\u00f5\5B\"\2\u00f5\u00f6\5\32\16\n\u00f6\u0110\3"+ + "\2\2\2\u00f7\u00f8\f\b\2\2\u00f8\u00f9\5@!\2\u00f9\u00fa\5\32\16\t\u00fa"+ + "\u0110\3\2\2\2\u00fb\u00fc\f\7\2\2\u00fc\u00fd\5> \2\u00fd\u00fe\5\32"+ + "\16\7\u00fe\u0110\3\2\2\2\u00ff\u0100\f\6\2\2\u0100\u0101\5<\37\2\u0101"+ + "\u0102\5\32\16\7\u0102\u0110\3\2\2\2\u0103\u0104\f\5\2\2\u0104\u0105\5"+ + ":\36\2\u0105\u0106\5\32\16\6\u0106\u0110\3\2\2\2\u0107\u0108\f\4\2\2\u0108"+ + "\u0109\58\35\2\u0109\u010a\5\32\16\5\u010a\u0110\3\2\2\2\u010b\u010c\f"+ + "\3\2\2\u010c\u010d\5D#\2\u010d\u010e\5\32\16\4\u010e\u0110\3\2\2\2\u010f"+ + "\u00ef\3\2\2\2\u010f\u00f3\3\2\2\2\u010f\u00f7\3\2\2\2\u010f\u00fb\3\2"+ + "\2\2\u010f\u00ff\3\2\2\2\u010f\u0103\3\2\2\2\u010f\u0107\3\2\2\2\u010f"+ + "\u010b\3\2\2\2\u0110\u0113\3\2\2\2\u0111\u010f\3\2\2\2\u0111\u0112\3\2"+ + "\2\2\u0112\33\3\2\2\2\u0113\u0111\3\2\2\2\u0114\u0118\5 \21\2\u0115\u0117"+ + "\5&\24\2\u0116\u0115\3\2\2\2\u0117\u011a\3\2\2\2\u0118\u0116\3\2\2\2\u0118"+ + "\u0119\3\2\2\2\u0119\35\3\2\2\2\u011a\u0118\3\2\2\2\u011b\u011d\5 \21"+ + "\2\u011c\u011e\5&\24\2\u011d\u011c\3\2\2\2\u011e\u011f\3\2\2\2\u011f\u011d"+ + "\3\2\2\2\u011f\u0120\3\2\2\2\u0120\37\3\2\2\2\u0121\u0127\5\"\22\2\u0122"+ + "\u0123\7\35\2\2\u0123\u0124\5\32\16\2\u0124\u0125\7\36\2\2\u0125\u0127"+ + "\3\2\2\2\u0126\u0121\3\2\2\2\u0126\u0122\3\2\2\2\u0127!\3\2\2\2\u0128"+ + "\u012f\7:\2\2\u0129\u012a\7\35\2\2\u012a\u012b\5\32\16\2\u012b\u012c\7"+ + "\36\2\2\u012c\u012d\5$\23\2\u012d\u012f\3\2\2\2\u012e\u0128\3\2\2\2\u012e"+ + "\u0129\3\2\2\2\u012f\u0133\3\2\2\2\u0130\u0132\5$\23\2\u0131\u0130\3\2"+ + "\2\2\u0132\u0135\3\2\2\2\u0133\u0131\3\2\2\2\u0133\u0134\3\2\2\2\u0134"+ + "#\3\2\2\2\u0135\u0133\3\2\2\2\u0136\u0138\5&\24\2\u0137\u0136\3\2\2\2"+ + "\u0138\u013b\3\2\2\2\u0139\u0137\3\2\2\2\u0139\u013a\3\2\2\2\u013a\u0142"+ + "\3\2\2\2\u013b\u0139\3\2\2\2\u013c\u013d\7\37\2\2\u013d\u013e\5\32\16"+ + "\2\u013e\u013f\7 \2\2\u013f\u0143\3\2\2\2\u0140\u0141\7\27\2\2\u0141\u0143"+ + "\7:\2\2\u0142\u013c\3\2\2\2\u0142\u0140\3\2\2\2\u0143%\3\2\2\2\u0144\u0145"+ + "\7\30\2\2\u0145\u0147\7:\2\2\u0146\u0144\3\2\2\2\u0146\u0147\3\2\2\2\u0147"+ + "\u0148\3\2\2\2\u0148\u0149\5(\25\2\u0149\'\3\2\2\2\u014a\u014c\7\35\2"+ + "\2\u014b\u014d\5\30\r\2\u014c\u014b\3\2\2\2\u014c\u014d\3\2\2\2\u014d"+ + "\u014e\3\2\2\2\u014e\u0152\7\36\2\2\u014f\u0152\5\60\31\2\u0150\u0152"+ + "\5L\'\2\u0151\u014a\3\2\2\2\u0151\u014f\3\2\2\2\u0151\u0150\3\2\2\2\u0152"+ + ")\3\2\2\2\u0153\u0154\7\17\2\2\u0154\u0155\5,\27\2\u0155+\3\2\2\2\u0156"+ + "\u0158\7\35\2\2\u0157\u0159\5.\30\2\u0158\u0157\3\2\2\2\u0158\u0159\3"+ + "\2\2\2\u0159\u015a\3\2\2\2\u015a\u015b\7\36\2\2\u015b\u015c\5\4\3\2\u015c"+ + "\u015d\7\b\2\2\u015d-\3\2\2\2\u015e\u0161\5\26\f\2\u015f\u0160\7\r\2\2"+ + "\u0160\u0162\7\34\2\2\u0161\u015f\3\2\2\2\u0161\u0162\3\2\2\2\u0162\u0165"+ + "\3\2\2\2\u0163\u0165\7\34\2\2\u0164\u015e\3\2\2\2\u0164\u0163\3\2\2\2"+ + "\u0165/\3\2\2\2\u0166\u0168\7!\2\2\u0167\u0169\5\62\32\2\u0168\u0167\3"+ + "\2\2\2\u0168\u0169\3\2\2\2\u0169\u016a\3\2\2\2\u016a\u016b\7\"\2\2\u016b"+ + "\61\3\2\2\2\u016c\u0172\5\64\33\2\u016d\u016e\5\66\34\2\u016e\u016f\5"+ + "\64\33\2\u016f\u0171\3\2\2\2\u0170\u016d\3\2\2\2\u0171\u0174\3\2\2\2\u0172"+ + "\u0170\3\2\2\2\u0172\u0173\3\2\2\2\u0173\u0176\3\2\2\2\u0174\u0172\3\2"+ + "\2\2\u0175\u0177\5\66\34\2\u0176\u0175\3\2\2\2\u0176\u0177\3\2\2\2\u0177"+ + "\63\3\2\2\2\u0178\u0179\7\37\2\2\u0179\u017a\5\32\16\2\u017a\u017b\7 "+ + "\2\2\u017b\u017c\7\4\2\2\u017c\u017d\5\32\16\2\u017d\u0183\3\2\2\2\u017e"+ + "\u017f\7:\2\2\u017f\u0180\7\4\2\2\u0180\u0183\5\32\16\2\u0181\u0183\5"+ + "\32\16\2\u0182\u0178\3\2\2\2\u0182\u017e\3\2\2\2\u0182\u0181\3\2\2\2\u0183"+ + "\65\3\2\2\2\u0184\u0185\t\2\2\2\u0185\67\3\2\2\2\u0186\u0187\7#\2\2\u0187"+ + "9\3\2\2\2\u0188\u0189\7$\2\2\u0189;\3\2\2\2\u018a\u018b\t\3\2\2\u018b"+ + "=\3\2\2\2\u018c\u018d\7+\2\2\u018d?\3\2\2\2\u018e\u018f\t\4\2\2\u018f"+ + "A\3\2\2\2\u0190\u0191\t\5\2\2\u0191C\3\2\2\2\u0192\u0193\t\6\2\2\u0193"+ + "E\3\2\2\2\u0194\u0195\t\7\2\2\u0195G\3\2\2\2\u0196\u0197\79\2\2\u0197"+ + "I\3\2\2\2\u0198\u0199\t\b\2\2\u0199K\3\2\2\2\u019a\u019b\t\t\2\2\u019b"+ + "M\3\2\2\2$TX\u0080\u009a\u009c\u00aa\u00af\u00b3\u00b6\u00c1\u00c6\u00cd"+ + "\u00d5\u00dd\u00ed\u010f\u0111\u0118\u011f\u0126\u012e\u0133\u0139\u0142"+ + "\u0146\u014c\u0151\u0158\u0161\u0164\u0168\u0172\u0176\u0182"; + public static final ATN _ATN = + new ATNDeserializer().deserialize(_serializedATN.toCharArray()); + static { + _decisionToDFA = new DFA[_ATN.getNumberOfDecisions()]; + for (int i = 0; i < _ATN.getNumberOfDecisions(); i++) { + _decisionToDFA[i] = new DFA(_ATN.getDecisionState(i), i); + } + } +} \ No newline at end of file diff --git a/src/LuaVM/LuaChunk.java b/src/LuaVM/LuaChunk.java new file mode 100644 index 0000000..44f81cb --- /dev/null +++ b/src/LuaVM/LuaChunk.java @@ -0,0 +1,69 @@ +package LuaVM; + +import java.util.ArrayList; +import java.util.List; + +public class LuaChunk { + public List insns = new ArrayList<>(); + public List constants = new ArrayList<>(); + public List prototypes = new ArrayList<>(); + public List debugLines = new ArrayList<>(); + + public double numUpVals; + public double numParams; + public double maxStackSize; + public double isVarArgFlag; + + public int lineDefined; + + public void print() { + System.out.println("DUMPING CHUNK"); + System.out.println(toString()); + System.out.println("END CHUNK"); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + + sb.append("#UPVALUES = " + numUpVals); + sb.append(System.lineSeparator()); + + sb.append("#PARAMS = " + numParams); + sb.append(System.lineSeparator()); + + sb.append("#MAXSTACKSIZE = " + maxStackSize); + sb.append(System.lineSeparator()); + + sb.append("#CONSTANTS = " + constants.size()); + sb.append(System.lineSeparator()); + + sb.append("#INSTRUCTIONS = " + insns.size()); + sb.append(System.lineSeparator()); + + sb.append("#PROTOTYPES = " + prototypes.size()); + sb.append(System.lineSeparator()); + + sb.append(System.lineSeparator()); + + for (int i = 0; i < constants.size(); i++) { + sb.append("Constant " + i + " = " + constants.get(i).toString() + System.lineSeparator()); + } + + sb.append(System.lineSeparator()); + + for (int i = 0; i < insns.size(); i++) { + sb.append("[" + i + "] " + insns.get(i).toString() + System.lineSeparator()); + } + + sb.append(System.lineSeparator()); + + for (int i = 0; i < prototypes.size(); i++) { + sb.append("[CHUNK " + i + "]"); + sb.append(System.lineSeparator()); + sb.append(prototypes.get(i).toString()); + } + + return sb.toString(); + } +} diff --git a/src/LuaVM/LuaValue.java b/src/LuaVM/LuaValue.java new file mode 100644 index 0000000..017eaab --- /dev/null +++ b/src/LuaVM/LuaValue.java @@ -0,0 +1,35 @@ +package LuaVM; + +public class LuaValue { + public enum Type { + INVALID, + TABLE, // ignored + + NUMBER, + BOOLEAN, + STRING, + NIL, + } + + @Override + public String toString() { + switch (type) { + default: + case INVALID: + case TABLE: + return ""; + case NUMBER: + return Double.toString(_nv); + case BOOLEAN: + return Boolean.toString(_bv); + case STRING: + return _sv; + } + } + + public Type type = Type.INVALID; + + public double _nv; + public boolean _bv; + public String _sv; +} diff --git a/src/LuaVM/VM.java b/src/LuaVM/VM.java new file mode 100644 index 0000000..3ef8933 --- /dev/null +++ b/src/LuaVM/VM.java @@ -0,0 +1,127 @@ +package LuaVM; + +import java.util.Arrays; + +public class VM { + public static boolean isVariableTargetBranch(VMOp opcode) { + return opcode == VMOp.JUMP || opcode == VMOp.FORPREP ||opcode == VMOp.FORLOOP; + } + + public static boolean isConstantTargetBranch(VMOp opcode) { + VMOp ops[] = { + VMOp.LOADBOOL, VMOp.EQ, VMOp.LT, VMOp.LTE, VMOp.TEST, VMOp.TESTSET, VMOp.TFORLOOP, + }; + + return Arrays.asList(ops).contains(opcode); + } + + public static boolean isOperandUsed(VMOperand operand, VMOpType type) { + switch(type) { + case A: + return operand == VMOperand.A; + + case AB: + return operand == VMOperand.A || operand == VMOperand.B; + + case AC: + return operand == VMOperand.A || operand == VMOperand.C; + + case ABx: + return operand == VMOperand.A || operand == VMOperand.Bx; + + case AsBx: + return operand == VMOperand.A || operand == VMOperand.sBx; + + case ABC: + return operand == VMOperand.A || operand == VMOperand.B || operand == VMOperand.C; + + case sBx: + return operand == VMOperand.sBx; + } + + return false; + } + + public static boolean isBitsUsed(VMOperand operand, VMOpType type) { + + switch(type) { + case A: + return operand == VMOperand.A; + + case AB: + return operand != VMOperand.C; + + case AC: + return operand != VMOperand.B; + + case ABx: + case AsBx: + case ABC: + return true; + + case sBx: + return operand != VMOperand.A; + } + + return false; + } + + public static VMOpType getOpType(VMOp type) throws RuntimeException { + switch (type) { + case MOVE: + case LOADNIL: + case GETUPVAL: + case SETUPVAL: + case UNM: + case NOT: + case LEN: + case RETURN: + case VARARG: + return VMOpType.AB; + + case LOADK: + case GETGLOBAL: + case SETGLOBAL: + case CLOSURE: + return VMOpType.ABx; + + case LOADBOOL: + case GETTABLE: + case SETTABLE: + case ADD: + case SUB: + case MUL: + case DIV: + case MOD: + case POW: + case CONCAT: + case CALL: + case TAILCALL: + case SELF: + case EQ: + case LT: + case LTE: + case TESTSET: + case NEWTABLE: + case SETLIST: + return VMOpType.ABC; + + case JUMP: + return VMOpType.sBx; + + case TEST: + case TFORLOOP: + return VMOpType.AC; + + case FORPREP: + case FORLOOP: + return VMOpType.AsBx; + + case CLOSE: + return VMOpType.A; + + } + + throw new RuntimeException("Invalid VMOp provided"); + } +} diff --git a/src/LuaVM/VMBasicBlock.java b/src/LuaVM/VMBasicBlock.java new file mode 100644 index 0000000..dc4f936 --- /dev/null +++ b/src/LuaVM/VMBasicBlock.java @@ -0,0 +1,193 @@ +package LuaVM; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + +public class VMBasicBlock { + public List insns = new ArrayList<>(); + public VMControlFlowGraph cfg; + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + + for (VMInstructionWrapper wrapper : insns) { + sb.append(wrapper.insn.toString()); + + sb.append(System.lineSeparator()); + } + + return sb.toString(); + } + + public static void print(List blocks) { + for (int i = 0; i < blocks.size(); i++) { + + String s = "[BLOCK #" + i + "]"; + + System.out.println(s); + System.out.println(blocks.get(i).toString()); + System.out.println(); + } + } + + private static VMBasicBlock findBlock(VMInstruction insn, List blocks) { + for (VMBasicBlock block : blocks) { + if (block.insns.get(0).insn == insn) { + return block; + } + } + + throw new RuntimeException("Reference not in basic block"); + } + + public static HashSet computeLeaders(List insns) { + HashSet leaders = new HashSet<>(); + + int PC = 0; + + leaders.add(insns.get(0)); + + for (int i = 0; i < insns.size(); i++) { + ++PC; + + VMInstruction insn = insns.get(i); + + if (insn.isBranch()) { + int target = insn.getExitTargetPC(PC); + + // Junk code sometimes has invalid targets, so we ignore them + if (target >= 0 && target < insns.size()) { + leaders.add(insns.get(target)); + } + + if (PC < insns.size()) { + leaders.add(insns.get(PC)); + } + } + else if (insn.opcode == VMOp.RETURN && PC < insns.size()) { + // Last instruction might be a return, hence PC == insns.size() as we increment + leaders.add(insns.get(PC)); + } + } + + return leaders; + } + + private static void setInstructionReferences(List blocks, List insns) { + int PC = 0; + for (VMBasicBlock bb : blocks) { + for (VMInstructionWrapper w : bb.insns) { + ++PC; + if (w.insn.isBranch()) { + int target = w.insn.getExitTargetPC(PC); + if (target >= 0 && target < insns.size()) { + w.ref = findBlock(insns.get(target), blocks); + } + } + } + } + } + + public static List generateBasicBlocks(List insns) { + List blocks = new ArrayList<>(); + HashSet leaders = computeLeaders(insns); + + VMBasicBlock block = new VMBasicBlock(); + + int PC = 0; + + for (int i = 0; i < insns.size(); i++) { + ++PC; + + VMInstruction insn = insns.get(i); + + if (i > 0 && leaders.contains(insn)) { + blocks.add(block); + block = new VMBasicBlock(); + } + + VMInstructionWrapper wrapper = new VMInstructionWrapper(); + wrapper.insn = insn; + + block.insns.add(wrapper); + } + + blocks.add(block); + + setInstructionReferences(blocks, insns); + + return blocks; + } + + public static VMBasicBlock getNextBlock(List blocks, VMBasicBlock block) { + for (int i = 0; i < blocks.size(); i++) { + if (blocks.get(i) == block) { + return getNextBlock(blocks, i); + } + } + + throw new RuntimeException("Block argument must be inside blocks list"); + } + + public static VMBasicBlock getNextBlock(List blocks, int idx) { + VMBasicBlock block = blocks.get(idx); + + if (block.insns.size() == 0) { + throw new RuntimeException("Basic block does not contain any instructions"); + } + else { + VMInstructionWrapper insn = block.insns.get(block.insns.size() - 1); + + if (insn.insn.opcode == VMOp.JUMP || insn.insn.opcode == VMOp.FORPREP) { + return insn.ref; + } + + if (idx + 1 < blocks.size()) { + VMBasicBlock next = blocks.get(idx + 1); + + // Two returns aren't necessary (even though lua compiler will generate them) + // and luraph will sometimes insert bogus unreachable returns that break decompilers + // so no point to do this, which is why I commented out the second expression + if (insn.insn.opcode == VMOp.RETURN /*&& next.insns.get(0).insn.opcode != VMOp.RETURN*/) { + return null; + } + + // Handle LOADBOOL functionality + // When C is 1 we skip the next instruction + if (insn.insn.opcode == VMOp.LOADBOOL && insn.insn.C == 1.0) { + if (idx + 2 < blocks.size()) { + return blocks.get(idx + 2); + } + return null; + } + + return next; + } + + return null; + } + } + + public static VMBasicBlock getTargetBlock(VMBasicBlock block) { + if (block.insns.size() == 0) { + throw new RuntimeException("Basic block does not contain any instructions"); + } + else { + VMInstructionWrapper insn = block.insns.get(block.insns.size() - 1); + + // Handle LOADBOOL functionality + // When C is 0 we fallthrough to the next instruction + if (insn.insn.opcode == VMOp.LOADBOOL && insn.insn.C == 0.0) { + return null; + } + + if (insn.insn.opcode != VMOp.JUMP && insn.insn.opcode != VMOp.FORPREP && insn.insn.isBranch()) { + return insn.ref; + } + + return null; + } + } +} diff --git a/src/LuaVM/VMControlFlowGraph.java b/src/LuaVM/VMControlFlowGraph.java new file mode 100644 index 0000000..2a339f1 --- /dev/null +++ b/src/LuaVM/VMControlFlowGraph.java @@ -0,0 +1,68 @@ +package LuaVM; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +public class VMControlFlowGraph { + public VMBasicBlock block; + public VMControlFlowGraph target; + public VMControlFlowGraph next; + + public List getChildren() { + List children = new ArrayList<>(); + + // order matters and affects topological ordering + + if (next != null) { + children.add(next); + } + + if (target != null) { + children.add(target); + } + + return children; + } + + public VMInstructionWrapper getLastInstruction() { + return block.insns.get(block.insns.size() - 1); + } + + private static VMControlFlowGraph generateControlFlowGraphHelper(List blocks, VMBasicBlock block, List cfg, HashMap created) { + if (block == null) { + return null; + } + + if (created.containsKey(block)) { + return created.get(block); + } + + VMControlFlowGraph root = new VMControlFlowGraph(); + cfg.add(root); + created.put(block, root); + + root.block = block; + root.block.cfg = root; + root.next = generateControlFlowGraphHelper(blocks, VMBasicBlock.getNextBlock(blocks, block), cfg, created); + root.target = generateControlFlowGraphHelper(blocks, VMBasicBlock.getTargetBlock(block), cfg, created); + + return root; + } + + // root is always first, nothing else guaranteed + public static List generateControlFlowGraph(List blocks) { + if (blocks.size() == 0) { + throw new RuntimeException("No basic blocks"); + } + + HashMap created = new HashMap<>(); + + VMBasicBlock rootBlock = blocks.get(0); + List cfg = new ArrayList<>(); + + generateControlFlowGraphHelper(blocks, rootBlock, cfg, created); + + return cfg; + } +} diff --git a/src/LuaVM/VMInstruction.java b/src/LuaVM/VMInstruction.java new file mode 100644 index 0000000..e47c943 --- /dev/null +++ b/src/LuaVM/VMInstruction.java @@ -0,0 +1,122 @@ +/** + * Lua instructions which change the PC + * + * Only JMP, FORPREP, and FORLOOP will be considered official branch instructions, because all other branches are followed by a JMP + * NOTE: ^ Turns out to not be true. Lua VM bytecode can generate LOADBOOL insns in sequence for example: local x = y > 100 + * + * Each basic block will have + * + * LOADBOOL A B C R(A) := (Bool)B; if (C) PC++ + * + * JMP sBx PC += sBx + * + * EQ A B C if ((RK(B) == RK(C)) ~= A) then PC++ + * + * LT A B C if ((RK(B) < RK(C)) ~= A) then PC++ + * + * LE A B C if ((RK(B) <= RK(C)) ~= A) then PC++ + * + * TEST A C if not (R(A) <=> C) then PC++ + * + * TESTSET A B C if (R(B) <=> C) then R(A) := R(B) else PC++ + * + * FORPREP A sBx R(A) -= R(A+2); PC += sBx + * + * FORLOOP A sBx + * R(A) += R(A+2) + * if R(A) <= R(A+1) then { + * PC += sBx; R(A+3) = R(A) + * } + * + * TFORLOOP A C + * R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2)); + * if R(A+3) ~= nil then { + * R(A+2) = R(A+3); + * } else { + * PC++; + * } + */ + +// TFORLOOP: for in +// FORPREP, FORLOOP for i = a, b, c + + +package LuaVM; + +import java.util.Arrays; + +public class VMInstruction { + public VMOp opcode; + public int opcodeNum; // value encoded into instruction + public double A, B, C, sBx, Bx; + + public double getValue(VMOperand operand) { + switch (operand) { + case A: + return A; + case B: + return B; + case C: + return C; + case sBx: + return sBx; + case Bx: + return Bx; + } + + throw new RuntimeException("Invalid operand"); + } + + public boolean isVariableTargetBranch() { + return VM.isVariableTargetBranch(opcode); + } + + public boolean isConstantTargetBranch() { + return VM.isConstantTargetBranch(opcode); + } + + public boolean isBranch() { + return isVariableTargetBranch() || isConstantTargetBranch(); + } + + public int getExitTargetPC(int PC) { + if (isVariableTargetBranch()) { + return (int)sBx + PC; + } + else if (isConstantTargetBranch()) { + return PC + 1; + } + else { + throw new RuntimeException("getExitTargetPC called on non branch instruction"); + } + } + + @Override + public String toString() { + String s = opcode.toString(); + + VMOpType type = VM.getOpType(opcode); + + if (VM.isOperandUsed(VMOperand.A, type)) { + s += " " + A; + } + + if (VM.isOperandUsed(VMOperand.B, type)) { + s += " " + B; + } + + if (VM.isOperandUsed(VMOperand.C, type)) { + s += " " + C; + } + + if (VM.isOperandUsed(VMOperand.Bx, type)) { + s += " " + Bx; + } + + if (VM.isOperandUsed(VMOperand.sBx, type)) { + s += " " + sBx; + } + + return s; + } +} diff --git a/src/LuaVM/VMInstructionRefWrapper.java b/src/LuaVM/VMInstructionRefWrapper.java new file mode 100644 index 0000000..e925e87 --- /dev/null +++ b/src/LuaVM/VMInstructionRefWrapper.java @@ -0,0 +1,6 @@ +package LuaVM; + +public class VMInstructionRefWrapper { + public VMInstruction insn; + public VMInstruction ref; +} diff --git a/src/LuaVM/VMInstructionWrapper.java b/src/LuaVM/VMInstructionWrapper.java new file mode 100644 index 0000000..99a3f9c --- /dev/null +++ b/src/LuaVM/VMInstructionWrapper.java @@ -0,0 +1,6 @@ +package LuaVM; + +public class VMInstructionWrapper { + public VMInstruction insn; + public VMBasicBlock ref; +} diff --git a/src/LuaVM/VMOp.java b/src/LuaVM/VMOp.java new file mode 100644 index 0000000..dbe3a1f --- /dev/null +++ b/src/LuaVM/VMOp.java @@ -0,0 +1,42 @@ +package LuaVM; + +public enum VMOp { + MOVE, + LOADK, + LOADBOOL, + LOADNIL, + GETUPVAL, + GETGLOBAL, + GETTABLE, + SETGLOBAL, + SETUPVAL, + SETTABLE, + NEWTABLE, + SELF, + ADD, + SUB, + MUL, + DIV, + MOD, + POW, + UNM, + NOT, + LEN, + CONCAT, + JUMP, + EQ, + LT, + LTE, + TEST, + TESTSET, + CALL, + TAILCALL, + RETURN, + FORLOOP, + FORPREP, + TFORLOOP, + SETLIST, + CLOSE, + CLOSURE, + VARARG, +} diff --git a/src/LuaVM/VMOpType.java b/src/LuaVM/VMOpType.java new file mode 100644 index 0000000..307057c --- /dev/null +++ b/src/LuaVM/VMOpType.java @@ -0,0 +1,12 @@ +package LuaVM; + +public enum VMOpType { + AB, + ABx, + ABC, + sBx, + AC, + AsBx, + A, +} + diff --git a/src/LuaVM/VMOperand.java b/src/LuaVM/VMOperand.java new file mode 100644 index 0000000..761a974 --- /dev/null +++ b/src/LuaVM/VMOperand.java @@ -0,0 +1,9 @@ +package LuaVM; + +public enum VMOperand { + A, + B, + C, + sBx, + Bx, +} diff --git a/src/LuaVisitor.java b/src/LuaVisitor.java new file mode 100644 index 0000000..6b9d734 --- /dev/null +++ b/src/LuaVisitor.java @@ -0,0 +1,458 @@ +import org.antlr.v4.runtime.tree.ParseTreeVisitor; + +/** + * This interface defines a complete generic visitor for a parse tree produced + * by {@link LuaParser}. + * + * @param The return type of the visit operation. Use {@link Void} for + * operations with no return type. + */ +public interface LuaVisitor extends ParseTreeVisitor { + /** + * Visit a parse tree produced by {@link LuaParser#chunk}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitChunk(LuaParser.ChunkContext ctx); + /** + * Visit a parse tree produced by {@link LuaParser#block}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitBlock(LuaParser.BlockContext ctx); + /** + * Visit a parse tree produced by the {@code stmtSemicolon} + * labeled alternative in {@link LuaParser#stat}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitStmtSemicolon(LuaParser.StmtSemicolonContext ctx); + /** + * Visit a parse tree produced by the {@code stmtAssign} + * labeled alternative in {@link LuaParser#stat}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitStmtAssign(LuaParser.StmtAssignContext ctx); + /** + * Visit a parse tree produced by the {@code stmtFuncCall} + * labeled alternative in {@link LuaParser#stat}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitStmtFuncCall(LuaParser.StmtFuncCallContext ctx); + /** + * Visit a parse tree produced by the {@code stmtLabel} + * labeled alternative in {@link LuaParser#stat}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitStmtLabel(LuaParser.StmtLabelContext ctx); + /** + * Visit a parse tree produced by the {@code stmtBreak} + * labeled alternative in {@link LuaParser#stat}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitStmtBreak(LuaParser.StmtBreakContext ctx); + /** + * Visit a parse tree produced by the {@code stmtGoto} + * labeled alternative in {@link LuaParser#stat}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitStmtGoto(LuaParser.StmtGotoContext ctx); + /** + * Visit a parse tree produced by the {@code stmtDo} + * labeled alternative in {@link LuaParser#stat}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitStmtDo(LuaParser.StmtDoContext ctx); + /** + * Visit a parse tree produced by the {@code stmtWhile} + * labeled alternative in {@link LuaParser#stat}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitStmtWhile(LuaParser.StmtWhileContext ctx); + /** + * Visit a parse tree produced by the {@code stmtRepeat} + * labeled alternative in {@link LuaParser#stat}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitStmtRepeat(LuaParser.StmtRepeatContext ctx); + /** + * Visit a parse tree produced by the {@code stmtIf} + * labeled alternative in {@link LuaParser#stat}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitStmtIf(LuaParser.StmtIfContext ctx); + /** + * Visit a parse tree produced by the {@code stmtForStep} + * labeled alternative in {@link LuaParser#stat}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitStmtForStep(LuaParser.StmtForStepContext ctx); + /** + * Visit a parse tree produced by the {@code stmtForIn} + * labeled alternative in {@link LuaParser#stat}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitStmtForIn(LuaParser.StmtForInContext ctx); + /** + * Visit a parse tree produced by the {@code stmtFuncDef} + * labeled alternative in {@link LuaParser#stat}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitStmtFuncDef(LuaParser.StmtFuncDefContext ctx); + /** + * Visit a parse tree produced by the {@code stmtLocalFuncDef} + * labeled alternative in {@link LuaParser#stat}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitStmtLocalFuncDef(LuaParser.StmtLocalFuncDefContext ctx); + /** + * Visit a parse tree produced by the {@code stmtLocalDecl} + * labeled alternative in {@link LuaParser#stat}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitStmtLocalDecl(LuaParser.StmtLocalDeclContext ctx); + /** + * Visit a parse tree produced by {@link LuaParser#ifstmt}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitIfstmt(LuaParser.IfstmtContext ctx); + /** + * Visit a parse tree produced by {@link LuaParser#elseifstmt}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitElseifstmt(LuaParser.ElseifstmtContext ctx); + /** + * Visit a parse tree produced by {@link LuaParser#elsestmt}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitElsestmt(LuaParser.ElsestmtContext ctx); + /** + * Visit a parse tree produced by {@link LuaParser#retstat}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitRetstat(LuaParser.RetstatContext ctx); + /** + * Visit a parse tree produced by {@link LuaParser#label}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitLabel(LuaParser.LabelContext ctx); + /** + * Visit a parse tree produced by {@link LuaParser#funcname}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitFuncname(LuaParser.FuncnameContext ctx); + /** + * Visit a parse tree produced by {@link LuaParser#varlist}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitVarlist(LuaParser.VarlistContext ctx); + /** + * Visit a parse tree produced by {@link LuaParser#namelist}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitNamelist(LuaParser.NamelistContext ctx); + /** + * Visit a parse tree produced by {@link LuaParser#explist}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitExplist(LuaParser.ExplistContext ctx); + /** + * Visit a parse tree produced by the {@code expCmp} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitExpCmp(LuaParser.ExpCmpContext ctx); + /** + * Visit a parse tree produced by the {@code expNumber} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitExpNumber(LuaParser.ExpNumberContext ctx); + /** + * Visit a parse tree produced by the {@code expThreeDots} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitExpThreeDots(LuaParser.ExpThreeDotsContext ctx); + /** + * Visit a parse tree produced by the {@code expStrcat} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitExpStrcat(LuaParser.ExpStrcatContext ctx); + /** + * Visit a parse tree produced by the {@code expTrue} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitExpTrue(LuaParser.ExpTrueContext ctx); + /** + * Visit a parse tree produced by the {@code expOr} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitExpOr(LuaParser.ExpOrContext ctx); + /** + * Visit a parse tree produced by the {@code expBitwise} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitExpBitwise(LuaParser.ExpBitwiseContext ctx); + /** + * Visit a parse tree produced by the {@code expTableCtor} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitExpTableCtor(LuaParser.ExpTableCtorContext ctx); + /** + * Visit a parse tree produced by the {@code expMulDivMod} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitExpMulDivMod(LuaParser.ExpMulDivModContext ctx); + /** + * Visit a parse tree produced by the {@code expFuncDef} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitExpFuncDef(LuaParser.ExpFuncDefContext ctx); + /** + * Visit a parse tree produced by the {@code expFalse} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitExpFalse(LuaParser.ExpFalseContext ctx); + /** + * Visit a parse tree produced by the {@code expString} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitExpString(LuaParser.ExpStringContext ctx); + /** + * Visit a parse tree produced by the {@code expPrefix} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitExpPrefix(LuaParser.ExpPrefixContext ctx); + /** + * Visit a parse tree produced by the {@code expUnary} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitExpUnary(LuaParser.ExpUnaryContext ctx); + /** + * Visit a parse tree produced by the {@code expAnd} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitExpAnd(LuaParser.ExpAndContext ctx); + /** + * Visit a parse tree produced by the {@code expPow} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitExpPow(LuaParser.ExpPowContext ctx); + /** + * Visit a parse tree produced by the {@code expNil} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitExpNil(LuaParser.ExpNilContext ctx); + /** + * Visit a parse tree produced by the {@code expAddSub} + * labeled alternative in {@link LuaParser#exp}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitExpAddSub(LuaParser.ExpAddSubContext ctx); + /** + * Visit a parse tree produced by {@link LuaParser#prefixexp}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitPrefixexp(LuaParser.PrefixexpContext ctx); + /** + * Visit a parse tree produced by {@link LuaParser#functioncall}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitFunctioncall(LuaParser.FunctioncallContext ctx); + /** + * Visit a parse tree produced by {@link LuaParser#varOrExp}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitVarOrExp(LuaParser.VarOrExpContext ctx); + /** + * Visit a parse tree produced by {@link LuaParser#var}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitVar(LuaParser.VarContext ctx); + /** + * Visit a parse tree produced by {@link LuaParser#varSuffix}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitVarSuffix(LuaParser.VarSuffixContext ctx); + /** + * Visit a parse tree produced by {@link LuaParser#nameAndArgs}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitNameAndArgs(LuaParser.NameAndArgsContext ctx); + /** + * Visit a parse tree produced by {@link LuaParser#args}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitArgs(LuaParser.ArgsContext ctx); + /** + * Visit a parse tree produced by {@link LuaParser#functiondef}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitFunctiondef(LuaParser.FunctiondefContext ctx); + /** + * Visit a parse tree produced by {@link LuaParser#funcbody}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitFuncbody(LuaParser.FuncbodyContext ctx); + /** + * Visit a parse tree produced by {@link LuaParser#parlist}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitParlist(LuaParser.ParlistContext ctx); + /** + * Visit a parse tree produced by {@link LuaParser#tableconstructor}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitTableconstructor(LuaParser.TableconstructorContext ctx); + /** + * Visit a parse tree produced by {@link LuaParser#fieldlist}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitFieldlist(LuaParser.FieldlistContext ctx); + /** + * Visit a parse tree produced by {@link LuaParser#field}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitField(LuaParser.FieldContext ctx); + /** + * Visit a parse tree produced by {@link LuaParser#fieldsep}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitFieldsep(LuaParser.FieldsepContext ctx); + /** + * Visit a parse tree produced by {@link LuaParser#operatorOr}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitOperatorOr(LuaParser.OperatorOrContext ctx); + /** + * Visit a parse tree produced by {@link LuaParser#operatorAnd}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitOperatorAnd(LuaParser.OperatorAndContext ctx); + /** + * Visit a parse tree produced by {@link LuaParser#operatorComparison}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitOperatorComparison(LuaParser.OperatorComparisonContext ctx); + /** + * Visit a parse tree produced by {@link LuaParser#operatorStrcat}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitOperatorStrcat(LuaParser.OperatorStrcatContext ctx); + /** + * Visit a parse tree produced by {@link LuaParser#operatorAddSub}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitOperatorAddSub(LuaParser.OperatorAddSubContext ctx); + /** + * Visit a parse tree produced by {@link LuaParser#operatorMulDivMod}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitOperatorMulDivMod(LuaParser.OperatorMulDivModContext ctx); + /** + * Visit a parse tree produced by {@link LuaParser#operatorBitwise}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitOperatorBitwise(LuaParser.OperatorBitwiseContext ctx); + /** + * Visit a parse tree produced by {@link LuaParser#operatorUnary}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitOperatorUnary(LuaParser.OperatorUnaryContext ctx); + /** + * Visit a parse tree produced by {@link LuaParser#operatorPower}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitOperatorPower(LuaParser.OperatorPowerContext ctx); + /** + * Visit a parse tree produced by {@link LuaParser#number}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitNumber(LuaParser.NumberContext ctx); + /** + * Visit a parse tree produced by {@link LuaParser#string}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitString(LuaParser.StringContext ctx); +} \ No newline at end of file diff --git a/src/LuacGenerator.java b/src/LuacGenerator.java new file mode 100644 index 0000000..32d257d --- /dev/null +++ b/src/LuacGenerator.java @@ -0,0 +1,207 @@ +import LuaVM.*; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.List; + +// NOTE: DataOutputStream is BIG Endian + +public class LuacGenerator { + private static void writeInt(DataOutputStream dos, int value) throws IOException { + byte[] buf = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(value).array(); + dos.write(buf); + } + + private static void writeDouble(DataOutputStream dos, double value) throws IOException { + byte[] buf = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putDouble(value).array(); + dos.write(buf); + } + + private static void writeHeader(DataOutputStream dos) throws IOException { + // Header signature + writeInt(dos, 0x61754C1B); + + // Version + dos.writeByte(0x51); + + // Format + dos.writeByte(0); + + // Endianness + dos.writeByte(1); + + // Size of int + dos.writeByte(4); + + // Size of size_t + dos.writeByte(4); + + // Size of instruction + dos.writeByte(4); + + // Size of lua_Number + dos.writeByte(8); + + // Integral flag + dos.writeByte(0); + } + + private static void writeChunk(DataOutputStream dos, LuaChunk chunk, boolean topLevel) throws IOException { + if (topLevel) { + writeString(dos, "@devirtualized.lua"); + } + else { + writeString(dos, ""); + } + + // Line defs + writeInt(dos, chunk.lineDefined); + writeInt(dos, chunk.lineDefined); + + // Upvals + dos.writeByte((byte)chunk.numUpVals); + + // Params + dos.writeByte((byte)chunk.numParams); + + // isVararg + dos.writeByte((byte)chunk.isVarArgFlag); + + // Max stacksize + dos.writeByte((byte)chunk.maxStackSize); + + writeInstructions(dos, chunk.insns); + writeConstants(dos, chunk.constants); + writePrototypes(dos, chunk.prototypes); + + // Source line positions (0 for now) + writeInt(dos, 0); + + // Locals + writeInt(dos, 0); + + // Upvalues + writeInt(dos, 0); + } + + private static int maskSize(double value, int size) { + int v = (int)value; + v = v & ((1 << size) - 1); + return v; + } + + private static void writeInstruction(DataOutputStream dos, VMInstruction insn) throws IOException { + int data = 0; + + data = data | insn.opcode.ordinal(); + + switch (VM.getOpType(insn.opcode)) { + case A: + data = data | maskSize(insn.A, 8) << 6; + break; + case AB: + data = data | maskSize(insn.A, 8) << 6; + data = data | maskSize(insn.B, 9) << 23; + break; + case AC: + data = data | maskSize(insn.A, 8) << 6; + data = data | maskSize(insn.C, 9) << 14; + break; + case ABC: + data = data | maskSize(insn.A, 8) << 6; + data = data | maskSize(insn.B, 9) << 23; + data = data | maskSize(insn.C, 9) << 14; + break; + case ABx: + data = data | maskSize(insn.A, 8) << 6; + data = data | maskSize(insn.Bx, 18) << 14; + break; + case AsBx: + data = data | maskSize(insn.A, 8) << 6; + data = data | maskSize(insn.sBx + 131071.0, 18) << 14; + break; + case sBx: + data = data | maskSize(insn.sBx + 131071.0, 18) << 14; + break; + } + + writeInt(dos, data); + } + + private static void writeInstructions(DataOutputStream dos, List insns) throws IOException { + writeInt(dos, insns.size()); + + for (VMInstruction insn : insns) { + writeInstruction(dos, insn); + } + } + + private static void writeConstant(DataOutputStream dos, LuaValue constant) throws IOException { + switch(constant.type) { + case NIL: + dos.writeByte(0); + break; + + case BOOLEAN: + dos.writeByte(1); + + // Data is 1 byte for boolean according to ChunkSpy src + if (constant._bv) { + dos.writeByte(1); + } + else { + dos.writeByte(0); + } + break; + + case NUMBER: + dos.writeByte(3); + writeDouble(dos, constant._nv); + break; + + case STRING: + dos.writeByte(4); + writeString(dos, constant._sv); + break; + + default: + throw new RuntimeException("Bad constant type in writeConstant"); + } + } + + private static void writeConstants(DataOutputStream dos, List constants) throws IOException { + writeInt(dos, constants.size()); + + for (LuaValue constant : constants) { + writeConstant(dos, constant); + } + } + + private static void writePrototypes(DataOutputStream dos, List chunks) throws IOException { + writeInt(dos, chunks.size()); + + for (LuaChunk chunk : chunks) { + writeChunk(dos, chunk, false); + } + } + + private static void writeString(DataOutputStream dos, String s) throws IOException { + writeInt(dos, s.length() + 1); + + for (int i = 0; i < s.length(); i++) { + dos.writeByte(s.charAt(i)); + } + + dos.writeByte(0); + } + + public static void write(OutputStream os, LuaChunk chunk) throws IOException { + DataOutputStream dos = new DataOutputStream(os); + + writeHeader(dos); + writeChunk(dos, chunk, true); + } +} diff --git a/src/LuraphDevirtualizer.java b/src/LuraphDevirtualizer.java new file mode 100644 index 0000000..663863c --- /dev/null +++ b/src/LuraphDevirtualizer.java @@ -0,0 +1,2371 @@ +import ASTNodes.*; +import ASTNodes.Number; + +import LuaVM.*; + +import java.util.*; + +public class LuraphDevirtualizer { + private Node root; + + private Function + vmRun, + getByte, + getDword, + getBits, + getFloat64, + getInstruction, + getString, + decodeChunk, + createWrapper, + vmRunFunc; + + private String vmRunName = "vm_run"; + private String getByteName = "get_byte"; + private String getDwordName = "get_dword"; + private String getBitsName = "get_bits"; + private String getFloat64Name = "get_float64"; + private String getInstructionName = "get_instruction"; + private String getStringName = "get_string"; + private String decodeChunkName = "decode_chunk"; + private String createWrapperName = "create_wrapper"; + private String vmRunFuncName = "vm_run_func"; + + // idx inside cache + // remember string constants must be stripped + private String constantTableIdx; + // contains line numbers for debug info + private String debugTableIdx; + private String instructionTableIdx; + private String prototypeTableIdx; + + // defer index in constant table + private String constantIdx; + + // instruction data + // also need to keep track of the instruction data bits + // check if data bits change btw diff VMs + private List opcodeIdxs = new ArrayList<>(); + private List aIdxs = new ArrayList<>(); + private List bcIdxs = new ArrayList<>(); + private List bxIdxs = new ArrayList<>(); + + private String aIdx, bIdx, cIdx, bxIdx, sbxIdx; + + private String cacheName = "cache"; + private String constantsName = "constants"; + private String environmentName = "environment"; + private String stackName = "stack"; + private String varargszName = "varargsz"; + private String varsName = "vars"; + private String upvaluesName = "upvalues"; + private String handleReturnName = "handle_return"; + private String vmHandlerTableName = "vm_handler_table"; + private String vmOpcodeDispatcherTableName = "vm_opcode_handler_table"; + private String biasName = "sbx_bias"; + + private String luraphVmString; + + private List luraphBytes = new ArrayList<>(); + + private int bytePos; + + // note: mapping indices start from 1 (as in Lua) + // note: this corresponds to the massive handler table in lua code + private HashMap vmHandlerMapping = new HashMap<>(); + + // map opcode number to vmHandlerMapping + private HashMap vmOpcodeDispatcherMapping = new HashMap<>(); + + // map Lua vm instruction to vmOpcodeDispatcherMapping + // indices start from 1 just as in lua + private HashMap instructionToOpcodeMapping = new HashMap<>(); + + private HashMap opcodeToInstructionMapping = new HashMap<>(); + + // key1 and key2 correspond to keys used for instruction and string decoding + // we will have to figure out the correspondence by looking at the source code + private String key1Name; + private String key2Name; + private double key1; + private double key2; + + // also need to parse some locals in decoding chunk (3 free ones) to find out what they are + + // ABC params never switched + + public LuraphDevirtualizer(Node root) { + this.root = root; + } + + public LuaChunk process() { + ASTOptimizerMgr mgr = new ASTOptimizerMgr(root); + mgr.addRenamer(new RenamerPass1()); + mgr.optimize(); + + getDecodeChunkData(); + + mgr = new ASTOptimizerMgr(root); + mgr.addRenamer(new RenamerPass2()); + mgr.optimize(); + + mgr = new ASTOptimizerMgr(root); + mgr.addRenamer(new RenamerPass3()); + mgr.optimize(); + + identifyVmHandlers(); + + verifyHandlers(); + + setupOpcodeToInstructionMapping(); + + luraphDecodeString(); + + //dumpHandlerInfo(); + + getInstructionOpcodeIndices(); + + getInstructionAndStringDecryptionKeys(); + + // todo: looks like one global is replaced with index 100000 in setglobal/getglobal + // will have to translate that global into something + + return loadChunks(); + } + + LuaValue evaluateVmExpression(Expression expr) { + LuaValue v = new LuaValue(); + + if (expr instanceof FunctionCall) { + String name = ((FunctionCall)expr).varOrExp.line(); + + if (name.equals(getByteName)) { + v.type = LuaValue.Type.NUMBER; + v._nv = get_byte(); + return v; + } + else if (name.equals(getDwordName)) { + v.type = LuaValue.Type.NUMBER; + v._nv = get_dword(); + return v; + } + else if (name.equals(getFloat64Name)) { + v.type = LuaValue.Type.NUMBER; + v._nv = get_float64(); + return v; + } + else if (name.equals(getStringName)) { + v.type = LuaValue.Type.STRING; + String args = ((FunctionCall)expr).nameAndArgs.get(0).line(); + if (args.equals(key1Name)) { + v._sv = stripConstantsString(get_string(key1)); + return v; + } + else if (args.equals(key2Name)) { + v._sv = stripConstantsString(get_string(key2)); + return v; + } + else { + try { + double key = Double.parseDouble(args); + v._sv = stripConstantsString(get_string(key)); + return v; + } + catch (NumberFormatException e) { + throw new RuntimeException("Unable to get string decryption key"); + } + } + + } + else if (name.equals(getInstructionName)) { + throw new RuntimeException("Unsupported location for instruction name"); + } + } + else if (expr instanceof BinaryExpression) { + BinaryExpression e = (BinaryExpression)expr; + + if (e.operator == BinaryExpression.Operator.ADD) { + LuaValue left = evaluateVmExpression(e.left); + LuaValue right = evaluateVmExpression(e.right); + + if (left.type == LuaValue.Type.NUMBER && right.type == LuaValue.Type.NUMBER) { + v.type = LuaValue.Type.NUMBER; + v._nv = left._nv + right._nv; + return v; + } + } + else if (e.operator == BinaryExpression.Operator.SUB) { + LuaValue left = evaluateVmExpression(e.left); + LuaValue right = evaluateVmExpression(e.right); + + if (left.type == LuaValue.Type.NUMBER && right.type == LuaValue.Type.NUMBER) { + v.type = LuaValue.Type.NUMBER; + v._nv = left._nv - right._nv; + return v; + } + } + } + else if (expr instanceof True) { + v.type = LuaValue.Type.BOOLEAN; + v._bv = true; + return v; + } + else if (expr instanceof False) { + v.type = LuaValue.Type.BOOLEAN; + v._bv = false; + return v; + } + else if (expr instanceof Number) { + v.type = LuaValue.Type.NUMBER; + v._nv = ((Number)expr).value; + return v; + } + else if (expr instanceof TableConstructor) { + v.type = LuaValue.Type.TABLE; + return v; + } + else if (expr instanceof Nil) { + v.type = LuaValue.Type.NIL; + return v; + } + + throw new RuntimeException("Could not evaluate expression"); + } + + private LuaValue evalHandleRedirectionHandlerExpression(VMInstruction instruction, Expression expr, HashMap varMap) { + LuaValue v = new LuaValue(); + + if (expr instanceof Variable) { + Variable var = (Variable)expr; + + v.type = LuaValue.Type.NUMBER; + v._nv = instruction.getValue(varMap.get(var.symbol())); + return v; + } + else if (expr instanceof BinaryExpression) { + BinaryExpression bexpr = (BinaryExpression)expr; + + LuaValue left = evalHandleRedirectionHandlerExpression(instruction, bexpr.left, varMap); + LuaValue right = evalHandleRedirectionHandlerExpression(instruction, bexpr.right, varMap); + + if (left.type != LuaValue.Type.NUMBER || right.type != LuaValue.Type.NUMBER) { + throw new RuntimeException("Unexpected types in binary expression"); + } + + if (bexpr.operator == BinaryExpression.Operator.MOD) { + v.type = LuaValue.Type.NUMBER; + v._nv = modulus(left._nv, right._nv); + return v; + } + else if (bexpr.operator == BinaryExpression.Operator.ADD) { + v.type = LuaValue.Type.NUMBER; + v._nv = left._nv + right._nv; + return v; + } + else if (bexpr.operator == BinaryExpression.Operator.SUB) { + v.type = LuaValue.Type.NUMBER; + v._nv = left._nv - right._nv; + return v; + } + else { + throw new RuntimeException("Unexpected binary expression operator"); + } + } + else if (expr instanceof Number) { + v.type = LuaValue.Type.NUMBER; + v._nv = ((Number)expr).value; + return v; + } + + throw new RuntimeException("Unexpected expression in redirect handler"); + } + + // decrypt instruction if it defers into a redirection handler + private void handleRedirectionHandlers(VMInstruction instruction) { + Integer opcodeIndex = instructionToOpcodeMapping.get(instruction.opcode); + Integer handlerIndex = vmOpcodeDispatcherMapping.get(opcodeIndex); + + Function fn = vmHandlerMapping.get(handlerIndex); + + HashMap varMap = new HashMap<>(); + + int i; + + for (i = 0; i < 5; i++) { + LocalDeclare decl = (LocalDeclare)fn.block.stmts.get(i); + + String varName = decl.names.names.get(0).symbol(); + + Expression expr = decl.exprs.exprs.get(0); + + if (expr instanceof BinaryExpression) { + varMap.put(varName, VMOperand.sBx); + } + else { + Variable var = (Variable)expr; + + String idx = var.suffixes.get(0).expOrName.line(); + + if (idx.equals(aIdx)) { + varMap.put(varName, VMOperand.A); + } + else if (idx.equals(bIdx)) { + varMap.put(varName, VMOperand.B); + } + else if (idx.equals(cIdx)) { + varMap.put(varName, VMOperand.C); + } + else if (idx.equals(bxIdx)) { + varMap.put(varName, VMOperand.Bx); + } + else { + throw new RuntimeException("Unexpected local variable index"); + } + } + } + + for (; i < fn.block.stmts.size(); i++) { + Statement node = fn.block.stmts.get(i); + + if (node instanceof If) { + If stmt = (If)node; + + if (stmt.ifstmt.second.ret == null || !(stmt.ifstmt.second.ret.exprs.exprs.get(0) instanceof FunctionCall)) { + continue; + } + + FunctionCall call = (FunctionCall)stmt.ifstmt.second.ret.exprs.exprs.get(0); + + if (call.varOrExp.symbol().equals(vmOpcodeDispatcherTableName)) { + + // get the variable name being compared + BinaryExpression cmpExpr = (BinaryExpression)stmt.ifstmt.first; + String cmpName = ((Variable)(cmpExpr).left).symbol(); + + VMOperand cmpOperand = varMap.get(cmpName); + + if (instruction.getValue(cmpOperand) == ((Number)cmpExpr.right).value) { + // handle redirection and recurse to handle multiple redirections + + int redirectOpcode = (int)((Number)(((Variable)call.varOrExp).suffixes.get(0).expOrName)).value; + + instruction.opcodeNum = redirectOpcode; + instruction.opcode = opcodeToInstructionMapping.get(redirectOpcode); + + // handle decryption + + TableConstructor tctor = (TableConstructor)((ExprList)call.nameAndArgs.get(0).args).exprs.get(0); + + for (Pair p : tctor.entries) { + String operandIdx = ((Number)p.first).toString(); + + LuaValue v = evalHandleRedirectionHandlerExpression(instruction, p.second, varMap); + + if (v.type != LuaValue.Type.NUMBER) { + throw new RuntimeException("Expected number value"); + } + + if (operandIdx.equals(aIdx)) { + instruction.A = v._nv; + } + else if (operandIdx.equals(bIdx)) { + instruction.B = v._nv; + } + else if (operandIdx.equals(cIdx)) { + instruction.C = v._nv; + } + else if (operandIdx.equals(bxIdx)) { + instruction.Bx = v._nv; + } + else if (operandIdx.equals(sbxIdx)) { + instruction.sBx = v._nv; + } + + } + + handleRedirectionHandlers(instruction); + } + + } + } + } + } + + private String stripConstantsString(String s) { + Function stripStringFunction = (Function)createWrapper.first(Function.class); + + If stmt = (If)stripStringFunction.first(If.class); + + FunctionCall stringSub = (FunctionCall)((TableConstructor)stmt.ifstmt.second.ret.exprs.exprs.get(0)).entries.get(0).second; + + if (stringSub.varOrExp.line().equals("string.sub")) { + ExprList exprs = (ExprList)stringSub.nameAndArgs.get(0).args; + int numArgs = exprs.exprs.size(); + + if (numArgs != 2) { + throw new RuntimeException("Unexpected number of arguments to string.sub"); + } + + int index = (int)((Number)exprs.exprs.get(1)).value - 1; + + return s.substring(index); + } + + return s; + } + + private void populateChunkParams(LuaChunk chunk, HashMap chunkParams) { + List lds = createWrapper.block.getChildren(LocalDeclare.class); + + // find 2 referenced params + for (Node node : lds) { + LocalDeclare ld = (LocalDeclare)node; + + Expression rhs = ld.exprs.exprs.get(0); + + if (rhs instanceof Variable) { + Variable var = (Variable)rhs; + + if (var.name.symbol().equals(cacheName)) { + String sid = var.suffixes.get(0).expOrName.line(); + + // found number of args + if (chunkParams.containsKey(sid)) { + double value = chunkParams.get(sid); + + chunk.numParams = value; + + chunkParams.remove(sid); + } + } + } + else if (rhs instanceof Number) { + String srcValue = ((Number)rhs).toString(); + + // found number of upvals + if (chunkParams.containsKey(srcValue)) { + double value = chunkParams.get(srcValue); + + chunk.numUpVals = value; + + chunkParams.remove(srcValue); + } + } + + if (chunkParams.size() == 1) { + break; + } + } + + // last contender is max stack size + chunk.maxStackSize = chunkParams.entrySet().iterator().next().getValue(); + } + + private LuaChunk loadChunks() { + List children = decodeChunk.block.getChildren(); + + LuaChunk chunk = new LuaChunk(); + + // keep track of variables + HashMap vals = new HashMap<>(); + + // max stack size, num upvals, num args + HashMap chunkParams = new HashMap<>(); + + // emulate lua code + for (int i = 0; i < children.size(); i++) { + Node node = children.get(i); + + if (node instanceof LocalDeclare) { + LocalDeclare ld = (LocalDeclare)node; + if (ld.names.names.size() != 1) + throw new RuntimeException("Namelist unsupported in loadChunks"); + String lvalue = ld.names.names.get(0).symbol(); + vals.put(lvalue, evaluateVmExpression(ld.exprs.exprs.get(0))); + } + else if (node instanceof Assign) { + Assign ld = (Assign)node; + if (ld.vars.vars.size() != 1) + throw new RuntimeException("Namelist unsupported in loadChunks"); + + // consume expression + LuaValue v = evaluateVmExpression(ld.exprs.exprs.get(0)); + + if (v.type != LuaValue.Type.NUMBER) { + throw new RuntimeException("LuaValue expected number while loading chunk"); + } + + Variable vr = (Variable)ld.vars.vars.get(0); + String sid = vr.suffixes.get(0).expOrName.line(); + chunkParams.put(sid, v._nv); + + } + else if (node instanceof ForStep) { + // determine if we are loading constants, debug info, or instructions + ForStep stmt = (ForStep)node; + + String vtoName = ((Variable)stmt.condition).symbol(); + int init = 1; + int to = (int)vals.get(vtoName)._nv; + + if (stmt.block.getChildren(If.class).size() > 0) { + // loading constants + + Block block = stmt.block; + + List cc = block.getChildren(); + + for (int j = init; j <= to; j++) { + double key = -1; + for (int k = 0; k < cc.size(); k++) { + Node n = cc.get(k); + + if (n instanceof LocalDeclare) { + LuaValue v = evaluateVmExpression(((LocalDeclare)n).exprs.exprs.get(0)); + if (v.type == LuaValue.Type.NUMBER) { + key = v._nv; + } + } + else if (n instanceof If) { + BinaryExpression e = (BinaryExpression)((If)n).ifstmt.first; + + if (key == ((Number)e.right).value) { + Assign assign = (Assign)((If)n).ifstmt.second.stmts.get(0); + LuaValue v = evaluateVmExpression(assign.exprs.exprs.get(0)); + + chunk.constants.add(v); + //System.out.println("-- Constant: " + v); + break; + } + } + } + } + } + else if (stmt.block.stmts.size() == 1 && stmt.block.stmts.get(0).line().contains(decodeChunkName)) { + // loading prototypes + + for (int j = init; j <= to; j++) { + chunk.prototypes.add(loadChunks()); + } + } + else if (stmt.block.stmts.size() == 1 && stmt.block.stmts.get(0).line().contains(getDwordName)) { + // loading debug info + + for (int j = init; j <= to; j++) { + chunk.debugLines.add(get_dword()); + } + } + else { + // loading instructions + + // get key used by get_instr + + FunctionCall call = (FunctionCall)stmt.block.first(FunctionCall.class); + String varName = call.nameAndArgs.get(0).args.symbol(); + double key = 0.0; + + if (varName.equals(key1Name)) { + key = key1; + } + else if (varName.equals(key2Name)) { + key = key2; + } + else { + throw new RuntimeException("Unknown encryption key used by get_instruction"); + } + + for (int j = init; j <= to; j++) { + double insnData = get_instruction(key); + VMInstruction insn = new VMInstruction(); + + for (Node stmtOps : stmt.block.stmts) { + if (stmtOps instanceof Assign) { + Assign assign = (Assign)stmtOps; + + String idx = ((Variable)assign.vars.vars.get(0)).suffixes.get(0).expOrName.line(); + + if (!(assign.exprs.exprs.get(0) instanceof FunctionCall)) + continue; + + FunctionCall getBitsCall = (FunctionCall)assign.exprs.exprs.get(0); + + double ix = Double.parseDouble(((ExprList)getBitsCall.nameAndArgs.get(0).args).exprs.get(1).line()); + double jx = Double.parseDouble(((ExprList)getBitsCall.nameAndArgs.get(0).args).exprs.get(2).line()); + + if (!getBitsCall.varOrExp.symbol().equals(getBitsName)) { + throw new RuntimeException("Expected get bits call here"); + } + + if (idx.equals(aIdx)) { + insn.A = get_bits(insnData, ix, jx); + } + else if (idx.equals(bIdx)) { + insn.B = get_bits(insnData, ix, jx); + } + else if (idx.equals(cIdx)) { + insn.C = get_bits(insnData, ix, jx); + } + else if (idx.equals(bxIdx)) { + insn.Bx = get_bits(insnData, ix, jx); + insn.sBx = insn.Bx - 131071.0; + } + else if (opcodeIdxs.contains(idx)) { + // Lua code indices into table, so we add 1 to the opcode + int val = (int)get_bits(insnData, ix, jx); + insn.opcode = opcodeToInstructionMapping.get(val + 1); + insn.opcodeNum = val; + if (insn.opcode == null) { + throw new RuntimeException("Bad operand"); + } + } + } + } + + handleRedirectionHandlers(insn); + chunk.insns.add(insn); + } + } + } + else if (node instanceof FunctionCall) { + evaluateVmExpression((FunctionCall)node); + } + } + + populateChunkParams(chunk, chunkParams); + + return chunk; + } + + private void getInstructionAndStringDecryptionKeys() { + List children = vmRun.block.getChildren(); + + int idx = -1; + + for (int i = 0; i < children.size(); i++) { + if (children.get(i) == decodeChunk) { + idx = i; + break; + } + } + + if (idx == -1) + throw new RuntimeException("Could not find string and instruction decryption keys in VM err1"); + + LocalDeclare l1 = (LocalDeclare)children.get(idx - 2); + LocalDeclare l2 = (LocalDeclare)children.get(idx - 1); + + if (l1.names.names.size() != 1 || l2.names.names.size() != 1) + throw new RuntimeException("Could not find string and instruction decryption keys in VM err2"); + + key1Name = l1.names.names.get(0).symbol(); + key2Name = l2.names.names.get(0).symbol(); + + String f1 = l1.exprs.exprs.get(0).line(); + String f2 = l2.exprs.exprs.get(0).line(); + + if (!f1.equals(getByteName + "()") || !f2.equals(getByteName + "()")) + throw new RuntimeException("Could not find string and instruction decryption keys in VM err3"); + + key1 = get_byte(); + key2 = get_byte(); + } + + private void dumpHandlerInfo() { + for (Map.Entry entry : instructionToOpcodeMapping.entrySet()) { + System.out.println("-- OPCODE " + entry.getValue() + "=" + entry.getKey().toString()); + Function fn = vmHandlerMapping.get(vmOpcodeDispatcherMapping.get(entry.getValue())); + + Block block = new Block(); + block.stmts.add(fn); + ASTSourceGenerator gen = new ASTSourceGenerator(block); + // dont confuse opcodes with table entries + System.out.println(gen.generate()); + } + + System.out.println(); + } + + private int getDecodeChunkConstantStatementIdx() { + int idx = -1; + int max = 0; + + for (int i = 0; i < decodeChunk.block.stmts.size(); i++) { + Statement stmt = decodeChunk.block.stmts.get(i); + + if (stmt instanceof ForStep) { + int count = stmt.count(If.class); + + if (count > max) { + count = max; + idx = i; + } + } + } + + return idx; + } + + private void getDecodeChunkConstantIndices() { + int constantStmtIdx = getDecodeChunkConstantStatementIdx(); + + ForStep constantStep = (ForStep)decodeChunk.block.stmts.get(constantStmtIdx); + + If stmt = (If)constantStep.block.first(If.class); + Variable var = (Variable)stmt.ifstmt.second.first(Variable.class); + constantIdx = var.suffixes.get(0).expOrName.line(); + + Assign last = (Assign)constantStep.block.stmts.get(constantStep.block.stmts.size() - 1); + constantTableIdx = ((Variable)last.first(Variable.class)).suffixes.get(0).expOrName.line(); + } + + private void getDecodeChunkDebugIndices() { + for (Node stmt : decodeChunk.block.getChildren(ForStep.class)) { + List children = ((ForStep)stmt).block.getChildren(); + + if (children.size() == 1) { + FunctionCall call = (FunctionCall)children.get(0).first(FunctionCall.class); + if (call.varOrExp.line().equals(getDwordName)) { + Assign assign = (Assign)children.get(0); + debugTableIdx = ((Variable)assign.first(Variable.class)).suffixes.get(0).expOrName.line(); + } + } + } + } + + private void getDecodeChunkInstructionIndicies() { + for (Node stmt : decodeChunk.block.getChildren(ForStep.class)) { + Block block = (Block)((ForStep)stmt).block; + FunctionCall call = (FunctionCall)block.first(FunctionCall.class); + if (call.varOrExp.line().equals(getInstructionName)) { + List assign = block.getChildren(Assign.class); + + for (Node s : assign) { + FunctionCall gbitsCall = (FunctionCall)s.first(FunctionCall.class); + String idx = ((Variable)s.first(Variable.class)).suffixes.get(0).expOrName.line(); + + if (gbitsCall != null && gbitsCall.varOrExp.line().equals(getBitsName)) { + for (NameAndArgs nargs : gbitsCall.nameAndArgs) { + ExprList list = (ExprList)nargs.args; + double from = ((Number)list.exprs.get(1)).value; + double to = ((Number)list.exprs.get(2)).value; + int size = (int)(to - from + 1); + + if (size == 6) { + opcodeIdxs.add(idx); + } + else if (size == 8){ + aIdxs.add(idx); + } + else if (size == 9) { + bcIdxs.add(idx); + } + else if (size == 18) { + bxIdxs.add(idx); + } + } + } + else { + instructionTableIdx = idx; + } + } + } + } + } + + private void getDecodeChunkPrototypeIndicies() { + for (Node stmt : decodeChunk.block.getChildren(ForStep.class)) { + List children = ((ForStep)stmt).block.getChildren(); + + if (children.size() == 1) { + FunctionCall call = (FunctionCall)children.get(0).first(FunctionCall.class); + if (call.varOrExp.line().equals(decodeChunkName)) { + Assign assign = (Assign)children.get(0); + prototypeTableIdx = ((Variable)assign.first(Variable.class)).suffixes.get(0).expOrName.line(); + } + } + } + } + + private void getDecodeChunkData() { + getDecodeChunkConstantIndices(); + getDecodeChunkDebugIndices(); + getDecodeChunkInstructionIndicies(); + getDecodeChunkPrototypeIndicies(); + } + + private void luraphDecodeString() { + boolean dup = false; + byte repeat = 0; + + bytePos = 0; + + for (int i = 0; i < luraphVmString.length(); i += 2) { + int high = Character.digit(luraphVmString.charAt(i), 16); + int low = Character.digit(luraphVmString.charAt(i + 1), 16); + + byte b = (byte)((high << 4) + low); + + // luraph constants + if (luraphVmString.charAt(i + 1) == 71) { + dup = true; + repeat = (byte)high; + } + else { + if (dup) { + dup = false; + for (int j = 0; j < repeat; j++) { + luraphBytes.add(b); + } + } + else { + luraphBytes.add(b); + } + } + } + } + + private double get_byte() { + int b = luraphBytes.get(bytePos++) & 0xFF; + + return (double)b; + } + + private double get_dword() { + double a = get_byte(), b = get_byte(), c = get_byte(), d = get_byte(); + + return ((((d * 1.6777216E7) + (c * 65536.0)) + (b * 256.0)) + a); + } + + private double modulus(double value, double mod) { + double val = value % mod; + + if (val < 0.0) { + val = val + mod; + } + + return val; + } + + private double get_bits(double value, double i) { + double mask = Math.pow(2.0, i - 1.0); + + double m = mask + mask; + + double val = modulus(value, m); + + if (mask <= val) + return 1.0; + else + return 0.0; + } + + private double get_bits(double value, double i, double j) { + double it = 0.0; + double res = 0.0; + + for (double k = i; k <= j; k += 1.0) { + res = res + Math.pow(2.0, it) * get_bits(value, k); + it += 1.0; + } + + return res; + } + + private double get_float64() { + double a = get_dword(), b = get_dword(); + + if (a == 0.0 && b == 0.0) { + return 0.0; + } + + return ((((-2.0 * get_bits(b, 32.0)) + 1.0) * (Math.pow(2.0, (get_bits(b, 21.0, 31.0) - 1023.0)))) * + ((((get_bits(b, 1.0, 20.0) * 4.294967296E9) + a) / 4.503599627370496E15) + 1.0)); + } + + // always the same + private double get_instruction(double key) { + double arr1[] = { get_byte(), get_byte(), get_byte(), get_byte() }; + double keys[] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; + + for (int i = 0; i < 8; i++) { + keys[i] = get_bits(key, i + 1); + } + + double arr2[] = { 0.0, 0.0, 0.0, 0.0 }; + + for (int i = 0; i < 4; i++) { + double a = 0.0, b = 0.0; + + for (int j = 0; j < 8; j++) { + double c = get_bits(arr1[i], j + 1); + + if (keys[j] == 1.0) { + if (c == 1.0) + c = 0.0; + else + c = 1.0; + } + + a = a + Math.pow(2.0, b) * c; + b = b + 1.0; + } + + arr2[i] = a; + } + + return ((((arr2[3] * 1.6777216E7) + (arr2[2] * 65536.0)) + (arr2[1] * 256.0)) + arr2[0]); + } + + private String get_string(double key) { + StringBuilder sb = new StringBuilder(); + + int stringSize = (int)get_dword(); + + double[] arr0 = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; + + for (int i = 0; i < 8; i++) { + arr0[i] = get_bits(key, i + 1.0); + } + + double raw[] = new double[stringSize]; + + for (int i = 0; i < stringSize; i++) { + raw[i] = get_byte(); + } + + for (int i = 0; i < stringSize; i++) { + double a = 0.0, b = 0.0; + + for (int j = 0; j < 8; j++) { + double c = get_bits(raw[i], j + 1); + + if (arr0[j] == 1.0) { + if (c == 1.0) + c = 0.0; + else + c = 1.0; + } + + a = a + Math.pow(2.0, b) * c; + b = b + 1.0; + } + + sb.append((char)a); + } + + return sb.toString(); + } + + private void getInstructionOpcodeIndices() throws RuntimeException { + // USE RETURN! its ALWAYS present!!! + if (!instructionToOpcodeMapping.containsKey(VMOp.RETURN)) { + throw new RuntimeException("VM does not contain RETURN instruction. Critical error here... All Lua bytecode HAS at least ONE return!"); + } + + + Function fn = purifyHandler(vmHandlerMapping.get( + vmOpcodeDispatcherMapping.get(instructionToOpcodeMapping.get(VMOp.RETURN)))); + + // get first if else stmt + + String varA = null, varB = null; + + List stmts = fn.block.getChildren(); + + for (Node node : stmts) { + if (!(node instanceof If)) + continue; + + If stmt = (If)node; + + if (stmt.elsestmt == null) + continue; + + Assign assign = (Assign)stmt.elsestmt.stmts.get(0); + BinaryExpression _Aexpr = (BinaryExpression)assign.exprs.exprs.get(0); + + BinaryExpression _Bexpr = (BinaryExpression)_Aexpr.left; + + Variable _A = (Variable)_Bexpr.left; + + Variable _B = (Variable)_Bexpr.right; + + varA = _A.line(); + varB = _B.line(); + } + + if (varA == null || varB == null) { + // try to search for A + (B - 2) in outer scope + for (Node node : stmts) { + if (!(node instanceof LocalDeclare)) + continue; + + LocalDeclare stmt = (LocalDeclare) node; + + if (stmt.exprs.exprs.size() == 1 && stmt.exprs.exprs.get(0) instanceof BinaryExpression) { + BinaryExpression expr1 = (BinaryExpression)stmt.exprs.exprs.get(0); + if (expr1.operator == BinaryExpression.Operator.SUB && expr1.right instanceof Number) { + if (((Number)expr1.right).value == 2.0) { + BinaryExpression expr2 = (BinaryExpression)expr1.left; + Variable _A = (Variable)expr2.left; + + Variable _B = (Variable)expr2.right; + + varA = _A.line(); + varB = _B.line(); + + } + } + } + } + } + + if (varA == null || varB == null) + throw new RuntimeException("Could not find A or B idx"); + + // extract A, B, C, Bx, sBx + for (int i = 0; i < 5; i++) { + LocalDeclare decl = (LocalDeclare)stmts.get(i); + + String str = decl.line(); + + Expression expr = decl.exprs.exprs.get(0); + + if (expr instanceof Variable) { + // A, B, C, Bx + + String idx = ((Variable) decl.exprs.exprs.get(0)).suffixes.get(0).expOrName.line(); + + if (str.contains(varA)) { + aIdx = idx; + } else if (str.contains(varB)) { + bIdx = idx; + } + else if (bcIdxs.contains(idx)) { + cIdx = idx; + } + else if (bxIdxs.contains(idx)) { + bxIdx = idx; + } + } + else if (expr instanceof BinaryExpression) { + // sBx + BinaryExpression e = (BinaryExpression)expr; + String idx = ((Variable)e.left).suffixes.get(0).expOrName.line(); + + sbxIdx = idx; + } + } + + if (!sbxIdx.equals(bxIdx)) + throw new RuntimeException("sbxIdx should be equal to bxIdx"); + + } + + private boolean hasDuplicateValues(HashMap map) { + List list = new ArrayList(); + + for (Map.Entry entry : map.entrySet()) { + if (list.contains(entry.getValue())) { + return false; + } + list.add(entry.getValue()); + } + + return false; + } + + private void setupOpcodeToInstructionMapping() { + for (HashMap.Entry entry : instructionToOpcodeMapping.entrySet()) { + opcodeToInstructionMapping.put(entry.getValue(), entry.getKey()); + } + } + + private void verifyHandlers() throws RuntimeException { + if (instructionToOpcodeMapping.size() != vmOpcodeDispatcherMapping.size()) { + throw new RuntimeException("Inconsistent opcode to opcode dispatcher sizes"); + } + + if (vmOpcodeDispatcherMapping.size() != vmHandlerMapping.size()) { + throw new RuntimeException("Inconsistent opcode dispatcher to handler sizes"); + } + + if (hasDuplicateValues(vmOpcodeDispatcherMapping)) { + throw new RuntimeException("Duplicates in opcode dispatcher"); + } + + if (hasDuplicateValues(vmHandlerMapping)) { + throw new RuntimeException("Duplicates in handlers"); + } + } + + // create purified representantion of handler removing all redirection if checks + private Function purifyHandler(Function handler) { + Function purified = handler.clone(); + + ListIterator it = purified.block.stmts.listIterator(); + while (it.hasNext()) { + Statement stmt = it.next(); + + if (stmt instanceof If) { + FunctionCall call = (FunctionCall)stmt.first(FunctionCall.class); + + if (call != null && call.varOrExp.symbol().equals(vmOpcodeDispatcherTableName)) { + it.remove(); + } + } + } + + return purified; + } + + private boolean matchAssign(Assign assign, String lhsVarName, String rhsVarName) { + if (assign.vars.vars.size() == 1 && assign.exprs.exprs.size() == 1) { + Variable lhs = (Variable)assign.vars.vars.get(0); + Expression rhs = assign.exprs.exprs.get(0); + if (rhs instanceof Variable) { + if ((lhs.symbol().equals(lhsVarName) || lhsVarName.isEmpty()) && + rhs.symbol().equals(rhsVarName) || rhsVarName.isEmpty()) { + return true; + } + } + } + + return false; + } + + private boolean matchAssign(Assign assign, String lhsVarName, Literal rhsLiteral) { + if (assign.vars.vars.size() == 1 && assign.exprs.exprs.size() == 1) { + Variable lhs = (Variable)assign.vars.vars.get(0); + Expression rhs = assign.exprs.exprs.get(0); + if (rhs instanceof Literal) { + if ((lhs.symbol().equals(lhsVarName) || lhsVarName.isEmpty()) && + rhs.line().equals(rhsLiteral.line())) { + return true; + } + } + } + + return false; + } + + private boolean matchBinOp(BinaryExpression expr, BinaryExpression.Operator op, String lhsVarName, Literal rhs) { + if (expr.operator == op && expr.left instanceof Variable && expr.right instanceof Literal) { + Variable var = (Variable)expr.left; + Literal lit = (Literal)expr.right; + + if (var.symbol().equals(lhsVarName) || lhsVarName.isEmpty()) { + if (rhs.line().equals(lit.line())) { + return true; + } + } + } + + return false; + } + + private boolean matchBinOp(BinaryExpression expr, BinaryExpression.Operator op, String lhsVarName, String rhsVarName) { + if (expr.operator == op && expr.left instanceof Variable && expr.right instanceof Variable) { + Variable var = (Variable)expr.left; + Variable var2 = (Variable)expr.right; + + if (var.symbol().equals(lhsVarName) || lhsVarName.isEmpty()) { + if (var2.symbol().equals(rhsVarName) || rhsVarName.isEmpty()) { + return true; + } + } + } + + return false; + } + + private boolean matchUnOp(UnaryExpression expr, UnaryExpression.Operator op, String name) { + if (expr.op == op && expr.expr instanceof Variable) { + Variable var = (Variable)expr.expr; + + if (var.symbol().equals(name) || name.isEmpty()) { + return true; + } + } + + return false; + } + + private boolean matchLoadConstantStack(If stmt) { + if (stmt.elseifstmt.size() != 0) + return false; + + if (stmt.ifstmt.first instanceof BinaryExpression && + matchBinOp((BinaryExpression)stmt.ifstmt.first, BinaryExpression.Operator.GT, "", new Number(255))) { + if (stmt.ifstmt.second.stmts.size() == 1 && stmt.ifstmt.second.stmts.get(0) instanceof Assign) { + if (matchAssign((Assign)stmt.ifstmt.second.stmts.get(0), "", constantsName)) { + if (stmt.elsestmt != null && stmt.elsestmt.stmts.size() == 1 && stmt.elsestmt.stmts.get(0) instanceof Assign) { + return matchAssign((Assign)stmt.elsestmt.stmts.get(0), "", stackName); + } + } + } + + } + + return false; + } + + private boolean identifyMove(Function fn, int opcodeIndex) { + if (fn.block.stmts.size() == 6 && fn.block.stmts.get(5) instanceof Assign) { + Assign assign = (Assign)fn.block.stmts.get(5); + if (matchAssign(assign, stackName, stackName)) { + instructionToOpcodeMapping.put(VMOp.MOVE, opcodeIndex); + return true; + } + } + return false; + } + + private boolean identifyLoadk(Function fn, int opcodeIndex) { + if (fn.block.stmts.size() == 6 && fn.block.stmts.get(5) instanceof Assign) { + Assign assign = (Assign)fn.block.stmts.get(5); + if (matchAssign(assign, stackName, constantsName)) { + instructionToOpcodeMapping.put(VMOp.LOADK, opcodeIndex); + return true; + } + } + return false; + } + + private boolean identifyLoadBool(Function fn, int opcodeIndex) { + if (fn.block.stmts.size() == 7 && fn.block.stmts.get(6) instanceof If && fn.block.stmts.get(5) instanceof Assign) { + If stmt = (If)fn.block.stmts.get(6); + Assign assign = (Assign)fn.block.stmts.get(5); + + boolean match1 = false, match2 = false; + + if (assign.exprs.exprs.size() == 1 && assign.exprs.exprs.get(0) instanceof BinaryExpression) { + if (assign.vars.vars.get(0).symbol().equals(stackName)) { + if (matchBinOp((BinaryExpression)assign.exprs.exprs.get(0), BinaryExpression.Operator.NEQ, "", new Number(0.0))) { + match1 = true; + } + } + } + + if (stmt.elsestmt == null && stmt.elseifstmt.size() == 0 && stmt.ifstmt.first instanceof BinaryExpression) { + BinaryExpression binOp = (BinaryExpression)stmt.ifstmt.first; + if (matchBinOp(binOp, BinaryExpression.Operator.NEQ, "", new Number(0.0))) { + match2 = true; + } + } + + if (match1 && match2) { + instructionToOpcodeMapping.put(VMOp.LOADBOOL, opcodeIndex); + return true; + } + } + return false; + } + + private boolean identifyLoadNil(Function fn, int opcodeIndex) { + if (fn.block.stmts.size() == 6 && fn.block.stmts.get(5) instanceof ForStep) { + ForStep stmt = (ForStep)fn.block.stmts.get(5); + if (stmt.step == null && stmt.block.stmts.size() == 1 && stmt.block.stmts.get(0) instanceof Assign) { + if (matchAssign((Assign)stmt.block.stmts.get(0), stackName, new Nil())) { + instructionToOpcodeMapping.put(VMOp.LOADNIL, opcodeIndex); + return true; + } + } + } + return false; + } + + private boolean identifyGetUpVal(Function fn, int opcodeIndex) { + if (fn.block.stmts.size() == 6 && fn.block.stmts.get(5) instanceof Assign) { + Assign assign = (Assign)fn.block.stmts.get(5); + if (matchAssign(assign, stackName, upvaluesName)) { + instructionToOpcodeMapping.put(VMOp.GETUPVAL, opcodeIndex); + return true; + } + } + return false; + } + + private boolean identifyGetGlobal(Function fn, int opcodeIndex) { + if (fn.block.stmts.size() == 8 && fn.block.stmts.get(7) instanceof Assign) { + If stmt = (If)fn.block.first(If.class); + + if (stmt != null && stmt.ifstmt.first instanceof BinaryExpression) { + BinaryExpression expr = (BinaryExpression)stmt.ifstmt.first; + + if (matchBinOp(expr, BinaryExpression.Operator.EQ, "", new Number(100000))) { + Assign assign = (Assign)fn.block.stmts.get(7); + + if (matchAssign(assign, stackName, environmentName)) { + instructionToOpcodeMapping.put(VMOp.GETGLOBAL, opcodeIndex); + return true; + } + } + } + } + return false; + } + + private boolean identifyGetTable(Function fn, int opcodeIndex) { + if (fn.block.stmts.size() == 7 && fn.block.stmts.get(6) instanceof Assign && fn.block.stmts.get(5) instanceof If) { + If stmt = (If)fn.block.stmts.get(5); + Assign assign = (Assign)fn.block.stmts.get(6); + + if (matchLoadConstantStack(stmt) && matchAssign(assign, stackName, stackName)) { + instructionToOpcodeMapping.put(VMOp.GETTABLE, opcodeIndex); + return true; + } + } + return false; + } + + private boolean identifySetGlobal(Function fn, int opcodeIndex) { + if (fn.block.stmts.size() == 8 && fn.block.stmts.get(7) instanceof Assign) { + If stmt = (If)fn.block.first(If.class); + + if (stmt != null && stmt.ifstmt.first instanceof BinaryExpression) { + BinaryExpression expr = (BinaryExpression)stmt.ifstmt.first; + + if (matchBinOp(expr, BinaryExpression.Operator.EQ, "", new Number(100000))) { + Assign assign = (Assign)fn.block.stmts.get(7); + + if (matchAssign(assign, environmentName, stackName)) { + instructionToOpcodeMapping.put(VMOp.SETGLOBAL, opcodeIndex); + return true; + } + } + } + } + return false; + } + + private boolean identifySetUpVal(Function fn, int opcodeIndex) { + if (fn.block.stmts.size() == 6 && fn.block.stmts.get(5) instanceof Assign) { + Assign assign = (Assign)fn.block.stmts.get(5); + if (matchAssign(assign, upvaluesName, stackName)) { + instructionToOpcodeMapping.put(VMOp.SETUPVAL, opcodeIndex); + return true; + } + } + return false; + } + + private boolean identifySetTable(Function fn, int opcodeIndex) { + if (fn.block.stmts.size() == 8 && fn.block.stmts.get(7) instanceof Assign && fn.block.stmts.get(6) instanceof If && fn.block.stmts.get(5) instanceof If) { + if (matchLoadConstantStack((If)fn.block.stmts.get(5)) && matchLoadConstantStack((If)fn.block.stmts.get(6))) { + if (matchAssign((Assign)fn.block.stmts.get(7), stackName, "")) { + instructionToOpcodeMapping.put(VMOp.SETTABLE, opcodeIndex); + return true; + } + } + } + return false; + } + + private boolean identifyNewTable(Function fn, int opcodeIndex) { + if (fn.block.stmts.size() == 6 && fn.block.stmts.get(5) instanceof Assign) { + Assign assign = (Assign)fn.block.stmts.get(5); + if (assign.vars.vars.size() == 1 && assign.vars.vars.get(0).symbol().equals(stackName)) { + if (assign.exprs.exprs.size() == 1 && assign.exprs.exprs.get(0) instanceof TableConstructor) { + instructionToOpcodeMapping.put(VMOp.NEWTABLE, opcodeIndex); + return true; + } + } + } + return false; + } + + private boolean identifySelf(Function fn, int opcodeIndex) { + if (fn.block.stmts.size() == 9 && fn.block.stmts.get(8) instanceof Assign && + fn.block.stmts.get(7) instanceof Assign && fn.block.stmts.get(6) instanceof If && + fn.block.stmts.get(5) instanceof Assign) { + + Assign assign0 = (Assign)fn.block.stmts.get(5); + If stmt = (If)fn.block.stmts.get(6); + Assign assign1 = (Assign)fn.block.stmts.get(7); + Assign assign2 = (Assign)fn.block.stmts.get(8); + + if (!matchLoadConstantStack(stmt)) + return false; + + if (!matchAssign(assign2, stackName, "") || !matchAssign(assign1, stackName, "")) + return false; + + if (!matchAssign(assign0, "", stackName)) + return false; + + if (assign1.vars.vars.get(0) instanceof Variable) { + Variable var = (Variable)assign1.vars.vars.get(0); + if (var.suffixes.get(0).expOrName instanceof BinaryExpression) { + BinaryExpression expr = (BinaryExpression)var.suffixes.get(0).expOrName; + if (matchBinOp(expr, BinaryExpression.Operator.ADD, "", new Number(1))) { + instructionToOpcodeMapping.put(VMOp.SELF, opcodeIndex); + return true; + } + } + } + + } + return false; + } + + private boolean identifyAdd(Function fn, int opcodeIndex) { + if (fn.block.stmts.size() == 8 && + fn.block.stmts.get(5) instanceof If && + fn.block.stmts.get(6) instanceof If && + fn.block.stmts.get(7) instanceof Assign) { + + If s1 = (If)fn.block.stmts.get(5); + If s2 = (If)fn.block.stmts.get(6); + Assign s3 = (Assign)fn.block.stmts.get(7); + + if (!matchLoadConstantStack(s1) || !matchLoadConstantStack(s2)) + return false; + + if (s3.vars.vars.size() == 1 && s3.exprs.exprs.size() == 1 && s3.exprs.exprs.get(0) instanceof BinaryExpression) { + BinaryExpression expr = (BinaryExpression)s3.exprs.exprs.get(0); + if (matchBinOp(expr, BinaryExpression.Operator.ADD, "", "")) { + instructionToOpcodeMapping.put(VMOp.ADD, opcodeIndex); + return true; + } + } + + } + return false; + } + + private boolean identifySub(Function fn, int opcodeIndex) { + if (fn.block.stmts.size() == 8 && + fn.block.stmts.get(5) instanceof If && + fn.block.stmts.get(6) instanceof If && + fn.block.stmts.get(7) instanceof Assign) { + + If s1 = (If)fn.block.stmts.get(5); + If s2 = (If)fn.block.stmts.get(6); + Assign s3 = (Assign)fn.block.stmts.get(7); + + if (!matchLoadConstantStack(s1) || !matchLoadConstantStack(s2)) + return false; + + if (s3.vars.vars.size() == 1 && s3.exprs.exprs.size() == 1 && s3.exprs.exprs.get(0) instanceof BinaryExpression) { + BinaryExpression expr = (BinaryExpression)s3.exprs.exprs.get(0); + if (matchBinOp(expr, BinaryExpression.Operator.SUB, "", "")) { + instructionToOpcodeMapping.put(VMOp.SUB, opcodeIndex); + return true; + } + } + + } + return false; + } + + private boolean identifyMul(Function fn, int opcodeIndex) { + if (fn.block.stmts.size() == 8 && + fn.block.stmts.get(5) instanceof If && + fn.block.stmts.get(6) instanceof If && + fn.block.stmts.get(7) instanceof Assign) { + + If s1 = (If)fn.block.stmts.get(5); + If s2 = (If)fn.block.stmts.get(6); + Assign s3 = (Assign)fn.block.stmts.get(7); + + if (!matchLoadConstantStack(s1) || !matchLoadConstantStack(s2)) + return false; + + if (s3.vars.vars.size() == 1 && s3.exprs.exprs.size() == 1 && s3.exprs.exprs.get(0) instanceof BinaryExpression) { + BinaryExpression expr = (BinaryExpression)s3.exprs.exprs.get(0); + if (matchBinOp(expr, BinaryExpression.Operator.MUL, "", "")) { + instructionToOpcodeMapping.put(VMOp.MUL, opcodeIndex); + return true; + } + } + + } + return false; + } + + private boolean identifyDiv(Function fn, int opcodeIndex) { + if (fn.block.stmts.size() == 8 && + fn.block.stmts.get(5) instanceof If && + fn.block.stmts.get(6) instanceof If && + fn.block.stmts.get(7) instanceof Assign) { + + If s1 = (If)fn.block.stmts.get(5); + If s2 = (If)fn.block.stmts.get(6); + Assign s3 = (Assign)fn.block.stmts.get(7); + + if (!matchLoadConstantStack(s1) || !matchLoadConstantStack(s2)) + return false; + + if (s3.vars.vars.size() == 1 && s3.exprs.exprs.size() == 1 && s3.exprs.exprs.get(0) instanceof BinaryExpression) { + BinaryExpression expr = (BinaryExpression)s3.exprs.exprs.get(0); + if (matchBinOp(expr, BinaryExpression.Operator.REAL_DIV, "", "")) { + instructionToOpcodeMapping.put(VMOp.DIV, opcodeIndex); + return true; + } + } + + } + return false; + } + + private boolean identifyMod(Function fn, int opcodeIndex) { + if (fn.block.stmts.size() == 8 && + fn.block.stmts.get(5) instanceof If && + fn.block.stmts.get(6) instanceof If && + fn.block.stmts.get(7) instanceof Assign) { + + If s1 = (If)fn.block.stmts.get(5); + If s2 = (If)fn.block.stmts.get(6); + Assign s3 = (Assign)fn.block.stmts.get(7); + + if (!matchLoadConstantStack(s1) || !matchLoadConstantStack(s2)) + return false; + + if (s3.vars.vars.size() == 1 && s3.exprs.exprs.size() == 1 && s3.exprs.exprs.get(0) instanceof BinaryExpression) { + BinaryExpression expr = (BinaryExpression)s3.exprs.exprs.get(0); + if (matchBinOp(expr, BinaryExpression.Operator.MOD, "", "")) { + instructionToOpcodeMapping.put(VMOp.MOD, opcodeIndex); + return true; + } + } + + } + return false; + } + + private boolean identifyPow(Function fn, int opcodeIndex) { + if (fn.block.stmts.size() == 8 && + fn.block.stmts.get(5) instanceof If && + fn.block.stmts.get(6) instanceof If && + fn.block.stmts.get(7) instanceof Assign) { + + If s1 = (If)fn.block.stmts.get(5); + If s2 = (If)fn.block.stmts.get(6); + Assign s3 = (Assign)fn.block.stmts.get(7); + + if (!matchLoadConstantStack(s1) || !matchLoadConstantStack(s2)) + return false; + + if (s3.vars.vars.size() == 1 && s3.exprs.exprs.size() == 1 && s3.exprs.exprs.get(0) instanceof BinaryExpression) { + BinaryExpression expr = (BinaryExpression)s3.exprs.exprs.get(0); + if (matchBinOp(expr, BinaryExpression.Operator.POW, "", "")) { + instructionToOpcodeMapping.put(VMOp.POW, opcodeIndex); + return true; + } + } + + } + return false; + } + + private boolean identifyUnm(Function fn, int opcodeIndex) { + if (fn.block.stmts.size() == 6 && fn.block.stmts.get(5) instanceof Assign) { + Assign assign = (Assign)fn.block.stmts.get(5); + if (assign.exprs.exprs.size() == 1 && assign.vars.vars.size() == 1 && + assign.vars.vars.get(0) instanceof Variable && assign.exprs.exprs.get(0) instanceof UnaryExpression) { + + Variable var = (Variable)assign.vars.vars.get(0); + UnaryExpression expr = (UnaryExpression)assign.exprs.exprs.get(0); + + if (var.symbol().equals(stackName) && matchUnOp(expr, UnaryExpression.Operator.MINUS, "")) { + instructionToOpcodeMapping.put(VMOp.UNM, opcodeIndex); + return true; + } + + } + } + return false; + } + + private boolean identifyNot(Function fn, int opcodeIndex) { + if (fn.block.stmts.size() == 6 && fn.block.stmts.get(5) instanceof Assign) { + Assign assign = (Assign)fn.block.stmts.get(5); + if (assign.exprs.exprs.size() == 1 && assign.vars.vars.size() == 1 && + assign.vars.vars.get(0) instanceof Variable && assign.exprs.exprs.get(0) instanceof UnaryExpression) { + + Variable var = (Variable)assign.vars.vars.get(0); + UnaryExpression expr = (UnaryExpression)assign.exprs.exprs.get(0); + + if (var.symbol().equals(stackName) && matchUnOp(expr, UnaryExpression.Operator.NOT, "")) { + instructionToOpcodeMapping.put(VMOp.NOT, opcodeIndex); + return true; + } + + } + } + return false; + } + + private boolean identifyLen(Function fn, int opcodeIndex) { + if (fn.block.stmts.size() == 6 && fn.block.stmts.get(5) instanceof Assign) { + Assign assign = (Assign)fn.block.stmts.get(5); + if (assign.exprs.exprs.size() == 1 && assign.vars.vars.size() == 1 && + assign.vars.vars.get(0) instanceof Variable && assign.exprs.exprs.get(0) instanceof UnaryExpression) { + + Variable var = (Variable)assign.vars.vars.get(0); + UnaryExpression expr = (UnaryExpression)assign.exprs.exprs.get(0); + + if (var.symbol().equals(stackName) && matchUnOp(expr, UnaryExpression.Operator.HASHTAG, "")) { + instructionToOpcodeMapping.put(VMOp.LEN, opcodeIndex); + return true; + } + + } + } + return false; + } + + private boolean identifyConcat(Function fn, int opcodeIndex) { + if (fn.block.stmts.size() == 8 && fn.block.stmts.get(7) instanceof Assign && fn.block.stmts.get(6) instanceof ForStep) { + if (matchAssign((Assign)fn.block.stmts.get(7), stackName, "")) { + Node node = ((ForStep) fn.block.stmts.get(6)).block.first(BinaryExpression.class); + if (node != null && ((BinaryExpression)node).operator == BinaryExpression.Operator.STRCAT) { + instructionToOpcodeMapping.put(VMOp.CONCAT, opcodeIndex); + return true; + } + } + } + return false; + } + + private boolean identifyJump(Function fn, int opcodeIndex) { + if (fn.block.stmts.size() == 6 && fn.block.stmts.get(5) instanceof Assign) { + Assign assign = (Assign)fn.block.stmts.get(5); + if (assign.vars.vars.size() == 1 && assign.exprs.exprs.size() == 1 && + assign.vars.vars.get(0) instanceof Variable && assign.exprs.exprs.get(0) instanceof BinaryExpression) { + + BinaryExpression expr = (BinaryExpression)assign.exprs.exprs.get(0); + + if (expr.operator == BinaryExpression.Operator.ADD) { + instructionToOpcodeMapping.put(VMOp.JUMP, opcodeIndex); + return true; + } + + } + } + return false; + } + + private boolean identifyEq(Function fn, int opcodeIndex) { + if (fn.block.stmts.size() == 9 && fn.block.stmts.get(6) instanceof If && fn.block.stmts.get(7) instanceof If + && fn.block.stmts.get(8) instanceof If) { + + If if1 = (If)fn.block.stmts.get(6); + If if2 = (If)fn.block.stmts.get(7); + If if3 = (If)fn.block.stmts.get(8); + + if (matchLoadConstantStack(if1) && matchLoadConstantStack(if2)) { + if (if3.ifstmt.first instanceof BinaryExpression) { + BinaryExpression expr1 = (BinaryExpression)if3.ifstmt.first; + if (expr1.left instanceof BinaryExpression && expr1.right instanceof Variable) { + BinaryExpression expr2 = (BinaryExpression)expr1.left; + + if (expr1.operator == BinaryExpression.Operator.NEQ && expr2.operator == BinaryExpression.Operator.EQ) { + instructionToOpcodeMapping.put(VMOp.EQ, opcodeIndex); + return true; + } + } + } + } + + } + else if (fn.block.stmts.size() == 8 && fn.block.stmts.get(5) instanceof If && fn.block.stmts.get(6) instanceof If + && fn.block.stmts.get(7) instanceof If) { + + If if1 = (If)fn.block.stmts.get(5); + If if2 = (If)fn.block.stmts.get(6); + If if3 = (If)fn.block.stmts.get(7); + + if (matchLoadConstantStack(if1) && matchLoadConstantStack(if2)) { + if (if3.ifstmt.first instanceof BinaryExpression) { + BinaryExpression expr1 = (BinaryExpression)if3.ifstmt.first; + if (expr1.left instanceof BinaryExpression && expr1.right instanceof BinaryExpression) { + BinaryExpression expr2 = (BinaryExpression)expr1.left; + + if (expr1.operator == BinaryExpression.Operator.NEQ && expr2.operator == BinaryExpression.Operator.EQ) { + instructionToOpcodeMapping.put(VMOp.EQ, opcodeIndex); + return true; + } + } + } + } + + } + return false; + } + + private boolean identifyLt(Function fn, int opcodeIndex) { + if (fn.block.stmts.size() == 9 && fn.block.stmts.get(6) instanceof If && fn.block.stmts.get(7) instanceof If + && fn.block.stmts.get(8) instanceof If) { + + If if1 = (If)fn.block.stmts.get(6); + If if2 = (If)fn.block.stmts.get(7); + If if3 = (If)fn.block.stmts.get(8); + + if (matchLoadConstantStack(if1) && matchLoadConstantStack(if2)) { + if (if3.ifstmt.first instanceof BinaryExpression) { + BinaryExpression expr1 = (BinaryExpression)if3.ifstmt.first; + if (expr1.left instanceof BinaryExpression && expr1.right instanceof Variable) { + BinaryExpression expr2 = (BinaryExpression)expr1.left; + + if (expr1.operator == BinaryExpression.Operator.NEQ && expr2.operator == BinaryExpression.Operator.LT) { + instructionToOpcodeMapping.put(VMOp.LT, opcodeIndex); + return true; + } + } + } + } + + } + else if (fn.block.stmts.size() == 8 && fn.block.stmts.get(5) instanceof If && fn.block.stmts.get(6) instanceof If + && fn.block.stmts.get(7) instanceof If) { + + If if1 = (If)fn.block.stmts.get(5); + If if2 = (If)fn.block.stmts.get(6); + If if3 = (If)fn.block.stmts.get(7); + + if (matchLoadConstantStack(if1) && matchLoadConstantStack(if2)) { + if (if3.ifstmt.first instanceof BinaryExpression) { + BinaryExpression expr1 = (BinaryExpression)if3.ifstmt.first; + if (expr1.left instanceof BinaryExpression && expr1.right instanceof BinaryExpression) { + BinaryExpression expr2 = (BinaryExpression)expr1.left; + + if (expr1.operator == BinaryExpression.Operator.NEQ && expr2.operator == BinaryExpression.Operator.LT) { + instructionToOpcodeMapping.put(VMOp.LT, opcodeIndex); + return true; + } + } + } + } + + } + return false; + } + + // todo: need to refactor this so i dont depend on stmt size + private boolean identifyLte(Function fn, int opcodeIndex) { + if (fn.block.stmts.size() == 9 && fn.block.stmts.get(6) instanceof If && fn.block.stmts.get(7) instanceof If + && fn.block.stmts.get(8) instanceof If) { + + If if1 = (If)fn.block.stmts.get(6); + If if2 = (If)fn.block.stmts.get(7); + If if3 = (If)fn.block.stmts.get(8); + + if (matchLoadConstantStack(if1) && matchLoadConstantStack(if2)) { + if (if3.ifstmt.first instanceof BinaryExpression) { + BinaryExpression expr1 = (BinaryExpression)if3.ifstmt.first; + if (expr1.left instanceof BinaryExpression && expr1.right instanceof Variable) { + BinaryExpression expr2 = (BinaryExpression)expr1.left; + + if (expr1.operator == BinaryExpression.Operator.NEQ && expr2.operator == BinaryExpression.Operator.LTE) { + instructionToOpcodeMapping.put(VMOp.LTE, opcodeIndex); + return true; + } + } + } + } + + } + else if (fn.block.stmts.size() == 8 && fn.block.stmts.get(5) instanceof If && fn.block.stmts.get(6) instanceof If + && fn.block.stmts.get(7) instanceof If) { + + If if1 = (If)fn.block.stmts.get(5); + If if2 = (If)fn.block.stmts.get(6); + If if3 = (If)fn.block.stmts.get(7); + + if (matchLoadConstantStack(if1) && matchLoadConstantStack(if2)) { + if (if3.ifstmt.first instanceof BinaryExpression) { + BinaryExpression expr1 = (BinaryExpression)if3.ifstmt.first; + if (expr1.left instanceof BinaryExpression && expr1.right instanceof BinaryExpression) { + BinaryExpression expr2 = (BinaryExpression)expr1.left; + + if (expr1.operator == BinaryExpression.Operator.NEQ && expr2.operator == BinaryExpression.Operator.LTE) { + instructionToOpcodeMapping.put(VMOp.LTE, opcodeIndex); + return true; + } + } + } + } + + } + return false; + } + + private boolean identifyTest(Function fn, int opcodeIndex) { + if (fn.block.stmts.size() == 6 && fn.block.stmts.get(5) instanceof If) { + If stmt = (If)fn.block.stmts.get(5); + + if (stmt.elseifstmt.size() == 0 && stmt.elsestmt == null) { + if (stmt.ifstmt.first instanceof BinaryExpression) { + BinaryExpression expr1 = (BinaryExpression)stmt.ifstmt.first; + if (expr1.operator != BinaryExpression.Operator.EQ) + return false; + if (expr1.left instanceof Variable && ((Variable)expr1.left).symbol().equals(stackName)) { + if (expr1.right instanceof BinaryExpression) { + BinaryExpression expr2 = (BinaryExpression)expr1.right; + if (expr2.operator == BinaryExpression.Operator.EQ && expr2.right instanceof Number) { + Number n = (Number)expr2.right; + if (n.value == 0.0 && stmt.ifstmt.second.stmts.size() == 1) { + instructionToOpcodeMapping.put(VMOp.TEST, opcodeIndex); + return true; + } + } + } + } + } + } + } + return false; + } + + private boolean identifyTestSet(Function fn, int opcodeIndex) { + if (fn.block.stmts.size() == 6 && fn.block.stmts.get(5) instanceof If) { + If stmt = (If)fn.block.stmts.get(5); + + if (stmt.elseifstmt.size() == 0 && stmt.elsestmt != null) { + if (stmt.ifstmt.first instanceof BinaryExpression) { + BinaryExpression expr1 = (BinaryExpression)stmt.ifstmt.first; + if (expr1.operator != BinaryExpression.Operator.EQ) + return false; + if (expr1.left instanceof Variable && ((Variable)expr1.left).symbol().equals(stackName)) { + if (expr1.right instanceof BinaryExpression) { + BinaryExpression expr2 = (BinaryExpression)expr1.right; + if (expr2.operator == BinaryExpression.Operator.EQ && expr2.right instanceof Number) { + Number n = (Number)expr2.right; + if (n.value == 0.0 && stmt.ifstmt.second.stmts.size() == 1) { + instructionToOpcodeMapping.put(VMOp.TESTSET, opcodeIndex); + return true; + } + } + } + } + } + } + } + return false; + } + + private int countIfNeq1(Function fn) { + int count = 0; + List children = fn.block.getChildren(If.class); + + for (Node node : children) { + Expression expr1 = ((If)node).ifstmt.first; + + if (expr1 instanceof BinaryExpression) { + BinaryExpression binexpr = (BinaryExpression)expr1; + if (binexpr.operator == BinaryExpression.Operator.NEQ && binexpr.right instanceof Number) { + if (((Number)binexpr.right).value == 1.0) { + ++ count; + } + } + } + } + + return count; + } + + private boolean identifyCall(Function fn, int opcodeIndex) { + If stmt = (If)fn.block.first(If.class); + + if (stmt != null) { + FunctionCall call = (FunctionCall)stmt.first(FunctionCall.class); + + if (call != null && call.varOrExp.symbol().equals(handleReturnName)) { + // handle return doesnt have a return + if (fn.block.ret == null) { + instructionToOpcodeMapping.put(VMOp.CALL, opcodeIndex); + return true; + } + } + } + + if (countIfNeq1(fn) == 2) { + instructionToOpcodeMapping.put(VMOp.CALL, opcodeIndex); + return true; + } + + return false; + } + + private boolean identifyTailCall(Function fn, int opcodeIndex) { + If stmt = (If)fn.block.first(If.class); + + if (stmt != null) { + FunctionCall call = (FunctionCall)stmt.first(FunctionCall.class); + + if (call != null && call.varOrExp.symbol().equals(handleReturnName)) { + // handle return doesnt have a return + if (fn.block.ret != null) { + instructionToOpcodeMapping.put(VMOp.TAILCALL, opcodeIndex); + return true; + } + } + } + + if (countIfNeq1(fn) == 1) { + instructionToOpcodeMapping.put(VMOp.TAILCALL, opcodeIndex); + return true; + } + + return false; + } + + private boolean identifyReturn(Function fn, int opcodeIndex) { + if (fn.block.ret != null) { + for (Node node : fn.block.getChildren()) { + if (node.first(Return.class) != null) { + instructionToOpcodeMapping.put(VMOp.RETURN, opcodeIndex); + return true; + } + } + } + + return false; + } + + private boolean identifyForLoop(Function fn, int opcodeIndex) { + if (fn.block.stmts.size() == 9 && fn.block.stmts.get(8) instanceof If) { + If stmt = (If)fn.block.stmts.get(8); + + if (stmt.elsestmt == null & stmt.elseifstmt.size() == 1) { + instructionToOpcodeMapping.put(VMOp.FORLOOP, opcodeIndex); + return true; + } + } + return false; + } + + private boolean identifyForPrep(Function fn, int opcodeIndex) { + FunctionCall call = (FunctionCall)fn.block.first(FunctionCall.class); + + if (call != null && call.varOrExp.symbol().equals("assert")) { + instructionToOpcodeMapping.put(VMOp.FORPREP, opcodeIndex); + return true; + } + + return false; + } + + private boolean identifyTForLoop(Function fn, int opcodeIndex) { + for (Node node : fn.block.getChildren()) { + BinaryExpression expr = (BinaryExpression)node.first(BinaryExpression.class); + + if (expr != null && expr.operator == BinaryExpression.Operator.NEQ && expr.right instanceof Nil) { + instructionToOpcodeMapping.put(VMOp.TFORLOOP, opcodeIndex); + return true; + } + } + + return false; + } + + private boolean identifySetList(Function fn, int opcodeIndex) { + if (fn.block.stmts.size() == 8 && fn.block.stmts.get(5) instanceof LocalDeclare) { + LocalDeclare decl = (LocalDeclare)fn.block.stmts.get(5); + if (decl.exprs.exprs.get(0) instanceof BinaryExpression) { + BinaryExpression expr = (BinaryExpression)decl.exprs.exprs.get(0); + if (expr.operator == BinaryExpression.Operator.MUL && expr.right instanceof Number) { + Number num = (Number)expr.right; + if (num.value == 50) { + instructionToOpcodeMapping.put(VMOp.SETLIST, opcodeIndex); + return true; + } + } + } + } + return false; + } + + private boolean identifyClose(Function fn, int opcodeIndex) { + ForStep stmt1 = (ForStep)fn.block.first(ForStep.class); + if (stmt1 != null) { + ForIn stmt2 = (ForIn)stmt1.block.first(ForIn.class); + if (stmt2 != null && stmt2.exprs.exprs.get(0).symbol().equals("next")) { + instructionToOpcodeMapping.put(VMOp.CLOSE, opcodeIndex); + return true; + } + } + return false; + } + + private boolean identifyClosure(Function fn, int opcodeIndex) { + FunctionCall call = (FunctionCall)fn.block.first(FunctionCall.class); + + if (call != null && call.varOrExp.symbol().equals("setmetatable")) { + instructionToOpcodeMapping.put(VMOp.CLOSURE, opcodeIndex); + return true; + } + + return false; + } + + private boolean identifyVarArg(Function fn, int opcodeIndex) { + /* + ForStep stmt = (ForStep)fn.first(ForStep.class); + + if (stmt != null) { + If s = (If)stmt.first(If.class); + if (s != null && s.elsestmt != null && s.ifstmt.first instanceof BinaryExpression) { + BinaryExpression expr = (BinaryExpression)s.ifstmt.first; + if (expr.operator == BinaryExpression.Operator.LTE && expr.right instanceof Variable) { + Variable var = (Variable)expr.right; + if (var.symbol().equals(varargszName)) { + instructionToOpcodeMapping.put(VMOp.VARARG, opcodeIndex); + return true; + } + } + } + } + + return false;*/ + + if (new ASTSourceGenerator(fn.block).generate().contains(varargszName)) { + instructionToOpcodeMapping.put(VMOp.VARARG, opcodeIndex); + return true; + } + + return false; + } + + private void identifyVmHandler(Function purified, int opcodeIndex) { + if (identifyMove(purified, opcodeIndex)) + return; + if (identifyLoadk(purified, opcodeIndex)) + return; + if (identifyLoadBool(purified, opcodeIndex)) + return; + if (identifyLoadNil(purified, opcodeIndex)) + return; + if (identifyGetUpVal(purified, opcodeIndex)) + return; + if (identifyGetGlobal(purified, opcodeIndex)) + return; + if (identifyGetTable(purified, opcodeIndex)) + return; + if (identifySetGlobal(purified, opcodeIndex)) + return; + if (identifySetUpVal(purified, opcodeIndex)) + return; + if (identifySetTable(purified, opcodeIndex)) + return; + if (identifyNewTable(purified, opcodeIndex)) + return; + if (identifySelf(purified, opcodeIndex)) + return; + if (identifyAdd(purified, opcodeIndex)) + return; + if (identifySub(purified, opcodeIndex)) + return; + if (identifyMul(purified, opcodeIndex)) + return; + if (identifyDiv(purified, opcodeIndex)) + return; + if (identifyMod(purified, opcodeIndex)) + return; + if (identifyPow(purified, opcodeIndex)) + return; + if (identifyUnm(purified, opcodeIndex)) + return; + if (identifyNot(purified, opcodeIndex)) + return; + if (identifyLen(purified, opcodeIndex)) + return; + if (identifyConcat(purified, opcodeIndex)) + return; + if (identifyJump(purified, opcodeIndex)) + return; + if (identifyEq(purified, opcodeIndex)) + return; + if (identifyLt(purified, opcodeIndex)) + return; + if (identifyLte(purified, opcodeIndex)) + return; + if (identifyTest(purified, opcodeIndex)) + return; + if (identifyTestSet(purified, opcodeIndex)) + return; + if (identifyCall(purified, opcodeIndex)) + return; + if (identifyTailCall(purified, opcodeIndex)) + return; + if (identifyReturn(purified, opcodeIndex)) + return; + if (identifyForLoop(purified, opcodeIndex)) + return; + if (identifyForPrep(purified, opcodeIndex)) + return; + if (identifyTForLoop(purified, opcodeIndex)) + return; + if (identifySetList(purified, opcodeIndex)) + return; + if (identifyClose(purified, opcodeIndex)) + return; + if (identifyClosure(purified, opcodeIndex)) + return; + if (identifyVarArg(purified, opcodeIndex)) + return; + + Block block = new Block(); + block.stmts.add(purified); + ASTSourceGenerator gen = new ASTSourceGenerator(block); + + System.out.println(gen.generate()); + + throw new RuntimeException("Above printed handler could not be identified"); + } + + private void identifyVmHandlers() { + for (Map.Entry element : vmOpcodeDispatcherMapping.entrySet()) { + int key = (int)element.getKey(); + int value = (int)element.getValue(); + Function fn = vmHandlerMapping.get(value); + Function purified = purifyHandler(fn); + identifyVmHandler(purified, key); + } + } + + private class RenamerPass1 extends ASTOptimizerBase { + private void renameCoreFunctions(Function node) { + if (node.name != null) { + String symbol = node.name.symbol(); + + switch(symbol) { + default: + break; + case "func0": + mgr.symbols.add(symbol, node, vmRunName); + vmRun = node; + break; + case "func1": + mgr.symbols.add(symbol, node, getByteName); + getByte = node; + break; + case "func2": + mgr.symbols.add(symbol, node, getDwordName); + getDword = node; + break; + case "func3": + mgr.symbols.add(symbol, node, getBitsName); + getBits = node; + break; + case "func4": + mgr.symbols.add(symbol, node, getFloat64Name); + getFloat64 = node; + break; + case "func5": + mgr.symbols.add(symbol, node, getInstructionName); + getInstruction = node; + break; + case "func6": + mgr.symbols.add(symbol, node, getStringName); + getString = node; + break; + case "func7": + mgr.symbols.add(symbol, node, decodeChunkName); + decodeChunk = node; + break; + case "func8": + mgr.symbols.add(symbol, node, createWrapperName); + createWrapper = node; + break; + case "func9": + mgr.symbols.add(symbol, node, vmRunFuncName); + vmRunFunc = node; + break; + } + } + } + + @Override + public Node visit(Function node) { + renameCoreFunctions(node); + + return node; + } + } + + private class RenamerPass2 extends ASTOptimizerBase { + + private void renameCreateWrapper(Function node) { + mgr.symbols.add(node.params.names.get(0).symbol(), null, cacheName); + mgr.symbols.add(node.params.names.get(1).symbol(), null, environmentName); + mgr.symbols.add(node.params.names.get(2).symbol(), null, upvaluesName); + + for (Node declare : node.block.getChildren(LocalDeclare.class)) { + FunctionCall call = (FunctionCall)declare.first(FunctionCall.class); + if (call != null && call.varOrExp.line().equals("setmetatable")) { + LuaString var = (LuaString)declare.first(LuaString.class); + mgr.symbols.add(var.symbol(), null, constantsName); + } + } + } + + private void renameVmLocalsAndBias(LocalDeclare decl) { + LuaString var = (LuaString)decl.first(LuaString.class); + + if (decl.exprs.exprs.size() == 0) { + mgr.symbols.add(var.symbol(), null, vmOpcodeDispatcherTableName); + } + else if (decl.exprs.first(TableConstructor.class) != null) { + mgr.symbols.add(var.symbol(), null, vmHandlerTableName); + } + else if (decl.exprs.first(Number.class) != null) { + mgr.symbols.add(var.symbol(), null, biasName); + } + } + + private void renameVmRunFunc(Function node) { + boolean setEnv = false, setVarArgsz = false; + + for (Node declare : node.block.getChildren(LocalDeclare.class)) { + FunctionCall call = (FunctionCall)declare.first(FunctionCall.class); + + if (call != null && call.varOrExp.line().equals("getfenv")) { + LuaString var = (LuaString)declare.first(LuaString.class); + mgr.symbols.add(var.symbol(), null, environmentName); + setEnv = true; + } + + UnaryExpression unop = (UnaryExpression)declare.first(UnaryExpression.class); + + if (unop != null && unop.op == UnaryExpression.Operator.HASHTAG) { + LuaString var = (LuaString)declare.first(LuaString.class); + mgr.symbols.add(var.symbol(), null, varargszName); + setVarArgsz = true; + } + + if (setEnv && setVarArgsz) { + break; + } + } + + ForStep stmt = (ForStep)node.block.getChildren(ForStep.class).get(0); + + Variable stack = (Variable)stmt.block.first(If.class).first(Assign.class).first(Variable.class); + mgr.symbols.add(stack.symbol(), null, stackName); + + List children; + + Function handleRet = (Function)node.block.getChildren(Function.class).get(0); + mgr.symbols.add(handleRet.name.symbol(), null, handleReturnName); + + children = node.block.getChildren(); + + // Old + + /* + int handleRetIdx = -1; + for (int i = 0; i < children.size(); i++) { + if (children.get(i) instanceof Function && children.get(i) == handleRet) { + handleRetIdx = i; + break; + } + } + + renameVmLocalsAndBias((LocalDeclare) children.get(handleRetIdx + 1)); + renameVmLocalsAndBias((LocalDeclare) children.get(handleRetIdx + 2)); + renameVmLocalsAndBias((LocalDeclare) children.get(handleRetIdx + 3)); + */ + + // New + + int handleRetIdx = -1; + for (int i = 0; i < children.size() - 1; i++) { + if ((children.get(i) instanceof Function || children.get(i) instanceof ForStep) && children.get(i + 1) instanceof LocalDeclare) { + handleRetIdx = i; + break; + } + } + + renameVmLocalsAndBias((LocalDeclare) children.get(handleRetIdx + 1)); + renameVmLocalsAndBias((LocalDeclare) children.get(handleRetIdx + 2)); + renameVmLocalsAndBias((LocalDeclare) children.get(handleRetIdx + 3)); + + } + + @Override + public Node visit(Block node) { + if (node.parent != null && node.parent instanceof Function) { + Function func = (Function)node.parent; + + if (func == createWrapper) { + renameCreateWrapper(createWrapper); + } + else if (func == vmRunFunc) { + renameVmRunFunc(vmRunFunc); + } + } + + return node; + } + } + + private class RenamerPass3 extends ASTOptimizerBase { + private void initVmHandlers(LocalDeclare decl) { + TableConstructor ctor = (TableConstructor)decl.first(TableConstructor.class); + + for (int i = 0; i < ctor.entries.size(); i++) { + Pair p = ctor.entries.get(i); + + if (p.second instanceof Function) { + populateVmHandler(i + 1, (Function)p.second); + } + } + } + + private void populateVmHandler(int index, Function function) { + vmHandlerMapping.put(index, function); + } + + @Override + public Node visit(Assign node) { + Variable var = (Variable)node.first(Variable.class); + + if (var.symbol().equals(vmHandlerTableName)) { + double index = ((Number)var.suffixes.get(0).expOrName).value; + + Function function = (Function)node.first(Function.class); + + if (function != null) { + populateVmHandler((int)index, function); + } + } + else if (var.symbol().equals(vmOpcodeDispatcherTableName)) { + TableConstructor ctor = (TableConstructor)node.first(TableConstructor.class); + + if (ctor != null) { + for (int i = 0; i < ctor.entries.size(); i++) { + Variable v = (Variable) ctor.entries.get(i).second; + + vmOpcodeDispatcherMapping.put(i + 1, + (int) Double.parseDouble(v.suffixes.get(0).expOrName.line())); + } + } + } + + return node; + } + + @Override + public Node visit(LocalDeclare node) { + LuaString str = (LuaString)node.first(LuaString.class); + + if (str.symbol().equals(vmHandlerTableName)) { + initVmHandlers(node); + } + + return node; + } + + @Override + public Node visit(LuaString node) { + if (node.value.startsWith("\"LPH|")) { + luraphVmString = node.value.substring(5, node.value.length() - 1); + } + + return node; + } + } +} diff --git a/src/META-INF/MANIFEST.MF b/src/META-INF/MANIFEST.MF new file mode 100644 index 0000000..5ee19cb --- /dev/null +++ b/src/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: Main + diff --git a/src/Main.java b/src/Main.java new file mode 100644 index 0000000..b3670ce --- /dev/null +++ b/src/Main.java @@ -0,0 +1,115 @@ +import ASTNodes.Node; +import LuaVM.LuaChunk; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; +import org.apache.commons.cli.*; + + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileOutputStream; +import java.io.IOException; + +public class Main { + public static void main(String[] args) throws IOException { + Options options = new Options(); + + options.addRequiredOption("i", "input", true, "The Luraph file to process."); + options.addOption("p", false, "Print the optimized VM."); + options.addOption("c", false, "Copy the optimized VM to clipboard."); + options.addOption("s", false, "Display the AST of the Luraph VM"); + options.addOption("b", false, "Print devirtualized luac."); + options.addOption("o", true, "Save devirtualized luac to given output file."); + + CommandLineParser cmdLineParser = new DefaultParser(); + CommandLine cmd = null; + + try { + cmd = cmdLineParser.parse(options, args); + } + catch (ParseException ex) { + System.out.println(ex.getMessage()); + new HelpFormatter().printHelp("LuraphDevirtualizer", options); + System.exit(0); + } + + String fileName = cmd.getOptionValue("i"); + + // generate parse tree + LuaLexer lexer = new LuaLexer(CharStreams.fromFileName(fileName)); + CommonTokenStream tokens = new CommonTokenStream(lexer); + LuaParser parser = new LuaParser(tokens); + LuaParser.ChunkContext parseTree = parser.chunk(); + + // generate AbstractSyntaxTree + Node root = new BuildASTVisitor().visitChunk(parseTree); + + // optimize AST + ASTOptimizerMgr optMgr = new ASTOptimizerMgr(root); + optMgr.addOptimizer(new ASTConstantFolder()); + optMgr.addOptimizer(new ASTConstantPropagator()); + optMgr.optimize(); + + // rename AST + ASTOptimizerMgr renameMgr = new ASTOptimizerMgr(root); + renameMgr.addRenamer(new ASTBasicRenamer()); + renameMgr.optimize(); + + // devirtualize + LuraphDevirtualizer devirtualizer = new LuraphDevirtualizer(root); + LuaChunk chunk = devirtualizer.process(); + + new LuaChunkOptimizer().optimize(chunk); + + chunk = LuaChunkOptimizer.removeClosureAntiSymbExecTrick(chunk); + + if (cmd.hasOption("p") || cmd.hasOption("c")) { + String source = new ASTSourceGenerator(root).generate(); + + if (cmd.hasOption("p")) { + System.out.println(source); + } + + if (cmd.hasOption("c")) { + ClipboardUtils.set(source); + } + } + + if (cmd.hasOption("s")) { + ASTTree tree = ASTTreeBuilder.create(root); + ASTTreeViewer viewer = new ASTTreeViewer(tree); + viewer.open(); + } + + if (cmd.hasOption("b")) { + chunk.print(); + } + + if (cmd.hasOption("o")) { + String outputFileName = cmd.getOptionValue("o"); + + File output = new File(outputFileName); + + if (output.exists()) { + System.out.println("ERROR: Output file already exists."); + System.exit(1); + } + + if (!output.createNewFile()) { + System.out.println("ERROR: Could not create output file."); + System.exit(1); + } + + try { + LuacGenerator generator = new LuacGenerator(); + generator.write(new FileOutputStream(outputFileName), chunk); + } + catch (IOException ex) { + System.out.println(ex.getMessage()); + System.exit(1); + } + + System.out.println("Written file."); + } + } +} diff --git a/src/SymbolTable.java b/src/SymbolTable.java new file mode 100644 index 0000000..9f494fb --- /dev/null +++ b/src/SymbolTable.java @@ -0,0 +1,95 @@ +import java.util.ArrayList; +import java.util.List; + +import ASTNodes.*; + +public class SymbolTable { + public SymbolTable parent; + public List children = new ArrayList<>(); + public List vars = new ArrayList<>(); + public Block block; + + public SymbolTable createChild(Block block) { + SymbolTable child = new SymbolTable(block); + child.parent = this; + children.add(child); + return child; + } + + public SymbolTable getScope(Block block) { + if (this.block == block) { + return this; + } + + for (SymbolTable child : children) { + if (child.block == block) { + return child; + } + } + + return null; + } + + public SymbolTable(Block block) { + this.block = block; + } + + public void add(String name, Expression value) { + add(name, value, null); + } + + public void add(String name, Expression value, String rename) { + if (!vars.contains(name)) { + VarInfo info = new VarInfo(name, value); + info.rename = rename; + vars.add(info); + } + } + + public SymbolTable getRoot() { + SymbolTable root = this; + + while (root.parent != null) { + root = root.parent; + } + + return root; + } + + public void addGlobal(String name, Expression value) { + getRoot().add(name, value); + } + + public VarInfo getScopedVariable(String name) { + for (VarInfo var : vars) { + if (var.name.equals(name)) { + return var; + } + } + + return parent == null ? null : parent.getScopedVariable(name); + } + + public class VarInfo { + String name; + Expression value; + String rename; + + public VarInfo(String name, Expression value) { + this.name = name; + this.value = value; + } + + public void setValue(Expression value) { + this.value = value; + } + + public boolean isConstant() { + if (value != null && value instanceof LuaString) { + return ConstantData.isConstant(((LuaString)value).value); + } + + return false; + } + } +}