diff --git a/ethereumj-core/pom.xml b/ethereumj-core/pom.xml index 9dddaf3e..d6483483 100644 --- a/ethereumj-core/pom.xml +++ b/ethereumj-core/pom.xml @@ -61,18 +61,20 @@ geoip-api 1.2.11 - - org.antlr - antlr - - 3.1.3 - - - - org.antlr - stringtemplate - 3.2.1 - + + org.antlr + antlr4-runtime + 4.1 + compile + + + + org.antlr + antlr4-maven-plugin + 4.1 + + + org.python jython @@ -92,7 +94,15 @@ slf4j-api ${slf4j.version} - + + + com.yuvalshavit + antlr-denter + 1.1 + + + + diff --git a/ethereumj-core/src/main/java/org/ethereum/gui/SerpentEditor.java b/ethereumj-core/src/main/java/org/ethereum/gui/SerpentEditor.java index bf5fbbfb..df0bf5a0 100644 --- a/ethereumj-core/src/main/java/org/ethereum/gui/SerpentEditor.java +++ b/ethereumj-core/src/main/java/org/ethereum/gui/SerpentEditor.java @@ -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); diff --git a/ethereumj-core/src/main/java/org/ethereum/serpent/ParserGenerator.java b/ethereumj-core/src/main/java/org/ethereum/serpent/ParserGenerator.java new file mode 100644 index 00000000..1cf194ba --- /dev/null +++ b/ethereumj-core/src/main/java/org/ethereum/serpent/ParserGenerator.java @@ -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"}); + + } + + +} \ No newline at end of file diff --git a/ethereumj-core/src/main/java/org/ethereum/serpent/ParserUtils.java b/ethereumj-core/src/main/java/org/ethereum/serpent/ParserUtils.java new file mode 100644 index 00000000..50f5556f --- /dev/null +++ b/ethereumj-core/src/main/java/org/ethereum/serpent/ParserUtils.java @@ -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 getLexer(Class 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 getParser(Class lexerClass, Class

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); + } + } +} diff --git a/ethereumj-core/src/main/java/org/ethereum/serpent/Serpent.g4 b/ethereumj-core/src/main/java/org/ethereum/serpent/Serpent.g4 new file mode 100644 index 00000000..6c17e79d --- /dev/null +++ b/ethereumj-core/src/main/java/org/ethereum/serpent/Serpent.g4 @@ -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+; diff --git a/ethereumj-core/src/main/java/org/ethereum/serpent/SerpentCompiler.java b/ethereumj-core/src/main/java/org/ethereum/serpent/SerpentCompiler.java new file mode 100644 index 00000000..dfdc77d2 --- /dev/null +++ b/ethereumj-core/src/main/java/org/ethereum/serpent/SerpentCompiler.java @@ -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; + } +} diff --git a/ethereumj-core/src/main/java/org/ethereum/serpent/SerpentToAssemblyCompiler.java b/ethereumj-core/src/main/java/org/ethereum/serpent/SerpentToAssemblyCompiler.java new file mode 100644 index 00000000..2e3d4f45 --- /dev/null +++ b/ethereumj-core/src/main/java/org/ethereum/serpent/SerpentToAssemblyCompiler.java @@ -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 { + + int labelIndex = 0; + private ArrayList vars = new ArrayList(); + + + + @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); + } + } + +} diff --git a/ethereumj-core/src/test/java/org/ethereum/serpent/SerpentCompileTest.java b/ethereumj-core/src/test/java/org/ethereum/serpent/SerpentCompileTest.java new file mode 100644 index 00000000..e5861c60 --- /dev/null +++ b/ethereumj-core/src/test/java/org/ethereum/serpent/SerpentCompileTest.java @@ -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 + */ +}