Major redesign of Serpent compiler
This commit is contained in:
parent
f1d292a2b4
commit
ea0a0bc754
|
@ -61,18 +61,20 @@
|
|||
<artifactId>geoip-api</artifactId>
|
||||
<version>1.2.11</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.antlr</groupId>
|
||||
<artifactId>antlr</artifactId>
|
||||
<!-- Using older version for jython compatibility -->
|
||||
<version>3.1.3</version>
|
||||
<!-- <version>3.5.2</version> -->
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.antlr</groupId>
|
||||
<artifactId>stringtemplate</artifactId>
|
||||
<version>3.2.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.antlr</groupId>
|
||||
<artifactId>antlr4-runtime</artifactId>
|
||||
<version>4.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.antlr</groupId>
|
||||
<artifactId>antlr4-maven-plugin</artifactId>
|
||||
<version>4.1</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>org.python</groupId>
|
||||
<artifactId>jython</artifactId>
|
||||
|
@ -92,7 +94,15 @@
|
|||
<artifactId>slf4j-api</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.yuvalshavit</groupId>
|
||||
<artifactId>antlr-denter</artifactId>
|
||||
<version>1.1</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.ethereum.gui;
|
||||
|
||||
import org.ethereum.serpent.SerpentCompiler;
|
||||
import org.fife.ui.rsyntaxtextarea.AbstractTokenMakerFactory;
|
||||
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
|
||||
import org.fife.ui.rsyntaxtextarea.TokenMakerFactory;
|
||||
|
@ -20,13 +21,23 @@ import java.awt.event.ActionListener;
|
|||
public class SerpentEditor extends JFrame {
|
||||
|
||||
private String codeSample = "\n\n\n" +
|
||||
"" +
|
||||
"" +
|
||||
"if !contract.storage[msg.data[0]]:\n" +
|
||||
" contract.storage[msg.data[0]] = msg.data[1]\n" +
|
||||
" return(1)\n" +
|
||||
"else:\n" +
|
||||
" return(0)\n";
|
||||
|
||||
private String codeSample2 = "\n\n\n" +
|
||||
"" +
|
||||
"a=block.gaslimit\n" +
|
||||
"b = block.difficulty\n" +
|
||||
"if 10*2+5 > 15:\n" +
|
||||
" b = 2\n" +
|
||||
"\n" +
|
||||
"return(0)\n";
|
||||
|
||||
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
|
@ -48,7 +59,7 @@ public class SerpentEditor extends JFrame {
|
|||
codeArea.setSyntaxEditingStyle("text/serpent");
|
||||
codeArea.setCodeFoldingEnabled(true);
|
||||
codeArea.setAntiAliasingEnabled(true);
|
||||
codeArea.setText(codeSample);
|
||||
codeArea.setText(codeSample2);
|
||||
|
||||
RTextScrollPane sp = new RTextScrollPane(codeArea);
|
||||
|
||||
|
@ -84,7 +95,7 @@ public class SerpentEditor extends JFrame {
|
|||
try {
|
||||
|
||||
// todo: integrate new compiler when avail
|
||||
//asmResult = SerpentCompiler.compile(codeArea.getText());
|
||||
asmResult = SerpentCompiler.compile(codeArea.getText());
|
||||
} catch (Throwable th) {th.printStackTrace();}
|
||||
|
||||
splitPanel.setDividerLocation(0.7);
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
package org.ethereum.serpent;
|
||||
|
||||
import org.antlr.mojo.antlr4.Antlr4ErrorLog;
|
||||
import org.antlr.mojo.antlr4.Antlr4Mojo;
|
||||
import org.antlr.v4.Tool;
|
||||
import org.antlr.v4.codegen.CodeGenerator;
|
||||
import org.antlr.v4.tool.Grammar;
|
||||
import org.apache.maven.plugin.MojoExecutionException;
|
||||
import org.apache.maven.plugin.MojoFailureException;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URI;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* www.ethereumJ.com
|
||||
* User: Roman Mandeleil
|
||||
* Created on: 25/04/14 17:06
|
||||
*/
|
||||
public class ParserGenerator {
|
||||
|
||||
|
||||
|
||||
public static void main(String args[]) throws MojoFailureException, MojoExecutionException {
|
||||
|
||||
String userDir = System.getProperty("user.dir");
|
||||
|
||||
|
||||
String grammarName = userDir + "\\src\\main\\java\\org\\ethereum\\serpent\\Serpent.g4";
|
||||
|
||||
|
||||
File inputDir = new File(userDir + "\\src\\main\\java\\org\\ethereum\\serpent\\");
|
||||
|
||||
|
||||
String options[] = {grammarName, "-visitor", "-package", "org.ethereum.serpent"};
|
||||
Tool tool = new Tool(options);
|
||||
tool.outputDirectory = userDir + "\\src\\main\\java\\org\\ethereum\\serpent\\";
|
||||
tool.processGrammarsOnCommandLine();
|
||||
|
||||
|
||||
|
||||
|
||||
// org.antlr.Tool.main(new String[]{userDir + "\\src\\main\\java\\org\\ethereum\\serpent\\Serpent.g4"});
|
||||
// org.antlr.Tool.main(new String[]{userDir + "\\src\\main\\java\\samples\\antlr\\PyEsque.g"});
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
package org.ethereum.serpent;
|
||||
|
||||
import org.antlr.v4.runtime.*;
|
||||
import org.antlr.v4.runtime.atn.PredictionMode;
|
||||
import org.antlr.v4.runtime.misc.Nullable;
|
||||
|
||||
public class ParserUtils {
|
||||
private ParserUtils() {}
|
||||
|
||||
public static <L extends Lexer> L getLexer(Class<L> lexerClass, String source) {
|
||||
CharStream input = new ANTLRInputStream(source);
|
||||
L lexer;
|
||||
try {
|
||||
lexer = lexerClass.getConstructor(CharStream.class).newInstance(input);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException("couldn't invoke lexer constructor", e);
|
||||
}
|
||||
lexer.addErrorListener(new AntlrFailureListener());
|
||||
return lexer;
|
||||
}
|
||||
|
||||
public static <P extends Parser> P getParser(Class<? extends Lexer> lexerClass, Class<P> parserClass, String source) {
|
||||
Lexer lexer = getLexer(lexerClass, source);
|
||||
TokenStream tokens = new CommonTokenStream(lexer);
|
||||
|
||||
P parser;
|
||||
try {
|
||||
parser = parserClass.getConstructor(TokenStream.class).newInstance(tokens);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException("couldn't invoke parser constructor", e);
|
||||
}
|
||||
parser.getInterpreter().setPredictionMode(PredictionMode.LL_EXACT_AMBIG_DETECTION);
|
||||
parser.removeErrorListeners(); // don't spit to stderr
|
||||
parser.addErrorListener(new DiagnosticErrorListener());
|
||||
parser.addErrorListener(new AntlrFailureListener());
|
||||
|
||||
return parser;
|
||||
}
|
||||
|
||||
private static class AntlrFailureListener extends BaseErrorListener {
|
||||
@Override
|
||||
public void syntaxError(Recognizer<?, ?> recognizer, @Nullable Object offendingSymbol, int line,
|
||||
int charPositionInLine, String msg, @Nullable RecognitionException e) {
|
||||
throw new AntlrParseException(line, charPositionInLine, msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
public static class AntlrParseException extends RuntimeException {
|
||||
public AntlrParseException(int line, int posInLine, String msg, Throwable cause) {
|
||||
// posInLine comes in 0-indexed, but we want to 1-index it so it lines up with what editors say (they
|
||||
// tend to 1-index)
|
||||
super(String.format("at line %d column %d: %s", line, posInLine+1, msg), cause);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,196 @@
|
|||
grammar Serpent;
|
||||
|
||||
tokens {
|
||||
INDENT, DEDENT }
|
||||
@lexer::header {
|
||||
import com.yuvalshavit.antlr4.DenterHelper;
|
||||
}
|
||||
|
||||
|
||||
@lexer::members {
|
||||
private final DenterHelper denter = new DenterHelper(NL, SerpentParser.INDENT, SerpentParser.DEDENT) {
|
||||
@Override
|
||||
public Token pullToken() {
|
||||
return SerpentLexer.super.nextToken(); // must be to super.nextToken, or we'll recurse forever!
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
@Override
|
||||
public Token nextToken() {
|
||||
return denter.nextToken();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
parse: block EOF
|
||||
;
|
||||
|
||||
|
||||
block: (assign | special_func | if_elif_else_stmt | while_stmt | ret_func)* ;
|
||||
|
||||
|
||||
if_elif_else_stmt: 'if' condition ':' INDENT block DEDENT
|
||||
('elif' condition ':' INDENT block DEDENT)*
|
||||
('else:' INDENT block DEDENT)?
|
||||
;
|
||||
|
||||
while_stmt: 'while' condition ':' INDENT block DEDENT;
|
||||
|
||||
|
||||
/* special functions */
|
||||
|
||||
special_func :
|
||||
msg_datasize |
|
||||
msg_sender |
|
||||
msg_value |
|
||||
tx_gasprice |
|
||||
tx_origin |
|
||||
tx_gas |
|
||||
contract_balance |
|
||||
block_prevhash |
|
||||
block_coinbase |
|
||||
block_timestamp |
|
||||
block_number |
|
||||
block_difficulty |
|
||||
block_gaslimit
|
||||
;
|
||||
|
||||
msg_datasize
|
||||
: 'msg.datasize' ;
|
||||
|
||||
msg_sender
|
||||
: 'msg.sender' ;
|
||||
|
||||
msg_value
|
||||
: 'msg.value' ;
|
||||
|
||||
tx_gasprice
|
||||
: 'tx.gasprice' ;
|
||||
|
||||
tx_origin
|
||||
: 'tx.origin' ;
|
||||
|
||||
tx_gas
|
||||
: 'tx.gas' ;
|
||||
|
||||
contract_balance
|
||||
: 'contract.balance' ;
|
||||
|
||||
block_prevhash
|
||||
: 'block.prevhash' ;
|
||||
|
||||
block_coinbase
|
||||
: 'block.coinbase' ;
|
||||
|
||||
block_timestamp
|
||||
: 'block.timestamp' ;
|
||||
|
||||
block_number
|
||||
: 'block.number' ;
|
||||
|
||||
block_difficulty
|
||||
: 'block.difficulty' ;
|
||||
|
||||
block_gaslimit
|
||||
: 'block.gaslimit' ;
|
||||
|
||||
|
||||
|
||||
assign: VAR EQ_OP expression NL;
|
||||
|
||||
|
||||
mul_expr
|
||||
: int_val
|
||||
| mul_expr OP_MUL int_val
|
||||
;
|
||||
|
||||
add_expr
|
||||
: mul_expr
|
||||
| add_expr OP_ADD mul_expr
|
||||
;
|
||||
|
||||
rel_exp
|
||||
: add_expr
|
||||
| rel_exp OP_REL add_expr
|
||||
;
|
||||
|
||||
eq_exp
|
||||
: rel_exp
|
||||
| eq_exp OP_EQ rel_exp
|
||||
;
|
||||
|
||||
and_exp
|
||||
: eq_exp
|
||||
| and_exp OP_AND eq_exp
|
||||
;
|
||||
|
||||
ex_or_exp
|
||||
: and_exp
|
||||
| ex_or_exp OP_EX_OR and_exp
|
||||
;
|
||||
|
||||
in_or_exp
|
||||
: ex_or_exp
|
||||
| in_or_exp OP_IN_OR ex_or_exp
|
||||
;
|
||||
|
||||
log_and_exp
|
||||
: in_or_exp
|
||||
| log_and_exp OP_LOG_AND in_or_exp
|
||||
;
|
||||
|
||||
log_or_exp
|
||||
: log_and_exp
|
||||
| log_or_exp OP_LOG_OR log_and_exp
|
||||
;
|
||||
|
||||
|
||||
expression : log_or_exp ;
|
||||
|
||||
condition: expression ;
|
||||
|
||||
|
||||
int_val : INT | hex_num | get_var | special_func | '(' expression ')' | OP_NOT '(' expression ')' ;
|
||||
// todo: here the val should include also retrieve a variable
|
||||
|
||||
hex_num
|
||||
: HEX_NUMBER
|
||||
;
|
||||
|
||||
ret_func: 'return' '(' INT ')' NL;
|
||||
|
||||
get_var: VAR;
|
||||
|
||||
|
||||
/* 'xor', 'and', 'or', 'not' operands should be defined
|
||||
first among lexer rules because it
|
||||
can be mismatched as a var lexa */
|
||||
OP_EX_OR: 'xor';
|
||||
OP_LOG_AND: '&&' | 'and';
|
||||
OP_LOG_OR: '||' | 'or';
|
||||
OP_NOT: '!' | 'not';
|
||||
|
||||
|
||||
EQ_OP: '=' ;
|
||||
|
||||
NL: ('\r'? '\n' ' '*); // note the ' '*;
|
||||
WS: [ \t]+ -> skip;
|
||||
LINE_COMMENT: '//' ~[\r\n]* -> skip;
|
||||
|
||||
INT: [0-9]+;
|
||||
VAR: [a-zA-Z][a-zA-Z0-9]* ;
|
||||
|
||||
|
||||
OP_ADD : '+' | '-';
|
||||
OP_MUL : '*' | '/' | '^' | '%' ;
|
||||
|
||||
OP_REL : '<' | '>' | '<=' | '>=';
|
||||
OP_EQ : '==' | '!=';
|
||||
OP_AND: '&';
|
||||
OP_IN_OR: '|';
|
||||
|
||||
|
||||
HEX_DIGIT : [0-9a-fA-F];
|
||||
HEX_NUMBER : ('0x' | '0X' )HEX_DIGIT+;
|
|
@ -0,0 +1,23 @@
|
|||
package org.ethereum.serpent;
|
||||
|
||||
import org.antlr.v4.runtime.tree.ParseTree;
|
||||
|
||||
/**
|
||||
* www.ethereumJ.com
|
||||
* User: Roman Mandeleil
|
||||
* Created on: 13/05/14 19:37
|
||||
*/
|
||||
public class SerpentCompiler {
|
||||
|
||||
public static String compile(String code){
|
||||
SerpentParser parser = ParserUtils.getParser(SerpentLexer.class, SerpentParser.class,
|
||||
code);
|
||||
ParseTree tree = parser.parse();
|
||||
|
||||
String result = new SerpentToAssemblyCompiler().visit(tree);
|
||||
result = result.replaceAll("\\s+", " ");
|
||||
result = result.trim();
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,434 @@
|
|||
package org.ethereum.serpent;
|
||||
|
||||
import org.antlr.v4.runtime.misc.NotNull;
|
||||
import org.spongycastle.util.encoders.Hex;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* www.ethereumJ.com
|
||||
* User: Roman Mandeleil
|
||||
* Created on: 05/05/14 13:41
|
||||
*/
|
||||
public class SerpentToAssemblyCompiler extends SerpentBaseVisitor<String> {
|
||||
|
||||
int labelIndex = 0;
|
||||
private ArrayList<String> vars = new ArrayList<String>();
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String visitParse(@NotNull SerpentParser.ParseContext ctx) {
|
||||
|
||||
StringBuffer result = new StringBuffer();
|
||||
|
||||
result.append( super.visitParse(ctx) );
|
||||
|
||||
// todo: calc the wrapping with memory usage data
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String visitIf_elif_else_stmt(@NotNull SerpentParser.If_elif_else_stmtContext ctx) {
|
||||
|
||||
//todo: when you find some error throw expectation exception
|
||||
StringBuffer retCode = new StringBuffer();
|
||||
|
||||
int endOfStmtLabel = labelIndex++ ;
|
||||
|
||||
// if body
|
||||
SerpentParser.BlockContext ifBlock = (SerpentParser.BlockContext) ctx.getChild(4);
|
||||
String blockCode = visitBlock(ifBlock);
|
||||
String ifCond = visitCondition(ctx.condition(0));
|
||||
|
||||
int nextLabel = labelIndex++;
|
||||
|
||||
|
||||
// if_cond [NOT REF_X JUMPI] if_body [REF_Y JUMP LABEL_X] Y=total_end
|
||||
retCode.append(ifCond).
|
||||
append(" NOT REF_").append(nextLabel).append(" JUMPI ").
|
||||
append(blockCode).
|
||||
append("REF_").append(endOfStmtLabel).
|
||||
append(" JUMP LABEL_").append(nextLabel).append(" ");
|
||||
|
||||
// traverse the children and find out all [elif] and [else] that exist
|
||||
int count = ctx.condition().size();
|
||||
|
||||
// traverse all 'elif' statements
|
||||
int i = 1; // define i here for 'else' option
|
||||
for (; i < count; ++i){
|
||||
|
||||
|
||||
// if the condition much at the end jump out of the main if
|
||||
// append to general retCode
|
||||
// todo: [NOT REF_X JUMPI] elif_body [REF_Y JUMP LABEL_X] Y=total_end
|
||||
// todo extract the condition and the body
|
||||
nextLabel = labelIndex++;
|
||||
|
||||
// elif condition
|
||||
String elifCond = visitCondition(ctx.condition(i));
|
||||
|
||||
// elif body
|
||||
String elifBlockCode = visitBlock(ctx.block(i));
|
||||
|
||||
retCode.append(elifCond).
|
||||
append(" NOT REF_").append(nextLabel).append(" JUMPI ").
|
||||
append(elifBlockCode).
|
||||
append("REF_").append(endOfStmtLabel).append(" JUMP LABEL_").
|
||||
append(nextLabel).append(" ");
|
||||
}
|
||||
|
||||
|
||||
// check if there is 'else'
|
||||
if (ctx.block(i) != null) {
|
||||
|
||||
// body
|
||||
String elseBlockCode = visitBlock(ctx.block(i));
|
||||
|
||||
// append to general retCode
|
||||
retCode.append(elseBlockCode);
|
||||
}
|
||||
|
||||
// [LABEL_Y] Y = end of statement
|
||||
retCode.append("LABEL_").append(endOfStmtLabel).append(" ");
|
||||
|
||||
return retCode.toString();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String visitWhile_stmt(@NotNull SerpentParser.While_stmtContext ctx) {
|
||||
|
||||
int whileStartRef = labelIndex++;
|
||||
int whileEndRef = labelIndex++;
|
||||
|
||||
// elif condition
|
||||
SerpentParser.ConditionContext whileCondition = (SerpentParser.ConditionContext)
|
||||
ctx.getChild(1);
|
||||
|
||||
// elif body
|
||||
SerpentParser.BlockContext whileBlock = (SerpentParser.BlockContext)
|
||||
ctx.getChild(4);
|
||||
|
||||
String retCode =
|
||||
String.format("LABEL_%d %s EQ NOT REF_%d JUMPI %s REF_%d JUMP LABEL_%d",
|
||||
whileStartRef, visitCondition(whileCondition), whileEndRef, visitBlock(whileBlock), whileStartRef, whileEndRef);
|
||||
|
||||
return retCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitBlock(@NotNull SerpentParser.BlockContext ctx) {
|
||||
|
||||
String blockStmt = super.visitBlock(ctx);
|
||||
|
||||
return blockStmt;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String visitCondition(@NotNull SerpentParser.ConditionContext ctx) {
|
||||
return super.visitCondition(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitGet_var(@NotNull SerpentParser.Get_varContext ctx) {
|
||||
|
||||
String varName = ctx.VAR().toString();
|
||||
int addr;
|
||||
|
||||
addr = vars.indexOf(varName);
|
||||
if (addr == -1){
|
||||
throw new UnassignVarException(varName);
|
||||
}
|
||||
|
||||
return String.format(" %d MLOAD ", addr * 32);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String visitAssign(@NotNull SerpentParser.AssignContext ctx) {
|
||||
|
||||
String varName = ctx.VAR().toString();
|
||||
int addr = 0;
|
||||
|
||||
addr = vars.indexOf(varName);
|
||||
if (addr == -1){
|
||||
addr = vars.size();
|
||||
vars.add(varName);
|
||||
}
|
||||
|
||||
String expression = visitExpression(ctx.expression());
|
||||
|
||||
return String.format(" %s %d MSTORE ", expression, addr * 32);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String visitInt_val(@NotNull SerpentParser.Int_valContext ctx) {
|
||||
|
||||
if (ctx.OP_NOT() != null)
|
||||
return visitExpression(ctx.expression()) + " NOT";
|
||||
|
||||
if (ctx.expression() != null)
|
||||
return visitExpression(ctx.expression());
|
||||
|
||||
if (ctx.get_var() != null)
|
||||
return visitGet_var(ctx.get_var());
|
||||
|
||||
if (ctx.hex_num() != null)
|
||||
return hexStringToDecimalString(ctx.hex_num().getText());
|
||||
|
||||
if (ctx.special_func() != null)
|
||||
return visitSpecial_func(ctx.special_func());
|
||||
|
||||
return ctx.INT().toString();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String visitMul_expr(@NotNull SerpentParser.Mul_exprContext ctx) {
|
||||
if (ctx.mul_expr() == null) return visit(ctx.int_val());
|
||||
|
||||
String operand0 = visit(ctx.mul_expr());
|
||||
String operand1 = visit(ctx.int_val());
|
||||
|
||||
switch (ctx.OP_MUL().getText().toLowerCase()) {
|
||||
case "*": return operand0 + " " + operand1 + " MUL";
|
||||
case "/": return operand0 + " " + operand1 + " DIV";
|
||||
case "^": return operand0 + " " + operand1 + " EXP";
|
||||
case "%": return operand0 + " " + operand1 + " MOD";
|
||||
default: throw new UnknownOperandException(ctx.OP_MUL().getText());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String visitAdd_expr(@NotNull SerpentParser.Add_exprContext ctx) {
|
||||
|
||||
if (ctx.add_expr() == null) return visit(ctx.mul_expr());
|
||||
|
||||
String operand0 = visit(ctx.add_expr());
|
||||
String operand1 = visit(ctx.mul_expr());
|
||||
|
||||
switch (ctx.OP_ADD().getText().toLowerCase()) {
|
||||
case "+": return operand0 + " " + operand1 + " ADD";
|
||||
case "-": return operand0 + " " + operand1 + " SUB";
|
||||
default: throw new UnknownOperandException(ctx.OP_ADD().getText());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitRel_exp(@NotNull SerpentParser.Rel_expContext ctx) {
|
||||
|
||||
if (ctx.rel_exp() == null) return visit(ctx.add_expr());
|
||||
|
||||
String operand0 = visit(ctx.rel_exp());
|
||||
String operand1 = visit(ctx.add_expr());
|
||||
|
||||
switch (ctx.OP_REL().getText().toLowerCase()) {
|
||||
case "<": return operand0 + " " + operand1 + " LT";
|
||||
case ">": return operand0 + " " + operand1 + " GT";
|
||||
case ">=": return operand0 + " " + operand1 + " LT NOT";
|
||||
case "<=": return operand0 + " " + operand1 + " GT NOT";
|
||||
default: throw new UnknownOperandException(ctx.OP_REL().getText());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitEq_exp(@NotNull SerpentParser.Eq_expContext ctx) {
|
||||
|
||||
if (ctx.eq_exp() == null) return visit(ctx.rel_exp());
|
||||
|
||||
String operand0 = visit(ctx.eq_exp());
|
||||
String operand1 = visit(ctx.rel_exp());
|
||||
|
||||
switch (ctx.OP_EQ().getText().toLowerCase()) {
|
||||
case "==": return operand0 + " " + operand1 + " EQ";
|
||||
case "!=": return operand0 + " " + operand1 + " EQ NOT";
|
||||
default: throw new UnknownOperandException(ctx.OP_EQ().getText());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitAnd_exp(@NotNull SerpentParser.And_expContext ctx) {
|
||||
|
||||
if (ctx.and_exp() == null) return visit(ctx.eq_exp());
|
||||
|
||||
String operand0 = visit(ctx.and_exp());
|
||||
String operand1 = visit(ctx.eq_exp());
|
||||
|
||||
switch (ctx.OP_AND().getText().toLowerCase()) {
|
||||
case "&": return operand0 + " " + operand1 + " AND";
|
||||
default: throw new UnknownOperandException(ctx.OP_AND().getText());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitEx_or_exp(@NotNull SerpentParser.Ex_or_expContext ctx) {
|
||||
|
||||
if (ctx.ex_or_exp() == null) return visit(ctx.and_exp());
|
||||
|
||||
String operand0 = visit(ctx.ex_or_exp());
|
||||
String operand1 = visit(ctx.and_exp());
|
||||
|
||||
switch (ctx.OP_EX_OR().getText().toLowerCase()) {
|
||||
case "xor": return operand0 + " " + operand1 + " XOR";
|
||||
default: throw new UnknownOperandException(ctx.OP_EX_OR().getText());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitIn_or_exp(@NotNull SerpentParser.In_or_expContext ctx) {
|
||||
|
||||
if (ctx.in_or_exp() == null) return visit(ctx.ex_or_exp());
|
||||
|
||||
String operand0 = visit(ctx.in_or_exp());
|
||||
String operand1 = visit(ctx.ex_or_exp());
|
||||
|
||||
switch (ctx.OP_IN_OR().getText().toLowerCase()) {
|
||||
case "|": return operand0 + " " + operand1 + " OR";
|
||||
default: throw new UnknownOperandException(ctx.OP_IN_OR().getText());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitLog_and_exp(@NotNull SerpentParser.Log_and_expContext ctx) {
|
||||
|
||||
if (ctx.log_and_exp() == null) return visit(ctx.in_or_exp());
|
||||
|
||||
String operand0 = visit(ctx.log_and_exp());
|
||||
String operand1 = visit(ctx.in_or_exp());
|
||||
|
||||
switch (ctx.OP_LOG_AND().getText().toLowerCase()) {
|
||||
case "and": return operand0 + " " + operand1 + " NOT NOT MUL";
|
||||
case "&&": return operand0 + " " + operand1 + " NOT NOT MUL";
|
||||
default: throw new UnknownOperandException(ctx.OP_LOG_AND().getText());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitLog_or_exp(@NotNull SerpentParser.Log_or_expContext ctx) {
|
||||
|
||||
if (ctx.log_or_exp() == null) return visit(ctx.log_and_exp());
|
||||
|
||||
String operand0 = visit(ctx.log_or_exp());
|
||||
String operand1 = visit(ctx.log_and_exp());
|
||||
|
||||
switch (ctx.OP_LOG_OR().getText().toLowerCase()) {
|
||||
case "||": return operand0 + " " + operand1 + " DUP 4 PC ADD JUMPI POP SWAP POP";
|
||||
case "or": return operand0 + " " + operand1 + " DUP 4 PC ADD JUMPI POP SWAP POP";
|
||||
default: throw new UnknownOperandException(ctx.OP_LOG_OR().getText());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String visitMsg_sender(@NotNull SerpentParser.Msg_senderContext ctx) {
|
||||
return "CALLER";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitMsg_datasize(@NotNull SerpentParser.Msg_datasizeContext ctx) {
|
||||
return "32 CALLDATASIZE DIV ";
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String visitMsg_value(@NotNull SerpentParser.Msg_valueContext ctx) {
|
||||
return " CALLVALUE ";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitContract_balance(@NotNull SerpentParser.Contract_balanceContext ctx) {
|
||||
return " BALANCE ";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitTx_origin(@NotNull SerpentParser.Tx_originContext ctx) {
|
||||
return " ORIGIN ";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitBlock_timestamp(@NotNull SerpentParser.Block_timestampContext ctx) {
|
||||
return " TIMESTAMP ";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitBlock_number(@NotNull SerpentParser.Block_numberContext ctx) {
|
||||
return " NUMBER ";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitTx_gas(@NotNull SerpentParser.Tx_gasContext ctx) {
|
||||
return " GAS ";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitBlock_difficulty(@NotNull SerpentParser.Block_difficultyContext ctx) {
|
||||
return " DIFFICULTY ";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitBlock_coinbase(@NotNull SerpentParser.Block_coinbaseContext ctx) {
|
||||
return " COINBASE ";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitTx_gasprice(@NotNull SerpentParser.Tx_gaspriceContext ctx) {
|
||||
return " GASPRICE ";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitBlock_prevhash(@NotNull SerpentParser.Block_prevhashContext ctx) {
|
||||
return " PREVHASH ";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitBlock_gaslimit(@NotNull SerpentParser.Block_gaslimitContext ctx) {
|
||||
return " GASLIMIT ";
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected String aggregateResult(String aggregate, String nextResult) {
|
||||
return aggregate + nextResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String defaultResult() {
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param hexNum should be in form '0x34fabd34....'
|
||||
* @return
|
||||
*/
|
||||
private String hexStringToDecimalString(String hexNum){
|
||||
String digits = hexNum.substring(2);
|
||||
if (digits.length() % 2 != 0) digits = "0" + digits;
|
||||
byte[] numberBytes = Hex.decode(digits);
|
||||
return (new BigInteger(1, numberBytes)).toString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static class UnknownOperandException extends RuntimeException {
|
||||
public UnknownOperandException(String name) {
|
||||
super("unknown operand: " + name);
|
||||
}
|
||||
}
|
||||
|
||||
public static class UnassignVarException extends RuntimeException {
|
||||
public UnassignVarException(String name) {
|
||||
super("attempt to access not assigned variable: " + name);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,856 @@
|
|||
package org.ethereum.serpent;
|
||||
|
||||
import org.antlr.v4.runtime.tree.ParseTree;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* www.ethereumJ.com
|
||||
* User: Roman Mandeleil
|
||||
* Created on: 13/05/14 10:07
|
||||
*/
|
||||
public class SerpentCompileTest {
|
||||
|
||||
|
||||
@Test // assign test 1
|
||||
public void test1(){
|
||||
|
||||
String code = "a=2";
|
||||
String expected = "2 0 MSTORE";
|
||||
|
||||
SerpentParser parser = ParserUtils.getParser(SerpentLexer.class, SerpentParser.class,
|
||||
code);
|
||||
ParseTree tree = parser.parse();
|
||||
|
||||
String result = new SerpentToAssemblyCompiler().visit(tree);
|
||||
result = result.replaceAll("\\s+", " ");
|
||||
result = result.trim();
|
||||
|
||||
Assert.assertEquals(result, expected);
|
||||
}
|
||||
|
||||
@Test // assign test 2
|
||||
public void test2(){
|
||||
|
||||
String code = "a=2\n" +
|
||||
"b=6";
|
||||
String expected = "2 0 MSTORE 6 32 MSTORE";
|
||||
|
||||
SerpentParser parser = ParserUtils.getParser(SerpentLexer.class, SerpentParser.class,
|
||||
code);
|
||||
ParseTree tree = parser.parse();
|
||||
|
||||
String result = new SerpentToAssemblyCompiler().visit(tree);
|
||||
result = result.replaceAll("\\s+", " ");
|
||||
result = result.trim();
|
||||
|
||||
Assert.assertEquals(result, expected);
|
||||
}
|
||||
|
||||
@Test // assign test 3
|
||||
public void test3(){
|
||||
|
||||
String code = "a=2\n" +
|
||||
"b=6\n" +
|
||||
"c=b";
|
||||
String expected = "2 0 MSTORE 6 32 MSTORE 32 MLOAD 64 MSTORE";
|
||||
|
||||
SerpentParser parser = ParserUtils.getParser(SerpentLexer.class, SerpentParser.class,
|
||||
code);
|
||||
ParseTree tree = parser.parse();
|
||||
|
||||
String result = new SerpentToAssemblyCompiler().visit(tree);
|
||||
result = result.replaceAll("\\s+", " ");
|
||||
result = result.trim();
|
||||
|
||||
Assert.assertEquals(result, expected);
|
||||
}
|
||||
|
||||
|
||||
@Test // assign test 4
|
||||
public void test4(){
|
||||
|
||||
String code = "a=2\n" +
|
||||
"b=6\n" +
|
||||
"c=b\n" +
|
||||
"a=c";
|
||||
String expected = "2 0 MSTORE 6 32 MSTORE 32 MLOAD 64 MSTORE 64 MLOAD 0 MSTORE";
|
||||
|
||||
SerpentParser parser = ParserUtils.getParser(SerpentLexer.class, SerpentParser.class,
|
||||
code);
|
||||
ParseTree tree = parser.parse();
|
||||
|
||||
String result = new SerpentToAssemblyCompiler().visit(tree);
|
||||
result = result.replaceAll("\\s+", " ");
|
||||
result = result.trim();
|
||||
|
||||
Assert.assertEquals(result, expected);
|
||||
}
|
||||
|
||||
@Test // assign test 5
|
||||
public void test5(){
|
||||
|
||||
String code = "a=2\n" +
|
||||
"b=6\n" +
|
||||
"c=b\n" +
|
||||
"a=c\n" +
|
||||
"a=d";
|
||||
String expected = "exception";
|
||||
|
||||
SerpentParser parser = ParserUtils.getParser(SerpentLexer.class, SerpentParser.class,
|
||||
code);
|
||||
ParseTree tree = parser.parse();
|
||||
|
||||
String result = null;
|
||||
|
||||
try {
|
||||
result = new SerpentToAssemblyCompiler().visit(tree);
|
||||
} catch (Exception e) {
|
||||
|
||||
Assert.assertTrue(e instanceof SerpentToAssemblyCompiler.UnassignVarException);
|
||||
return;
|
||||
}
|
||||
|
||||
// No exception was thrown
|
||||
Assert.fail();
|
||||
}
|
||||
|
||||
|
||||
@Test // expression test 1
|
||||
public void test6(){
|
||||
|
||||
String code = "a = 2 * 2";
|
||||
String expected = "2 2 MUL 0 MSTORE";
|
||||
|
||||
SerpentParser parser = ParserUtils.getParser(SerpentLexer.class, SerpentParser.class,
|
||||
code);
|
||||
ParseTree tree = parser.parse();
|
||||
|
||||
String result = new SerpentToAssemblyCompiler().visit(tree);
|
||||
result = result.replaceAll("\\s+", " ");
|
||||
result = result.trim();
|
||||
|
||||
Assert.assertEquals(result, expected);
|
||||
}
|
||||
|
||||
@Test // expression test 2
|
||||
public void test7(){
|
||||
|
||||
String code = "a = 2 * 2 xor 2 * 2";
|
||||
String expected = "2 2 MUL 2 2 MUL XOR 0 MSTORE";
|
||||
|
||||
SerpentParser parser = ParserUtils.getParser(SerpentLexer.class, SerpentParser.class,
|
||||
code);
|
||||
ParseTree tree = parser.parse();
|
||||
|
||||
String result = new SerpentToAssemblyCompiler().visit(tree);
|
||||
result = result.replaceAll("\\s+", " ");
|
||||
result = result.trim();
|
||||
|
||||
Assert.assertEquals(result, expected);
|
||||
}
|
||||
|
||||
@Test // expression test 3
|
||||
public void test8(){
|
||||
|
||||
String code = "a = 2 | 2 xor 2 * 2";
|
||||
String expected = "2 2 2 2 MUL XOR OR 0 MSTORE";
|
||||
|
||||
SerpentParser parser = ParserUtils.getParser(SerpentLexer.class, SerpentParser.class,
|
||||
code);
|
||||
ParseTree tree = parser.parse();
|
||||
|
||||
String result = new SerpentToAssemblyCompiler().visit(tree);
|
||||
result = result.replaceAll("\\s+", " ");
|
||||
result = result.trim();
|
||||
|
||||
Assert.assertEquals(result, expected);
|
||||
}
|
||||
|
||||
@Test // expression test 4
|
||||
public void test9(){
|
||||
|
||||
String code = "a = (2 | 2) xor (2 * 2)";
|
||||
String expected = "2 2 OR 2 2 MUL XOR 0 MSTORE";
|
||||
|
||||
SerpentParser parser = ParserUtils.getParser(SerpentLexer.class, SerpentParser.class,
|
||||
code);
|
||||
ParseTree tree = parser.parse();
|
||||
|
||||
String result = new SerpentToAssemblyCompiler().visit(tree);
|
||||
result = result.replaceAll("\\s+", " ");
|
||||
result = result.trim();
|
||||
|
||||
Assert.assertEquals(result, expected);
|
||||
}
|
||||
|
||||
|
||||
@Test // expression test 5
|
||||
public void test10(){
|
||||
|
||||
String code = "a = !(2 | 2 xor 2 * 2)";
|
||||
String expected = "2 2 2 2 MUL XOR OR NOT 0 MSTORE";
|
||||
|
||||
SerpentParser parser = ParserUtils.getParser(SerpentLexer.class, SerpentParser.class,
|
||||
code);
|
||||
ParseTree tree = parser.parse();
|
||||
|
||||
String result = new SerpentToAssemblyCompiler().visit(tree);
|
||||
result = result.replaceAll("\\s+", " ");
|
||||
result = result.trim();
|
||||
|
||||
Assert.assertEquals(result, expected);
|
||||
}
|
||||
|
||||
@Test // expression test 6
|
||||
public void test11(){
|
||||
|
||||
String code = "a = 2 + 2 * 2 + 2";
|
||||
String expected = "2 2 2 MUL ADD 2 ADD 0 MSTORE";
|
||||
|
||||
SerpentParser parser = ParserUtils.getParser(SerpentLexer.class, SerpentParser.class,
|
||||
code);
|
||||
ParseTree tree = parser.parse();
|
||||
|
||||
String result = new SerpentToAssemblyCompiler().visit(tree);
|
||||
result = result.replaceAll("\\s+", " ");
|
||||
result = result.trim();
|
||||
|
||||
Assert.assertEquals(result, expected);
|
||||
}
|
||||
|
||||
@Test // expression test 7
|
||||
public void test12(){
|
||||
|
||||
String code = "a = 2 / 2 * 2 + 2";
|
||||
String expected = "2 2 DIV 2 MUL 2 ADD 0 MSTORE";
|
||||
|
||||
SerpentParser parser = ParserUtils.getParser(SerpentLexer.class, SerpentParser.class,
|
||||
code);
|
||||
ParseTree tree = parser.parse();
|
||||
|
||||
String result = new SerpentToAssemblyCompiler().visit(tree);
|
||||
result = result.replaceAll("\\s+", " ");
|
||||
result = result.trim();
|
||||
|
||||
Assert.assertEquals(result, expected);
|
||||
}
|
||||
|
||||
@Test // expression test 8
|
||||
public void test13(){
|
||||
|
||||
String code = "a = 2 - 0x1a * 5 + 0xA";
|
||||
String expected = "2 26 5 MUL SUB 10 ADD 0 MSTORE";
|
||||
|
||||
SerpentParser parser = ParserUtils.getParser(SerpentLexer.class, SerpentParser.class,
|
||||
code);
|
||||
ParseTree tree = parser.parse();
|
||||
|
||||
String result = new SerpentToAssemblyCompiler().visit(tree);
|
||||
result = result.replaceAll("\\s+", " ");
|
||||
result = result.trim();
|
||||
|
||||
Assert.assertEquals(result, expected);
|
||||
}
|
||||
|
||||
@Test // expression test 9
|
||||
public void test14(){
|
||||
|
||||
String code = "a = 1 > 2 > 3 > 4";
|
||||
String expected = "1 2 GT 3 GT 4 GT 0 MSTORE";
|
||||
|
||||
SerpentParser parser = ParserUtils.getParser(SerpentLexer.class, SerpentParser.class,
|
||||
code);
|
||||
ParseTree tree = parser.parse();
|
||||
|
||||
String result = new SerpentToAssemblyCompiler().visit(tree);
|
||||
result = result.replaceAll("\\s+", " ");
|
||||
result = result.trim();
|
||||
|
||||
Assert.assertEquals(result, expected);
|
||||
}
|
||||
|
||||
@Test // expression test 10
|
||||
public void test15(){
|
||||
|
||||
String code = "a = not ( 1 + 2 * 9 | 8 == 2)";
|
||||
String expected = "1 2 9 MUL ADD 8 2 EQ OR NOT 0 MSTORE";
|
||||
|
||||
SerpentParser parser = ParserUtils.getParser(SerpentLexer.class, SerpentParser.class,
|
||||
code);
|
||||
ParseTree tree = parser.parse();
|
||||
|
||||
String result = new SerpentToAssemblyCompiler().visit(tree);
|
||||
result = result.replaceAll("\\s+", " ");
|
||||
result = result.trim();
|
||||
|
||||
Assert.assertEquals(result, expected);
|
||||
}
|
||||
|
||||
@Test // if elif else test 1
|
||||
public void test16(){
|
||||
|
||||
String code = "if 1>2: \n" +
|
||||
" a=2";
|
||||
String expected = "1 2 GT NOT REF_1 JUMPI 2 0 MSTORE REF_0 JUMP LABEL_1 LABEL_0";
|
||||
|
||||
/**
|
||||
1 2 GT NOT REF_1 JUMPI
|
||||
2 0 MSTORE
|
||||
REF_0 JUMP
|
||||
LABEL_1
|
||||
LABEL_0
|
||||
*/
|
||||
SerpentParser parser = ParserUtils.getParser(SerpentLexer.class, SerpentParser.class,
|
||||
code);
|
||||
ParseTree tree = parser.parse();
|
||||
|
||||
String result = new SerpentToAssemblyCompiler().visit(tree);
|
||||
result = result.replaceAll("\\s+", " ");
|
||||
result = result.trim();
|
||||
|
||||
Assert.assertEquals(result, expected);
|
||||
}
|
||||
|
||||
@Test // if elif else test 2
|
||||
public void test17(){
|
||||
|
||||
String code = "if 10 > 2 + 5: \n" +
|
||||
" a=2";
|
||||
String expected = "10 2 5 ADD GT NOT REF_1 JUMPI 2 0 MSTORE REF_0 JUMP LABEL_1 LABEL_0";
|
||||
|
||||
/**
|
||||
|
||||
10 2 5 ADD GT NOT REF_1 JUMPI
|
||||
2 0 MSTORE
|
||||
REF_0 JUMP
|
||||
LABEL_1
|
||||
LABEL_0
|
||||
|
||||
*/
|
||||
|
||||
SerpentParser parser = ParserUtils.getParser(SerpentLexer.class, SerpentParser.class,
|
||||
code);
|
||||
ParseTree tree = parser.parse();
|
||||
|
||||
String result = new SerpentToAssemblyCompiler().visit(tree);
|
||||
result = result.replaceAll("\\s+", " ");
|
||||
result = result.trim();
|
||||
|
||||
Assert.assertEquals(result, expected);
|
||||
}
|
||||
|
||||
@Test // if elif else test 3
|
||||
public void test18(){
|
||||
|
||||
String code = "if 10 > 2 + 5: \n" +
|
||||
" a=2\n" +
|
||||
"else: \n" +
|
||||
" c=3\n";
|
||||
String expected = "10 2 5 ADD GT NOT REF_1 JUMPI 2 0 MSTORE REF_0 JUMP LABEL_1 3 32 MSTORE LABEL_0";
|
||||
|
||||
/**
|
||||
10 2 5 ADD GT NOT REF_1 JUMPI
|
||||
2 0 MSTORE REF_0 JUMP
|
||||
LABEL_1
|
||||
3 32 MSTORE
|
||||
LABEL_0
|
||||
*/
|
||||
|
||||
SerpentParser parser = ParserUtils.getParser(SerpentLexer.class, SerpentParser.class,
|
||||
code);
|
||||
ParseTree tree = parser.parse();
|
||||
|
||||
String result = new SerpentToAssemblyCompiler().visit(tree);
|
||||
result = result.replaceAll("\\s+", " ");
|
||||
result = result.trim();
|
||||
|
||||
Assert.assertEquals(result, expected);
|
||||
}
|
||||
|
||||
@Test // if elif else test 4
|
||||
public void test19(){
|
||||
|
||||
String code = "if 10 > 2 + 5: \n" +
|
||||
" a=2\n" +
|
||||
"else: \n" +
|
||||
" c=123\n" +
|
||||
" d=0xFFAA";
|
||||
String expected = "10 2 5 ADD GT NOT REF_1 JUMPI 2 0 MSTORE REF_0 JUMP LABEL_1 123 32 MSTORE 65450 64 MSTORE LABEL_0";
|
||||
|
||||
/**
|
||||
10 2 5 ADD GT NOT REF_1 JUMPI
|
||||
2 0 MSTORE REF_0 JUMP
|
||||
LABEL_1
|
||||
123 32 MSTORE
|
||||
65450 64 MSTORE
|
||||
LABEL_0
|
||||
*/
|
||||
|
||||
SerpentParser parser = ParserUtils.getParser(SerpentLexer.class, SerpentParser.class,
|
||||
code);
|
||||
ParseTree tree = parser.parse();
|
||||
|
||||
String result = new SerpentToAssemblyCompiler().visit(tree);
|
||||
result = result.replaceAll("\\s+", " ");
|
||||
result = result.trim();
|
||||
|
||||
Assert.assertEquals(result, expected);
|
||||
}
|
||||
|
||||
@Test // if elif else test 5
|
||||
public void test20(){
|
||||
|
||||
String code = "if 10 > 2 + 5: \n" +
|
||||
" a=2\n" +
|
||||
"elif 2*2==4: \n" +
|
||||
" a=3\n" +
|
||||
"else: \n" +
|
||||
" c=123\n" +
|
||||
" d=0xFFAA";
|
||||
String expected = "10 2 5 ADD GT NOT REF_1 JUMPI 2 0 MSTORE REF_0 JUMP LABEL_1 2 2 MUL 4 EQ NOT REF_2 JUMPI 3 0 MSTORE REF_0 JUMP LABEL_2 123 32 MSTORE 65450 64 MSTORE LABEL_0";
|
||||
|
||||
/**
|
||||
10 2 5 ADD GT NOT REF_1 JUMPI
|
||||
2 0 MSTORE REF_0 JUMP
|
||||
LABEL_1
|
||||
2 2 MUL 4 EQ NOT REF_2 JUMPI
|
||||
3 0 MSTORE REF_0 JUMP
|
||||
LABEL_2
|
||||
123 32 MSTORE
|
||||
65450 64 MSTORE
|
||||
LABEL_0
|
||||
*/
|
||||
|
||||
SerpentParser parser = ParserUtils.getParser(SerpentLexer.class, SerpentParser.class,
|
||||
code);
|
||||
ParseTree tree = parser.parse();
|
||||
|
||||
String result = new SerpentToAssemblyCompiler().visit(tree);
|
||||
result = result.replaceAll("\\s+", " ");
|
||||
result = result.trim();
|
||||
|
||||
Assert.assertEquals(result, expected);
|
||||
}
|
||||
|
||||
@Test // if elif else test 6
|
||||
public void test21(){
|
||||
|
||||
String code = "if 10 > 2 + 5: \n" +
|
||||
" a=2\n" +
|
||||
"elif 2*2==4: \n" +
|
||||
" a=3\n" +
|
||||
"elif 2*2+10==40: \n" +
|
||||
" a=3\n" +
|
||||
" a=9\n" +
|
||||
" a=21\n" +
|
||||
"else: \n" +
|
||||
" c=123\n" +
|
||||
" d=0xFFAA";
|
||||
String expected = "10 2 5 ADD GT NOT REF_1 JUMPI 2 0 MSTORE REF_0 JUMP LABEL_1 2 2 MUL 4 EQ NOT REF_2 JUMPI 3 0 MSTORE REF_0 JUMP LABEL_2 2 2 MUL 10 ADD 40 EQ NOT REF_3 JUMPI 3 0 MSTORE 9 0 MSTORE 21 0 MSTORE REF_0 JUMP LABEL_3 123 32 MSTORE 65450 64 MSTORE LABEL_0";
|
||||
|
||||
/**
|
||||
|
||||
10 2 5 ADD GT NOT REF_1 JUMPI
|
||||
2 0 MSTORE REF_0 JUMP
|
||||
LABEL_1
|
||||
2 2 MUL 4 EQ NOT REF_2 JUMPI
|
||||
3 0 MSTORE
|
||||
REF_0 JUMP
|
||||
LABEL_2
|
||||
2 2 MUL 10 ADD 40 EQ NOT REF_3 JUMPI
|
||||
3 0 MSTORE
|
||||
9 0 MSTORE
|
||||
21 0 MSTORE
|
||||
REF_0 JUMP
|
||||
LABEL_3
|
||||
123 32 MSTORE
|
||||
65450 64 MSTORE
|
||||
LABEL_0 */
|
||||
|
||||
SerpentParser parser = ParserUtils.getParser(SerpentLexer.class, SerpentParser.class,
|
||||
code);
|
||||
ParseTree tree = parser.parse();
|
||||
|
||||
String result = new SerpentToAssemblyCompiler().visit(tree);
|
||||
result = result.replaceAll("\\s+", " ");
|
||||
result = result.trim();
|
||||
|
||||
Assert.assertEquals(result, expected);
|
||||
}
|
||||
|
||||
|
||||
@Test // if elif else test 7
|
||||
public void test22(){
|
||||
|
||||
String code = "if 10 > 2 + 5: \n" +
|
||||
" a=2\n" +
|
||||
"elif 2*2==4: \n" +
|
||||
" a=3\n" +
|
||||
" if a==3:\n" +
|
||||
" q=123\n" +
|
||||
"elif 2*2+10==40: \n" +
|
||||
" a=3\n" +
|
||||
" a=9\n" +
|
||||
" a=21\n" +
|
||||
"else: \n" +
|
||||
" c=123\n" +
|
||||
" d=0xFFAA";
|
||||
String expected = "10 2 5 ADD GT NOT REF_1 JUMPI 2 0 MSTORE REF_0 JUMP LABEL_1 2 2 MUL 4 EQ NOT REF_2 JUMPI 3 0 MSTORE 0 MLOAD 3 EQ NOT REF_4 JUMPI 123 32 MSTORE REF_3 JUMP LABEL_4 LABEL_3 REF_0 JUMP LABEL_2 2 2 MUL 10 ADD 40 EQ NOT REF_5 JUMPI 3 0 MSTORE 9 0 MSTORE 21 0 MSTORE REF_0 JUMP LABEL_5 123 64 MSTORE 65450 96 MSTORE LABEL_0";
|
||||
|
||||
/**
|
||||
10 2 5 ADD GT NOT REF_1 JUMPI
|
||||
2 0 MSTORE
|
||||
REF_0 JUMP
|
||||
LABEL_1
|
||||
2 2 MUL 4 EQ NOT REF_2 JUMPI
|
||||
3 0 MSTORE
|
||||
0 MLOAD 3 EQ NOT REF_4 JUMPI
|
||||
123 32 MSTORE
|
||||
REF_3 JUMP
|
||||
LABEL_4
|
||||
LABEL_3
|
||||
REF_0 JUMP
|
||||
LABEL_2
|
||||
2 2 MUL 10 ADD 40 EQ NOT REF_5 JUMPI
|
||||
3 0 MSTORE
|
||||
9 0 MSTORE
|
||||
21 0 MSTORE
|
||||
REF_0 JUMP
|
||||
LABEL_5
|
||||
123 64 MSTORE
|
||||
65450 96 MSTORE
|
||||
LABEL_0
|
||||
*/
|
||||
|
||||
SerpentParser parser = ParserUtils.getParser(SerpentLexer.class, SerpentParser.class,
|
||||
code);
|
||||
ParseTree tree = parser.parse();
|
||||
|
||||
String result = new SerpentToAssemblyCompiler().visit(tree);
|
||||
result = result.replaceAll("\\s+", " ");
|
||||
result = result.trim();
|
||||
|
||||
Assert.assertEquals(result, expected);
|
||||
}
|
||||
|
||||
@Test // if elif else test 8
|
||||
public void test23(){
|
||||
|
||||
String code = "if (10 > 2 + 5) && (2 * 7 > 96): \n" +
|
||||
" a=2\n" +
|
||||
"elif 2*2==4: \n" +
|
||||
" a=3\n" +
|
||||
" if a==3:\n" +
|
||||
" q=123\n" +
|
||||
"elif 2*2+10==40: \n" +
|
||||
" a=3\n" +
|
||||
" a=9\n" +
|
||||
" a=21\n" +
|
||||
"else: \n" +
|
||||
" c=123\n" +
|
||||
" d=0xFFAA";
|
||||
String expected = "10 2 5 ADD GT 2 7 MUL 96 GT NOT NOT MUL NOT REF_1 JUMPI 2 0 MSTORE REF_0 JUMP LABEL_1 2 2 MUL 4 EQ NOT REF_2 JUMPI 3 0 MSTORE 0 MLOAD 3 EQ NOT REF_4 JUMPI 123 32 MSTORE REF_3 JUMP LABEL_4 LABEL_3 REF_0 JUMP LABEL_2 2 2 MUL 10 ADD 40 EQ NOT REF_5 JUMPI 3 0 MSTORE 9 0 MSTORE 21 0 MSTORE REF_0 JUMP LABEL_5 123 64 MSTORE 65450 96 MSTORE LABEL_0";
|
||||
|
||||
/**
|
||||
10 2 5 ADD GT 2 7 MUL 96 GT NOT NOT MUL NOT REF_1 JUMPI
|
||||
2 0 MSTORE
|
||||
REF_0 JUMP
|
||||
LABEL_1
|
||||
2 2 MUL 4 EQ NOT REF_2 JUMPI
|
||||
3 0 MSTORE
|
||||
0 MLOAD 3 EQ NOT REF_4 JUMPI
|
||||
123 32 MSTORE REF_3 JUMP
|
||||
LABEL_4
|
||||
LABEL_3
|
||||
REF_0 JUMP
|
||||
LABEL_2
|
||||
2 2 MUL 10 ADD 40 EQ NOT REF_5 JUMPI
|
||||
3 0 MSTORE
|
||||
9 0 MSTORE
|
||||
21 0 MSTORE
|
||||
REF_0 JUMP
|
||||
LABEL_5
|
||||
123 64 MSTORE
|
||||
65450 96 MSTORE
|
||||
|
||||
LABEL_0
|
||||
|
||||
*/
|
||||
|
||||
SerpentParser parser = ParserUtils.getParser(SerpentLexer.class, SerpentParser.class,
|
||||
code);
|
||||
ParseTree tree = parser.parse();
|
||||
|
||||
String result = new SerpentToAssemblyCompiler().visit(tree);
|
||||
result = result.replaceAll("\\s+", " ");
|
||||
result = result.trim();
|
||||
|
||||
Assert.assertEquals(result, expected);
|
||||
}
|
||||
|
||||
@Test // if elif else test 9
|
||||
public void test24(){
|
||||
|
||||
String code = "a = 20\n" +
|
||||
"b = 40\n" +
|
||||
"if a == 20: \n" +
|
||||
" a = 30\n" +
|
||||
"if b == 40: \n" +
|
||||
" b = 50\n";
|
||||
String expected = "20 0 MSTORE 40 32 MSTORE 0 MLOAD 20 EQ NOT REF_1 JUMPI 30 0 MSTORE REF_0 JUMP LABEL_1 LABEL_0 32 MLOAD 40 EQ NOT REF_3 JUMPI 50 32 MSTORE REF_2 JUMP LABEL_3 LABEL_2";
|
||||
|
||||
/**
|
||||
|
||||
20 0 MSTORE
|
||||
40 32 MSTORE
|
||||
0 MLOAD 20 EQ NOT REF_1 JUMPI
|
||||
30 0 MSTORE
|
||||
REF_0 JUMP
|
||||
LABEL_1
|
||||
LABEL_0
|
||||
32 MLOAD 40 EQ NOT REF_3 JUMPI
|
||||
50 32 MSTORE
|
||||
REF_2 JUMP
|
||||
LABEL_3
|
||||
LABEL_2
|
||||
|
||||
*/
|
||||
|
||||
SerpentParser parser = ParserUtils.getParser(SerpentLexer.class, SerpentParser.class,
|
||||
code);
|
||||
ParseTree tree = parser.parse();
|
||||
|
||||
String result = new SerpentToAssemblyCompiler().visit(tree);
|
||||
result = result.replaceAll("\\s+", " ");
|
||||
result = result.trim();
|
||||
|
||||
Assert.assertEquals(result, expected);
|
||||
}
|
||||
|
||||
@Test // if elif else test 10
|
||||
public void test25(){
|
||||
|
||||
String code = "a = 20\n" +
|
||||
"b = 40\n" +
|
||||
"if a == 20: \n" +
|
||||
" a = 30\n" +
|
||||
"a = 70\n" +
|
||||
"if b == 40: \n" +
|
||||
" b = 50\n";
|
||||
String expected = "20 0 MSTORE 40 32 MSTORE 0 MLOAD 20 EQ NOT REF_1 JUMPI 30 0 MSTORE REF_0 JUMP LABEL_1 LABEL_0 70 0 MSTORE 32 MLOAD 40 EQ NOT REF_3 JUMPI 50 32 MSTORE REF_2 JUMP LABEL_3 LABEL_2";
|
||||
|
||||
/**
|
||||
|
||||
20 0 MSTORE
|
||||
40 32 MSTORE
|
||||
0 MLOAD 20 EQ NOT REF_1 JUMPI
|
||||
30 0 MSTORE
|
||||
REF_0 JUMP
|
||||
LABEL_1
|
||||
LABEL_0
|
||||
70 0 MSTORE
|
||||
32 MLOAD 40 EQ NOT REF_3 JUMPI
|
||||
50 32 MSTORE
|
||||
REF_2 JUMP
|
||||
LABEL_3
|
||||
LABEL_2
|
||||
|
||||
*/
|
||||
|
||||
SerpentParser parser = ParserUtils.getParser(SerpentLexer.class, SerpentParser.class,
|
||||
code);
|
||||
ParseTree tree = parser.parse();
|
||||
|
||||
String result = new SerpentToAssemblyCompiler().visit(tree);
|
||||
result = result.replaceAll("\\s+", " ");
|
||||
result = result.trim();
|
||||
|
||||
Assert.assertEquals(result, expected);
|
||||
}
|
||||
|
||||
@Test // if elif else test 11
|
||||
public void test26(){
|
||||
|
||||
String code = "if 2>1: \n" +
|
||||
" if 3>2: \n" +
|
||||
" if 4>3:\n" +
|
||||
" if 5>4:\n" +
|
||||
" a = 10\n";
|
||||
String expected = "2 1 GT NOT REF_7 JUMPI 3 2 GT NOT REF_6 JUMPI 4 3 GT NOT REF_5 JUMPI 5 4 GT NOT REF_4 JUMPI 10 0 MSTORE REF_3 JUMP LABEL_4 LABEL_3 REF_2 JUMP LABEL_5 LABEL_2 REF_1 JUMP LABEL_6 LABEL_1 REF_0 JUMP LABEL_7 LABEL_0";
|
||||
|
||||
/**
|
||||
|
||||
2 1 GT NOT REF_7 JUMPI
|
||||
3 2 GT NOT REF_6 JUMPI
|
||||
4 3 GT NOT REF_5 JUMPI
|
||||
5 4 GT NOT REF_4 JUMPI
|
||||
10 0 MSTORE
|
||||
REF_3 JUMP
|
||||
LABEL_4 LABEL_3 REF_2 JUMP LABEL_5 LABEL_2 REF_1 JUMP LABEL_6 LABEL_1 REF_0 JUMP LABEL_7 LABEL_0
|
||||
|
||||
*/
|
||||
|
||||
SerpentParser parser = ParserUtils.getParser(SerpentLexer.class, SerpentParser.class,
|
||||
code);
|
||||
ParseTree tree = parser.parse();
|
||||
|
||||
String result = new SerpentToAssemblyCompiler().visit(tree);
|
||||
result = result.replaceAll("\\s+", " ");
|
||||
result = result.trim();
|
||||
|
||||
Assert.assertEquals(result, expected);
|
||||
}
|
||||
|
||||
@Test // if elif else test 12
|
||||
public void test27(){
|
||||
|
||||
String code = "if 2>1: \n" +
|
||||
" if 3>2: \n" +
|
||||
" if 4>3:\n" +
|
||||
" if 5>4:\n" +
|
||||
" a = 10\n";
|
||||
String expected = "2 1 GT NOT REF_7 JUMPI 3 2 GT NOT REF_6 JUMPI 4 3 GT NOT REF_5 JUMPI 5 4 GT NOT REF_4 JUMPI 10 0 MSTORE REF_3 JUMP LABEL_4 LABEL_3 REF_2 JUMP LABEL_5 LABEL_2 REF_1 JUMP LABEL_6 LABEL_1 REF_0 JUMP LABEL_7 LABEL_0";
|
||||
|
||||
/**
|
||||
|
||||
2 1 GT NOT REF_7 JUMPI
|
||||
3 2 GT NOT REF_6 JUMPI
|
||||
4 3 GT NOT REF_5 JUMPI
|
||||
5 4 GT NOT REF_4 JUMPI
|
||||
10 0 MSTORE
|
||||
REF_3 JUMP
|
||||
LABEL_4 LABEL_3 REF_2 JUMP LABEL_5 LABEL_2 REF_1 JUMP LABEL_6 LABEL_1 REF_0 JUMP LABEL_7 LABEL_0
|
||||
|
||||
*/
|
||||
|
||||
SerpentParser parser = ParserUtils.getParser(SerpentLexer.class, SerpentParser.class,
|
||||
code);
|
||||
ParseTree tree = parser.parse();
|
||||
|
||||
String result = new SerpentToAssemblyCompiler().visit(tree);
|
||||
result = result.replaceAll("\\s+", " ");
|
||||
result = result.trim();
|
||||
|
||||
Assert.assertEquals(result, expected);
|
||||
}
|
||||
|
||||
|
||||
@Test // if elif else test 13
|
||||
public void test28(){
|
||||
|
||||
String code = "if 2>1: \n" +
|
||||
" if 3>2: \n" +
|
||||
" if 4>3:\n" +
|
||||
" if 5>4:\n" +
|
||||
" a = 10\n" +
|
||||
" else:\n" +
|
||||
" b = 20\n";
|
||||
String expected = "2 1 GT NOT REF_7 JUMPI 3 2 GT NOT REF_6 JUMPI 4 3 GT NOT REF_5 JUMPI 5 4 GT NOT REF_4 JUMPI 10 0 MSTORE REF_3 JUMP LABEL_4 20 32 MSTORE LABEL_3 REF_2 JUMP LABEL_5 LABEL_2 REF_1 JUMP LABEL_6 LABEL_1 REF_0 JUMP LABEL_7 LABEL_0";
|
||||
|
||||
/**
|
||||
|
||||
2 1 GT NOT REF_7 JUMPI
|
||||
3 2 GT NOT REF_6 JUMPI
|
||||
4 3 GT NOT REF_5 JUMPI
|
||||
5 4 GT NOT REF_4 JUMPI
|
||||
10 0 MSTORE REF_3 JUMP
|
||||
LABEL_4
|
||||
20 32 MSTORE
|
||||
LABEL_3
|
||||
REF_2 JUMP LABEL_5 LABEL_2 REF_1 JUMP LABEL_6 LABEL_1 REF_0 JUMP LABEL_7 LABEL_0
|
||||
*/
|
||||
|
||||
SerpentParser parser = ParserUtils.getParser(SerpentLexer.class, SerpentParser.class,
|
||||
code);
|
||||
ParseTree tree = parser.parse();
|
||||
|
||||
String result = new SerpentToAssemblyCompiler().visit(tree);
|
||||
result = result.replaceAll("\\s+", " ");
|
||||
result = result.trim();
|
||||
|
||||
Assert.assertEquals(result, expected);
|
||||
}
|
||||
|
||||
|
||||
@Test // if elif else test 14
|
||||
public void test29(){
|
||||
|
||||
String code = " if 2>4: \n" +
|
||||
" a=20 \n" +
|
||||
" \n" +
|
||||
" \n";
|
||||
String expected = "";
|
||||
|
||||
SerpentParser parser = ParserUtils.getParser(SerpentLexer.class, SerpentParser.class,
|
||||
code);
|
||||
|
||||
ParseTree tree = null;
|
||||
try {
|
||||
tree = parser.parse();
|
||||
} catch (Throwable e) {
|
||||
|
||||
Assert.assertTrue(e instanceof ParserUtils.AntlrParseException);
|
||||
return;
|
||||
}
|
||||
|
||||
Assert.fail("Should be indent error thrown");
|
||||
}
|
||||
|
||||
@Test // if elif else test 15
|
||||
public void test30(){
|
||||
|
||||
String code = "if 2>4: \n" +
|
||||
" a=20 \n" +
|
||||
" else: \n" +
|
||||
" a=40 \n";
|
||||
String expected = "";
|
||||
|
||||
SerpentParser parser = ParserUtils.getParser(SerpentLexer.class, SerpentParser.class,
|
||||
code);
|
||||
|
||||
ParseTree tree = null;
|
||||
try {
|
||||
tree = parser.parse();
|
||||
} catch (Throwable e) {
|
||||
|
||||
Assert.assertTrue(e instanceof ParserUtils.AntlrParseException);
|
||||
return;
|
||||
}
|
||||
|
||||
Assert.fail("Should be indent error thrown");
|
||||
}
|
||||
|
||||
|
||||
@Test // if elif else test 16
|
||||
public void test31(){
|
||||
|
||||
String code = "if 2>4: \n" +
|
||||
" a=20 \n" +
|
||||
" elif 2<9: \n" +
|
||||
" a=40 \n" +
|
||||
"else: \n" +
|
||||
" a=40 \n";
|
||||
String expected = "";
|
||||
|
||||
SerpentParser parser = ParserUtils.getParser(SerpentLexer.class, SerpentParser.class,
|
||||
code);
|
||||
|
||||
ParseTree tree = null;
|
||||
try {
|
||||
tree = parser.parse();
|
||||
} catch (Throwable e) {
|
||||
|
||||
Assert.assertTrue(e instanceof ParserUtils.AntlrParseException);
|
||||
return;
|
||||
}
|
||||
|
||||
Assert.fail("Should be indent error thrown");
|
||||
}
|
||||
|
||||
/**
|
||||
* todo: more testing for if-elif-else 4 tests
|
||||
* todo: more testing for special functions 10
|
||||
* todo: more testing for while function 10
|
||||
*/
|
||||
}
|
Loading…
Reference in New Issue