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;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		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);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		Stack<Reg> inUse = new Stack<>();
 | 
			
		||||
		for (Reg reg : regPool.getInUse()) {
 | 
			
		||||
			inUse.push(reg);
 | 
			
		||||
			emit(base + "register save " + reg.getName(), OpCode.push, reg);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
			Reg res = regPool.withReg((r1, r2) -> {
 | 
			
		||||
				emit(base + "load function reference", OpCode.loadAI, getVar(ctx.variable()),
 | 
			
		||||
						new Num(an.variables.get(ctx.variable()).getOffset()), r2);
 | 
			
		||||
		regPool.blockReg(ar, () -> {
 | 
			
		||||
			regPool.withReg((r1, r2) -> {
 | 
			
		||||
				Reg addr = visit(ctx.variable());
 | 
			
		||||
				emit(base + "load function reference", OpCode.load, addr, r2);
 | 
			
		||||
				emit(base + "link caller ARP", OpCode.storeAI, arp, ar, OFFSET_ARP);
 | 
			
		||||
				emit(base + "load AL", OpCode.loadAI, r2, OFFSET_FUNCREF_ARP, r1);
 | 
			
		||||
				emit(base + "link AL", OpCode.storeAI, r1, ar, OFFSET_AL);
 | 
			
		||||
| 
						 | 
				
			
			@ -433,26 +479,45 @@ public class BoppiGenerator extends BoppiBaseVisitor<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);
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			while (!inUse.isEmpty()) {
 | 
			
		||||
				Reg reg = inUse.pop();
 | 
			
		||||
				emit(base + "register unsave " + reg.getName(), OpCode.pop, reg);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return res;
 | 
			
		||||
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		regPool.withReg(result, (temp) -> {
 | 
			
		||||
		regPool.withReg((temp) -> {
 | 
			
		||||
			decARReferences(temp, arp);
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		emit(base + "reset ARP", OpCode.loadAI, arp, OFFSET_ARP, arp);
 | 
			
		||||
		while (!inUse.isEmpty()) {
 | 
			
		||||
			Reg reg = inUse.pop();
 | 
			
		||||
			emit(base + "register unsave " + reg.getName(), OpCode.pop, reg);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return result;
 | 
			
		||||
		return regPool.withReg((res) -> {
 | 
			
		||||
			emit(base + "load result", OpCode.loadAI, arp, OFFSET_RETURN_VAL, res);
 | 
			
		||||
			emit(base + "reset ARP", OpCode.loadAI, arp, OFFSET_ARP, arp);
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Reg visitDeclare(DeclareContext ctx) {
 | 
			
		||||
		Variable<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);
 | 
			
		||||
		emit(base + "skip target", skip, OpCode.nop);
 | 
			
		||||
 | 
			
		||||
		regPool.withReg((r1, r2) -> {
 | 
			
		||||
			malloc(r2, type.getReferenceSize());
 | 
			
		||||
			emit(base + "load target address", OpCode.loadI, new Num(callAddress), r1);
 | 
			
		||||
			emit(base + "set target address", OpCode.storeAI, r1, r2, OFFSET_FUNCREF_ADDR);
 | 
			
		||||
			emit(base + "copy ARP", OpCode.storeAI, arp, r2, OFFSET_FUNCREF_ARP);
 | 
			
		||||
			emit(base + "load AR size", OpCode.loadI, new Num(ARBASESIZE + an.function.get(ctx).getLocalDataSize()),
 | 
			
		||||
| 
						 | 
				
			
			@ -520,6 +589,22 @@ public class BoppiGenerator extends BoppiBaseVisitor<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));
 | 
			
		||||
		emit(ctx.getChild(1).getText(), ops.get(ctx.op.getType()), lhs, rhs, lhs);
 | 
			
		||||
		Type type = an.types.get(ctx.lhs);
 | 
			
		||||
		if (type instanceof SimpleType)
 | 
			
		||||
			emit(ctx.getChild(1).getText(), ops.get(ctx.op.getType()), lhs, rhs, lhs);
 | 
			
		||||
		else if (type instanceof ArrayType) {
 | 
			
		||||
			return regPool.blockReg(lhs, rhs, () -> {
 | 
			
		||||
				ArrayType arr = (ArrayType) type;
 | 
			
		||||
				Num elementSize = new Num(arr.getType().getSize());
 | 
			
		||||
 | 
			
		||||
				return regPool.withReg((i, temp1) -> {
 | 
			
		||||
					regPool.withReg((temp2) -> {
 | 
			
		||||
						Label cond = makeLabel("aeqc"), loop = makeLabel("aeql"), end = makeLabel("aeqe");
 | 
			
		||||
 | 
			
		||||
						emit("iterate from 0", OpCode.loadI, ZERO, i);
 | 
			
		||||
						emit("equality true", OpCode.loadI, new Num(1), temp1);
 | 
			
		||||
 | 
			
		||||
						emit("load arr size", cond, OpCode.loadAI, lhs, OFFSET_REF_SIZE, temp2);
 | 
			
		||||
						emit("check i in bounds", OpCode.cmp_LT, i, temp2, temp2);
 | 
			
		||||
						emit("loop", OpCode.cbr, temp2, loop, end);
 | 
			
		||||
 | 
			
		||||
						emit("load left", loop, OpCode.loadAO, lhs, i, temp1);
 | 
			
		||||
						emit("load right", OpCode.loadAO, rhs, i, temp2);
 | 
			
		||||
						emit("increment i", OpCode.addI, i, elementSize, i);
 | 
			
		||||
 | 
			
		||||
						emit("compare elements", OpCode.cmp_EQ, temp1, temp2, temp1);
 | 
			
		||||
						emit("loop or break", OpCode.cbr, temp1, cond, end);
 | 
			
		||||
 | 
			
		||||
						emit("end equality check", end, OpCode.nop);
 | 
			
		||||
					});
 | 
			
		||||
				});
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return lhs;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -623,13 +741,15 @@ public class BoppiGenerator extends BoppiBaseVisitor<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));
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		regFree.remove(reg);
 | 
			
		||||
		regInUse.add(reg);
 | 
			
		||||
 | 
			
		||||
		return reg;
 | 
			
		||||
	private void blockReg(Reg reg) {
 | 
			
		||||
		if (reg == null)
 | 
			
		||||
			logger.severe("INTERNAL: cannot reserve null register.");
 | 
			
		||||
		else if (!regFree.contains(reg)) {
 | 
			
		||||
			logger.severe(String.format("INTERNAL: cannot reserve register %s because it is in use.", reg.getName()));
 | 
			
		||||
		} else {
 | 
			
		||||
			regFree.remove(reg);
 | 
			
		||||
			regInUse.add(reg);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private void freeReg(Reg reg) {
 | 
			
		||||
| 
						 | 
				
			
			@ -61,15 +45,35 @@ public class RegisterPool {
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private void blockReg(Reg reg) {
 | 
			
		||||
		if (reg == null)
 | 
			
		||||
			logger.severe("INTERNAL: cannot reserve null register.");
 | 
			
		||||
		else if (!regFree.contains(reg)) {
 | 
			
		||||
			logger.severe(String.format("INTERNAL: cannot reserve register %s because it is in use.", reg.getName()));
 | 
			
		||||
		} else {
 | 
			
		||||
			regFree.remove(reg);
 | 
			
		||||
			regInUse.add(reg);
 | 
			
		||||
		}
 | 
			
		||||
	private Reg makeReg() {
 | 
			
		||||
		return makeRegThatIsNot(null);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private Reg makeRegThatIsNot(Reg r1) {
 | 
			
		||||
		Reg reg = regFree.parallelStream().filter((r2) -> !r2.equals(r1)).findAny().orElseGet(() -> {
 | 
			
		||||
			if (regInUse.size() == RECOMMENDED_REGISTER_COUNT + 1)
 | 
			
		||||
				logger.warning(String.format("Using more than %d registers. Consider rebalancing your expressions.",
 | 
			
		||||
						RECOMMENDED_REGISTER_COUNT));
 | 
			
		||||
 | 
			
		||||
			return new Reg("__" + (regInUse.size() + 1));
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		regFree.remove(reg);
 | 
			
		||||
		regInUse.add(reg);
 | 
			
		||||
 | 
			
		||||
		return reg;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Creates a new register pool
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param logger
 | 
			
		||||
	 *            the logger to use
 | 
			
		||||
	 */
 | 
			
		||||
	public RegisterPool(Logger logger) {
 | 
			
		||||
		regFree = new ArrayList<>();
 | 
			
		||||
		regInUse = new ArrayList<>();
 | 
			
		||||
		this.logger = logger;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			@ -81,22 +85,6 @@ public class RegisterPool {
 | 
			
		|||
		return regInUse;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Runs some code with a single register allocated.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param <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