implemented arrays, split antlr file into grammar and lexer
This commit is contained in:
parent
5a48e93674
commit
f6cabe0ccb
|
@ -0,0 +1,2 @@
|
|||
eclipse.preferences.version=1
|
||||
encoding/<project>=UTF-8
|
|
@ -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(
|
||||
|
|
|
@ -168,6 +168,11 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
|
|||
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(() -> {
|
||||
|
@ -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
|
||||
public Type visitTypeFunction(TypeFunctionContext ctx) {
|
||||
return new FunctionType(visit(ctx.type(1)), visit(ctx.type(0)));
|
||||
|
@ -308,7 +326,42 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
|
|||
}
|
||||
|
||||
@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<Type> var = an.symbols.get(ctx.getText());
|
||||
|
||||
|
@ -322,14 +375,8 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
|
|||
return var.getType();
|
||||
} catch (SymbolTableException e) {
|
||||
log.severe(getError(ctx, e.getMessage()));
|
||||
}
|
||||
|
||||
return SimpleType.VOID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type visitVariableExpr(VariableExprContext ctx) {
|
||||
return visit(ctx.variable());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -22,12 +22,12 @@ import pp.s1184725.boppi.util.RegisterPool;
|
|||
* @author Frank Wibbelink
|
||||
*/
|
||||
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 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<Reg> {
|
|||
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
|
||||
* under construction.
|
||||
|
@ -135,7 +120,7 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
|
|||
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<Reg> {
|
|||
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<Reg> {
|
|||
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<Reg> {
|
|||
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<Reg> {
|
|||
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<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) -> {
|
||||
for (Variable<Type> var : scope.getVars()) {
|
||||
Type type = var.getType();
|
||||
|
@ -306,7 +332,7 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
|
|||
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<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
|
||||
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<Reg> {
|
|||
|
||||
@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<Reg> {
|
|||
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<Type> 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<Reg> inUse = new Stack<>();
|
||||
for (Reg reg : regPool.getInUse()) {
|
||||
if (!reg.equals(ar)) {
|
||||
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,8 +479,11 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
|
|||
|
||||
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);
|
||||
regPool.withReg((temp) -> {
|
||||
decARReferences(temp, arp);
|
||||
});
|
||||
|
||||
while (!inUse.isEmpty()) {
|
||||
|
@ -442,17 +491,33 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
|
|||
emit(base + "register unsave " + reg.getName(), OpCode.pop, reg);
|
||||
}
|
||||
|
||||
return res;
|
||||
|
||||
});
|
||||
|
||||
regPool.withReg(result, (temp) -> {
|
||||
decARReferences(temp, arp);
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
@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
|
||||
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);
|
||||
|
||||
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<Reg> {
|
|||
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<Reg> {
|
|||
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<Reg> {
|
|||
|
||||
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<Reg> {
|
|||
public Reg visitInfix3(Infix3Context ctx) {
|
||||
Reg lhs = visit(ctx.lhs);
|
||||
Reg rhs = regPool.blockReg(lhs, () -> visit(ctx.rhs));
|
||||
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<Reg> {
|
|||
@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<Reg> {
|
|||
|
||||
@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<Reg> {
|
|||
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<Reg> {
|
|||
|
||||
// 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<Reg> {
|
|||
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<Reg> {
|
|||
|
||||
// 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<Reg> {
|
|||
});
|
||||
});
|
||||
} 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<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(""));
|
||||
});
|
||||
} 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<Reg> {
|
|||
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<Reg> {
|
|||
});
|
||||
return result;
|
||||
} else {
|
||||
emit("writing unknown type", OpCode.haltI, new Num(1));
|
||||
emit("writing unknown type", OpCode.haltI, new Num(0x77726974));
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
|
|
@ -3,4 +3,7 @@
|
|||
/BoppiLexer.java
|
||||
/BoppiLexer.tokens
|
||||
/BoppiParser.java
|
||||
/BoppiTokens.java
|
||||
/BoppiTokens.tokens
|
||||
/BoppiVisitor.java
|
||||
/BoppiTokens.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
|
||||
| <assoc=right> 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
|
||||
;
|
||||
|
|
|
@ -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)));
|
||||
}
|
||||
|
||||
}
|
|
@ -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<LogRecord> 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;
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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]);
|
||||
|
|
@ -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;
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -28,6 +28,11 @@ public class FunctionType implements ReferenceType {
|
|||
return Machine.INT_SIZE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getReferenceSize() {
|
||||
return 3*Machine.INT_SIZE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof FunctionType))
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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<Reg> 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));
|
||||
});
|
||||
|
||||
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);
|
||||
|
||||
return 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 {
|
||||
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 <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.
|
||||
*
|
||||
|
@ -119,46 +107,7 @@ public class RegisterPool {
|
|||
* @return the second register used in the code
|
||||
*/
|
||||
public Reg withReg(BiConsumer<Reg, Reg> 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<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;
|
||||
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 <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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue