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;
|
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(
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
|
||||||
|
|
|
@ -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 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;
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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;
|
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))
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue