mirror of
https://github.com/PhoenixZeng/LuraphDeobfuscator.git
synced 2025-07-01 08:32:07 -06:00
Initial commit
This commit is contained in:
commit
96962f1814
240
README.md
Normal file
240
README.md
Normal 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:
|
||||
|
||||

|
||||
|
||||
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!
|
||||
|
||||

|
||||
|
||||
# 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:
|
||||
|
||||

|
||||
|
||||
# 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
352
grammar/Lua.g4
Normal 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
BIN
lib/antlr-4.8-complete.jar
Normal file
Binary file not shown.
BIN
lib/commons-cli-1.4.jar
Normal file
BIN
lib/commons-cli-1.4.jar
Normal file
Binary file not shown.
74
src/ASTBasicRenamer.java
Normal file
74
src/ASTBasicRenamer.java
Normal 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
295
src/ASTConstantFolder.java
Normal 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);
|
||||
}
|
||||
}
|
144
src/ASTConstantPropagator.java
Normal file
144
src/ASTConstantPropagator.java
Normal 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;
|
||||
}
|
||||
}
|
178
src/ASTNodes/ASTVisitor.java
Normal file
178
src/ASTNodes/ASTVisitor.java
Normal 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
68
src/ASTNodes/Assign.java
Normal 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;
|
||||
}
|
||||
}
|
152
src/ASTNodes/BinaryExpression.java
Normal file
152
src/ASTNodes/BinaryExpression.java
Normal 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
94
src/ASTNodes/Block.java
Normal 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
20
src/ASTNodes/Break.java
Normal 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
56
src/ASTNodes/Do.java
Normal 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;
|
||||
}
|
||||
}
|
90
src/ASTNodes/ExprList.java
Normal file
90
src/ASTNodes/ExprList.java
Normal 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;
|
||||
}
|
||||
}
|
8
src/ASTNodes/Expression.java
Normal file
8
src/ASTNodes/Expression.java
Normal 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
18
src/ASTNodes/False.java
Normal 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
68
src/ASTNodes/ForIn.java
Normal 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
111
src/ASTNodes/ForStep.java
Normal 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
120
src/ASTNodes/Function.java
Normal 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;
|
||||
}
|
||||
}
|
105
src/ASTNodes/FunctionCall.java
Normal file
105
src/ASTNodes/FunctionCall.java
Normal 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
58
src/ASTNodes/GoTo.java
Normal 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;
|
||||
}
|
||||
}
|
73
src/ASTNodes/IASTVisitor.java
Normal file
73
src/ASTNodes/IASTVisitor.java
Normal 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
109
src/ASTNodes/If.java
Normal 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
57
src/ASTNodes/Label.java
Normal 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;
|
||||
}
|
||||
}
|
4
src/ASTNodes/Literal.java
Normal file
4
src/ASTNodes/Literal.java
Normal file
|
@ -0,0 +1,4 @@
|
|||
package ASTNodes;
|
||||
|
||||
public class Literal extends Expression {
|
||||
}
|
76
src/ASTNodes/LocalDeclare.java
Normal file
76
src/ASTNodes/LocalDeclare.java
Normal 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;
|
||||
}
|
||||
}
|
40
src/ASTNodes/LuaString.java
Normal file
40
src/ASTNodes/LuaString.java
Normal 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);
|
||||
}
|
||||
}
|
80
src/ASTNodes/NameAndArgs.java
Normal file
80
src/ASTNodes/NameAndArgs.java
Normal 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;
|
||||
}
|
||||
}
|
90
src/ASTNodes/NameList.java
Normal file
90
src/ASTNodes/NameList.java
Normal 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
18
src/ASTNodes/Nil.java
Normal 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
121
src/ASTNodes/Node.java
Normal 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
42
src/ASTNodes/Number.java
Normal 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
71
src/ASTNodes/Pair.java
Normal 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
64
src/ASTNodes/Repeat.java
Normal 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
59
src/ASTNodes/Return.java
Normal 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;
|
||||
}
|
||||
}
|
5
src/ASTNodes/Semicolon.java
Normal file
5
src/ASTNodes/Semicolon.java
Normal file
|
@ -0,0 +1,5 @@
|
|||
package ASTNodes;
|
||||
|
||||
// This node isn't added to AST and is effectively ignored
|
||||
public class Semicolon extends Statement {
|
||||
}
|
4
src/ASTNodes/Statement.java
Normal file
4
src/ASTNodes/Statement.java
Normal file
|
@ -0,0 +1,4 @@
|
|||
package ASTNodes;
|
||||
|
||||
public class Statement extends Node {
|
||||
}
|
101
src/ASTNodes/Suffix.java
Normal file
101
src/ASTNodes/Suffix.java
Normal 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;
|
||||
}
|
||||
}
|
97
src/ASTNodes/TableConstructor.java
Normal file
97
src/ASTNodes/TableConstructor.java
Normal 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
18
src/ASTNodes/True.java
Normal 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);
|
||||
}
|
||||
}
|
87
src/ASTNodes/UnaryExpression.java
Normal file
87
src/ASTNodes/UnaryExpression.java
Normal 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
90
src/ASTNodes/VarList.java
Normal 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
109
src/ASTNodes/Variable.java
Normal 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
64
src/ASTNodes/While.java
Normal 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;
|
||||
}
|
||||
}
|
9
src/ASTOptimizerBase.java
Normal file
9
src/ASTOptimizerBase.java
Normal 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
671
src/ASTOptimizerMgr.java
Normal 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
215
src/ASTSourceGenerator.java
Normal 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
36
src/ASTTree.java
Normal 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
33
src/ASTTreeBuilder.java
Normal 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
33
src/ASTTreeViewer.java
Normal 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
652
src/BuildASTVisitor.java
Normal 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
141
src/CFGOrderer.java
Normal 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
11
src/ClipboardUtils.java
Normal 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
27
src/ConstantData.java
Normal 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
864
src/LuaBaseListener.java
Normal 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
495
src/LuaBaseVisitor.java
Normal 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
234
src/LuaChunkOptimizer.java
Normal 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
357
src/LuaLexer.java
Normal 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
764
src/LuaListener.java
Normal 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
3691
src/LuaParser.java
Normal file
File diff suppressed because it is too large
Load diff
69
src/LuaVM/LuaChunk.java
Normal file
69
src/LuaVM/LuaChunk.java
Normal 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
35
src/LuaVM/LuaValue.java
Normal 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
127
src/LuaVM/VM.java
Normal 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
193
src/LuaVM/VMBasicBlock.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
68
src/LuaVM/VMControlFlowGraph.java
Normal file
68
src/LuaVM/VMControlFlowGraph.java
Normal 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;
|
||||
}
|
||||
}
|
122
src/LuaVM/VMInstruction.java
Normal file
122
src/LuaVM/VMInstruction.java
Normal 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;
|
||||
}
|
||||
}
|
6
src/LuaVM/VMInstructionRefWrapper.java
Normal file
6
src/LuaVM/VMInstructionRefWrapper.java
Normal file
|
@ -0,0 +1,6 @@
|
|||
package LuaVM;
|
||||
|
||||
public class VMInstructionRefWrapper {
|
||||
public VMInstruction insn;
|
||||
public VMInstruction ref;
|
||||
}
|
6
src/LuaVM/VMInstructionWrapper.java
Normal file
6
src/LuaVM/VMInstructionWrapper.java
Normal file
|
@ -0,0 +1,6 @@
|
|||
package LuaVM;
|
||||
|
||||
public class VMInstructionWrapper {
|
||||
public VMInstruction insn;
|
||||
public VMBasicBlock ref;
|
||||
}
|
42
src/LuaVM/VMOp.java
Normal file
42
src/LuaVM/VMOp.java
Normal 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
12
src/LuaVM/VMOpType.java
Normal file
|
@ -0,0 +1,12 @@
|
|||
package LuaVM;
|
||||
|
||||
public enum VMOpType {
|
||||
AB,
|
||||
ABx,
|
||||
ABC,
|
||||
sBx,
|
||||
AC,
|
||||
AsBx,
|
||||
A,
|
||||
}
|
||||
|
9
src/LuaVM/VMOperand.java
Normal file
9
src/LuaVM/VMOperand.java
Normal file
|
@ -0,0 +1,9 @@
|
|||
package LuaVM;
|
||||
|
||||
public enum VMOperand {
|
||||
A,
|
||||
B,
|
||||
C,
|
||||
sBx,
|
||||
Bx,
|
||||
}
|
458
src/LuaVisitor.java
Normal file
458
src/LuaVisitor.java
Normal 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
207
src/LuacGenerator.java
Normal 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
2371
src/LuraphDevirtualizer.java
Normal file
File diff suppressed because it is too large
Load diff
3
src/META-INF/MANIFEST.MF
Normal file
3
src/META-INF/MANIFEST.MF
Normal file
|
@ -0,0 +1,3 @@
|
|||
Manifest-Version: 1.0
|
||||
Main-Class: Main
|
||||
|
115
src/Main.java
Normal file
115
src/Main.java
Normal 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
95
src/SymbolTable.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue