basic generator
This commit is contained in:
parent
21dca82f4a
commit
a845321785
|
@ -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<Void> {
|
||||||
|
public Program prog;
|
||||||
|
private Annotations an;
|
||||||
|
private int regNum;
|
||||||
|
private final Reg zero = new Reg("zero");
|
||||||
|
|
||||||
|
static Map<Integer, OpCode> ops = new HashMap<Integer, OpCode>() {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,8 +2,6 @@ package pp.s1184725.boppi;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import pp.iloc.eval.Machine;
|
|
||||||
|
|
||||||
public class CachingSymbolTable<T> {
|
public class CachingSymbolTable<T> {
|
||||||
protected Stack<Map<String,Variable<T>>> symbolMapStack;
|
protected Stack<Map<String,Variable<T>>> symbolMapStack;
|
||||||
protected Stack<Integer> offsets;
|
protected Stack<Integer> offsets;
|
||||||
|
@ -56,7 +54,8 @@ public class CachingSymbolTable<T> {
|
||||||
throw new Exception(String.format("Identifier '%s' already declared in this scope.", id));
|
throw new Exception(String.format("Identifier '%s' already declared in this scope.", id));
|
||||||
|
|
||||||
Variable<T> var = new Variable<>(type, offset);
|
Variable<T> var = new Variable<>(type, offset);
|
||||||
offset += Machine.INT_SIZE;
|
// TODO refactor to get type size
|
||||||
|
offset += ((SimpleType)type).getSize();
|
||||||
symbolMapStack.peek().put(id, var);
|
symbolMapStack.peek().put(id, var);
|
||||||
symbolCache.put(id, var);
|
symbolCache.put(id, var);
|
||||||
return var;
|
return var;
|
||||||
|
|
|
@ -1,17 +1,30 @@
|
||||||
package pp.s1184725.boppi;
|
package pp.s1184725.boppi;
|
||||||
|
import static pp.s1184725.boppi.BasicLexer.*;
|
||||||
|
|
||||||
|
import pp.iloc.eval.Machine;
|
||||||
|
|
||||||
public enum SimpleType {
|
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) {
|
public static SimpleType fromToken(int token) {
|
||||||
switch (token) {
|
switch (token) {
|
||||||
case BasicLexer.INTTYPE:
|
case INTTYPE:
|
||||||
return SimpleType.INT;
|
return SimpleType.INT;
|
||||||
|
|
||||||
case BasicLexer.CHARTYPE:
|
case CHARTYPE:
|
||||||
return SimpleType.CHAR;
|
return SimpleType.CHAR;
|
||||||
|
|
||||||
case BasicLexer.BOOLTYPE:
|
case BOOLTYPE:
|
||||||
return SimpleType.BOOL;
|
return SimpleType.BOOL;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -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<LogRecord> generateAndGetLog(String code) {
|
||||||
|
Logger logger = Logger.getAnonymousLogger();
|
||||||
|
Pair<BasicParser, List<LogRecord>> 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");
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue