From f6cabe0ccb2757d9b311a23b73ad89e7ae78ef16 Mon Sep 17 00:00:00 2001 From: User <> Date: Sun, 30 Jul 2017 14:53:53 +0200 Subject: [PATCH] implemented arrays, split antlr file into grammar and lexer --- .settings/org.eclipse.core.resources.prefs | 2 + src/pp/iloc/Simulator.java | 12 +- src/pp/s1184725/boppi/BoppiChecker.java | 67 ++- src/pp/s1184725/boppi/BoppiGenerator.java | 455 ++++++++++++------ src/pp/s1184725/boppi/antlr/.gitignore | 3 + src/pp/s1184725/boppi/antlr/Boppi.g4 | 62 +-- src/pp/s1184725/boppi/test/ArrayTest.java | 95 ++++ src/pp/s1184725/boppi/test/BoppiTests.java | 44 +- src/pp/s1184725/boppi/test/ClosureTest.java | 14 +- .../boppi/test/programs/complexArray.boppi | 84 ++++ .../boppi/test/programs/simpleArray.boppi | 28 ++ src/pp/s1184725/boppi/type/ArrayType.java | 60 +++ src/pp/s1184725/boppi/type/FunctionType.java | 5 + src/pp/s1184725/boppi/type/ReferenceType.java | 5 + src/pp/s1184725/boppi/util/RegisterPool.java | 156 +++--- 15 files changed, 770 insertions(+), 322 deletions(-) create mode 100644 .settings/org.eclipse.core.resources.prefs create mode 100644 src/pp/s1184725/boppi/test/ArrayTest.java create mode 100644 src/pp/s1184725/boppi/test/programs/complexArray.boppi create mode 100644 src/pp/s1184725/boppi/test/programs/simpleArray.boppi create mode 100644 src/pp/s1184725/boppi/type/ArrayType.java diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000..99f26c0 --- /dev/null +++ b/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/=UTF-8 diff --git a/src/pp/iloc/Simulator.java b/src/pp/iloc/Simulator.java index 74468a1..e1f2a58 100644 --- a/src/pp/iloc/Simulator.java +++ b/src/pp/iloc/Simulator.java @@ -311,15 +311,15 @@ public class Simulator { break; case halt: this.vm.setInterrupt(c.reg(0)); - System.err.printf( - "Program halted with code %d at line %d.%n", - c.num(0), o.getLine()); + + if (DEBUG) + System.err.printf("Program halted with code %d at line %d.", c.reg(0), o.getLine()); break; case haltI: this.vm.setInterrupt(c.num(0)); - System.err.printf( - "Program halted with code %d at line %d.%n", - c.num(0), o.getLine()); + + if (DEBUG) + System.err.printf("Program halted with code %d at line %d.", c.num(0), o.getLine()); break; default: System.err.printf( diff --git a/src/pp/s1184725/boppi/BoppiChecker.java b/src/pp/s1184725/boppi/BoppiChecker.java index abb8cb0..0b4a2a4 100644 --- a/src/pp/s1184725/boppi/BoppiChecker.java +++ b/src/pp/s1184725/boppi/BoppiChecker.java @@ -168,6 +168,11 @@ public class BoppiChecker extends BoppiBaseVisitor { return ctx.singleExpr().stream().map(this::visit).reduce((__, snd) -> snd).get(); } + @Override + public Type visitGetVariable(GetVariableContext ctx) { + return visit(ctx.variable()); + } + @Override public Type visitIf(IfContext ctx) { return an.symbols.withScope(() -> { @@ -286,6 +291,19 @@ public class BoppiChecker extends BoppiBaseVisitor { return SimpleType.VOID; } } + + @Override + public Type visitTypeArray(TypeArrayContext ctx) { + int size = 0; + + try { + size = Integer.parseInt(ctx.LITERAL10().getText()); + }catch (NumberFormatException e) { + log.severe(getError(ctx, "Error parsing number '%s'", ctx.LITERAL10().getText())); + } + + return new ArrayType(size, visit(ctx.type())); + } @Override public Type visitTypeFunction(TypeFunctionContext ctx) { @@ -308,28 +326,57 @@ public class BoppiChecker extends BoppiBaseVisitor { } @Override - public Type visitVariable(VariableContext ctx) { + public Type visitVariableArray(VariableArrayContext ctx) { + Type t = visit(ctx.variable()); + + checkConstraint(visit(ctx.expr()), SimpleType.INT, ctx); + + if (t instanceof ArrayType) { + ArrayType arrayType = (ArrayType) t; + return arrayType.getType(); + } else { + log.severe(getError(ctx, "Expected array type but got %s", t)); + return SimpleType.VOID; + } + } + + @Override + public Type visitVariableProperty(VariablePropertyContext ctx) { + Type varType = visit(ctx.variable()); + String prop = ctx.IDENTIFIER().getText(); + + if (varType instanceof ArrayType) { + if (prop.equals("length")) { + return SimpleType.INT; + } + else { + log.severe(getError(ctx, "Unknown array property '%s'.", prop)); + return SimpleType.VOID; + } + } + else { + log.severe(getError(ctx, "Record types not implemented.")); + return SimpleType.VOID; + } + } + + @Override + public Type visitVariableSimple(VariableSimpleContext ctx) { try { Variable var = an.symbols.get(ctx.getText()); - + try { an.function.put(ctx, an.symbols.getFunctionScope()); } catch (NoProgramException e) { log.severe(getError(ctx, e.getMessage())); } - + an.variables.put(ctx, var); return var.getType(); } catch (SymbolTableException e) { log.severe(getError(ctx, e.getMessage())); + return SimpleType.VOID; } - - return SimpleType.VOID; - } - - @Override - public Type visitVariableExpr(VariableExprContext ctx) { - return visit(ctx.variable()); } @Override diff --git a/src/pp/s1184725/boppi/BoppiGenerator.java b/src/pp/s1184725/boppi/BoppiGenerator.java index 5c5b054..e24ff91 100644 --- a/src/pp/s1184725/boppi/BoppiGenerator.java +++ b/src/pp/s1184725/boppi/BoppiGenerator.java @@ -22,12 +22,12 @@ import pp.s1184725.boppi.util.RegisterPool; * @author Frank Wibbelink */ public class BoppiGenerator extends BoppiBaseVisitor { - private static final int ARBASESIZE = 16, FUNCREF_SIZE = 12; + private static final int ARBASESIZE = 16; private static final Num ZERO = new Num(0); - private static final Reg ZERO_REG = new Reg("zero"); private static final Num OFFSET_ARP = new Num(-4), OFFSET_RETURN_ADDR = new Num(-8), OFFSET_RETURN_VAL = new Num(-12), OFFSET_AL = new Num(-16), OFFSET_FUNCREF_ADDR = ZERO, - OFFSET_FUNCREF_ARP = new Num(4), OFFSET_FUNCREF_ARSIZE = new Num(8); + OFFSET_FUNCREF_ARP = new Num(4), OFFSET_FUNCREF_ARSIZE = new Num(8), OFFSET_REF_COUNT = new Num(-8), + OFFSET_REF_SIZE = new Num(-4); private Program prog; private Annotations an; @@ -89,21 +89,6 @@ public class BoppiGenerator extends BoppiBaseVisitor { this.logger = logger; } - private Reg getVar(VariableContext node) { - Variable var = an.variables.get(node); - - int functionDepth = an.function.get(node).getFunctionDepth(); - - if (var.getDepth() < functionDepth) { - emit("travelling ALs", OpCode.i2i, arp, tempArp); - for (int i = var.getDepth(); i < functionDepth; i++) - emit(StringUtils.repeat(' ', i) + "\\", OpCode.loadAI, tempArp, OFFSET_AL, tempArp); - - return tempArp; - } else - return arp; - } - /** * Constructs an operation from the parameters and adds it to the program * under construction. @@ -135,7 +120,7 @@ public class BoppiGenerator extends BoppiBaseVisitor { String comment = "AR incRef"; Label loop = makeLabel("aril"), done = makeLabel("arid"); emit(comment, OpCode.i2i, ar, tempArp); - emit(comment, OpCode.cmp_NE, tempArp, ZERO_REG, temp); + emit(comment, OpCode.cmp_NE, tempArp, RegisterPool.ZERO, temp); emit(comment, OpCode.cbr, temp, loop, done); emit(comment, loop, OpCode.loadI, new Num(prog.size() + 5), temp); emit(comment, OpCode.push, temp); @@ -144,7 +129,7 @@ public class BoppiGenerator extends BoppiBaseVisitor { emit(comment, OpCode.jumpI, new Label("memaddref")); emit(comment, OpCode.loadAI, tempArp, OFFSET_AL, tempArp); - emit(comment, OpCode.cmp_NE, tempArp, ZERO_REG, temp); + emit(comment, OpCode.cmp_NE, tempArp, RegisterPool.ZERO, temp); emit(comment, OpCode.cbr, temp, loop, done); emit(comment, done, OpCode.nop); } @@ -161,7 +146,7 @@ public class BoppiGenerator extends BoppiBaseVisitor { String comment = "AR decRef"; Label loop = makeLabel("ardl"), done = makeLabel("ardd"); emit(comment, OpCode.i2i, ar, tempArp); - emit(comment, OpCode.cmp_NE, tempArp, ZERO_REG, temp); + emit(comment, OpCode.cmp_NE, tempArp, RegisterPool.ZERO, temp); emit(comment, OpCode.cbr, temp, loop, done); emit(comment, loop, OpCode.loadI, new Num(prog.size() + 5), temp); emit(comment, OpCode.push, temp); @@ -170,7 +155,7 @@ public class BoppiGenerator extends BoppiBaseVisitor { emit(comment, OpCode.jumpI, new Label("memfree")); emit(comment, OpCode.loadAI, tempArp, OFFSET_AL, tempArp); - emit(comment, OpCode.cmp_NE, tempArp, ZERO_REG, temp); + emit(comment, OpCode.cmp_NE, tempArp, RegisterPool.ZERO, temp); emit(comment, OpCode.cbr, temp, loop, done); emit(comment, done, OpCode.nop); } @@ -256,30 +241,64 @@ public class BoppiGenerator extends BoppiBaseVisitor { return String.format("Line %d:%d - %s", line, column, String.format(message, args)); } + /** + * Constructs a new unique label with the given prefix to make it + * recognisable in ILOC. + * + * @param prefix + * a short prefix for the label + * @return the label + */ private Label makeLabel(String prefix) { return new Label(prefix + (labels++)); } + /** + * Increments the reference count to a type if it is a reference type. For + * function types this includes all access links. Assumes registers are + * reserved. + * + * @param type + * the type of this reference + * @param addr + * the address of the reference object + */ private void incrementReference(Type type, Reg addr) { - regPool.withReg((temp) -> { + if (!(type instanceof ReferenceType)) + logger.severe(String.format("INTERNAL: trying to increment reference for '%s'", type)); + + regPool.withReg((temp, ar) -> { String comment = "add new reference"; incRef(temp, addr); if (type instanceof FunctionType) { - emit(comment, OpCode.loadAI, addr, OFFSET_FUNCREF_ARP, addr); + emit(comment, OpCode.loadAI, addr, OFFSET_FUNCREF_ARP, ar); - incARReferences(temp, addr); + incARReferences(temp, ar); } }); } + /** + * Decrements the reference count to a type if it is a reference type. For + * function types this includes all access links. Assumes registers are + * reserved. + * + * @param type + * the type of this reference + * @param addr + * the address of the reference object + */ private void decrementReference(Type type, Reg addr) { + if (!(type instanceof ReferenceType)) + logger.severe(String.format("INTERNAL: trying to increment reference for '%s'", type)); + regPool.withReg((temp) -> { Label isNull = makeLabel("ynul"), notNull = makeLabel("nnul"); String comment = "remove old reference"; - emit(comment, OpCode.cmp_EQ, addr, ZERO_REG, temp); + emit(comment, OpCode.cmp_EQ, addr, RegisterPool.ZERO, temp); emit(comment, OpCode.cbr, temp, isNull, notNull); emit(comment, notNull, OpCode.nop); @@ -296,7 +315,14 @@ public class BoppiGenerator extends BoppiBaseVisitor { }); } - private void cleanScope(FunctionScope scope) { + /** + * Decrements all reference-type variables of a function. Must be called at + * the end of a function declaration. Assumes registers are reserved. + * + * @param scope + * the function scope with all relevant variables + */ + private void dereferenceLocalVariables(FunctionScope scope) { regPool.withReg((addr, ft) -> { for (Variable var : scope.getVars()) { Type type = var.getType(); @@ -306,7 +332,7 @@ public class BoppiGenerator extends BoppiBaseVisitor { String comment = "remove reference"; emit(comment + " get var", OpCode.loadAI, arp, new Num(var.getOffset()), addr); - emit(comment, OpCode.cmp_EQ, addr, ZERO_REG, ft); + emit(comment, OpCode.cmp_EQ, addr, RegisterPool.ZERO, ft); emit(comment, OpCode.cbr, ft, isNull, notNull); emit(comment, notNull, OpCode.nop); @@ -326,13 +352,41 @@ public class BoppiGenerator extends BoppiBaseVisitor { }); } + /** + * {@inheritDoc} + *

+ * This override only aggregates {@link ParserRuleContext}s. + */ + @Override + public Reg visitChildren(RuleNode node) { + Reg result = defaultResult(); + int n = node.getChildCount(); + for (int i = 0; i < n; i++) { + if (!shouldVisitNextChild(node, result)) { + break; + } + + ParseTree c = node.getChild(i); + Reg childResult = c.accept(this); + if (c instanceof ParserRuleContext) + result = aggregateResult(result, childResult); + } + + return result; + } + + /** + * {@inheritDoc} + *

+ * This override logs any exceptions to the {@link Logger} attached to this + * generator. + */ @Override public Reg visit(ParseTree tree) { try { return super.visit(tree); } catch (Exception e) { - e.printStackTrace(); - System.out.println(prog.prettyPrint()); + if (tree instanceof ParserRuleContext) logger.severe(getError((ParserRuleContext) tree, "%s: %s", e.getClass().getName(), e.getMessage())); else @@ -343,33 +397,31 @@ public class BoppiGenerator extends BoppiBaseVisitor { @Override public Reg visitAssign(AssignContext ctx) { - Reg result = visit(ctx.singleExpr()); regPool.blockReg(result, () -> { Type type = an.types.get(ctx); + Reg addr = visit(ctx.variable()); if (type instanceof ReferenceType) { - regPool.withReg((addr) -> { - emit("remove old ref for " + ctx.variable().getText(), OpCode.loadAI, getVar(ctx.variable()), - new Num(an.variables.get(ctx.variable()).getOffset()), addr); - decrementReference(an.types.get(ctx), addr); + regPool.blockReg(addr, () -> { + regPool.withReg((temp) -> { + emit("load reference", OpCode.load, addr, temp); + decrementReference(an.types.get(ctx), temp); + }); }); } - if (SimpleType.CHAR.equals(type)) - emit("to " + ctx.variable().getText(), OpCode.cstoreAI, result, getVar(ctx.variable()), - new Num(an.variables.get(ctx.variable()).getOffset())); - else - emit("to " + ctx.variable().getText(), OpCode.storeAI, result, getVar(ctx.variable()), - new Num(an.variables.get(ctx.variable()).getOffset())); + OpCode op = (SimpleType.CHAR.equals(type)) ? OpCode.cstore : OpCode.store; + + emit("to " + ctx.variable().getText(), op, result, addr); if (type instanceof ReferenceType) { - regPool.withReg((addr) -> { - emit("add new ref for " + ctx.variable().getText(), OpCode.loadAI, getVar(ctx.variable()), - new Num(an.variables.get(ctx.variable()).getOffset()), addr); - - incrementReference(an.types.get(ctx), addr); + regPool.blockReg(addr, () -> { + regPool.withReg((temp) -> { + emit("load reference", OpCode.load, addr, temp); + incrementReference(an.types.get(ctx), temp); + }); }); } }); @@ -377,49 +429,43 @@ public class BoppiGenerator extends BoppiBaseVisitor { return result; } - @Override - public Reg visitBlock(BlockContext ctx) { - return visit(ctx.expr()); - } - @Override public Reg visitCall(CallContext ctx) { - String base = "call " + ctx.variable().getText() + " - "; Variable function = an.variables.get(ctx.variable()); TupleType parameters = (TupleType) ((FunctionType) function.getType()).getParameter(); - visit(ctx.variable()); - - Reg result = regPool.withReg((ar) -> { + Reg ar = regPool.withReg((tempAR) -> { regPool.withReg((rt) -> { - emit(base + "load function reference", OpCode.loadAI, getVar(ctx.variable()), - new Num(an.variables.get(ctx.variable()).getOffset()), rt); + Reg addr = visit(ctx.variable()); + emit(base + "load function reference", OpCode.load, addr, rt); emit(base + "load AR size", OpCode.loadAI, rt, OFFSET_FUNCREF_ARSIZE, rt); - malloc(ar, rt); + malloc(tempAR, rt); }); - emit(base + "shift AR", OpCode.addI, ar, new Num(ARBASESIZE), ar); + + emit(base + "shift AR", OpCode.addI, tempAR, new Num(ARBASESIZE), tempAR); for (int i = 0; i < ctx.expr().size(); i++) { Reg exprReg = visit(ctx.expr(i)); if (SimpleType.CHAR.equals(parameters.get(i))) - emit(base + "store param " + i, OpCode.cstoreAI, exprReg, ar, new Num(parameters.getOffset(i))); + emit(base + "store param " + i, OpCode.cstoreAI, exprReg, tempAR, new Num(parameters.getOffset(i))); else - emit(base + "store param " + i, OpCode.storeAI, exprReg, ar, new Num(parameters.getOffset(i))); + emit(base + "store param " + i, OpCode.storeAI, exprReg, tempAR, new Num(parameters.getOffset(i))); } + ; + }); - Stack inUse = new Stack<>(); - for (Reg reg : regPool.getInUse()) { - if (!reg.equals(ar)) { - inUse.push(reg); - emit(base + "register save " + reg.getName(), OpCode.push, reg); - } - } + Stack inUse = new Stack<>(); + for (Reg reg : regPool.getInUse()) { + inUse.push(reg); + emit(base + "register save " + reg.getName(), OpCode.push, reg); + } - Reg res = regPool.withReg((r1, r2) -> { - emit(base + "load function reference", OpCode.loadAI, getVar(ctx.variable()), - new Num(an.variables.get(ctx.variable()).getOffset()), r2); + regPool.blockReg(ar, () -> { + regPool.withReg((r1, r2) -> { + Reg addr = visit(ctx.variable()); + emit(base + "load function reference", OpCode.load, addr, r2); emit(base + "link caller ARP", OpCode.storeAI, arp, ar, OFFSET_ARP); emit(base + "load AL", OpCode.loadAI, r2, OFFSET_FUNCREF_ARP, r1); emit(base + "link AL", OpCode.storeAI, r1, ar, OFFSET_AL); @@ -433,26 +479,45 @@ public class BoppiGenerator extends BoppiBaseVisitor { emit(base + "load target address", OpCode.loadAI, r2, OFFSET_FUNCREF_ADDR, r1); emit(base + "execute", OpCode.jump, r1); - - emit(base + "load result", OpCode.loadAI, arp, OFFSET_RETURN_VAL, r2); }); - - while (!inUse.isEmpty()) { - Reg reg = inUse.pop(); - emit(base + "register unsave " + reg.getName(), OpCode.pop, reg); - } - - return res; - }); - regPool.withReg(result, (temp) -> { + regPool.withReg((temp) -> { decARReferences(temp, arp); }); - emit(base + "reset ARP", OpCode.loadAI, arp, OFFSET_ARP, arp); + while (!inUse.isEmpty()) { + Reg reg = inUse.pop(); + emit(base + "register unsave " + reg.getName(), OpCode.pop, reg); + } - return result; + return regPool.withReg((res) -> { + emit(base + "load result", OpCode.loadAI, arp, OFFSET_RETURN_VAL, res); + emit(base + "reset ARP", OpCode.loadAI, arp, OFFSET_ARP, arp); + }); + } + + @Override + public Reg visitDeclare(DeclareContext ctx) { + Variable var = an.variables.get(ctx); + Type t = var.getType(); + + if (t instanceof ReferenceType) { + + regPool.withReg((addr) -> { + emit("load var address", OpCode.addI, arp, new Num(var.getOffset()), addr); + + if (t instanceof ArrayType) { + ArrayType type = (ArrayType) t; + regPool.withReg((temp) -> { + malloc(temp, type.getReferenceSize()); + emit("link array", OpCode.store, temp, addr); + }); + } + }); + } + + return null; } /** @@ -469,24 +534,28 @@ public class BoppiGenerator extends BoppiBaseVisitor { */ @Override public Reg visitDeclareFunction(DeclareFunctionContext ctx) { - + ReferenceType type = (ReferenceType) an.variables.get(ctx).getType(); String base = "define " + ctx.IDENTIFIER(0).getText() + " - "; + Label skip = makeLabel("s"); emit(base + "jump over body", OpCode.jumpI, skip); - int targetAddress = emit(base + "entry point", OpCode.nop).getLine(); + int callAddress = emit(base + "entry point", OpCode.nop).getLine(); Reg result = visit(ctx.body); emit(base + "move result", OpCode.storeAI, result, arp, OFFSET_RETURN_VAL); - cleanScope(an.function.get(ctx)); + dereferenceLocalVariables(an.function.get(ctx)); - regPool.withReg((r1, r2) -> { + regPool.withReg((r1) -> { emit(base + "load return address", OpCode.loadAI, arp, OFFSET_RETURN_ADDR, r1); emit(base + "go to return address", OpCode.jump, r1); + }); - emit(base + "skip target", skip, OpCode.nop); - malloc(r2, FUNCREF_SIZE); - emit(base + "load target address", OpCode.loadI, new Num(targetAddress), r1); + emit(base + "skip target", skip, OpCode.nop); + + regPool.withReg((r1, r2) -> { + malloc(r2, type.getReferenceSize()); + emit(base + "load target address", OpCode.loadI, new Num(callAddress), r1); emit(base + "set target address", OpCode.storeAI, r1, r2, OFFSET_FUNCREF_ADDR); emit(base + "copy ARP", OpCode.storeAI, arp, r2, OFFSET_FUNCREF_ARP); emit(base + "load AR size", OpCode.loadI, new Num(ARBASESIZE + an.function.get(ctx).getLocalDataSize()), @@ -520,6 +589,22 @@ public class BoppiGenerator extends BoppiBaseVisitor { return visit(last); } + @Override + public Reg visitGetVariable(GetVariableContext ctx) { + Type type = an.types.get(ctx); + Reg result = visit(ctx.variable()); + OpCode op = SimpleType.CHAR.equals(an.types.get(ctx)) ? OpCode.cload : OpCode.load; + + emit("load address", op, result, result); + + if (type instanceof ReferenceType) + regPool.blockReg(result, () -> { + incrementReference(type, result); + }); + + return result; + } + @Override public Reg visitIf(IfContext ctx) { Label toTrue = makeLabel("if_t"); @@ -527,7 +612,27 @@ public class BoppiGenerator extends BoppiBaseVisitor { Label toEnd = makeLabel("if_e"); Reg cond = visit(ctx.cond); - if (an.types.get(ctx) != SimpleType.VOID) { + if (ctx.onFalse == null) { + emit("", OpCode.cbr, cond, toTrue, toEnd); + + emit("", toTrue, OpCode.nop); + visit(ctx.onTrue); + + emit("end target", toEnd, OpCode.nop); + return null; + } else if (an.types.get(ctx) == SimpleType.VOID) { + emit("", OpCode.cbr, cond, toTrue, toFalse); + + emit("", toTrue, OpCode.nop); + visit(ctx.onTrue); + emit("", OpCode.jumpI, toEnd); + + emit("", toFalse, OpCode.nop); + visit(ctx.onFalse); + + emit("end target", toEnd, OpCode.nop); + return null; + } else { return regPool.withReg((target) -> { emit("", OpCode.cbr, cond, toTrue, toFalse); @@ -537,27 +642,10 @@ public class BoppiGenerator extends BoppiBaseVisitor { emit("", toFalse, OpCode.nop); emit("result", OpCode.i2i, visit(ctx.onFalse), target); + emit("end target", toEnd, OpCode.nop); }); - } - - if (ctx.onFalse == null) { - emit("", OpCode.cbr, cond, toTrue, toEnd); - emit("", toTrue, OpCode.nop); - visit(ctx.onTrue); - emit("end target", toEnd, OpCode.nop); - } else { - emit("", OpCode.cbr, cond, toTrue, toFalse); - emit("", toTrue, OpCode.nop); - visit(ctx.onTrue); - emit("", OpCode.jumpI, toEnd); - emit("", toFalse, OpCode.nop); - visit(ctx.onFalse); - emit("end target", toEnd, OpCode.nop); - } - - return null; } @Override @@ -582,7 +670,37 @@ public class BoppiGenerator extends BoppiBaseVisitor { public Reg visitInfix3(Infix3Context ctx) { Reg lhs = visit(ctx.lhs); Reg rhs = regPool.blockReg(lhs, () -> visit(ctx.rhs)); - emit(ctx.getChild(1).getText(), ops.get(ctx.op.getType()), lhs, rhs, lhs); + Type type = an.types.get(ctx.lhs); + if (type instanceof SimpleType) + emit(ctx.getChild(1).getText(), ops.get(ctx.op.getType()), lhs, rhs, lhs); + else if (type instanceof ArrayType) { + return regPool.blockReg(lhs, rhs, () -> { + ArrayType arr = (ArrayType) type; + Num elementSize = new Num(arr.getType().getSize()); + + return regPool.withReg((i, temp1) -> { + regPool.withReg((temp2) -> { + Label cond = makeLabel("aeqc"), loop = makeLabel("aeql"), end = makeLabel("aeqe"); + + emit("iterate from 0", OpCode.loadI, ZERO, i); + emit("equality true", OpCode.loadI, new Num(1), temp1); + + emit("load arr size", cond, OpCode.loadAI, lhs, OFFSET_REF_SIZE, temp2); + emit("check i in bounds", OpCode.cmp_LT, i, temp2, temp2); + emit("loop", OpCode.cbr, temp2, loop, end); + + emit("load left", loop, OpCode.loadAO, lhs, i, temp1); + emit("load right", OpCode.loadAO, rhs, i, temp2); + emit("increment i", OpCode.addI, i, elementSize, i); + + emit("compare elements", OpCode.cmp_EQ, temp1, temp2, temp1); + emit("loop or break", OpCode.cbr, temp1, cond, end); + + emit("end equality check", end, OpCode.nop); + }); + }); + }); + } return lhs; } @@ -623,13 +741,15 @@ public class BoppiGenerator extends BoppiBaseVisitor { @Override public Reg visitLiteralInteger(LiteralIntegerContext ctx) { return regPool.withReg((reg) -> { - emit(ctx.getText(), OpCode.loadI, new Num(Integer.parseInt(ctx.LITERAL10().getText())), reg); - }); - } + int val = 0; + try { + val = Integer.parseInt(ctx.LITERAL10().getText()); + } catch (NumberFormatException e) { + logger.severe(getError(ctx, "Error parsing number '%s'", ctx.LITERAL10().getText())); + } - @Override - public Reg visitParens(ParensContext ctx) { - return visit(ctx.expr()); + emit(ctx.getText(), OpCode.loadI, new Num(val), reg); + }); } @Override @@ -650,13 +770,13 @@ public class BoppiGenerator extends BoppiBaseVisitor { @Override public Reg visitProgram(ProgramContext ctx) { - emit("initialise zero register", OpCode.loadI, ZERO, ZERO_REG); + emit("initialise zero register", OpCode.loadI, ZERO, RegisterPool.ZERO); malloc(arp, an.function.get(ctx).getLocalDataSize() + ARBASESIZE); emit("construct main AR", OpCode.addI, arp, new Num(ARBASESIZE), arp); visitChildren(ctx); - cleanScope(an.function.get(ctx)); + dereferenceLocalVariables(an.function.get(ctx)); emit("deconstruct main AR", OpCode.subI, arp, new Num(ARBASESIZE), arp); regPool.withReg((temp) -> { @@ -671,11 +791,12 @@ public class BoppiGenerator extends BoppiBaseVisitor { if (ctx.variable().size() > 1) { for (VariableContext expr : ctx.variable()) { visit(expr); + if (SimpleType.BOOL.equals(an.types.get(expr)) || SimpleType.INT.equals(an.types.get(expr))) { regPool.withReg((temp) -> { emit("", OpCode.in, new Str(""), temp); - emit("var " + expr.getText(), OpCode.storeAI, temp, getVar(expr), - new Num(an.variables.get(expr).getOffset())); + Reg addr = visit(expr); + emit("save to var " + expr.getText(), OpCode.store, temp, addr); }); } else if (SimpleType.CHAR.equals(an.types.get(expr))) { // Get input until at least 1 character @@ -689,8 +810,8 @@ public class BoppiGenerator extends BoppiBaseVisitor { // Get character emit("pop whole string", continueTarget, OpCode.cpop, temp); - emit("var " + expr.getText(), OpCode.cstoreAI, temp, getVar(expr), - new Num(an.variables.get(expr).getOffset())); + Reg addr = visit(expr); + emit("save to var " + expr.getText(), OpCode.cstore, temp, addr); // Pop all remaining characters Label loopTarget = makeLabel("lcpop_l"); @@ -703,19 +824,19 @@ public class BoppiGenerator extends BoppiBaseVisitor { emit("pop remaining", stopTarget, OpCode.nop); }); } else { - emit("reading unknown type", OpCode.haltI, new Num(1)); + emit("reading unknown type", OpCode.haltI, new Num(0x72656164)); } } return null; } else { VariableContext expr = ctx.variable(0); - visit(expr); + if (SimpleType.BOOL.equals(an.types.get(expr)) || SimpleType.INT.equals(an.types.get(expr))) { return regPool.withReg((result) -> { emit("", OpCode.in, new Str(""), result); - emit("var " + expr.getText(), OpCode.storeAI, result, getVar(expr), - new Num(an.variables.get(expr).getOffset())); + Reg addr = visit(expr); + emit("save to var " + expr.getText(), OpCode.store, result, addr); }); } else if (SimpleType.CHAR.equals(an.types.get(expr))) { return regPool.withReg((count, result) -> { @@ -729,8 +850,8 @@ public class BoppiGenerator extends BoppiBaseVisitor { // Get character emit("pop whole string", continueTarget, OpCode.cpop, result); - emit("var " + expr.getText(), OpCode.cstoreAI, result, getVar(expr), - new Num(an.variables.get(expr).getOffset())); + Reg addr = visit(expr); + emit("save to var " + expr.getText(), OpCode.cstore, result, addr); // Pop all remaining characters Label loopTarget = makeLabel("lcpop_l"); @@ -745,37 +866,73 @@ public class BoppiGenerator extends BoppiBaseVisitor { }); }); } else { - emit("reading unknown type", OpCode.haltI, new Num(1)); + emit("reading unknown type", OpCode.haltI, new Num(0x72656164)); return null; } } } @Override - public Reg visitVariableExpr(VariableExprContext ctx) { - Type type = an.types.get(ctx); - Num offset = new Num(an.variables.get(ctx.variable()).getOffset()); + public Reg visitVariableArray(VariableArrayContext ctx) { + ArrayType type = (ArrayType) an.types.get(ctx.variable()); + Reg addr = visit(ctx.variable()); - return regPool.withReg((result) -> { - if (SimpleType.CHAR.equals(type)) - emit("get " + ctx.variable().getText(), OpCode.cloadAI, getVar(ctx.variable()), offset, result); - else - emit("get " + ctx.variable().getText(), OpCode.loadAI, getVar(ctx.variable()), offset, result); + emit("get array object", OpCode.load, addr, addr); - if (type instanceof ReferenceType) { + regPool.blockReg(addr, () -> { + Reg offset = visit(ctx.expr()); + regPool.blockReg(offset, () -> { regPool.withReg((r1, r2) -> { - String comment = "add new ref for " + ctx.variable().getText(); + Label outOfBounds = makeLabel("oob"), inBounds = makeLabel("nob"); - incRef(r2, result); + emit("check array index", OpCode.loadAI, addr, OFFSET_REF_SIZE, r1); + emit("check array index", OpCode.divI, r1, new Num(type.getType().getSize()), r1); + emit("check array index", OpCode.cmp_LT, offset, r1, r1); + emit("check array index", OpCode.cmp_GE, offset, RegisterPool.ZERO, r2); + emit("check array index", OpCode.and, r1, r2, r2); + emit("check array index", OpCode.cbr, r2, inBounds, outOfBounds); + emit("array index out of bounds", outOfBounds, OpCode.haltI, new Num(0x616f6f62)); - if (type instanceof FunctionType) { - emit(comment, OpCode.loadAI, result, OFFSET_FUNCREF_ARP, r1); - - incARReferences(r2, r1); - } + emit("multiply index by size", inBounds, OpCode.multI, offset, new Num(type.getType().getSize()), + offset); + emit("get array index address", OpCode.add, addr, offset, addr); }); + }); + }); + + return addr; + } + + @Override + public Reg visitVariableProperty(VariablePropertyContext ctx) { + Type innerType = an.types.get(ctx.variable()); + Reg addr = visit(ctx.variable()); + + if (innerType instanceof ArrayType) { + emit("get size", OpCode.loadAI, addr, OFFSET_REF_SIZE, addr); + return addr; + } else { + return addr; + } + } + + @Override + public Reg visitVariableSimple(VariableSimpleContext ctx) { + return regPool.withReg((result) -> { + Variable var = an.variables.get(ctx); + Reg ar = arp; + + int functionDepth = an.function.get(ctx).getFunctionDepth(); + + if (var.getDepth() < functionDepth) { + emit("travelling ALs", OpCode.i2i, ar, tempArp); + for (int i = var.getDepth(); i < functionDepth; i++) + emit(StringUtils.repeat(' ', i) + "\\", OpCode.loadAI, tempArp, OFFSET_AL, tempArp); + + ar = tempArp; } + emit("add offset", OpCode.addI, ar, new Num(var.getOffset()), result); }); } @@ -815,7 +972,7 @@ public class BoppiGenerator extends BoppiBaseVisitor { emit("print character", OpCode.cout, new Str("")); }); } else { - emit("writing unknown type", OpCode.haltI, new Num(1)); + emit("writing unknown type", OpCode.haltI, new Num(0x77726974)); } if (!expr.equals(ctx.expr(ctx.expr().size() - 1))) @@ -835,7 +992,7 @@ public class BoppiGenerator extends BoppiBaseVisitor { emit("", OpCode.out, new Str(""), result); return result; } else if (SimpleType.CHAR.equals(type)) { - regPool.withReg(result, (temp) -> { + regPool.withReg((temp) -> { emit("push character", OpCode.cpush, result); emit("load 1", OpCode.loadI, new Num(1), temp); emit("push 1", OpCode.push, temp); @@ -843,7 +1000,7 @@ public class BoppiGenerator extends BoppiBaseVisitor { }); return result; } else { - emit("writing unknown type", OpCode.haltI, new Num(1)); + emit("writing unknown type", OpCode.haltI, new Num(0x77726974)); return null; } }); diff --git a/src/pp/s1184725/boppi/antlr/.gitignore b/src/pp/s1184725/boppi/antlr/.gitignore index 0d2b878..b7234d8 100644 --- a/src/pp/s1184725/boppi/antlr/.gitignore +++ b/src/pp/s1184725/boppi/antlr/.gitignore @@ -3,4 +3,7 @@ /BoppiLexer.java /BoppiLexer.tokens /BoppiParser.java +/BoppiTokens.java +/BoppiTokens.tokens /BoppiVisitor.java +/BoppiTokens.g4 diff --git a/src/pp/s1184725/boppi/antlr/Boppi.g4 b/src/pp/s1184725/boppi/antlr/Boppi.g4 index b2130d9..b8c9894 100644 --- a/src/pp/s1184725/boppi/antlr/Boppi.g4 +++ b/src/pp/s1184725/boppi/antlr/Boppi.g4 @@ -1,4 +1,5 @@ grammar Boppi; +import BoppiTokens; program: expr EOF; @@ -21,7 +22,7 @@ singleExpr | lhs=singleExpr OR rhs=singleExpr #infix5 | DECLARE type IDENTIFIER #declare | variable ASSIGN singleExpr #assign - | variable #variableExpr + | variable #getVariable | LITERAL10 #literalInteger | CHAR #literalCharacter | (TRUE|FALSE) #literalBoolean @@ -30,58 +31,15 @@ singleExpr ; type - : type ARROW type #typeFunction + : ARROPEN LITERAL10 ARRCLOSE type #typeArray + | type ARROW type #typeFunction | staticType=(INTTYPE | BOOLTYPE | CHARTYPE) #typeSimple - | PAROPEN (type (LISTDELIM type )*)? PARCLOSE #typeTuple + | PAROPEN (type (LISTDELIM type)*)? PARCLOSE #typeTuple | variable #typeVariable ; -variable: IDENTIFIER; - -PAROPEN: '('; -PARCLOSE: ')'; -BRAOPEN: '{'; -BRACLOSE: '}'; -IN: 'read'; -OUT: 'print'; -IFOPEN: 'if'; -IFTRUE: 'then'; -IFFALSE: 'else'; -IFCLOSE: 'fi'; -WHILEOPEN: 'while'; -WHILETRUE: 'do'; -WHILECLOSE: 'od'; - -PLUS: '+'; -MINUS: '-'; -NOT: '!'; -MULTIPLY: '*'; -DIVIDE: '/'; -LEQ: '<='; -GTE: '>='; -NEQ: '<>'; -EQ: '=='; -LT: '<'; -GT: '>'; -AND: '&&'; -OR: '||'; - -DECLARE: 'var'; -FUNCTION: 'function'; -INTTYPE: 'int'; -BOOLTYPE: 'bool'; -CHARTYPE: 'char'; -ARROW: '->'; -ASSIGN: ':='; -COMPOUND: ';'; -LISTDELIM: ','; - -CHAR: '\'' . '\''; -TRUE: 'true'; -FALSE: 'false'; -IDENTIFIER: [A-Za-z_][A-Za-z0-9_]*; -LITERAL10: ('0' | [1-9] [0-9]*); - -WHITESPACE: [\t\r\n ] -> skip; -LINECOMMENT: '//' ~[\r\n]* -> skip; -BLOCKCOMMENT: '/*' .*? '*/' -> skip; +variable + : variable ARROPEN expr ARRCLOSE #variableArray + | variable PROP IDENTIFIER #variableProperty + | IDENTIFIER #variableSimple + ; diff --git a/src/pp/s1184725/boppi/test/ArrayTest.java b/src/pp/s1184725/boppi/test/ArrayTest.java new file mode 100644 index 0000000..fc6d6b4 --- /dev/null +++ b/src/pp/s1184725/boppi/test/ArrayTest.java @@ -0,0 +1,95 @@ +package pp.s1184725.boppi.test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; +import org.junit.Test; + +/** + * Test cases for fixed-size arrays. + * + * @author Frank Wibbelink + */ +public class ArrayTest { + + /** + * Correct array parsing + */ + @Test + public void correctArrayParsing() { + BoppiTests.parseFile("simpleArray.boppi"); + BoppiTests.log.forEach((l)->System.out.println(l.getMessage())); + assertThat(BoppiTests.log, empty()); + } + + /** + * Arrays with syntax errors + */ + @Test + public void wrongArrayParsing() { + BoppiTests.parseString("var []int arr; 0"); + assertThat(BoppiTests.log, not(empty())); + + BoppiTests.parseString("var int a; a := 5; var [5][a]int arr; 0"); + assertThat(BoppiTests.log, not(empty())); + + BoppiTests.parseString("var [4]int arr; (arr)[5]"); + assertThat(BoppiTests.log, hasSize(1)); + } + + /** + * Correct array checking + */ + @Test + public void correctArrayChecking() { + BoppiTests.checkFile("simpleArray.boppi"); + assertThat(BoppiTests.log, is(empty())); + } + + /** + * Arrays with type errors + */ + @Test + public void wrongArrayChecking() { + BoppiTests.checkString("var int a; a[5]"); + assertThat(BoppiTests.log, not(empty())); + + BoppiTests.checkString("var [5]int arr; arr := 5"); + assertThat(BoppiTests.log, not(empty())); + + BoppiTests.checkString("var [5]int arr; arr[4][2] := 5"); + assertThat(BoppiTests.log, not(empty())); + + BoppiTests.checkString("var int a; var int[5] arr; a := arr"); + assertThat(BoppiTests.log, not(empty())); + } + + /** + * Correct array generation + */ + @Test + public void correctArrayGeneration() { + BoppiTests.compileAndRunFile("simpleArray.boppi"); + assertThat(BoppiTests.vm.getInterrupt(), is(0)); + assertThat(BoppiTests.log, is(empty())); + assertThat(BoppiTests.out, is(arrayContaining("9", "H", "a", "Y"))); + + BoppiTests.compileAndRunFile("complexArray.boppi"); + assertThat(BoppiTests.vm.getInterrupt(), is(0)); + assertThat(BoppiTests.log, is(empty())); + assertThat(BoppiTests.out, is(arrayContaining("1337", "13", "2", "15"))); + + } + + /** + * Out of bounds checking + */ + @Test + public void wrongArrayGeneration() { + BoppiTests.compileAndRunString("var [10]int arr; arr[10] := 5"); + assertThat(BoppiTests.vm.getInterrupt(), is(not(0))); + + BoppiTests.compileAndRunString("var [10]int arr; arr[-1]"); + assertThat(BoppiTests.vm.getInterrupt(), is(not(0))); + } + +} diff --git a/src/pp/s1184725/boppi/test/BoppiTests.java b/src/pp/s1184725/boppi/test/BoppiTests.java index 54c217c..3fb0e94 100644 --- a/src/pp/s1184725/boppi/test/BoppiTests.java +++ b/src/pp/s1184725/boppi/test/BoppiTests.java @@ -11,6 +11,7 @@ import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; import pp.iloc.Simulator; +import pp.iloc.eval.Machine; import pp.iloc.model.Program; import pp.s1184725.boppi.*; @@ -22,7 +23,8 @@ import pp.s1184725.boppi.*; * @author Frank Wibbelink */ @RunWith(Suite.class) -@SuiteClasses({ ExpressionTest.class, SimpleVariableTest.class, ConditionalTest.class, SimpleFunctionTest.class, ClosureTest.class }) +@SuiteClasses({ ExpressionTest.class, SimpleVariableTest.class, ConditionalTest.class, SimpleFunctionTest.class, + ClosureTest.class, ArrayTest.class }) public class BoppiTests { /** * The path for test programs @@ -41,10 +43,15 @@ public class BoppiTests { public static List log; /** - * Output of last program split by line breaks + * Output of last {@link #compileAndRun} call split by line breaks */ public static String[] out; + /** + * Machine used for the last {@link #compileAndRun} call. + */ + public static Machine vm; + /** * The level of error reporting to use for the tests. Can be changed to * include finer tests. @@ -158,6 +165,35 @@ public class BoppiTests { System.out.println(program.prettyPrint()); out = ToolChain.execute(program, logger, input).split("\n"); + vm = ToolChain.machine; + } + + /** + * Compiles and runs a program in debug mode. This writes the ILOC code, the + * simulation, program output and all logged messages to the standard + * output. + * + * @param code + * the code to run + * @param input + * lines of input for the program + */ + public static void debugRunString(String code, String... input) { + debugRun(ToolChain.getCharStream(code), String.join("\n", input) + "\n"); + } + + /** + * Compiles and runs a program in debug mode. This writes the ILOC code, the + * simulation, program output and all logged messages to the standard + * output. + * + * @param file + * the file name + * @param input + * lines of input for the program + */ + public static void debugRunFile(String file, String... input) { + debugRun(ToolChain.getCharStream(TEST_PROGRAM_LOCATION.resolve(file)), String.join("\n", input) + "\n"); } /** @@ -183,6 +219,10 @@ public class BoppiTests { System.out.println(program.prettyPrint()); out = ToolChain.execute(program, logger, in).split("\n"); + vm = ToolChain.machine; + + for (String line : out) + System.out.println(">>> "+line); log.forEach((entry) -> System.out.println(entry.getMessage())); Simulator.DEBUG = false; diff --git a/src/pp/s1184725/boppi/test/ClosureTest.java b/src/pp/s1184725/boppi/test/ClosureTest.java index db11b54..ced835e 100644 --- a/src/pp/s1184725/boppi/test/ClosureTest.java +++ b/src/pp/s1184725/boppi/test/ClosureTest.java @@ -4,8 +4,6 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.*; import org.junit.Test; -import pp.s1184725.boppi.ToolChain; - /** * Tests for function closures, mostly testing runtime correctness and garbage collection. * @@ -43,17 +41,17 @@ public class ClosureTest { @Test public void correctClosureGeneration() { BoppiTests.compileAndRunFile("closure1.boppi"); - assertThat(ToolChain.machine.getInterrupt(), is(0)); + assertThat(BoppiTests.vm.getInterrupt(), is(0)); assertThat(BoppiTests.log, is(empty())); assertThat(BoppiTests.out, is(arrayContaining("8", "9"))); BoppiTests.compileAndRunFile("closure2.boppi"); - assertThat(ToolChain.machine.getInterrupt(), is(0)); + assertThat(BoppiTests.vm.getInterrupt(), is(0)); assertThat(BoppiTests.log, is(empty())); assertThat(BoppiTests.out, is(arrayContaining("8", "7", "15", "2"))); BoppiTests.compileAndRunFile("closure3.boppi"); - assertThat(ToolChain.machine.getInterrupt(), is(0)); + assertThat(BoppiTests.vm.getInterrupt(), is(0)); assertThat(BoppiTests.log, is(empty())); assertThat(BoppiTests.out, is(arrayContaining("7", "15", "2"))); } @@ -64,13 +62,13 @@ public class ClosureTest { @Test public void correctClosureCleanup() { BoppiTests.compileAndRunFile("closure1.boppi"); - assertThat(ToolChain.machine.load(0), is(4)); + assertThat(BoppiTests.vm.load(0), is(4)); BoppiTests.compileAndRunFile("closure2.boppi"); - assertThat(ToolChain.machine.load(0), is(4)); + assertThat(BoppiTests.vm.load(0), is(4)); BoppiTests.compileAndRunFile("closure3.boppi"); - assertThat(ToolChain.machine.load(0), is(4)); + assertThat(BoppiTests.vm.load(0), is(4)); } diff --git a/src/pp/s1184725/boppi/test/programs/complexArray.boppi b/src/pp/s1184725/boppi/test/programs/complexArray.boppi new file mode 100644 index 0000000..74b4f94 --- /dev/null +++ b/src/pp/s1184725/boppi/test/programs/complexArray.boppi @@ -0,0 +1,84 @@ +var int i; + + +// multi-dimensional array +// like multi-dimensional arrays in most languages, they are arrays of (n-1)-dimensional array references, so they won't be initialised by default +var [2]int arr1_1; +var arr1_1 arr1_2; + +var [8][2]int arr2_1; +var arr2_1 arr2_2; + +var [3][8][2]int arr3; + +arr3[0] := arr2_1; +arr2_1[0] := arr1_1; + +arr3[2] := arr2_2; +arr2_2[7] := arr1_2; + +arr3[0][0][0] := arr3[2][7][1] := 1337; + +print(arr3[2][7][1]); + + + +// array passing +var [2][4]int target; + +function [4]int populate(int start) { + var [4]int arr; + var int i; i := 0; + while i < 4 do + arr[i] := start+i; + i := i+1; + od; + arr +}; + +i := 0; +while i < 2 do + target[i] := populate(10*i); + i := i+1; +od; + +print(target[1][3]); + + +var target target2; +target2 := target; + +var [4]int firstRow; +firstRow := target2[0]; + +print(firstRow[2]); + + + +// function array +var [10]int arr10; + +function arr10 mapArray((int)->int f, arr10 array) { + var arr10 newArr; + var int i; i := 0; + while i < 10 do + newArr[i] := f(array[i]); + i := i+1; + od; + newArr +}; + +function int increment(int a) a+1; + +var arr10 myArray; + +i := 0; +while i < 10 do + myArray[i] := i*i+10; + i := i+1; +od; + +myArray := mapArray(increment, myArray); + +print(myArray[2]); + diff --git a/src/pp/s1184725/boppi/test/programs/simpleArray.boppi b/src/pp/s1184725/boppi/test/programs/simpleArray.boppi new file mode 100644 index 0000000..c97abc7 --- /dev/null +++ b/src/pp/s1184725/boppi/test/programs/simpleArray.boppi @@ -0,0 +1,28 @@ +var [5]int arr; + +var int i; i := 0; + +while (i < 5) do + arr[i] := i*i; + i := i+1; +od; + +print(arr[3]); + + + +var [2]char arr2; +arr2[0] := 'H'; +arr2[1] := 'a'; + +print(arr2[0], arr2[1]); + +var [2]char arr3; +arr3[1] := 'a'; +arr3[0] := 'H'; + +if arr2 == arr3 then + print('Y') +else + print('N') +fi; diff --git a/src/pp/s1184725/boppi/type/ArrayType.java b/src/pp/s1184725/boppi/type/ArrayType.java new file mode 100644 index 0000000..0c75f8d --- /dev/null +++ b/src/pp/s1184725/boppi/type/ArrayType.java @@ -0,0 +1,60 @@ +package pp.s1184725.boppi.type; + +import pp.iloc.eval.Machine; + +/** + * A vector type of fixed size. Takes a length and a type parameter. + * + * @author Frank Wibbelink + */ +public class ArrayType implements ReferenceType { + private int count; + private Type elType; + + /** + * Creates a new array type with the given number of elements and their + * type. + * + * @param count + * the number of elements + * @param type + * the type of elements + */ + public ArrayType(int count, Type type) { + this.count = count; + this.elType = type; + } + + @Override + public int getReferenceSize() { + return count * elType.getSize(); + } + + @Override + public int getSize() { + return Machine.INT_SIZE; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ArrayType)) + return false; + + ArrayType other = (ArrayType) obj; + return count == other.count && elType.equals(other.elType); + } + + @Override + public String toString() { + return elType.toString()+"["+count+"]"; + } + + /** + * Returns the type of the elements in this array. + * + * @return the type of the elements + */ + public Type getType() { + return elType; + } +} diff --git a/src/pp/s1184725/boppi/type/FunctionType.java b/src/pp/s1184725/boppi/type/FunctionType.java index 41db290..636e086 100644 --- a/src/pp/s1184725/boppi/type/FunctionType.java +++ b/src/pp/s1184725/boppi/type/FunctionType.java @@ -27,6 +27,11 @@ public class FunctionType implements ReferenceType { public int getSize() { return Machine.INT_SIZE; } + + @Override + public int getReferenceSize() { + return 3*Machine.INT_SIZE; + } @Override public boolean equals(Object obj) { diff --git a/src/pp/s1184725/boppi/type/ReferenceType.java b/src/pp/s1184725/boppi/type/ReferenceType.java index 7331f9e..fd0e6d3 100644 --- a/src/pp/s1184725/boppi/type/ReferenceType.java +++ b/src/pp/s1184725/boppi/type/ReferenceType.java @@ -7,4 +7,9 @@ package pp.s1184725.boppi.type; */ public interface ReferenceType extends Type { + /** + * Returns the size of the object this type references. + * @return the size (in bytes) of the referenced object + */ + public int getReferenceSize(); } diff --git a/src/pp/s1184725/boppi/util/RegisterPool.java b/src/pp/s1184725/boppi/util/RegisterPool.java index a3a72ce..58ebf24 100644 --- a/src/pp/s1184725/boppi/util/RegisterPool.java +++ b/src/pp/s1184725/boppi/util/RegisterPool.java @@ -1,6 +1,5 @@ package pp.s1184725.boppi.util; -import java.util.ArrayList; import java.util.*; import java.util.function.*; import java.util.logging.Logger; @@ -16,38 +15,23 @@ public class RegisterPool { */ public static int RECOMMENDED_REGISTER_COUNT = 10; + /** + * Register that must have a value of 0. + */ + public static final Reg ZERO = new Reg("r_nul"); + private Logger logger; private List regFree, regInUse; - /** - * Creates a new register pool - * - * @param logger - * the logger to use - */ - public RegisterPool(Logger logger) { - regFree = new ArrayList<>(); - regInUse = new ArrayList<>(); - this.logger = logger; - } - - private Reg makeReg() { - return makeRegThatIsNot(null); - } - - private Reg makeRegThatIsNot(Reg r1) { - Reg reg = regFree.stream().filter((r2) -> !r2.equals(r1)).findAny().orElseGet(() -> { - if (regInUse.size() == RECOMMENDED_REGISTER_COUNT + 1) - logger.warning(String.format("Using more than %d registers. Consider rebalancing your expressions.", - RECOMMENDED_REGISTER_COUNT)); - - return new Reg("__" + (regInUse.size() + 1)); - }); - - regFree.remove(reg); - regInUse.add(reg); - - return reg; + private void blockReg(Reg reg) { + if (reg == null) + logger.severe("INTERNAL: cannot reserve null register."); + else if (!regFree.contains(reg)) { + logger.severe(String.format("INTERNAL: cannot reserve register %s because it is in use.", reg.getName())); + } else { + regFree.remove(reg); + regInUse.add(reg); + } } private void freeReg(Reg reg) { @@ -61,15 +45,35 @@ public class RegisterPool { } } - private void blockReg(Reg reg) { - if (reg == null) - logger.severe("INTERNAL: cannot reserve null register."); - else if (!regFree.contains(reg)) { - logger.severe(String.format("INTERNAL: cannot reserve register %s because it is in use.", reg.getName())); - } else { - regFree.remove(reg); - regInUse.add(reg); - } + private Reg makeReg() { + return makeRegThatIsNot(null); + } + + private Reg makeRegThatIsNot(Reg r1) { + Reg reg = regFree.parallelStream().filter((r2) -> !r2.equals(r1)).findAny().orElseGet(() -> { + if (regInUse.size() == RECOMMENDED_REGISTER_COUNT + 1) + logger.warning(String.format("Using more than %d registers. Consider rebalancing your expressions.", + RECOMMENDED_REGISTER_COUNT)); + + return new Reg("__" + (regInUse.size() + 1)); + }); + + regFree.remove(reg); + regInUse.add(reg); + + return reg; + } + + /** + * Creates a new register pool + * + * @param logger + * the logger to use + */ + public RegisterPool(Logger logger) { + regFree = new ArrayList<>(); + regInUse = new ArrayList<>(); + this.logger = logger; } /** @@ -81,22 +85,6 @@ public class RegisterPool { return regInUse; } - /** - * Runs some code with a single register allocated. - * - * @param - * the return type of the code - * @param code - * the code to run - * @return the return value of the code - */ - public T withReg(Function code) { - Reg r = makeReg(); - T result = code.apply(r); - freeReg(r); - return result; - } - /** * Runs some code with a single register allocated. * @@ -119,46 +107,7 @@ public class RegisterPool { * @return the second register used in the code */ public Reg withReg(BiConsumer code) { - Reg r1 = makeReg(); - Reg r2 = makeReg(); - code.accept(r1, r2); - freeReg(r1); - freeReg(r2); - return r2; - } - - /** - * Runs some code with one register given and one allocated. - * - * @param r1 - * the first register to use - * @param code - * the code to run - * @return the register used in the code - */ - public Reg withReg(Reg r1, Consumer code) { - Reg r = makeRegThatIsNot(r1); - code.accept(r); - freeReg(r); - return r; - } - - /** - * Runs some code with one register given and two allocated. - * - * @param r1 - * the first register to use - * @param code - * the code to run - * @return the second register used in the code - */ - public Reg withReg(Reg r1, BiConsumer code) { - Reg r2 = makeRegThatIsNot(r1); - Reg r3 = makeRegThatIsNot(r1); - code.accept(r2, r3); - freeReg(r2); - freeReg(r3); - return r3; + return withReg((r2) -> withReg((r1) -> code.accept(r1, r2))); } /** @@ -193,4 +142,21 @@ public class RegisterPool { freeReg(r1); } + /** + * Blocks two registers while running some code. + * + * @param + * the return type of the code + * @param r1 + * the first register to reserve + * @param r2 + * the second register to reserve + * @param code + * the code to run + * @return the return value of the code + */ + public T blockReg(Reg r1, Reg r2, Supplier code) { + return blockReg(r1, () -> blockReg(r2, code)); + } + }