Initial commit

This commit is contained in:
reet15 2021-05-30 17:54:41 -07:00
commit 96962f1814
77 changed files with 15901 additions and 0 deletions

240
README.md Normal file
View file

@ -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
```

352
grammar/Lua.g4 Normal file
View file

@ -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
| <assoc=right> exp operatorPower exp #expPow
| operatorUnary exp #expUnary
| exp operatorMulDivMod exp #expMulDivMod
| exp operatorAddSub exp #expAddSub
| <assoc=right> 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)
;

BIN
lib/antlr-4.8-complete.jar Normal file

Binary file not shown.

BIN
lib/commons-cli-1.4.jar Normal file

Binary file not shown.

74
src/ASTBasicRenamer.java Normal file
View file

@ -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;
}
}

295
src/ASTConstantFolder.java Normal file
View file

@ -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 <null, Literal>
for (Pair<Expression, Expression> 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<Expression, Expression> 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);
}
}

View file

@ -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;
}
}

View file

@ -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) {
}
}

68
src/ASTNodes/Assign.java Normal file
View file

@ -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<Node> getChildren() {
List<Node> 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;
}
}

View file

@ -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<Node> getChildren() {
List<Node> 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;
}
}

94
src/ASTNodes/Block.java Normal file
View file

@ -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<Statement> 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<Node> getChildren() {
List<Node> 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;
}
}

20
src/ASTNodes/Break.java Normal file
View file

@ -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();
}
}

56
src/ASTNodes/Do.java Normal file
View file

@ -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<Node> getChildren() {
List<Node> 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;
}
}

View file

@ -0,0 +1,90 @@
package ASTNodes;
import java.util.ArrayList;
import java.util.List;
public class ExprList extends Expression {
public List<Expression> 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<Node> getChildren() {
List<Node> 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;
}
}

View file

@ -0,0 +1,8 @@
package ASTNodes;
public class Expression extends Statement {
@Override
public Expression clone() {
return new Expression();
}
}

18
src/ASTNodes/False.java Normal file
View file

@ -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();
}
}

68
src/ASTNodes/ForIn.java Normal file
View file

@ -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<Node> getChildren() {
List<Node> 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;
}
}

111
src/ASTNodes/ForStep.java Normal file
View file

@ -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<Node> getChildren() {
List<Node> 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;
}
}

120
src/ASTNodes/Function.java Normal file
View file

@ -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<Node> getChildren() {
List<Node> 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;
}
}

View file

@ -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> nameAndArgs = new ArrayList<>();
// if the function call occurred in a statement grammar
public boolean isStatement = false;
@Override
public List<Node> getChildren() {
List<Node> 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;
}
}

58
src/ASTNodes/GoTo.java Normal file
View file

@ -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<Node> getChildren() {
List<Node> 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;
}
}

View file

@ -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);
}

109
src/ASTNodes/If.java Normal file
View file

@ -0,0 +1,109 @@
package ASTNodes;
import java.util.ArrayList;
import java.util.List;
public class If extends Statement {
public Pair<Expression, Block> ifstmt;
public List<Pair<Expression, Block>> elseifstmt = new ArrayList<>();
public Block elsestmt;
// cannot be represented on a line, call line() on members
@Override
public List<Node> getChildren() {
List<Node> children = new ArrayList<>();
children.add(ifstmt);
for (Pair<Expression, Block> 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<Expression, Block> 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<Expression, Block>)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;
}
}

57
src/ASTNodes/Label.java Normal file
View file

@ -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<Node> getChildren() {
List<Node> 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;
}
}

View file

@ -0,0 +1,4 @@
package ASTNodes;
public class Literal extends Expression {
}

View file

@ -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<Node> getChildren() {
List<Node> 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;
}
}

View file

@ -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 "<<Removed String>>";
}
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);
}
}

View file

@ -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<Node> getChildren() {
List<Node> 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;
}
}

View file

@ -0,0 +1,90 @@
package ASTNodes;
import java.util.ArrayList;
import java.util.List;
public class NameList extends Expression {
public List<LuaString> names = new ArrayList<>();
@Override
public List<Node> getChildren() {
List<Node> 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;
}
}

18
src/ASTNodes/Nil.java Normal file
View file

@ -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();
}
}

121
src/ASTNodes/Node.java Normal file
View file

@ -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<Node> 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<Node> children = getChildren();
if (children == null) {
return 0;
}
for (Node node : children) {
if (type.isInstance(node)) {
++count;
}
count += node.count(type);
}
return count;
}
public List<Node> getChildren(Class type) {
List<Node> res = new ArrayList<>();
List<Node> 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<Node> 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<Node> 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;
}
}

42
src/ASTNodes/Number.java Normal file
View file

@ -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);
}
}

71
src/ASTNodes/Pair.java Normal file
View file

@ -0,0 +1,71 @@
package ASTNodes;
import java.util.ArrayList;
import java.util.List;
public class Pair<K extends Node, V extends Node> 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<K, V> p = (Pair<K, V>)obj;
return first.matches(p.first) && second.matches(p.second);
}
return false;
}
@Override
public List<Node> getChildren() {
List<Node> children = new ArrayList<>();
if (first != null) {
children.add(first);
}
if (second != null) {
children.add(second);
}
return children;
}
@Override
public Pair<K, V> 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;
}
}

64
src/ASTNodes/Repeat.java Normal file
View file

@ -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<Node> getChildren() {
List<Node> 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;
}
}

59
src/ASTNodes/Return.java Normal file
View file

@ -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<Node> getChildren() {
List<Node> 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;
}
}

View file

@ -0,0 +1,5 @@
package ASTNodes;
// This node isn't added to AST and is effectively ignored
public class Semicolon extends Statement {
}

View file

@ -0,0 +1,4 @@
package ASTNodes;
public class Statement extends Node {
}

101
src/ASTNodes/Suffix.java Normal file
View file

@ -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> 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<Node> getChildren() {
List<Node> 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;
}
}

View file

@ -0,0 +1,97 @@
package ASTNodes;
import java.util.ArrayList;
import java.util.List;
public class TableConstructor extends Expression {
public List<Pair<Expression, Expression>> entries = new ArrayList<>();
@Override
public String line() {
String s = "{ ";
for (int i = 0; i < entries.size(); i++) {
Pair<Expression, Expression> 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<Node> getChildren() {
List<Node> children = new ArrayList<>();
for (Pair<Expression, Expression> 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<Expression, Expression> 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<Expression, Expression>)node);
}
}
}
@Override
public Node accept(IASTVisitor visitor) {
Node replacement = visitor.visit(this);
if (replacement != this) {
return replacement;
}
acceptChildren(visitor);
return this;
}
}

18
src/ASTNodes/True.java Normal file
View file

@ -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);
}
}

View file

@ -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<Node> getChildren() {
List<Node> 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;
}
}

90
src/ASTNodes/VarList.java Normal file
View file

@ -0,0 +1,90 @@
package ASTNodes;
import java.util.ArrayList;
import java.util.List;
public class VarList extends Expression {
public List<Expression> vars = new ArrayList<>();
@Override
public List<Node> getChildren() {
List<Node> 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;
}
}

109
src/ASTNodes/Variable.java Normal file
View file

@ -0,0 +1,109 @@
package ASTNodes;
import java.util.ArrayList;
import java.util.List;
public class Variable extends Expression {
public Expression name;
public List<Suffix> 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<Node> getChildren() {
List<Node> 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;
}
}

64
src/ASTNodes/While.java Normal file
View file

@ -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<Node> getChildren() {
List<Node> 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;
}
}

View file

@ -0,0 +1,9 @@
import ASTNodes.ASTVisitor;
public class ASTOptimizerBase extends ASTVisitor {
protected ASTOptimizerMgr mgr;
public void setMgr(ASTOptimizerMgr mgr) {
this.mgr = mgr;
}
}

671
src/ASTOptimizerMgr.java Normal file
View file

@ -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<ASTOptimizerBase> 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;
}
}
}

215
src/ASTSourceGenerator.java Normal file
View file

@ -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<Expression, Expression> 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<Expression, Block> 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<Expression, Block> 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();
}
}

36
src/ASTTree.java Normal file
View file

@ -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<Tree> 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 "";
}
}

33
src/ASTTreeBuilder.java Normal file
View file

@ -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<Node> 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);
}
}

33
src/ASTTreeViewer.java Normal file
View file

@ -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();
}
}

652
src/BuildASTVisitor.java Normal file
View file

@ -0,0 +1,652 @@
import ASTNodes.*;
import ASTNodes.Number;
import ASTNodes.LuaString;
import org.antlr.v4.runtime.tree.TerminalNode;
public class BuildASTVisitor extends LuaBaseVisitor<Node> {
@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;
}
}

141
src/CFGOrderer.java Normal file
View file

@ -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<VMControlFlowGraph> cfg;
private VMControlFlowGraph root;
private Stack<VMControlFlowGraph> stack;
private List<VMControlFlowGraph> order;
private HashMap<VMControlFlowGraph, IndexState> index;
private HashMap<VMControlFlowGraph, Integer> 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<VMControlFlowGraph> 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<VMControlFlowGraph> 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<VMControlFlowGraph> 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<VMControlFlowGraph> suborder = orderer.getOrder();
suborder.addAll(order);
order = suborder;
}
}
}
public List<VMControlFlowGraph> 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;
}
}

11
src/ClipboardUtils.java Normal file
View file

@ -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);
}
}

27
src/ConstantData.java Normal file
View file

@ -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);
}
}

864
src/LuaBaseListener.java Normal file
View file

@ -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}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterChunk(LuaParser.ChunkContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitChunk(LuaParser.ChunkContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterBlock(LuaParser.BlockContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitBlock(LuaParser.BlockContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterStmtSemicolon(LuaParser.StmtSemicolonContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitStmtSemicolon(LuaParser.StmtSemicolonContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterStmtAssign(LuaParser.StmtAssignContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitStmtAssign(LuaParser.StmtAssignContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterStmtFuncCall(LuaParser.StmtFuncCallContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitStmtFuncCall(LuaParser.StmtFuncCallContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterStmtLabel(LuaParser.StmtLabelContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitStmtLabel(LuaParser.StmtLabelContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterStmtBreak(LuaParser.StmtBreakContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitStmtBreak(LuaParser.StmtBreakContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterStmtGoto(LuaParser.StmtGotoContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitStmtGoto(LuaParser.StmtGotoContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterStmtDo(LuaParser.StmtDoContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitStmtDo(LuaParser.StmtDoContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterStmtWhile(LuaParser.StmtWhileContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitStmtWhile(LuaParser.StmtWhileContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterStmtRepeat(LuaParser.StmtRepeatContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitStmtRepeat(LuaParser.StmtRepeatContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterStmtIf(LuaParser.StmtIfContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitStmtIf(LuaParser.StmtIfContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterStmtForStep(LuaParser.StmtForStepContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitStmtForStep(LuaParser.StmtForStepContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterStmtForIn(LuaParser.StmtForInContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitStmtForIn(LuaParser.StmtForInContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterStmtFuncDef(LuaParser.StmtFuncDefContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitStmtFuncDef(LuaParser.StmtFuncDefContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterStmtLocalFuncDef(LuaParser.StmtLocalFuncDefContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitStmtLocalFuncDef(LuaParser.StmtLocalFuncDefContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterStmtLocalDecl(LuaParser.StmtLocalDeclContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitStmtLocalDecl(LuaParser.StmtLocalDeclContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterIfstmt(LuaParser.IfstmtContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitIfstmt(LuaParser.IfstmtContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterElseifstmt(LuaParser.ElseifstmtContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitElseifstmt(LuaParser.ElseifstmtContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterElsestmt(LuaParser.ElsestmtContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitElsestmt(LuaParser.ElsestmtContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterRetstat(LuaParser.RetstatContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitRetstat(LuaParser.RetstatContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterLabel(LuaParser.LabelContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitLabel(LuaParser.LabelContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterFuncname(LuaParser.FuncnameContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitFuncname(LuaParser.FuncnameContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterVarlist(LuaParser.VarlistContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitVarlist(LuaParser.VarlistContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterNamelist(LuaParser.NamelistContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitNamelist(LuaParser.NamelistContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterExplist(LuaParser.ExplistContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitExplist(LuaParser.ExplistContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterExpCmp(LuaParser.ExpCmpContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitExpCmp(LuaParser.ExpCmpContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterExpNumber(LuaParser.ExpNumberContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitExpNumber(LuaParser.ExpNumberContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterExpThreeDots(LuaParser.ExpThreeDotsContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitExpThreeDots(LuaParser.ExpThreeDotsContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterExpStrcat(LuaParser.ExpStrcatContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitExpStrcat(LuaParser.ExpStrcatContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterExpTrue(LuaParser.ExpTrueContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitExpTrue(LuaParser.ExpTrueContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterExpOr(LuaParser.ExpOrContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitExpOr(LuaParser.ExpOrContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterExpBitwise(LuaParser.ExpBitwiseContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitExpBitwise(LuaParser.ExpBitwiseContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterExpTableCtor(LuaParser.ExpTableCtorContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitExpTableCtor(LuaParser.ExpTableCtorContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterExpMulDivMod(LuaParser.ExpMulDivModContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitExpMulDivMod(LuaParser.ExpMulDivModContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterExpFuncDef(LuaParser.ExpFuncDefContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitExpFuncDef(LuaParser.ExpFuncDefContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterExpFalse(LuaParser.ExpFalseContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitExpFalse(LuaParser.ExpFalseContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterExpString(LuaParser.ExpStringContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitExpString(LuaParser.ExpStringContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterExpPrefix(LuaParser.ExpPrefixContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitExpPrefix(LuaParser.ExpPrefixContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterExpUnary(LuaParser.ExpUnaryContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitExpUnary(LuaParser.ExpUnaryContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterExpAnd(LuaParser.ExpAndContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitExpAnd(LuaParser.ExpAndContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterExpPow(LuaParser.ExpPowContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitExpPow(LuaParser.ExpPowContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterExpNil(LuaParser.ExpNilContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitExpNil(LuaParser.ExpNilContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterExpAddSub(LuaParser.ExpAddSubContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitExpAddSub(LuaParser.ExpAddSubContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterPrefixexp(LuaParser.PrefixexpContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitPrefixexp(LuaParser.PrefixexpContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterFunctioncall(LuaParser.FunctioncallContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitFunctioncall(LuaParser.FunctioncallContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterVarOrExp(LuaParser.VarOrExpContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitVarOrExp(LuaParser.VarOrExpContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterVar(LuaParser.VarContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitVar(LuaParser.VarContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterVarSuffix(LuaParser.VarSuffixContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitVarSuffix(LuaParser.VarSuffixContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterNameAndArgs(LuaParser.NameAndArgsContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitNameAndArgs(LuaParser.NameAndArgsContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterArgs(LuaParser.ArgsContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitArgs(LuaParser.ArgsContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterFunctiondef(LuaParser.FunctiondefContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitFunctiondef(LuaParser.FunctiondefContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterFuncbody(LuaParser.FuncbodyContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitFuncbody(LuaParser.FuncbodyContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterParlist(LuaParser.ParlistContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitParlist(LuaParser.ParlistContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterTableconstructor(LuaParser.TableconstructorContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitTableconstructor(LuaParser.TableconstructorContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterFieldlist(LuaParser.FieldlistContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitFieldlist(LuaParser.FieldlistContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterField(LuaParser.FieldContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitField(LuaParser.FieldContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterFieldsep(LuaParser.FieldsepContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitFieldsep(LuaParser.FieldsepContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterOperatorOr(LuaParser.OperatorOrContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitOperatorOr(LuaParser.OperatorOrContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterOperatorAnd(LuaParser.OperatorAndContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitOperatorAnd(LuaParser.OperatorAndContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterOperatorComparison(LuaParser.OperatorComparisonContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitOperatorComparison(LuaParser.OperatorComparisonContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterOperatorStrcat(LuaParser.OperatorStrcatContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitOperatorStrcat(LuaParser.OperatorStrcatContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterOperatorAddSub(LuaParser.OperatorAddSubContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitOperatorAddSub(LuaParser.OperatorAddSubContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterOperatorMulDivMod(LuaParser.OperatorMulDivModContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitOperatorMulDivMod(LuaParser.OperatorMulDivModContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterOperatorBitwise(LuaParser.OperatorBitwiseContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitOperatorBitwise(LuaParser.OperatorBitwiseContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterOperatorUnary(LuaParser.OperatorUnaryContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitOperatorUnary(LuaParser.OperatorUnaryContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterOperatorPower(LuaParser.OperatorPowerContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitOperatorPower(LuaParser.OperatorPowerContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterNumber(LuaParser.NumberContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitNumber(LuaParser.NumberContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterString(LuaParser.StringContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitString(LuaParser.StringContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterEveryRule(ParserRuleContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitEveryRule(ParserRuleContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void visitTerminal(TerminalNode node) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void visitErrorNode(ErrorNode node) { }
}

495
src/LuaBaseVisitor.java Normal file
View file

@ -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 <T> The return type of the visit operation. Use {@link Void} for
* operations with no return type.
*/
public class LuaBaseVisitor<T> extends AbstractParseTreeVisitor<T> implements LuaVisitor<T> {
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitChunk(LuaParser.ChunkContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitBlock(LuaParser.BlockContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitStmtSemicolon(LuaParser.StmtSemicolonContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitStmtAssign(LuaParser.StmtAssignContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitStmtFuncCall(LuaParser.StmtFuncCallContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitStmtLabel(LuaParser.StmtLabelContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitStmtBreak(LuaParser.StmtBreakContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitStmtGoto(LuaParser.StmtGotoContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitStmtDo(LuaParser.StmtDoContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitStmtWhile(LuaParser.StmtWhileContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitStmtRepeat(LuaParser.StmtRepeatContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitStmtIf(LuaParser.StmtIfContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitStmtForStep(LuaParser.StmtForStepContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitStmtForIn(LuaParser.StmtForInContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitStmtFuncDef(LuaParser.StmtFuncDefContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitStmtLocalFuncDef(LuaParser.StmtLocalFuncDefContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitStmtLocalDecl(LuaParser.StmtLocalDeclContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitIfstmt(LuaParser.IfstmtContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitElseifstmt(LuaParser.ElseifstmtContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitElsestmt(LuaParser.ElsestmtContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitRetstat(LuaParser.RetstatContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitLabel(LuaParser.LabelContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitFuncname(LuaParser.FuncnameContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitVarlist(LuaParser.VarlistContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitNamelist(LuaParser.NamelistContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitExplist(LuaParser.ExplistContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitExpCmp(LuaParser.ExpCmpContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitExpNumber(LuaParser.ExpNumberContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitExpThreeDots(LuaParser.ExpThreeDotsContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitExpStrcat(LuaParser.ExpStrcatContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitExpTrue(LuaParser.ExpTrueContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitExpOr(LuaParser.ExpOrContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitExpBitwise(LuaParser.ExpBitwiseContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitExpTableCtor(LuaParser.ExpTableCtorContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitExpMulDivMod(LuaParser.ExpMulDivModContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitExpFuncDef(LuaParser.ExpFuncDefContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitExpFalse(LuaParser.ExpFalseContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitExpString(LuaParser.ExpStringContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitExpPrefix(LuaParser.ExpPrefixContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitExpUnary(LuaParser.ExpUnaryContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitExpAnd(LuaParser.ExpAndContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitExpPow(LuaParser.ExpPowContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitExpNil(LuaParser.ExpNilContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitExpAddSub(LuaParser.ExpAddSubContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitPrefixexp(LuaParser.PrefixexpContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitFunctioncall(LuaParser.FunctioncallContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitVarOrExp(LuaParser.VarOrExpContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitVar(LuaParser.VarContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitVarSuffix(LuaParser.VarSuffixContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitNameAndArgs(LuaParser.NameAndArgsContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitArgs(LuaParser.ArgsContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitFunctiondef(LuaParser.FunctiondefContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitFuncbody(LuaParser.FuncbodyContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitParlist(LuaParser.ParlistContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitTableconstructor(LuaParser.TableconstructorContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitFieldlist(LuaParser.FieldlistContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitField(LuaParser.FieldContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitFieldsep(LuaParser.FieldsepContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitOperatorOr(LuaParser.OperatorOrContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitOperatorAnd(LuaParser.OperatorAndContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitOperatorComparison(LuaParser.OperatorComparisonContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitOperatorStrcat(LuaParser.OperatorStrcatContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitOperatorAddSub(LuaParser.OperatorAddSubContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitOperatorMulDivMod(LuaParser.OperatorMulDivModContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitOperatorBitwise(LuaParser.OperatorBitwiseContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitOperatorUnary(LuaParser.OperatorUnaryContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitOperatorPower(LuaParser.OperatorPowerContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitNumber(LuaParser.NumberContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitString(LuaParser.StringContext ctx) { return visitChildren(ctx); }
}

234
src/LuaChunkOptimizer.java Normal file
View file

@ -0,0 +1,234 @@
import LuaVM.*;
import java.util.*;
public class LuaChunkOptimizer {
private int line;
public LuaChunkOptimizer() {
line = 0;
}
private static List<VMInstructionWrapper> generateFromCfgWithFallthroughJumps(List<VMControlFlowGraph> orderedCfg) {
// Get instructions from CFG and append jumps to handle fallthrough (will be optimized out later if not necessary)
List<VMInstructionWrapper> 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<VMInstructionRefWrapper> generateInstructionRefWrappers(List<VMInstructionWrapper> insns) {
// Convert refs from basic blocks to refs to instructions
List<VMInstructionRefWrapper> 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<Integer, VMInstructionRefWrapper> getInstructionPCs(List<VMInstructionRefWrapper> insns) {
int PC = 0;
HashMap<Integer, VMInstructionRefWrapper> insnPCs = new HashMap<>();
for (VMInstructionRefWrapper insn : insns) {
insnPCs.put(PC, insn);
++PC;
}
return insnPCs;
}
private static HashMap<VMInstruction, Integer> getPCInstructions(List<VMInstructionRefWrapper> insns) {
int PC = 0;
HashMap<VMInstruction, Integer> pcInsns = new HashMap<>();
for (VMInstructionRefWrapper insn : insns) {
pcInsns.put(insn.insn, PC);
++PC;
}
return pcInsns;
}
private static void fixBranchTargets(List<VMInstructionRefWrapper> insns) {
int PC = 0;
HashMap<VMInstruction, Integer> 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<VMInstructionRefWrapper> 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<VMInstruction> generateInstructionsFromCfg(List<VMControlFlowGraph> orderedCfg) {
List<VMInstructionWrapper> insnsWithFallthroughJumps = generateFromCfgWithFallthroughJumps(orderedCfg);
List<VMInstructionRefWrapper> insnsWithRefs = generateInstructionRefWrappers(insnsWithFallthroughJumps);
do {
fixBranchTargets(insnsWithRefs);
} while (removeZeroBranches(insnsWithRefs) > 0);
List<VMInstruction> 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<VMBasicBlock> blocks = VMBasicBlock.generateBasicBlocks(chunk.insns);
List<VMControlFlowGraph> cfg = VMControlFlowGraph.generateControlFlowGraph(blocks);
List<VMControlFlowGraph> orderedCfg = new CFGOrderer(cfg, cfg.get(0)).getOrder();
List<VMInstruction> insns = generateInstructionsFromCfg(orderedCfg);
chunk.insns = insns;
chunk.lineDefined = line++;
setVarArgFlag(chunk);
renameGlobalIntegerRefs(chunk);
for (LuaChunk child : chunk.prototypes) {
optimize(child);
}
}
}

357
src/LuaLexer.java Normal file
View file

@ -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] = "<INVALID>";
}
}
}
@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<w=y\2{>}?\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);
}
}
}

764
src/LuaListener.java Normal file
View file

@ -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);
}

3691
src/LuaParser.java Normal file

File diff suppressed because it is too large Load diff

69
src/LuaVM/LuaChunk.java Normal file
View file

@ -0,0 +1,69 @@
package LuaVM;
import java.util.ArrayList;
import java.util.List;
public class LuaChunk {
public List<VMInstruction> insns = new ArrayList<>();
public List<LuaValue> constants = new ArrayList<>();
public List<LuaChunk> prototypes = new ArrayList<>();
public List<Double> 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();
}
}

35
src/LuaVM/LuaValue.java Normal file
View file

@ -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 "<INVALID>";
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;
}

127
src/LuaVM/VM.java Normal file
View file

@ -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");
}
}

193
src/LuaVM/VMBasicBlock.java Normal file
View file

@ -0,0 +1,193 @@
package LuaVM;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
public class VMBasicBlock {
public List<VMInstructionWrapper> 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<VMBasicBlock> 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<VMBasicBlock> 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<VMInstruction> computeLeaders(List<VMInstruction> insns) {
HashSet<VMInstruction> 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<VMBasicBlock> blocks, List<VMInstruction> 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<VMBasicBlock> generateBasicBlocks(List<VMInstruction> insns) {
List<VMBasicBlock> blocks = new ArrayList<>();
HashSet<VMInstruction> 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<VMBasicBlock> 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<VMBasicBlock> 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;
}
}
}

View file

@ -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<VMControlFlowGraph> getChildren() {
List<VMControlFlowGraph> 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<VMBasicBlock> blocks, VMBasicBlock block, List<VMControlFlowGraph> cfg, HashMap<VMBasicBlock, VMControlFlowGraph> 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<VMControlFlowGraph> generateControlFlowGraph(List<VMBasicBlock> blocks) {
if (blocks.size() == 0) {
throw new RuntimeException("No basic blocks");
}
HashMap<VMBasicBlock, VMControlFlowGraph> created = new HashMap<>();
VMBasicBlock rootBlock = blocks.get(0);
List<VMControlFlowGraph> cfg = new ArrayList<>();
generateControlFlowGraphHelper(blocks, rootBlock, cfg, created);
return cfg;
}
}

View file

@ -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;
}
}

View file

@ -0,0 +1,6 @@
package LuaVM;
public class VMInstructionRefWrapper {
public VMInstruction insn;
public VMInstruction ref;
}

View file

@ -0,0 +1,6 @@
package LuaVM;
public class VMInstructionWrapper {
public VMInstruction insn;
public VMBasicBlock ref;
}

42
src/LuaVM/VMOp.java Normal file
View file

@ -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,
}

12
src/LuaVM/VMOpType.java Normal file
View file

@ -0,0 +1,12 @@
package LuaVM;
public enum VMOpType {
AB,
ABx,
ABC,
sBx,
AC,
AsBx,
A,
}

9
src/LuaVM/VMOperand.java Normal file
View file

@ -0,0 +1,9 @@
package LuaVM;
public enum VMOperand {
A,
B,
C,
sBx,
Bx,
}

458
src/LuaVisitor.java Normal file
View file

@ -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 <T> The return type of the visit operation. Use {@link Void} for
* operations with no return type.
*/
public interface LuaVisitor<T> extends ParseTreeVisitor<T> {
/**
* 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);
}

207
src/LuacGenerator.java Normal file
View file

@ -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<VMInstruction> 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<LuaValue> constants) throws IOException {
writeInt(dos, constants.size());
for (LuaValue constant : constants) {
writeConstant(dos, constant);
}
}
private static void writePrototypes(DataOutputStream dos, List<LuaChunk> 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);
}
}

2371
src/LuraphDevirtualizer.java Normal file

File diff suppressed because it is too large Load diff

3
src/META-INF/MANIFEST.MF Normal file
View file

@ -0,0 +1,3 @@
Manifest-Version: 1.0
Main-Class: Main

115
src/Main.java Normal file
View file

@ -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.");
}
}
}

95
src/SymbolTable.java Normal file
View file

@ -0,0 +1,95 @@
import java.util.ArrayList;
import java.util.List;
import ASTNodes.*;
public class SymbolTable {
public SymbolTable parent;
public List<SymbolTable> children = new ArrayList<>();
public List<VarInfo> 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;
}
}
}