From a845321785f6088bd4e407d7a21570c9afbf56e7 Mon Sep 17 00:00:00 2001 From: User <> Date: Mon, 15 May 2017 16:22:00 +0200 Subject: [PATCH] basic generator --- .../s1184725/boppi/BoppiBasicGenerator.java | 296 ++++++++++++++++++ src/pp/s1184725/boppi/CachingSymbolTable.java | 5 +- src/pp/s1184725/boppi/SimpleType.java | 21 +- src/pp/s1184725/boppi/test/GeneratorTest.java | 51 +++ 4 files changed, 366 insertions(+), 7 deletions(-) create mode 100644 src/pp/s1184725/boppi/BoppiBasicGenerator.java create mode 100644 src/pp/s1184725/boppi/test/GeneratorTest.java diff --git a/src/pp/s1184725/boppi/BoppiBasicGenerator.java b/src/pp/s1184725/boppi/BoppiBasicGenerator.java new file mode 100644 index 0000000..88c3142 --- /dev/null +++ b/src/pp/s1184725/boppi/BoppiBasicGenerator.java @@ -0,0 +1,296 @@ +package pp.s1184725.boppi; + +import java.util.HashMap; +import java.util.Map; + +import org.antlr.v4.runtime.RuleContext; +import org.antlr.v4.runtime.tree.ParseTree; +import org.antlr.v4.runtime.tree.RuleNode; + +import pp.iloc.model.*; +import pp.s1184725.boppi.BasicParser.*; + +public class BoppiBasicGenerator extends BasicBaseVisitor { + public Program prog; + private Annotations an; + private int regNum; + private final Reg zero = new Reg("zero"); + + static Map ops = new HashMap() { + private static final long serialVersionUID = 8979722313842633807L; + { + put(BasicLexer.AND, OpCode.and); + put(BasicLexer.DIVIDE, OpCode.div); + put(BasicLexer.EQ, OpCode.cmp_EQ); + put(BasicLexer.GT, OpCode.cmp_GT); + put(BasicLexer.GTE, OpCode.cmp_GE); + put(BasicLexer.LEQ, OpCode.cmp_LE); + put(BasicLexer.LT, OpCode.cmp_LT); + put(BasicLexer.MINUS, OpCode.sub); + put(BasicLexer.MULTIPLY, OpCode.mult); + put(BasicLexer.NEQ, OpCode.cmp_NE); + put(BasicLexer.PLUS, OpCode.add); + put(BasicLexer.OR, OpCode.or); + } + }; + + public BoppiBasicGenerator(Annotations annotations) { + an = annotations; + prog = new Program(); + regNum = 0; + + emit(OpCode.loadI, new Num(0), zero); + } + + private Reg getReg(ParseTree parseTree) { + Reg reg = an.registers.get(parseTree); + if (reg == null) { + reg = new Reg("r_" + (regNum++)); + an.registers.put(parseTree, reg); + } + return reg; + } + + private Reg copyReg(ParseTree source, ParseTree destination) { + Reg reg = an.registers.get(source); + an.registers.put(destination, reg); + return reg; + } + + /** + * Constructs an operation from the parameters and adds it to the program + * under construction. + */ + private Op emit(Label label, OpCode opCode, Operand... args) { + Op result = new Op(label, opCode, args); + this.prog.addInstr(result); + return result; + } + + /** + * Constructs an operation from the parameters and adds it to the program + * under construction. + */ + private Op emit(OpCode opCode, Operand... args) { + return emit((Label) null, opCode, args); + } + + @Override + public Void visitChildren(RuleNode tree) { + super.visitChildren(tree); + if (an.registers.get(tree) == null) + for (int i = tree.getChildCount() - 1; i >= 0; i--) + if (tree.getChild(i) instanceof RuleContext) { + copyReg(tree.getChild(i), tree); + break; + } + + return null; + } + + @Override + public Void visitAssign(AssignContext ctx) { + visit(ctx.singleExpr()); + copyReg(ctx.singleExpr(), ctx); + + switch (an.types.get(ctx)) { + case CHAR: + emit(OpCode.cstoreAI, getReg(ctx), zero, new Num(an.variables.get(ctx.variable()).getOffset())); + break; + default: + emit(OpCode.storeAI, getReg(ctx), zero, new Num(an.variables.get(ctx.variable()).getOffset())); + } + return null; + } + + @Override + public Void visitBool(BoolContext ctx) { + emit(OpCode.loadI, new Num(ctx.TRUE() != null ? 1 : 0), getReg(ctx)); + return null; + } + + @Override + public Void visitNumber(NumberContext ctx) { + emit(OpCode.loadI, new Num(Integer.parseInt(ctx.LITERAL10().getText())), getReg(ctx)); + return null; + } + + @Override + public Void visitInfix1(Infix1Context ctx) { + visitChildren(ctx); + emit(ops.get(ctx.op.getType()), getReg(ctx.lhs), getReg(ctx.rhs), getReg(ctx)); + return null; + } + + @Override + public Void visitInfix2(Infix2Context ctx) { + visitChildren(ctx); + emit(ops.get(ctx.op.getType()), getReg(ctx.lhs), getReg(ctx.rhs), getReg(ctx)); + return null; + } + + @Override + public Void visitInfix3(Infix3Context ctx) { + visitChildren(ctx); + emit(ops.get(ctx.op.getType()), getReg(ctx.lhs), getReg(ctx.rhs), getReg(ctx)); + return null; + } + + @Override + public Void visitInfix4(Infix4Context ctx) { + visitChildren(ctx); + emit(OpCode.and, getReg(ctx.lhs), getReg(ctx.rhs), getReg(ctx)); + return null; + } + + @Override + public Void visitInfix5(Infix5Context ctx) { + visitChildren(ctx); + emit(OpCode.or, getReg(ctx.lhs), getReg(ctx.rhs), getReg(ctx)); + return null; + } + + @Override + public Void visitPrefix1(Prefix1Context ctx) { + visitChildren(ctx); + + switch (ctx.op.getType()) { + case BasicLexer.MINUS: + emit(OpCode.rsubI, new Num(0), getReg(ctx.singleExpr()), getReg(ctx)); + } + return null; + } + + @Override + public Void visitVar(VarContext ctx) { + switch (an.types.get(ctx)) { + case CHAR: + emit(OpCode.cloadAI, zero, new Num(an.variables.get(ctx.variable()).getOffset()), getReg(ctx)); + break; + default: + emit(OpCode.loadAI, zero, new Num(an.variables.get(ctx.variable()).getOffset()), getReg(ctx)); + } + return null; + } + + @Override + public Void visitChar(CharContext ctx) { + emit(OpCode.loadI, new Num(ctx.CHAR().getText().codePointAt(1)), getReg(ctx)); + emit(OpCode.i2c, getReg(ctx), getReg(ctx)); + return null; + } + + @Override + public Void visitRead(ReadContext ctx) { + for (VariableContext expr : ctx.variable()) { + visit(expr); + switch (an.types.get(expr)) { + case BOOL: + case INT: + emit(OpCode.in, new Str(""), getReg(ctx)); + emit(OpCode.storeAI, getReg(ctx), zero, new Num(an.variables.get(expr).getOffset())); + break; + case CHAR: + //Get input until at least 1 character + Label getTarget = new Label("lcin_l"+ctx.hashCode()); + Label continueTarget = new Label("lcin_e"+ctx.hashCode()); + Reg countReg = new Reg("tempReg"); + emit(getTarget, OpCode.cin, new Str("")); + emit(OpCode.pop, countReg); + emit(OpCode.cbr, countReg, continueTarget, getTarget); + + //Get character + emit(continueTarget, OpCode.cpop, getReg(ctx)); + emit(OpCode.cstoreAI, getReg(ctx), zero, new Num(an.variables.get(expr).getOffset())); + + //Pop all remaining characters + Label loopTarget = new Label("lcpop_l"+ctx.hashCode()); + Label iterTarget = new Label("lcpop_c"+ctx.hashCode()); + Label stopTarget = new Label("lcpop_e"+ctx.hashCode()); + Reg tempReg2 = new Reg("tempReg2"); + emit(loopTarget, OpCode.subI, countReg, new Num(1), countReg); + emit(OpCode.cbr, countReg, iterTarget, stopTarget); + emit(iterTarget, OpCode.cpop, tempReg2); + emit(OpCode.jumpI, loopTarget); + emit(stopTarget, OpCode.nop); + break; + default: + emit(OpCode.out, new Str("Unknown type: "), an.registers.get(expr)); + } + } + return null; + } + + @Override + public Void visitIf(IfContext ctx) { + Label toTrue = new Label("if_t"+ctx.hashCode()); + Label toFalse = new Label("if_f"+ctx.hashCode()); + Label toEnd = new Label("if_e"+ctx.hashCode()); + + visit(ctx.cond); + if (ctx.onFalse == null) { + emit(OpCode.cbr, getReg(ctx.cond), toTrue, toEnd); + + emit(toTrue, OpCode.nop); + visit(ctx.onTrue); + } + else { + emit(OpCode.cbr, getReg(ctx.cond), toTrue, toFalse); + + emit(toTrue, OpCode.nop); + visit(ctx.onTrue); + if (an.types.get(ctx) != SimpleType.VOID) + emit(OpCode.i2i, getReg(ctx.onTrue), getReg(ctx)); + emit(OpCode.jumpI, toEnd); + + emit(toFalse, OpCode.nop); + visit(ctx.onFalse); + if (an.types.get(ctx) != SimpleType.VOID) + emit(OpCode.i2i, getReg(ctx.onFalse), getReg(ctx)); + + } + emit(toEnd, OpCode.nop); + + return null; + } + + @Override + public Void visitWhile(WhileContext ctx) { + Label toLoop = new Label("while_t"+ctx.hashCode()); + Label toCond = new Label("while_f"+ctx.hashCode()); + Label toEnd = new Label("while_e"+ctx.hashCode()); + + emit(OpCode.jumpI, toCond); + emit(toLoop, OpCode.nop); + visit(ctx.onTrue); + emit(toCond, OpCode.nop); + visit(ctx.cond); + emit(OpCode.cbr, getReg(ctx.cond), toLoop, toEnd); + emit(toEnd, OpCode.nop); + + return null; + } + + @Override + public Void visitWrite(WriteContext ctx) { + for (ExprContext expr : ctx.expr()) { + visit(expr); + switch (an.types.get(expr)) { + case BOOL: + case INT: + emit(OpCode.out, new Str(""), getReg(expr)); + break; + case CHAR: + Reg tempReg = new Reg("tempReg"); + emit(OpCode.cpush, getReg(expr)); + emit(OpCode.loadI, new Num(1), tempReg); + emit(OpCode.push, tempReg); + emit(OpCode.cout, new Str("")); + break; + default: + emit(OpCode.out, new Str("Unknown type: "), getReg(expr)); + } + } + return null; + } +} diff --git a/src/pp/s1184725/boppi/CachingSymbolTable.java b/src/pp/s1184725/boppi/CachingSymbolTable.java index b212bea..e375b5a 100644 --- a/src/pp/s1184725/boppi/CachingSymbolTable.java +++ b/src/pp/s1184725/boppi/CachingSymbolTable.java @@ -2,8 +2,6 @@ package pp.s1184725.boppi; import java.util.*; -import pp.iloc.eval.Machine; - public class CachingSymbolTable { protected Stack>> symbolMapStack; protected Stack offsets; @@ -56,7 +54,8 @@ public class CachingSymbolTable { throw new Exception(String.format("Identifier '%s' already declared in this scope.", id)); Variable var = new Variable<>(type, offset); - offset += Machine.INT_SIZE; + // TODO refactor to get type size + offset += ((SimpleType)type).getSize(); symbolMapStack.peek().put(id, var); symbolCache.put(id, var); return var; diff --git a/src/pp/s1184725/boppi/SimpleType.java b/src/pp/s1184725/boppi/SimpleType.java index 4a818ec..fadd815 100644 --- a/src/pp/s1184725/boppi/SimpleType.java +++ b/src/pp/s1184725/boppi/SimpleType.java @@ -1,17 +1,30 @@ package pp.s1184725.boppi; +import static pp.s1184725.boppi.BasicLexer.*; + +import pp.iloc.eval.Machine; public enum SimpleType { - INT, CHAR, BOOL, VOID; + INT(Machine.INT_SIZE), CHAR(Machine.DEFAULT_CHAR_SIZE), BOOL(Machine.INT_SIZE), VOID(0); + + private final int size; + + public int getSize() { + return size; + } + + private SimpleType(int typeSize) { + size = typeSize; + } public static SimpleType fromToken(int token) { switch (token) { - case BasicLexer.INTTYPE: + case INTTYPE: return SimpleType.INT; - case BasicLexer.CHARTYPE: + case CHARTYPE: return SimpleType.CHAR; - case BasicLexer.BOOLTYPE: + case BOOLTYPE: return SimpleType.BOOL; default: diff --git a/src/pp/s1184725/boppi/test/GeneratorTest.java b/src/pp/s1184725/boppi/test/GeneratorTest.java new file mode 100644 index 0000000..3f0de67 --- /dev/null +++ b/src/pp/s1184725/boppi/test/GeneratorTest.java @@ -0,0 +1,51 @@ +package pp.s1184725.boppi.test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.logging.LogRecord; +import java.util.logging.Logger; + +import org.antlr.v4.runtime.ParserRuleContext; +import org.apache.commons.lang3.tuple.Pair; +import org.junit.Test; + +import pp.iloc.Simulator; +import pp.s1184725.boppi.Annotations; +import pp.s1184725.boppi.BasicParser; +import pp.s1184725.boppi.BasicParserHelper; +import pp.s1184725.boppi.BoppiBasicChecker; +import pp.s1184725.boppi.BoppiBasicGenerator; + +public class GeneratorTest { + static private List generateAndGetLog(String code) { + Logger logger = Logger.getAnonymousLogger(); + Pair> pair = BasicParserHelper.getParserWithLog(code, logger); + Annotations annotater = new Annotations(); + ParserRuleContext tree = pair.getLeft().program(); + new BoppiBasicChecker(logger, annotater).visit(tree); + BoppiBasicGenerator generator = new BoppiBasicGenerator(annotater); + generator.visit(tree); +// System.out.println(generator.prog.prettyPrint()); +// System.out.println(BasicParserHelper.getAnnotatedDOT(tree, annotater)); + Simulator s = new Simulator(generator.prog); + InputStream in = new ByteArrayInputStream("1\nq\n".getBytes(StandardCharsets.UTF_8)); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + s.setIn(in); + s.setOut(out); + s.run(); + System.out.println(out.toString()); + return pair.getRight(); + } + + @Test + public void basicTest() { +// generateAndGetLog("print(5*3)"); +// generateAndGetLog("var int x; var int y; x := 3*(y := 4); print(x,y)"); +// generateAndGetLog("print('T', 'e', 's', 't', '!')"); +// generateAndGetLog("var int i; if read(i) == 1 then var char c; read(c); print(c,c); else print(i) fi"); + generateAndGetLog("var int i; i := 10; while i > 0 do print('A', i); i := i-1 od"); + } +}