implemented arrays, split antlr file into grammar and lexer

This commit is contained in:
User 2017-07-30 14:53:53 +02:00
parent 5a48e93674
commit f6cabe0ccb
15 changed files with 770 additions and 322 deletions

View File

@ -0,0 +1,2 @@
eclipse.preferences.version=1
encoding/<project>=UTF-8

View File

@ -311,15 +311,15 @@ public class Simulator {
break; break;
case halt: case halt:
this.vm.setInterrupt(c.reg(0)); this.vm.setInterrupt(c.reg(0));
System.err.printf(
"Program halted with code %d at line %d.%n", if (DEBUG)
c.num(0), o.getLine()); System.err.printf("Program halted with code %d at line %d.", c.reg(0), o.getLine());
break; break;
case haltI: case haltI:
this.vm.setInterrupt(c.num(0)); this.vm.setInterrupt(c.num(0));
System.err.printf(
"Program halted with code %d at line %d.%n", if (DEBUG)
c.num(0), o.getLine()); System.err.printf("Program halted with code %d at line %d.", c.num(0), o.getLine());
break; break;
default: default:
System.err.printf( System.err.printf(

View File

@ -168,6 +168,11 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
return ctx.singleExpr().stream().map(this::visit).reduce((__, snd) -> snd).get(); return ctx.singleExpr().stream().map(this::visit).reduce((__, snd) -> snd).get();
} }
@Override
public Type visitGetVariable(GetVariableContext ctx) {
return visit(ctx.variable());
}
@Override @Override
public Type visitIf(IfContext ctx) { public Type visitIf(IfContext ctx) {
return an.symbols.withScope(() -> { return an.symbols.withScope(() -> {
@ -287,6 +292,19 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
} }
} }
@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 @Override
public Type visitTypeFunction(TypeFunctionContext ctx) { public Type visitTypeFunction(TypeFunctionContext ctx) {
return new FunctionType(visit(ctx.type(1)), visit(ctx.type(0))); return new FunctionType(visit(ctx.type(1)), visit(ctx.type(0)));
@ -308,7 +326,42 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
} }
@Override @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 { try {
Variable<Type> var = an.symbols.get(ctx.getText()); Variable<Type> var = an.symbols.get(ctx.getText());
@ -322,14 +375,8 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
return var.getType(); return var.getType();
} catch (SymbolTableException e) { } catch (SymbolTableException e) {
log.severe(getError(ctx, e.getMessage())); log.severe(getError(ctx, e.getMessage()));
return SimpleType.VOID;
} }
return SimpleType.VOID;
}
@Override
public Type visitVariableExpr(VariableExprContext ctx) {
return visit(ctx.variable());
} }
@Override @Override

View File

@ -22,12 +22,12 @@ import pp.s1184725.boppi.util.RegisterPool;
* @author Frank Wibbelink * @author Frank Wibbelink
*/ */
public class BoppiGenerator extends BoppiBaseVisitor<Reg> { public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
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 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), 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_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 Program prog;
private Annotations an; private Annotations an;
@ -89,21 +89,6 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
this.logger = logger; this.logger = logger;
} }
private Reg getVar(VariableContext node) {
Variable<Type> 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 * Constructs an operation from the parameters and adds it to the program
* under construction. * under construction.
@ -135,7 +120,7 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
String comment = "AR incRef"; String comment = "AR incRef";
Label loop = makeLabel("aril"), done = makeLabel("arid"); Label loop = makeLabel("aril"), done = makeLabel("arid");
emit(comment, OpCode.i2i, ar, tempArp); 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, OpCode.cbr, temp, loop, done);
emit(comment, loop, OpCode.loadI, new Num(prog.size() + 5), temp); emit(comment, loop, OpCode.loadI, new Num(prog.size() + 5), temp);
emit(comment, OpCode.push, temp); emit(comment, OpCode.push, temp);
@ -144,7 +129,7 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
emit(comment, OpCode.jumpI, new Label("memaddref")); emit(comment, OpCode.jumpI, new Label("memaddref"));
emit(comment, OpCode.loadAI, tempArp, OFFSET_AL, tempArp); 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, OpCode.cbr, temp, loop, done);
emit(comment, done, OpCode.nop); emit(comment, done, OpCode.nop);
} }
@ -161,7 +146,7 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
String comment = "AR decRef"; String comment = "AR decRef";
Label loop = makeLabel("ardl"), done = makeLabel("ardd"); Label loop = makeLabel("ardl"), done = makeLabel("ardd");
emit(comment, OpCode.i2i, ar, tempArp); 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, OpCode.cbr, temp, loop, done);
emit(comment, loop, OpCode.loadI, new Num(prog.size() + 5), temp); emit(comment, loop, OpCode.loadI, new Num(prog.size() + 5), temp);
emit(comment, OpCode.push, temp); emit(comment, OpCode.push, temp);
@ -170,7 +155,7 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
emit(comment, OpCode.jumpI, new Label("memfree")); emit(comment, OpCode.jumpI, new Label("memfree"));
emit(comment, OpCode.loadAI, tempArp, OFFSET_AL, tempArp); 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, OpCode.cbr, temp, loop, done);
emit(comment, done, OpCode.nop); emit(comment, done, OpCode.nop);
} }
@ -256,30 +241,64 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
return String.format("Line %d:%d - %s", line, column, String.format(message, args)); 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) { private Label makeLabel(String prefix) {
return new Label(prefix + (labels++)); 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) { 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"; String comment = "add new reference";
incRef(temp, addr); incRef(temp, addr);
if (type instanceof FunctionType) { 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) { 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) -> { regPool.withReg((temp) -> {
Label isNull = makeLabel("ynul"), notNull = makeLabel("nnul"); Label isNull = makeLabel("ynul"), notNull = makeLabel("nnul");
String comment = "remove old reference"; 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, OpCode.cbr, temp, isNull, notNull);
emit(comment, notNull, OpCode.nop); emit(comment, notNull, OpCode.nop);
@ -296,7 +315,14 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
}); });
} }
private void cleanScope(FunctionScope<Type> 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<Type> scope) {
regPool.withReg((addr, ft) -> { regPool.withReg((addr, ft) -> {
for (Variable<Type> var : scope.getVars()) { for (Variable<Type> var : scope.getVars()) {
Type type = var.getType(); Type type = var.getType();
@ -306,7 +332,7 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
String comment = "remove reference"; String comment = "remove reference";
emit(comment + " get var", OpCode.loadAI, arp, new Num(var.getOffset()), addr); 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, OpCode.cbr, ft, isNull, notNull);
emit(comment, notNull, OpCode.nop); emit(comment, notNull, OpCode.nop);
@ -326,13 +352,41 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
}); });
} }
/**
* {@inheritDoc}
* <p>
* 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}
* <p>
* This override logs any exceptions to the {@link Logger} attached to this
* generator.
*/
@Override @Override
public Reg visit(ParseTree tree) { public Reg visit(ParseTree tree) {
try { try {
return super.visit(tree); return super.visit(tree);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace();
System.out.println(prog.prettyPrint());
if (tree instanceof ParserRuleContext) if (tree instanceof ParserRuleContext)
logger.severe(getError((ParserRuleContext) tree, "%s: %s", e.getClass().getName(), e.getMessage())); logger.severe(getError((ParserRuleContext) tree, "%s: %s", e.getClass().getName(), e.getMessage()));
else else
@ -343,33 +397,31 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
@Override @Override
public Reg visitAssign(AssignContext ctx) { public Reg visitAssign(AssignContext ctx) {
Reg result = visit(ctx.singleExpr()); Reg result = visit(ctx.singleExpr());
regPool.blockReg(result, () -> { regPool.blockReg(result, () -> {
Type type = an.types.get(ctx); Type type = an.types.get(ctx);
Reg addr = visit(ctx.variable());
if (type instanceof ReferenceType) { if (type instanceof ReferenceType) {
regPool.withReg((addr) -> { regPool.blockReg(addr, () -> {
emit("remove old ref for " + ctx.variable().getText(), OpCode.loadAI, getVar(ctx.variable()), regPool.withReg((temp) -> {
new Num(an.variables.get(ctx.variable()).getOffset()), addr); emit("load reference", OpCode.load, addr, temp);
decrementReference(an.types.get(ctx), addr); decrementReference(an.types.get(ctx), temp);
});
}); });
} }
if (SimpleType.CHAR.equals(type)) OpCode op = (SimpleType.CHAR.equals(type)) ? OpCode.cstore : OpCode.store;
emit("to " + ctx.variable().getText(), OpCode.cstoreAI, result, getVar(ctx.variable()),
new Num(an.variables.get(ctx.variable()).getOffset())); emit("to " + ctx.variable().getText(), op, result, addr);
else
emit("to " + ctx.variable().getText(), OpCode.storeAI, result, getVar(ctx.variable()),
new Num(an.variables.get(ctx.variable()).getOffset()));
if (type instanceof ReferenceType) { if (type instanceof ReferenceType) {
regPool.withReg((addr) -> { regPool.blockReg(addr, () -> {
emit("add new ref for " + ctx.variable().getText(), OpCode.loadAI, getVar(ctx.variable()), regPool.withReg((temp) -> {
new Num(an.variables.get(ctx.variable()).getOffset()), addr); emit("load reference", OpCode.load, addr, temp);
incrementReference(an.types.get(ctx), temp);
incrementReference(an.types.get(ctx), addr); });
}); });
} }
}); });
@ -377,49 +429,43 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
return result; return result;
} }
@Override
public Reg visitBlock(BlockContext ctx) {
return visit(ctx.expr());
}
@Override @Override
public Reg visitCall(CallContext ctx) { public Reg visitCall(CallContext ctx) {
String base = "call " + ctx.variable().getText() + " - "; String base = "call " + ctx.variable().getText() + " - ";
Variable<Type> function = an.variables.get(ctx.variable()); Variable<Type> function = an.variables.get(ctx.variable());
TupleType parameters = (TupleType) ((FunctionType) function.getType()).getParameter(); TupleType parameters = (TupleType) ((FunctionType) function.getType()).getParameter();
visit(ctx.variable()); Reg ar = regPool.withReg((tempAR) -> {
Reg result = regPool.withReg((ar) -> {
regPool.withReg((rt) -> { regPool.withReg((rt) -> {
emit(base + "load function reference", OpCode.loadAI, getVar(ctx.variable()), Reg addr = visit(ctx.variable());
new Num(an.variables.get(ctx.variable()).getOffset()), rt); emit(base + "load function reference", OpCode.load, addr, rt);
emit(base + "load AR size", OpCode.loadAI, rt, OFFSET_FUNCREF_ARSIZE, 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++) { for (int i = 0; i < ctx.expr().size(); i++) {
Reg exprReg = visit(ctx.expr(i)); Reg exprReg = visit(ctx.expr(i));
if (SimpleType.CHAR.equals(parameters.get(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 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<Reg> inUse = new Stack<>(); Stack<Reg> inUse = new Stack<>();
for (Reg reg : regPool.getInUse()) { for (Reg reg : regPool.getInUse()) {
if (!reg.equals(ar)) { inUse.push(reg);
inUse.push(reg); emit(base + "register save " + reg.getName(), OpCode.push, reg);
emit(base + "register save " + reg.getName(), OpCode.push, reg); }
}
}
Reg res = regPool.withReg((r1, r2) -> { regPool.blockReg(ar, () -> {
emit(base + "load function reference", OpCode.loadAI, getVar(ctx.variable()), regPool.withReg((r1, r2) -> {
new Num(an.variables.get(ctx.variable()).getOffset()), 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 + "link caller ARP", OpCode.storeAI, arp, ar, OFFSET_ARP);
emit(base + "load AL", OpCode.loadAI, r2, OFFSET_FUNCREF_ARP, r1); emit(base + "load AL", OpCode.loadAI, r2, OFFSET_FUNCREF_ARP, r1);
emit(base + "link AL", OpCode.storeAI, r1, ar, OFFSET_AL); emit(base + "link AL", OpCode.storeAI, r1, ar, OFFSET_AL);
@ -433,26 +479,45 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
emit(base + "load target address", OpCode.loadAI, r2, OFFSET_FUNCREF_ADDR, r1); emit(base + "load target address", OpCode.loadAI, r2, OFFSET_FUNCREF_ADDR, r1);
emit(base + "execute", OpCode.jump, 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); 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<Type> 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<Reg> {
*/ */
@Override @Override
public Reg visitDeclareFunction(DeclareFunctionContext ctx) { public Reg visitDeclareFunction(DeclareFunctionContext ctx) {
ReferenceType type = (ReferenceType) an.variables.get(ctx).getType();
String base = "define " + ctx.IDENTIFIER(0).getText() + " - "; String base = "define " + ctx.IDENTIFIER(0).getText() + " - ";
Label skip = makeLabel("s"); Label skip = makeLabel("s");
emit(base + "jump over body", OpCode.jumpI, skip); 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); Reg result = visit(ctx.body);
emit(base + "move result", OpCode.storeAI, result, arp, OFFSET_RETURN_VAL); 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 + "load return address", OpCode.loadAI, arp, OFFSET_RETURN_ADDR, r1);
emit(base + "go to return address", OpCode.jump, r1); emit(base + "go to return address", OpCode.jump, r1);
});
emit(base + "skip target", skip, OpCode.nop); emit(base + "skip target", skip, OpCode.nop);
malloc(r2, FUNCREF_SIZE);
emit(base + "load target address", OpCode.loadI, new Num(targetAddress), r1); 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 + "set target address", OpCode.storeAI, r1, r2, OFFSET_FUNCREF_ADDR);
emit(base + "copy ARP", OpCode.storeAI, arp, r2, OFFSET_FUNCREF_ARP); 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()), emit(base + "load AR size", OpCode.loadI, new Num(ARBASESIZE + an.function.get(ctx).getLocalDataSize()),
@ -520,6 +589,22 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
return visit(last); 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 @Override
public Reg visitIf(IfContext ctx) { public Reg visitIf(IfContext ctx) {
Label toTrue = makeLabel("if_t"); Label toTrue = makeLabel("if_t");
@ -527,7 +612,27 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
Label toEnd = makeLabel("if_e"); Label toEnd = makeLabel("if_e");
Reg cond = visit(ctx.cond); 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) -> { return regPool.withReg((target) -> {
emit("", OpCode.cbr, cond, toTrue, toFalse); emit("", OpCode.cbr, cond, toTrue, toFalse);
@ -537,27 +642,10 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
emit("", toFalse, OpCode.nop); emit("", toFalse, OpCode.nop);
emit("result", OpCode.i2i, visit(ctx.onFalse), target); emit("result", OpCode.i2i, visit(ctx.onFalse), target);
emit("end target", toEnd, OpCode.nop); 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 @Override
@ -582,7 +670,37 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
public Reg visitInfix3(Infix3Context ctx) { public Reg visitInfix3(Infix3Context ctx) {
Reg lhs = visit(ctx.lhs); Reg lhs = visit(ctx.lhs);
Reg rhs = regPool.blockReg(lhs, () -> visit(ctx.rhs)); 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; return lhs;
} }
@ -623,13 +741,15 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
@Override @Override
public Reg visitLiteralInteger(LiteralIntegerContext ctx) { public Reg visitLiteralInteger(LiteralIntegerContext ctx) {
return regPool.withReg((reg) -> { 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 emit(ctx.getText(), OpCode.loadI, new Num(val), reg);
public Reg visitParens(ParensContext ctx) { });
return visit(ctx.expr());
} }
@Override @Override
@ -650,13 +770,13 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
@Override @Override
public Reg visitProgram(ProgramContext ctx) { 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); malloc(arp, an.function.get(ctx).getLocalDataSize() + ARBASESIZE);
emit("construct main AR", OpCode.addI, arp, new Num(ARBASESIZE), arp); emit("construct main AR", OpCode.addI, arp, new Num(ARBASESIZE), arp);
visitChildren(ctx); visitChildren(ctx);
cleanScope(an.function.get(ctx)); dereferenceLocalVariables(an.function.get(ctx));
emit("deconstruct main AR", OpCode.subI, arp, new Num(ARBASESIZE), arp); emit("deconstruct main AR", OpCode.subI, arp, new Num(ARBASESIZE), arp);
regPool.withReg((temp) -> { regPool.withReg((temp) -> {
@ -671,11 +791,12 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
if (ctx.variable().size() > 1) { if (ctx.variable().size() > 1) {
for (VariableContext expr : ctx.variable()) { for (VariableContext expr : ctx.variable()) {
visit(expr); visit(expr);
if (SimpleType.BOOL.equals(an.types.get(expr)) || SimpleType.INT.equals(an.types.get(expr))) { if (SimpleType.BOOL.equals(an.types.get(expr)) || SimpleType.INT.equals(an.types.get(expr))) {
regPool.withReg((temp) -> { regPool.withReg((temp) -> {
emit("", OpCode.in, new Str(""), temp); emit("", OpCode.in, new Str(""), temp);
emit("var " + expr.getText(), OpCode.storeAI, temp, getVar(expr), Reg addr = visit(expr);
new Num(an.variables.get(expr).getOffset())); emit("save to var " + expr.getText(), OpCode.store, temp, addr);
}); });
} else if (SimpleType.CHAR.equals(an.types.get(expr))) { } else if (SimpleType.CHAR.equals(an.types.get(expr))) {
// Get input until at least 1 character // Get input until at least 1 character
@ -689,8 +810,8 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
// Get character // Get character
emit("pop whole string", continueTarget, OpCode.cpop, temp); emit("pop whole string", continueTarget, OpCode.cpop, temp);
emit("var " + expr.getText(), OpCode.cstoreAI, temp, getVar(expr), Reg addr = visit(expr);
new Num(an.variables.get(expr).getOffset())); emit("save to var " + expr.getText(), OpCode.cstore, temp, addr);
// Pop all remaining characters // Pop all remaining characters
Label loopTarget = makeLabel("lcpop_l"); Label loopTarget = makeLabel("lcpop_l");
@ -703,19 +824,19 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
emit("pop remaining", stopTarget, OpCode.nop); emit("pop remaining", stopTarget, OpCode.nop);
}); });
} else { } else {
emit("reading unknown type", OpCode.haltI, new Num(1)); emit("reading unknown type", OpCode.haltI, new Num(0x72656164));
} }
} }
return null; return null;
} else { } else {
VariableContext expr = ctx.variable(0); VariableContext expr = ctx.variable(0);
visit(expr);
if (SimpleType.BOOL.equals(an.types.get(expr)) || SimpleType.INT.equals(an.types.get(expr))) { if (SimpleType.BOOL.equals(an.types.get(expr)) || SimpleType.INT.equals(an.types.get(expr))) {
return regPool.withReg((result) -> { return regPool.withReg((result) -> {
emit("", OpCode.in, new Str(""), result); emit("", OpCode.in, new Str(""), result);
emit("var " + expr.getText(), OpCode.storeAI, result, getVar(expr), Reg addr = visit(expr);
new Num(an.variables.get(expr).getOffset())); emit("save to var " + expr.getText(), OpCode.store, result, addr);
}); });
} else if (SimpleType.CHAR.equals(an.types.get(expr))) { } else if (SimpleType.CHAR.equals(an.types.get(expr))) {
return regPool.withReg((count, result) -> { return regPool.withReg((count, result) -> {
@ -729,8 +850,8 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
// Get character // Get character
emit("pop whole string", continueTarget, OpCode.cpop, result); emit("pop whole string", continueTarget, OpCode.cpop, result);
emit("var " + expr.getText(), OpCode.cstoreAI, result, getVar(expr), Reg addr = visit(expr);
new Num(an.variables.get(expr).getOffset())); emit("save to var " + expr.getText(), OpCode.cstore, result, addr);
// Pop all remaining characters // Pop all remaining characters
Label loopTarget = makeLabel("lcpop_l"); Label loopTarget = makeLabel("lcpop_l");
@ -745,37 +866,73 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
}); });
}); });
} else { } else {
emit("reading unknown type", OpCode.haltI, new Num(1)); emit("reading unknown type", OpCode.haltI, new Num(0x72656164));
return null; return null;
} }
} }
} }
@Override @Override
public Reg visitVariableExpr(VariableExprContext ctx) { public Reg visitVariableArray(VariableArrayContext ctx) {
Type type = an.types.get(ctx); ArrayType type = (ArrayType) an.types.get(ctx.variable());
Num offset = new Num(an.variables.get(ctx.variable()).getOffset()); Reg addr = visit(ctx.variable());
return regPool.withReg((result) -> { emit("get array object", OpCode.load, addr, addr);
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);
if (type instanceof ReferenceType) { regPool.blockReg(addr, () -> {
Reg offset = visit(ctx.expr());
regPool.blockReg(offset, () -> {
regPool.withReg((r1, r2) -> { 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("multiply index by size", inBounds, OpCode.multI, offset, new Num(type.getType().getSize()),
emit(comment, OpCode.loadAI, result, OFFSET_FUNCREF_ARP, r1); offset);
emit("get array index address", OpCode.add, addr, offset, addr);
incARReferences(r2, r1);
}
}); });
});
});
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<Type> 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<Reg> {
emit("print character", OpCode.cout, new Str("")); emit("print character", OpCode.cout, new Str(""));
}); });
} else { } 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))) if (!expr.equals(ctx.expr(ctx.expr().size() - 1)))
@ -835,7 +992,7 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
emit("", OpCode.out, new Str(""), result); emit("", OpCode.out, new Str(""), result);
return result; return result;
} else if (SimpleType.CHAR.equals(type)) { } else if (SimpleType.CHAR.equals(type)) {
regPool.withReg(result, (temp) -> { regPool.withReg((temp) -> {
emit("push character", OpCode.cpush, result); emit("push character", OpCode.cpush, result);
emit("load 1", OpCode.loadI, new Num(1), temp); emit("load 1", OpCode.loadI, new Num(1), temp);
emit("push 1", OpCode.push, temp); emit("push 1", OpCode.push, temp);
@ -843,7 +1000,7 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
}); });
return result; return result;
} else { } else {
emit("writing unknown type", OpCode.haltI, new Num(1)); emit("writing unknown type", OpCode.haltI, new Num(0x77726974));
return null; return null;
} }
}); });

View File

@ -3,4 +3,7 @@
/BoppiLexer.java /BoppiLexer.java
/BoppiLexer.tokens /BoppiLexer.tokens
/BoppiParser.java /BoppiParser.java
/BoppiTokens.java
/BoppiTokens.tokens
/BoppiVisitor.java /BoppiVisitor.java
/BoppiTokens.g4

View File

@ -1,4 +1,5 @@
grammar Boppi; grammar Boppi;
import BoppiTokens;
program: expr EOF; program: expr EOF;
@ -21,7 +22,7 @@ singleExpr
| lhs=singleExpr OR rhs=singleExpr #infix5 | lhs=singleExpr OR rhs=singleExpr #infix5
| DECLARE type IDENTIFIER #declare | DECLARE type IDENTIFIER #declare
| <assoc=right> variable ASSIGN singleExpr #assign | <assoc=right> variable ASSIGN singleExpr #assign
| variable #variableExpr | variable #getVariable
| LITERAL10 #literalInteger | LITERAL10 #literalInteger
| CHAR #literalCharacter | CHAR #literalCharacter
| (TRUE|FALSE) #literalBoolean | (TRUE|FALSE) #literalBoolean
@ -30,58 +31,15 @@ singleExpr
; ;
type type
: type ARROW type #typeFunction : ARROPEN LITERAL10 ARRCLOSE type #typeArray
| type ARROW type #typeFunction
| staticType=(INTTYPE | BOOLTYPE | CHARTYPE) #typeSimple | staticType=(INTTYPE | BOOLTYPE | CHARTYPE) #typeSimple
| PAROPEN (type (LISTDELIM type )*)? PARCLOSE #typeTuple | PAROPEN (type (LISTDELIM type)*)? PARCLOSE #typeTuple
| variable #typeVariable | variable #typeVariable
; ;
variable: IDENTIFIER; variable
: variable ARROPEN expr ARRCLOSE #variableArray
PAROPEN: '('; | variable PROP IDENTIFIER #variableProperty
PARCLOSE: ')'; | IDENTIFIER #variableSimple
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;

View File

@ -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)));
}
}

View File

@ -11,6 +11,7 @@ import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses; import org.junit.runners.Suite.SuiteClasses;
import pp.iloc.Simulator; import pp.iloc.Simulator;
import pp.iloc.eval.Machine;
import pp.iloc.model.Program; import pp.iloc.model.Program;
import pp.s1184725.boppi.*; import pp.s1184725.boppi.*;
@ -22,7 +23,8 @@ import pp.s1184725.boppi.*;
* @author Frank Wibbelink * @author Frank Wibbelink
*/ */
@RunWith(Suite.class) @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 { public class BoppiTests {
/** /**
* The path for test programs * The path for test programs
@ -41,10 +43,15 @@ public class BoppiTests {
public static List<LogRecord> log; public static List<LogRecord> log;
/** /**
* Output of last program split by line breaks * Output of last {@link #compileAndRun} call split by line breaks
*/ */
public static String[] out; 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 * The level of error reporting to use for the tests. Can be changed to
* include finer tests. * include finer tests.
@ -158,6 +165,35 @@ public class BoppiTests {
System.out.println(program.prettyPrint()); System.out.println(program.prettyPrint());
out = ToolChain.execute(program, logger, input).split("\n"); 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()); System.out.println(program.prettyPrint());
out = ToolChain.execute(program, logger, in).split("\n"); 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())); log.forEach((entry) -> System.out.println(entry.getMessage()));
Simulator.DEBUG = false; Simulator.DEBUG = false;

View File

@ -4,8 +4,6 @@ import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.*;
import org.junit.Test; import org.junit.Test;
import pp.s1184725.boppi.ToolChain;
/** /**
* Tests for function closures, mostly testing runtime correctness and garbage collection. * Tests for function closures, mostly testing runtime correctness and garbage collection.
* *
@ -43,17 +41,17 @@ public class ClosureTest {
@Test @Test
public void correctClosureGeneration() { public void correctClosureGeneration() {
BoppiTests.compileAndRunFile("closure1.boppi"); BoppiTests.compileAndRunFile("closure1.boppi");
assertThat(ToolChain.machine.getInterrupt(), is(0)); assertThat(BoppiTests.vm.getInterrupt(), is(0));
assertThat(BoppiTests.log, is(empty())); assertThat(BoppiTests.log, is(empty()));
assertThat(BoppiTests.out, is(arrayContaining("8", "9"))); assertThat(BoppiTests.out, is(arrayContaining("8", "9")));
BoppiTests.compileAndRunFile("closure2.boppi"); BoppiTests.compileAndRunFile("closure2.boppi");
assertThat(ToolChain.machine.getInterrupt(), is(0)); assertThat(BoppiTests.vm.getInterrupt(), is(0));
assertThat(BoppiTests.log, is(empty())); assertThat(BoppiTests.log, is(empty()));
assertThat(BoppiTests.out, is(arrayContaining("8", "7", "15", "2"))); assertThat(BoppiTests.out, is(arrayContaining("8", "7", "15", "2")));
BoppiTests.compileAndRunFile("closure3.boppi"); BoppiTests.compileAndRunFile("closure3.boppi");
assertThat(ToolChain.machine.getInterrupt(), is(0)); assertThat(BoppiTests.vm.getInterrupt(), is(0));
assertThat(BoppiTests.log, is(empty())); assertThat(BoppiTests.log, is(empty()));
assertThat(BoppiTests.out, is(arrayContaining("7", "15", "2"))); assertThat(BoppiTests.out, is(arrayContaining("7", "15", "2")));
} }
@ -64,13 +62,13 @@ public class ClosureTest {
@Test @Test
public void correctClosureCleanup() { public void correctClosureCleanup() {
BoppiTests.compileAndRunFile("closure1.boppi"); BoppiTests.compileAndRunFile("closure1.boppi");
assertThat(ToolChain.machine.load(0), is(4)); assertThat(BoppiTests.vm.load(0), is(4));
BoppiTests.compileAndRunFile("closure2.boppi"); BoppiTests.compileAndRunFile("closure2.boppi");
assertThat(ToolChain.machine.load(0), is(4)); assertThat(BoppiTests.vm.load(0), is(4));
BoppiTests.compileAndRunFile("closure3.boppi"); BoppiTests.compileAndRunFile("closure3.boppi");
assertThat(ToolChain.machine.load(0), is(4)); assertThat(BoppiTests.vm.load(0), is(4));
} }

View File

@ -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]);

View File

@ -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;

View File

@ -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;
}
}

View File

@ -28,6 +28,11 @@ public class FunctionType implements ReferenceType {
return Machine.INT_SIZE; return Machine.INT_SIZE;
} }
@Override
public int getReferenceSize() {
return 3*Machine.INT_SIZE;
}
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (!(obj instanceof FunctionType)) if (!(obj instanceof FunctionType))

View File

@ -7,4 +7,9 @@ package pp.s1184725.boppi.type;
*/ */
public interface ReferenceType extends 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();
} }

View File

@ -1,6 +1,5 @@
package pp.s1184725.boppi.util; package pp.s1184725.boppi.util;
import java.util.ArrayList;
import java.util.*; import java.util.*;
import java.util.function.*; import java.util.function.*;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -16,38 +15,23 @@ public class RegisterPool {
*/ */
public static int RECOMMENDED_REGISTER_COUNT = 10; 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 Logger logger;
private List<Reg> regFree, regInUse; private List<Reg> regFree, regInUse;
/** private void blockReg(Reg reg) {
* Creates a new register pool if (reg == null)
* logger.severe("INTERNAL: cannot reserve null register.");
* @param logger else if (!regFree.contains(reg)) {
* the logger to use logger.severe(String.format("INTERNAL: cannot reserve register %s because it is in use.", reg.getName()));
*/ } else {
public RegisterPool(Logger logger) { regFree.remove(reg);
regFree = new ArrayList<>(); regInUse.add(reg);
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 freeReg(Reg reg) { private void freeReg(Reg reg) {
@ -61,15 +45,35 @@ public class RegisterPool {
} }
} }
private void blockReg(Reg reg) { private Reg makeReg() {
if (reg == null) return makeRegThatIsNot(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())); private Reg makeRegThatIsNot(Reg r1) {
} else { Reg reg = regFree.parallelStream().filter((r2) -> !r2.equals(r1)).findAny().orElseGet(() -> {
regFree.remove(reg); if (regInUse.size() == RECOMMENDED_REGISTER_COUNT + 1)
regInUse.add(reg); 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; return regInUse;
} }
/**
* Runs some code with a single register allocated.
*
* @param <T>
* the return type of the code
* @param code
* the code to run
* @return the return value of the code
*/
public <T> T withReg(Function<Reg, T> code) {
Reg r = makeReg();
T result = code.apply(r);
freeReg(r);
return result;
}
/** /**
* Runs some code with a single register allocated. * Runs some code with a single register allocated.
* *
@ -119,46 +107,7 @@ public class RegisterPool {
* @return the second register used in the code * @return the second register used in the code
*/ */
public Reg withReg(BiConsumer<Reg, Reg> code) { public Reg withReg(BiConsumer<Reg, Reg> code) {
Reg r1 = makeReg(); return withReg((r2) -> withReg((r1) -> code.accept(r1, r2)));
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<Reg> 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<Reg, Reg> code) {
Reg r2 = makeRegThatIsNot(r1);
Reg r3 = makeRegThatIsNot(r1);
code.accept(r2, r3);
freeReg(r2);
freeReg(r3);
return r3;
} }
/** /**
@ -193,4 +142,21 @@ public class RegisterPool {
freeReg(r1); freeReg(r1);
} }
/**
* Blocks two registers while running some code.
*
* @param <T>
* 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> T blockReg(Reg r1, Reg r2, Supplier<T> code) {
return blockReg(r1, () -> blockReg(r2, code));
}
} }