extracted strings, sorted methods, added constants, added procedures, added more tests, restructured parser rules
This commit is contained in:
		
							parent
							
								
									f6cabe0ccb
								
							
						
					
					
						commit
						8fa96cb362
					
				| 
						 | 
				
			
			@ -13,6 +13,7 @@ import pp.iloc.model.Num;
 | 
			
		|||
import pp.iloc.model.Op;
 | 
			
		||||
import pp.iloc.model.OpClaz;
 | 
			
		||||
import pp.iloc.model.OpCode;
 | 
			
		||||
import pp.iloc.model.Operand.Type;
 | 
			
		||||
import pp.iloc.model.Program;
 | 
			
		||||
import pp.iloc.parse.FormatException;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -101,10 +102,8 @@ public class Simulator {
 | 
			
		|||
		Op o = this.prg.getOpAt(this.vm.getPC());
 | 
			
		||||
		OPContext c = new OPContext(o);
 | 
			
		||||
		Machine vm = this.vm;
 | 
			
		||||
		if (DEBUG) {
 | 
			
		||||
			System.out.printf("Op %d: %s%n", vm.getPC(), o);
 | 
			
		||||
			System.out.println(vm);
 | 
			
		||||
		}
 | 
			
		||||
		String pre = c.toPreString();
 | 
			
		||||
		
 | 
			
		||||
		switch (o.getOpCode()) {
 | 
			
		||||
		case nop:
 | 
			
		||||
			// do nothing
 | 
			
		||||
| 
						 | 
				
			
			@ -328,6 +327,13 @@ public class Simulator {
 | 
			
		|||
			this.vm.incPC();
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		if (DEBUG) {
 | 
			
		||||
			System.out.printf("Op %d: %s%n", vm.getPC(), o);
 | 
			
		||||
			System.out.printf("Op %d: %s%n", vm.getPC(), pre+c.toPostString());
 | 
			
		||||
			System.out.println(vm);
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		if (o.getClaz() != OpClaz.CONTROL) {
 | 
			
		||||
			this.vm.incPC();
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -474,5 +480,76 @@ public class Simulator {
 | 
			
		|||
		public int label(int ix) {
 | 
			
		||||
			return getProgram().getLine(this.op.label(ix));
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		protected String toPreString() {
 | 
			
		||||
			OpCode opcode = op.getOpCode();
 | 
			
		||||
			StringBuilder sb = new StringBuilder();
 | 
			
		||||
			sb.append(opcode.toString());
 | 
			
		||||
			sb.append(" ");
 | 
			
		||||
			
 | 
			
		||||
			for (int i = 0; i < opcode.getSigSize(); i++) {
 | 
			
		||||
				if (i == opcode.getSourceCount())
 | 
			
		||||
					sb.append(" -> ");
 | 
			
		||||
				else if (i > 0)
 | 
			
		||||
					sb.append(", ");
 | 
			
		||||
				
 | 
			
		||||
				if (i >= opcode.getSourceCount())
 | 
			
		||||
					break;
 | 
			
		||||
				
 | 
			
		||||
				try {
 | 
			
		||||
				switch (opcode.getSig().get(i)) {
 | 
			
		||||
				case LABEL:
 | 
			
		||||
					sb.append(String.format("%d", label(i)));
 | 
			
		||||
					break;
 | 
			
		||||
				case NUM:
 | 
			
		||||
					sb.append(String.format("%8x", num(i)).replace(" ", "."));
 | 
			
		||||
					break;
 | 
			
		||||
				case REG:
 | 
			
		||||
					sb.append(String.format("%8x", reg(i)).replace(" ", "."));
 | 
			
		||||
					break;
 | 
			
		||||
				case STR:
 | 
			
		||||
					sb.append("");
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
				}
 | 
			
		||||
				catch (IllegalArgumentException e) {
 | 
			
		||||
					sb.append("undefine");
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			
 | 
			
		||||
			return sb.toString();
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		protected String toPostString() {
 | 
			
		||||
			OpCode opcode = op.getOpCode();
 | 
			
		||||
			StringBuilder sb = new StringBuilder();
 | 
			
		||||
			
 | 
			
		||||
			for (int i = opcode.getSourceCount(); i < opcode.getSigSize(); i++) {
 | 
			
		||||
				if (i > opcode.getSourceCount())
 | 
			
		||||
					sb.append(", ");
 | 
			
		||||
				
 | 
			
		||||
				try {
 | 
			
		||||
				switch (opcode.getSig().get(i)) {
 | 
			
		||||
				case LABEL:
 | 
			
		||||
					sb.append(String.format("%d", label(i)));
 | 
			
		||||
					break;
 | 
			
		||||
				case NUM:
 | 
			
		||||
					sb.append(String.format("%8x", num(i)).replace(" ", "."));
 | 
			
		||||
					break;
 | 
			
		||||
				case REG:
 | 
			
		||||
					sb.append(String.format("%8x", reg(i)).replace(" ", "."));
 | 
			
		||||
					break;
 | 
			
		||||
				case STR:
 | 
			
		||||
					sb.append("");
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
				}
 | 
			
		||||
				catch (IllegalArgumentException e) {
 | 
			
		||||
					sb.append("undefine");
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			
 | 
			
		||||
			return sb.toString();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,6 +2,7 @@ package pp.iloc.eval;
 | 
			
		|||
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.stream.Collectors;
 | 
			
		||||
 | 
			
		||||
import pp.iloc.model.Num;
 | 
			
		||||
import pp.iloc.model.Reg;
 | 
			
		||||
| 
						 | 
				
			
			@ -292,11 +293,10 @@ public class Machine {
 | 
			
		|||
	}
 | 
			
		||||
	
 | 
			
		||||
	/**
 | 
			
		||||
	 * Sets the interrupt value of the machine.
 | 
			
		||||
	 * @param interruptCode the new interrupt value
 | 
			
		||||
	 * Clears the interrupt value of the machine.
 | 
			
		||||
	 */
 | 
			
		||||
	public void setInterrupt(int interruptCode) {
 | 
			
		||||
		this.interrupt = interruptCode;
 | 
			
		||||
	public void clearInterrupt() {
 | 
			
		||||
		this.interrupt = 0;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			@ -307,6 +307,14 @@ public class Machine {
 | 
			
		|||
		return interrupt;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Sets the interrupt value of the machine.
 | 
			
		||||
	 * @param interruptCode the new interrupt value
 | 
			
		||||
	 */
 | 
			
		||||
	public void setInterrupt(int interruptCode) {
 | 
			
		||||
		this.interrupt = interruptCode;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	/** Clears the registers, constants, memory, PC and interrupt. */
 | 
			
		||||
	public void clear() {
 | 
			
		||||
		this.registers.clear();
 | 
			
		||||
| 
						 | 
				
			
			@ -321,7 +329,9 @@ public class Machine {
 | 
			
		|||
 | 
			
		||||
	@Override
 | 
			
		||||
	public String toString() {
 | 
			
		||||
		return String.format("Registers: %s%nConstants: %s%nMemory: %s%n",
 | 
			
		||||
				this.registers, this.symbMap, this.memory);
 | 
			
		||||
		String regs = this.registers.entrySet().parallelStream().sorted((el1, el2) -> el1.getKey().compareTo(el2.getKey())).map((el) -> el.getKey()+": "+String.format("%8x", el.getValue()).replace(" ", ".")).collect(Collectors.joining(", ", "{", "}"));
 | 
			
		||||
		
 | 
			
		||||
		return String.format("Registers: %s%nConstants: %s%nMemory: %n%s%n",
 | 
			
		||||
				regs, this.symbMap, this.memory);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,6 +2,8 @@ package pp.iloc.eval;
 | 
			
		|||
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
 | 
			
		||||
import pp.s1184725.boppi.ToolChain;
 | 
			
		||||
 | 
			
		||||
/** Simulated memory. */
 | 
			
		||||
public class Memory {
 | 
			
		||||
	/** The default size of the memory, in number of bytes. */
 | 
			
		||||
| 
						 | 
				
			
			@ -40,17 +42,76 @@ public class Memory {
 | 
			
		|||
	@Override
 | 
			
		||||
	public String toString() {
 | 
			
		||||
		StringBuilder result = new StringBuilder();
 | 
			
		||||
		for (int i = 0; i < size(); i++) {
 | 
			
		||||
			if (get(i) == 0) {
 | 
			
		||||
				continue;
 | 
			
		||||
//		boolean wasEmpty = false;
 | 
			
		||||
//		for (int i = 0; i < size(); i++) {
 | 
			
		||||
////			if (get(i) == 0) {
 | 
			
		||||
////				continue;
 | 
			
		||||
////			}
 | 
			
		||||
////			if (result.length() > 0) {
 | 
			
		||||
////				result.append(", ");
 | 
			
		||||
////			}
 | 
			
		||||
////			result.append(i);
 | 
			
		||||
////			result.append(":");
 | 
			
		||||
////			result.append(String.format("%02X", get(i) & 0xFF));
 | 
			
		||||
//			if (i % 32 == 0) {
 | 
			
		||||
//				boolean anyNonzero = false;
 | 
			
		||||
//				
 | 
			
		||||
//				for (int j = i; j < i+32 && j < size(); j++)
 | 
			
		||||
//					if ((get(j) & 0xFF) != 0) {
 | 
			
		||||
//						anyNonzero = true;
 | 
			
		||||
//						break;
 | 
			
		||||
//					}
 | 
			
		||||
//				
 | 
			
		||||
//				if (!anyNonzero) {
 | 
			
		||||
//					if (!wasEmpty)
 | 
			
		||||
//						result.append("\n. . .");
 | 
			
		||||
//					wasEmpty = true;
 | 
			
		||||
//					
 | 
			
		||||
//					i += 31;
 | 
			
		||||
//					continue;
 | 
			
		||||
//				}
 | 
			
		||||
//				else
 | 
			
		||||
//					wasEmpty = false;
 | 
			
		||||
//				
 | 
			
		||||
//				result.append("\n");
 | 
			
		||||
//				result.append(String.format("%08x  ", i));
 | 
			
		||||
//			}
 | 
			
		||||
//			if ((get(i) & 0xFF) != 0)
 | 
			
		||||
//				result.append(String.format("%02X", get(i) & 0xFF));
 | 
			
		||||
//			else
 | 
			
		||||
//				result.append("..");
 | 
			
		||||
//			if (i % 4 == 3)
 | 
			
		||||
//				result.append(" ");
 | 
			
		||||
//		}
 | 
			
		||||
		
 | 
			
		||||
		int loc = 4;
 | 
			
		||||
		
 | 
			
		||||
		System.err.flush();
 | 
			
		||||
		
 | 
			
		||||
		while (loc != 0 && loc < ToolChain.machine.getReg("brk")) {
 | 
			
		||||
			int size = ToolChain.machine.load(loc+4);
 | 
			
		||||
			int nloc = ToolChain.machine.load(loc);
 | 
			
		||||
			
 | 
			
		||||
			if (!(size == 0 && nloc == 0)) {
 | 
			
		||||
			result.append(String.format("0x%05x  ", loc+8));
 | 
			
		||||
			
 | 
			
		||||
			if ((nloc >= ToolChain.machine.load(0) && nloc > loc) || nloc == 0)
 | 
			
		||||
				result.append("   free");
 | 
			
		||||
			else
 | 
			
		||||
				result.append(String.format("% 2d ref ", ToolChain.machine.load(loc)));
 | 
			
		||||
			
 | 
			
		||||
			for(int i = 0; i < size && i < 40; i += 4) {
 | 
			
		||||
				if (ToolChain.machine.load(loc+8+i) == 0)
 | 
			
		||||
					result.append("........");
 | 
			
		||||
				else
 | 
			
		||||
					result.append(String.format("%8x", ToolChain.machine.load(loc+8+i)).replace(" ", "."));
 | 
			
		||||
				result.append(" ");
 | 
			
		||||
			}
 | 
			
		||||
			if (result.length() > 0) {
 | 
			
		||||
				result.append(", ");
 | 
			
		||||
			result.append(size > 40 ? "...\n" : "\n");
 | 
			
		||||
			}
 | 
			
		||||
			result.append(i);
 | 
			
		||||
			result.append(":");
 | 
			
		||||
			result.append(String.format("%02X", get(i) & 0xFF));
 | 
			
		||||
			loc += size+8;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return result.toString();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,8 @@
 | 
			
		|||
package pp.s1184725.boppi;
 | 
			
		||||
 | 
			
		||||
import java.util.*;
 | 
			
		||||
 | 
			
		||||
import org.antlr.v4.runtime.ParserRuleContext;
 | 
			
		||||
import org.antlr.v4.runtime.tree.ParseTreeProperty;
 | 
			
		||||
 | 
			
		||||
import pp.iloc.model.Reg;
 | 
			
		||||
| 
						 | 
				
			
			@ -31,6 +34,10 @@ public class Annotations {
 | 
			
		|||
	 * A symbol table
 | 
			
		||||
	 */
 | 
			
		||||
	public CachingSymbolTable<Type> symbols;
 | 
			
		||||
	/**
 | 
			
		||||
	 * Maps variable instances to the AST node they reside in.
 | 
			
		||||
	 */
 | 
			
		||||
	public Map<Variable<Type>,ParserRuleContext> variableRoot;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Creates a new annotations object with empty maps.
 | 
			
		||||
| 
						 | 
				
			
			@ -41,5 +48,6 @@ public class Annotations {
 | 
			
		|||
		function = new ParseTreeProperty<>();
 | 
			
		||||
		variables = new ParseTreeProperty<>();
 | 
			
		||||
		symbols = new CachingSymbolTable<>();
 | 
			
		||||
		variableRoot = new HashMap<>();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -20,6 +20,7 @@ import pp.s1184725.boppi.type.*;
 | 
			
		|||
public class BoppiChecker extends BoppiBaseVisitor<Type> {
 | 
			
		||||
	private Annotations an;
 | 
			
		||||
	private Logger log;
 | 
			
		||||
	private boolean inLhs, inType;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Checks and annotates a program. Problems are reported to the given
 | 
			
		||||
| 
						 | 
				
			
			@ -40,6 +41,8 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
 | 
			
		|||
	protected BoppiChecker(Logger logger, Annotations annotations) {
 | 
			
		||||
		an = annotations;
 | 
			
		||||
		log = logger;
 | 
			
		||||
		inLhs = false;
 | 
			
		||||
		inType = false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			@ -54,7 +57,7 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
 | 
			
		|||
	 */
 | 
			
		||||
	private void checkConstraint(Type type1, Type type2, ParserRuleContext node) {
 | 
			
		||||
		if (!type2.equals(type1))
 | 
			
		||||
			log.severe(getError(node, "Could not match type %s with type %s.", type1.toString(), type2.toString()));
 | 
			
		||||
			log.severe(getError(node, Messages.getString("BoppiChecker.0"), type1.toString(), type2.toString())); //$NON-NLS-1$
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			@ -70,7 +73,33 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
 | 
			
		|||
	private String getError(ParserRuleContext node, String message, Object... args) {
 | 
			
		||||
		int line = node.getStart().getLine();
 | 
			
		||||
		int column = node.getStart().getCharPositionInLine();
 | 
			
		||||
		return String.format("Line %d:%d - %s", line, column, String.format(message, args));
 | 
			
		||||
		return String.format(Messages.getString("BoppiChecker.1"), line, column, String.format(message, args)); //$NON-NLS-1$
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Returns whether the current context resides within a given rule context
 | 
			
		||||
	 * within a set root (may be {@code null}). Effectively traverses between
 | 
			
		||||
	 * {@code current} and {@code root} in the parent hierarchy looking for a
 | 
			
		||||
	 * {@code rule} class.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param <T>
 | 
			
		||||
	 *            the rule class
 | 
			
		||||
	 * @param ctx
 | 
			
		||||
	 *            the node to start traversing
 | 
			
		||||
	 * @param root
 | 
			
		||||
	 *            the node to stop traversing
 | 
			
		||||
	 * @param rule
 | 
			
		||||
	 *            the ParserRuleContext class to look for
 | 
			
		||||
	 * @return whether a matching rule is found between the current and root
 | 
			
		||||
	 *         node
 | 
			
		||||
	 */
 | 
			
		||||
	private <T extends ParserRuleContext> boolean isWithinRule(ParserRuleContext ctx, ParserRuleContext root,
 | 
			
		||||
			Class<T> rule) {
 | 
			
		||||
		for (ParserRuleContext node = ctx; node != root && node != null; node = node.getParent())
 | 
			
		||||
			if (node.getClass().isAssignableFrom(rule))
 | 
			
		||||
				return true;
 | 
			
		||||
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
| 
						 | 
				
			
			@ -82,15 +111,17 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
 | 
			
		|||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Type visitAssign(AssignContext ctx) {
 | 
			
		||||
		Type expr = visit(ctx.singleExpr());
 | 
			
		||||
		Type expr = visit(ctx.assignStat() != null ? ctx.assignStat() : ctx.expr());
 | 
			
		||||
		inLhs = true;
 | 
			
		||||
		Type var = visit(ctx.variable());
 | 
			
		||||
		inLhs = false;
 | 
			
		||||
		checkConstraint(expr, var, ctx);
 | 
			
		||||
		return expr;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Type visitBlock(BlockContext ctx) {
 | 
			
		||||
		return an.symbols.withScope(() -> visit(ctx.expr()));
 | 
			
		||||
		return an.symbols.withScope(() -> visit(ctx.stats()));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
| 
						 | 
				
			
			@ -101,14 +132,14 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
 | 
			
		|||
			FunctionType type = (FunctionType) t;
 | 
			
		||||
			TupleType parameters = (TupleType) type.getParameter();
 | 
			
		||||
			if (parameters.size() != ctx.expr().size())
 | 
			
		||||
				log.severe(getError(ctx, "Expected %d arguments but got %d.", parameters.size(), ctx.expr().size()));
 | 
			
		||||
				log.severe(getError(ctx, Messages.getString("BoppiChecker.2"), parameters.size(), ctx.expr().size())); //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
			for (int i = 0; i < ctx.expr().size() && i < parameters.size(); i++)
 | 
			
		||||
				checkConstraint(visit(ctx.expr(i)), parameters.get(i), ctx.expr(i));
 | 
			
		||||
 | 
			
		||||
			return type.getReturn();
 | 
			
		||||
		} else {
 | 
			
		||||
			log.severe(getError(ctx, "'%s' is not a function.", ctx.variable().getText()));
 | 
			
		||||
			log.severe(getError(ctx, Messages.getString("BoppiChecker.3"), ctx.variable().getText())); //$NON-NLS-1$
 | 
			
		||||
			return SimpleType.VOID;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -117,16 +148,18 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
 | 
			
		|||
	public Type visitDeclare(DeclareContext ctx) {
 | 
			
		||||
		try {
 | 
			
		||||
			Variable<Type> var = an.symbols.put(ctx.IDENTIFIER().getText(), visit(ctx.type()));
 | 
			
		||||
			var.setConstant(ctx.CONSTANT() != null);
 | 
			
		||||
			an.variables.put(ctx, var);
 | 
			
		||||
			an.variableRoot.put(var, ctx.getParent());
 | 
			
		||||
 | 
			
		||||
			if (var.getType() instanceof TupleType)
 | 
			
		||||
				log.severe(getError(ctx, "Variable must have a type %s, %s, %s or function.",
 | 
			
		||||
				log.severe(getError(ctx, Messages.getString("BoppiChecker.4"), //$NON-NLS-1$
 | 
			
		||||
						BoppiLexer.VOCABULARY.getLiteralName(BoppiLexer.BOOLTYPE),
 | 
			
		||||
						BoppiLexer.VOCABULARY.getLiteralName(BoppiLexer.CHARTYPE),
 | 
			
		||||
						BoppiLexer.VOCABULARY.getLiteralName(BoppiLexer.INTTYPE)));
 | 
			
		||||
 | 
			
		||||
			if (!(var.getType() instanceof SimpleType))
 | 
			
		||||
				log.warning("Be careful only to pass pure functions outside their scope.");
 | 
			
		||||
			if (var.getType() instanceof ReferenceType)
 | 
			
		||||
				var.assign();
 | 
			
		||||
		} catch (SymbolTableException e) {
 | 
			
		||||
			log.severe(getError(ctx, e.getMessage()));
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -136,21 +169,28 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
 | 
			
		|||
	@Override
 | 
			
		||||
	public Type visitDeclareFunction(DeclareFunctionContext ctx) {
 | 
			
		||||
		try {
 | 
			
		||||
			TupleType parameterTypes = new TupleType(
 | 
			
		||||
					ctx.type().stream().skip(1).map(this::visit).collect(Collectors.toList()));
 | 
			
		||||
			FunctionType type = new FunctionType(visit(ctx.result), parameterTypes);
 | 
			
		||||
			TupleType parameterTypes;
 | 
			
		||||
			if (ctx.parameters() != null)
 | 
			
		||||
				parameterTypes = new TupleType(
 | 
			
		||||
						ctx.parameters().type().stream().map(this::visit).collect(Collectors.toList()));
 | 
			
		||||
			else
 | 
			
		||||
				parameterTypes = TupleType.UNIT;
 | 
			
		||||
 | 
			
		||||
			Type returnType = ctx.result != null ? visit(ctx.result) : SimpleType.VOID;
 | 
			
		||||
			FunctionType type = new FunctionType(returnType, parameterTypes);
 | 
			
		||||
 | 
			
		||||
			Variable<Type> func = an.symbols.put(ctx.name.getText(), type);
 | 
			
		||||
			func.assign();
 | 
			
		||||
			an.variables.put(ctx, func);
 | 
			
		||||
 | 
			
		||||
			an.function.put(ctx, an.symbols.withFunctionScope(() -> {
 | 
			
		||||
				for (int i = 1; i < ctx.type().size(); i++)
 | 
			
		||||
					try {
 | 
			
		||||
						an.symbols.put(ctx.IDENTIFIER(i).getText(), an.types.get(ctx.type(i)));
 | 
			
		||||
					} catch (SymbolTableException e) {
 | 
			
		||||
						log.severe(getError(ctx, e.getMessage()));
 | 
			
		||||
					}
 | 
			
		||||
				if (ctx.parameters() != null)
 | 
			
		||||
					visit(ctx.parameters());
 | 
			
		||||
 | 
			
		||||
				checkConstraint(an.symbols.withScope(() -> visit(ctx.body)), an.types.get(ctx.result), ctx);
 | 
			
		||||
				Type resultType = an.symbols.withScope(() -> visit(ctx.body));
 | 
			
		||||
 | 
			
		||||
				if (returnType != SimpleType.VOID)
 | 
			
		||||
					checkConstraint(resultType, returnType, ctx);
 | 
			
		||||
			}));
 | 
			
		||||
		} catch (SymbolTableException e) {
 | 
			
		||||
			log.severe(getError(ctx, e.getMessage()));
 | 
			
		||||
| 
						 | 
				
			
			@ -159,15 +199,6 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
 | 
			
		|||
		return SimpleType.VOID;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Type visitExpr(ExprContext ctx) {
 | 
			
		||||
		ParserRuleContext lastExpr = ctx.singleExpr(ctx.singleExpr().size() - 1);
 | 
			
		||||
		if (lastExpr instanceof DeclareContext || lastExpr instanceof DeclareFunctionContext)
 | 
			
		||||
			log.severe(getError(ctx, "Compound expression ends with declaration."));
 | 
			
		||||
 | 
			
		||||
		return ctx.singleExpr().stream().map(this::visit).reduce((__, snd) -> snd).get();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Type visitGetVariable(GetVariableContext ctx) {
 | 
			
		||||
		return visit(ctx.variable());
 | 
			
		||||
| 
						 | 
				
			
			@ -238,6 +269,36 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
 | 
			
		|||
		return SimpleType.BOOL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Type visitLambda(LambdaContext ctx) {
 | 
			
		||||
		// try {
 | 
			
		||||
		// TupleType parameterTypes = new TupleType(
 | 
			
		||||
		// ctx.type().stream().skip(1).map(this::visit).collect(Collectors.toList()));
 | 
			
		||||
		// FunctionType type = new FunctionType(visit(ctx.result),
 | 
			
		||||
		// parameterTypes);
 | 
			
		||||
		// Variable<Type> func = an.symbols.put(ctx.name.getText(), type);
 | 
			
		||||
		// an.variables.put(ctx, func);
 | 
			
		||||
		//
 | 
			
		||||
		// an.function.put(ctx, an.symbols.withFunctionScope(() -> {
 | 
			
		||||
		// for (int i = 1; i < ctx.type().size(); i++)
 | 
			
		||||
		// try {
 | 
			
		||||
		// an.symbols.put(ctx.IDENTIFIER(i).getText(),
 | 
			
		||||
		// an.types.get(ctx.type(i)));
 | 
			
		||||
		// } catch (SymbolTableException e) {
 | 
			
		||||
		// log.severe(getError(ctx, e.getMessage()));
 | 
			
		||||
		// }
 | 
			
		||||
		//
 | 
			
		||||
		// checkConstraint(an.symbols.withScope(() -> visit(ctx.body)),
 | 
			
		||||
		// an.types.get(ctx.result), ctx);
 | 
			
		||||
		// }));
 | 
			
		||||
		// } catch (SymbolTableException e) {
 | 
			
		||||
		// log.severe(getError(ctx, e.getMessage()));
 | 
			
		||||
		// }
 | 
			
		||||
		log.severe("Lambda not yet implemented"); //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
		return SimpleType.VOID;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Type visitLiteralBoolean(LiteralBooleanContext ctx) {
 | 
			
		||||
		return SimpleType.BOOL;
 | 
			
		||||
| 
						 | 
				
			
			@ -255,27 +316,38 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
 | 
			
		|||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Type visitParens(ParensContext ctx) {
 | 
			
		||||
		return visit(ctx.expr());
 | 
			
		||||
		return visit(ctx.stats());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Type visitPrefix1(Prefix1Context ctx) {
 | 
			
		||||
		Type type = visit(ctx.singleExpr());
 | 
			
		||||
		Type type = visit(ctx.expr());
 | 
			
		||||
 | 
			
		||||
		switch (ctx.op.getType()) {
 | 
			
		||||
		case BoppiLexer.NOT:
 | 
			
		||||
			checkConstraint(type, SimpleType.BOOL, ctx.singleExpr());
 | 
			
		||||
			checkConstraint(type, SimpleType.BOOL, ctx.expr());
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		case BoppiLexer.PLUS:
 | 
			
		||||
		case BoppiLexer.MINUS:
 | 
			
		||||
			checkConstraint(type, SimpleType.INT, ctx.singleExpr());
 | 
			
		||||
			checkConstraint(type, SimpleType.INT, ctx.expr());
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return type;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Type visitParameters(ParametersContext ctx) {
 | 
			
		||||
		for (int i = 0; i < ctx.type().size(); i++)
 | 
			
		||||
			try {
 | 
			
		||||
				an.symbols.put(ctx.IDENTIFIER(i).getText(), an.types.get(ctx.type(i))).assign();
 | 
			
		||||
			} catch (SymbolTableException e) {
 | 
			
		||||
				log.severe(getError(ctx, e.getMessage()));
 | 
			
		||||
			}
 | 
			
		||||
		return null;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Type visitProgram(ProgramContext ctx) {
 | 
			
		||||
		an.function.put(ctx, an.symbols.withFunctionScope(() -> super.visitProgram(ctx)));
 | 
			
		||||
| 
						 | 
				
			
			@ -284,24 +356,46 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
 | 
			
		|||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Type visitRead(ReadContext ctx) {
 | 
			
		||||
		Type result;
 | 
			
		||||
 | 
			
		||||
		inLhs = true;
 | 
			
		||||
		if (ctx.variable().size() == 1) {
 | 
			
		||||
			return visit(ctx.variable(0));
 | 
			
		||||
			result = visit(ctx.variable(0));
 | 
			
		||||
		} else {
 | 
			
		||||
			ctx.variable().forEach(this::visit);
 | 
			
		||||
			return SimpleType.VOID;
 | 
			
		||||
			result = SimpleType.VOID;
 | 
			
		||||
		}
 | 
			
		||||
		inLhs = false;
 | 
			
		||||
 | 
			
		||||
		return result;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Type visitStat(StatContext ctx) {
 | 
			
		||||
		return visit(ctx.getChild(0));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Type visitStats(StatsContext ctx) {
 | 
			
		||||
		Type resultType = ctx.stat().stream().map(this::visit).reduce((__, snd) -> snd).get();
 | 
			
		||||
 | 
			
		||||
		ParseTree lastExpr = ctx.stat(ctx.stat().size() - 1).getChild(0);
 | 
			
		||||
		if (lastExpr instanceof DeclareContext || lastExpr instanceof DeclareFunctionContext)
 | 
			
		||||
			log.severe(getError(ctx, Messages.getString("BoppiChecker.5"))); //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
		return resultType;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@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()));
 | 
			
		||||
		} catch (NumberFormatException e) {
 | 
			
		||||
			log.severe(getError(ctx, Messages.getString("BoppiChecker.6"), ctx.LITERAL10().getText())); //$NON-NLS-1$
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
 | 
			
		||||
		return new ArrayType(size, visit(ctx.type()));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -322,40 +416,44 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
 | 
			
		|||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Type visitTypeVariable(TypeVariableContext ctx) {
 | 
			
		||||
		return visit(ctx.variable());
 | 
			
		||||
		inType = true;
 | 
			
		||||
		Type type = visit(ctx.variable());
 | 
			
		||||
		inType = false;
 | 
			
		||||
		return type;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Type visitVariableArray(VariableArrayContext ctx) {
 | 
			
		||||
		Type t = visit(ctx.variable());
 | 
			
		||||
 | 
			
		||||
		boolean wasOnLhs = inLhs;
 | 
			
		||||
		inLhs = false;
 | 
			
		||||
		checkConstraint(visit(ctx.expr()), SimpleType.INT, ctx);
 | 
			
		||||
		inLhs = wasOnLhs;
 | 
			
		||||
 | 
			
		||||
		Type t = visit(ctx.variable());
 | 
			
		||||
 | 
			
		||||
		if (t instanceof ArrayType) {
 | 
			
		||||
			ArrayType arrayType = (ArrayType) t;
 | 
			
		||||
			return arrayType.getType();
 | 
			
		||||
		} else {
 | 
			
		||||
			log.severe(getError(ctx, "Expected array type but got %s", t));
 | 
			
		||||
			log.severe(getError(ctx, Messages.getString("BoppiChecker.7"), t)); //$NON-NLS-1$
 | 
			
		||||
			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")) {
 | 
			
		||||
			if (prop.equals("length")) { //$NON-NLS-1$
 | 
			
		||||
				return SimpleType.INT;
 | 
			
		||||
			}
 | 
			
		||||
			else {
 | 
			
		||||
				log.severe(getError(ctx, "Unknown array property '%s'.", prop));
 | 
			
		||||
			} else {
 | 
			
		||||
				log.severe(getError(ctx, Messages.getString("BoppiChecker.9"), prop)); //$NON-NLS-1$
 | 
			
		||||
				return SimpleType.VOID;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else {
 | 
			
		||||
			log.severe(getError(ctx, "Record types not implemented."));
 | 
			
		||||
		} else {
 | 
			
		||||
			log.severe(getError(ctx, Messages.getString("BoppiChecker.10"))); //$NON-NLS-1$
 | 
			
		||||
			return SimpleType.VOID;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -365,6 +463,34 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
 | 
			
		|||
		try {
 | 
			
		||||
			Variable<Type> var = an.symbols.get(ctx.getText());
 | 
			
		||||
 | 
			
		||||
			if (!inType) {
 | 
			
		||||
				if (inLhs) {
 | 
			
		||||
					if (var.isConstant()) {
 | 
			
		||||
						if (var.isAssigned())
 | 
			
		||||
							log.severe(getError(ctx, Messages.getString("BoppiChecker.8"), ctx.getText())); //$NON-NLS-1$
 | 
			
		||||
						else if (var.isPossiblyAssigned())
 | 
			
		||||
							log.warning(getError(ctx, Messages.getString("BoppiChecker.13"), ctx.getText())); //$NON-NLS-1$
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					if (inLhs) {
 | 
			
		||||
						if (isWithinRule(ctx, an.variableRoot.get(var), IfContext.class)
 | 
			
		||||
								|| isWithinRule(ctx, an.variableRoot.get(var), WhileContext.class)
 | 
			
		||||
								|| isWithinRule(ctx, an.variableRoot.get(var), DeclareFunctionContext.class))
 | 
			
		||||
							var.possiblyAssign();
 | 
			
		||||
						else
 | 
			
		||||
							var.assign();
 | 
			
		||||
					}
 | 
			
		||||
				} else {
 | 
			
		||||
					if (!var.isAssigned()) {
 | 
			
		||||
						if (var.isPossiblyAssigned()
 | 
			
		||||
								|| isWithinRule(ctx, an.variableRoot.get(var), DeclareFunctionContext.class))
 | 
			
		||||
							log.warning(getError(ctx, Messages.getString("BoppiChecker.14"), ctx.getText())); //$NON-NLS-1$
 | 
			
		||||
						else
 | 
			
		||||
							log.severe(getError(ctx, Messages.getString("BoppiChecker.15"), ctx.getText())); //$NON-NLS-1$
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			try {
 | 
			
		||||
				an.function.put(ctx, an.symbols.getFunctionScope());
 | 
			
		||||
			} catch (NoProgramException e) {
 | 
			
		||||
| 
						 | 
				
			
			@ -393,13 +519,13 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
 | 
			
		|||
		if (ctx.expr().size() == 1) {
 | 
			
		||||
			Type type = visit(ctx.expr(0));
 | 
			
		||||
			if (SimpleType.VOID.equals(type))
 | 
			
		||||
				log.severe(getError(ctx, "Cannot print argument of type %s.", type));
 | 
			
		||||
				log.severe(getError(ctx, Messages.getString("BoppiChecker.11"), type)); //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
			return type;
 | 
			
		||||
		} else {
 | 
			
		||||
			ctx.expr().stream().map(this::visit).forEach((type) -> {
 | 
			
		||||
				if (SimpleType.VOID.equals(type))
 | 
			
		||||
					log.severe(getError(ctx, "Cannot print argument of type %s.", type));
 | 
			
		||||
					log.severe(getError(ctx, Messages.getString("BoppiChecker.12"), type)); //$NON-NLS-1$
 | 
			
		||||
			});
 | 
			
		||||
			return SimpleType.VOID;
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,6 +22,13 @@ import pp.s1184725.boppi.util.RegisterPool;
 | 
			
		|||
 * @author Frank Wibbelink
 | 
			
		||||
 */
 | 
			
		||||
public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
			
		||||
	/**
 | 
			
		||||
	 * Whether local variables are always deallocated at the end of a function
 | 
			
		||||
	 * call to provide good memory management or kept if there are multiple
 | 
			
		||||
	 * references to the function for full closure support.
 | 
			
		||||
	 */
 | 
			
		||||
	public static final boolean PROPER_CLEANUP_INSTEAD_OF_PROPER_CLOSURES = false;
 | 
			
		||||
 | 
			
		||||
	private static final int ARBASESIZE = 16;
 | 
			
		||||
	private static final Num ZERO = new Num(0);
 | 
			
		||||
	private static final Num OFFSET_ARP = new Num(-4), OFFSET_RETURN_ADDR = new Num(-8),
 | 
			
		||||
| 
						 | 
				
			
			@ -31,7 +38,7 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
			
		|||
 | 
			
		||||
	private Program prog;
 | 
			
		||||
	private Annotations an;
 | 
			
		||||
	private final Reg tempArp = new Reg("ART"), arp = Machine.ARP_REG;
 | 
			
		||||
	private final Reg tempArp = new Reg("ART"), arp = Machine.ARP_REG; //$NON-NLS-1$
 | 
			
		||||
	private Logger logger;
 | 
			
		||||
	private RegisterPool regPool;
 | 
			
		||||
	private int labels = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -78,7 +85,7 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
			
		|||
 | 
			
		||||
	protected BoppiGenerator(Annotations annotations, Logger logger) {
 | 
			
		||||
		try {
 | 
			
		||||
			String memlib = new String(Files.readAllBytes(ToolChain.PATH.resolve("memlib.iloc")));
 | 
			
		||||
			String memlib = new String(Files.readAllBytes(ToolChain.PATH.resolve("memlib.iloc"))); //$NON-NLS-1$
 | 
			
		||||
			prog = Assembler.instance().assemble(memlib);
 | 
			
		||||
		} catch (Exception e) {
 | 
			
		||||
			throw new RuntimeException(e);
 | 
			
		||||
| 
						 | 
				
			
			@ -95,7 +102,7 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
			
		|||
	 */
 | 
			
		||||
	private Op emit(String comment, Label label, OpCode opCode, Operand... args) {
 | 
			
		||||
		Op result = new Op(label, opCode, args);
 | 
			
		||||
		result.setComment(String.format("%s", comment));
 | 
			
		||||
		result.setComment(String.format("%s", comment)); //$NON-NLS-1$
 | 
			
		||||
		this.prog.addInstr(result);
 | 
			
		||||
		return result;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -117,8 +124,8 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
			
		|||
	 *            register containing the top-level AR
 | 
			
		||||
	 */
 | 
			
		||||
	private void incARReferences(Reg temp, Reg ar) {
 | 
			
		||||
		String comment = "AR incRef";
 | 
			
		||||
		Label loop = makeLabel("aril"), done = makeLabel("arid");
 | 
			
		||||
		String comment = "AR incRef"; //$NON-NLS-1$
 | 
			
		||||
		Label loop = makeLabel("aril"), done = makeLabel("arid"); //$NON-NLS-1$ //$NON-NLS-2$
 | 
			
		||||
		emit(comment, OpCode.i2i, ar, tempArp);
 | 
			
		||||
		emit(comment, OpCode.cmp_NE, tempArp, RegisterPool.ZERO, temp);
 | 
			
		||||
		emit(comment, OpCode.cbr, temp, loop, done);
 | 
			
		||||
| 
						 | 
				
			
			@ -126,7 +133,7 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
			
		|||
		emit(comment, OpCode.push, temp);
 | 
			
		||||
		emit(comment, OpCode.subI, tempArp, new Num(ARBASESIZE), temp);
 | 
			
		||||
		emit(comment, OpCode.push, temp);
 | 
			
		||||
		emit(comment, OpCode.jumpI, new Label("memaddref"));
 | 
			
		||||
		emit(comment, OpCode.jumpI, new Label("memaddref")); //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
		emit(comment, OpCode.loadAI, tempArp, OFFSET_AL, tempArp);
 | 
			
		||||
		emit(comment, OpCode.cmp_NE, tempArp, RegisterPool.ZERO, temp);
 | 
			
		||||
| 
						 | 
				
			
			@ -143,8 +150,8 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
			
		|||
	 *            register containing the top-level AR
 | 
			
		||||
	 */
 | 
			
		||||
	private void decARReferences(Reg temp, Reg ar) {
 | 
			
		||||
		String comment = "AR decRef";
 | 
			
		||||
		Label loop = makeLabel("ardl"), done = makeLabel("ardd");
 | 
			
		||||
		String comment = "AR decRef"; //$NON-NLS-1$
 | 
			
		||||
		Label loop = makeLabel("ardl"), done = makeLabel("ardd"); //$NON-NLS-1$ //$NON-NLS-2$
 | 
			
		||||
		emit(comment, OpCode.i2i, ar, tempArp);
 | 
			
		||||
		emit(comment, OpCode.cmp_NE, tempArp, RegisterPool.ZERO, temp);
 | 
			
		||||
		emit(comment, OpCode.cbr, temp, loop, done);
 | 
			
		||||
| 
						 | 
				
			
			@ -152,7 +159,7 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
			
		|||
		emit(comment, OpCode.push, temp);
 | 
			
		||||
		emit(comment, OpCode.subI, tempArp, new Num(ARBASESIZE), temp);
 | 
			
		||||
		emit(comment, OpCode.push, temp);
 | 
			
		||||
		emit(comment, OpCode.jumpI, new Label("memfree"));
 | 
			
		||||
		emit(comment, OpCode.jumpI, new Label("memfree")); //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
		emit(comment, OpCode.loadAI, tempArp, OFFSET_AL, tempArp);
 | 
			
		||||
		emit(comment, OpCode.cmp_NE, tempArp, RegisterPool.ZERO, temp);
 | 
			
		||||
| 
						 | 
				
			
			@ -170,12 +177,12 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
			
		|||
	 *            the size of the memory chunk
 | 
			
		||||
	 */
 | 
			
		||||
	private void malloc(Reg temp, int size) {
 | 
			
		||||
		emit("malloc", OpCode.loadI, new Num(prog.size() + 5), temp);
 | 
			
		||||
		emit("malloc", OpCode.push, temp);
 | 
			
		||||
		emit("malloc", OpCode.loadI, new Num(size), temp);
 | 
			
		||||
		emit("malloc", OpCode.push, temp);
 | 
			
		||||
		emit("malloc", OpCode.jumpI, new Label("memalloc"));
 | 
			
		||||
		emit("malloc", OpCode.pop, temp);
 | 
			
		||||
		emit("malloc", OpCode.loadI, new Num(prog.size() + 5), temp); //$NON-NLS-1$
 | 
			
		||||
		emit("malloc", OpCode.push, temp); //$NON-NLS-1$
 | 
			
		||||
		emit("malloc", OpCode.loadI, new Num(size), temp); //$NON-NLS-1$
 | 
			
		||||
		emit("malloc", OpCode.push, temp); //$NON-NLS-1$
 | 
			
		||||
		emit("malloc", OpCode.jumpI, new Label("memalloc")); //$NON-NLS-1$ //$NON-NLS-2$
 | 
			
		||||
		emit("malloc", OpCode.pop, temp); //$NON-NLS-1$
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			@ -188,11 +195,11 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
			
		|||
	 *            the register containing the size of the memory chunk
 | 
			
		||||
	 */
 | 
			
		||||
	private void malloc(Reg temp, Reg size) {
 | 
			
		||||
		emit("malloc", OpCode.loadI, new Num(prog.size() + 4), temp);
 | 
			
		||||
		emit("malloc", OpCode.push, temp);
 | 
			
		||||
		emit("malloc", OpCode.push, size);
 | 
			
		||||
		emit("malloc", OpCode.jumpI, new Label("memalloc"));
 | 
			
		||||
		emit("malloc", OpCode.pop, temp);
 | 
			
		||||
		emit("malloc", OpCode.loadI, new Num(prog.size() + 4), temp); //$NON-NLS-1$
 | 
			
		||||
		emit("malloc", OpCode.push, temp); //$NON-NLS-1$
 | 
			
		||||
		emit("malloc", OpCode.push, size); //$NON-NLS-1$
 | 
			
		||||
		emit("malloc", OpCode.jumpI, new Label("memalloc")); //$NON-NLS-1$ //$NON-NLS-2$
 | 
			
		||||
		emit("malloc", OpCode.pop, temp); //$NON-NLS-1$
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			@ -204,10 +211,10 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
			
		|||
	 *            the register containing the memory pointer
 | 
			
		||||
	 */
 | 
			
		||||
	private void free(Reg temp, Reg ptr) {
 | 
			
		||||
		emit("free", OpCode.loadI, new Num(prog.size() + 4), temp);
 | 
			
		||||
		emit("free", OpCode.push, temp);
 | 
			
		||||
		emit("free", OpCode.push, ptr);
 | 
			
		||||
		emit("free", OpCode.jumpI, new Label("memfree"));
 | 
			
		||||
		emit("free", OpCode.loadI, new Num(prog.size() + 4), temp); //$NON-NLS-1$
 | 
			
		||||
		emit("free", OpCode.push, temp); //$NON-NLS-1$
 | 
			
		||||
		emit("free", OpCode.push, ptr); //$NON-NLS-1$
 | 
			
		||||
		emit("free", OpCode.jumpI, new Label("memfree")); //$NON-NLS-1$ //$NON-NLS-2$
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			@ -219,10 +226,10 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
			
		|||
	 *            the register containing the memory pointer
 | 
			
		||||
	 */
 | 
			
		||||
	private void incRef(Reg temp, Reg ptr) {
 | 
			
		||||
		emit("memaddref", OpCode.loadI, new Num(prog.size() + 4), temp);
 | 
			
		||||
		emit("memaddref", OpCode.push, temp);
 | 
			
		||||
		emit("memaddref", OpCode.push, ptr);
 | 
			
		||||
		emit("memaddref", OpCode.jumpI, new Label("memaddref"));
 | 
			
		||||
		emit("memaddref", OpCode.loadI, new Num(prog.size() + 4), temp); //$NON-NLS-1$
 | 
			
		||||
		emit("memaddref", OpCode.push, temp); //$NON-NLS-1$
 | 
			
		||||
		emit("memaddref", OpCode.push, ptr); //$NON-NLS-1$
 | 
			
		||||
		emit("memaddref", OpCode.jumpI, new Label("memaddref")); //$NON-NLS-1$ //$NON-NLS-2$
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			@ -238,7 +245,7 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
			
		|||
	private String getError(ParserRuleContext node, String message, Object... args) {
 | 
			
		||||
		int line = node.getStart().getLine();
 | 
			
		||||
		int column = node.getStart().getCharPositionInLine();
 | 
			
		||||
		return String.format("Line %d:%d - %s", line, column, String.format(message, args));
 | 
			
		||||
		return String.format(Messages.getString("BoppiGenerator.1"), line, column, String.format(message, args)); //$NON-NLS-1$
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			@ -265,10 +272,10 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
			
		|||
	 */
 | 
			
		||||
	private void incrementReference(Type type, Reg addr) {
 | 
			
		||||
		if (!(type instanceof ReferenceType))
 | 
			
		||||
			logger.severe(String.format("INTERNAL: trying to increment reference for '%s'", type));
 | 
			
		||||
			logger.severe(String.format(Messages.getString("BoppiGenerator.0"), type)); //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
		regPool.withReg((temp, ar) -> {
 | 
			
		||||
			String comment = "add new reference";
 | 
			
		||||
			String comment = "add new reference"; //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
			incRef(temp, addr);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -292,11 +299,11 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
			
		|||
	 */
 | 
			
		||||
	private void decrementReference(Type type, Reg addr) {
 | 
			
		||||
		if (!(type instanceof ReferenceType))
 | 
			
		||||
			logger.severe(String.format("INTERNAL: trying to increment reference for '%s'", type));
 | 
			
		||||
			logger.severe(String.format(Messages.getString("BoppiGenerator.2"), type)); //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
		regPool.withReg((temp) -> {
 | 
			
		||||
			Label isNull = makeLabel("ynul"), notNull = makeLabel("nnul");
 | 
			
		||||
			String comment = "remove old reference";
 | 
			
		||||
			Label isNull = makeLabel("ynul"), notNull = makeLabel("nnul"); //$NON-NLS-1$ //$NON-NLS-2$
 | 
			
		||||
			String comment = "remove old reference"; //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
			emit(comment, OpCode.cmp_EQ, addr, RegisterPool.ZERO, temp);
 | 
			
		||||
			emit(comment, OpCode.cbr, temp, isNull, notNull);
 | 
			
		||||
| 
						 | 
				
			
			@ -328,9 +335,9 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
			
		|||
				Type type = var.getType();
 | 
			
		||||
 | 
			
		||||
				if (type instanceof ReferenceType) {
 | 
			
		||||
					Label isNull = makeLabel("ynul"), notNull = makeLabel("nnul");
 | 
			
		||||
					String comment = "remove reference";
 | 
			
		||||
					emit(comment + " get var", OpCode.loadAI, arp, new Num(var.getOffset()), addr);
 | 
			
		||||
					Label isNull = makeLabel("ynul"), notNull = makeLabel("nnul"); //$NON-NLS-1$ //$NON-NLS-2$
 | 
			
		||||
					String comment = "remove reference"; //$NON-NLS-1$
 | 
			
		||||
					emit(comment + " get var", OpCode.loadAI, arp, new Num(var.getOffset()), addr); //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
					emit(comment, OpCode.cmp_EQ, addr, RegisterPool.ZERO, ft);
 | 
			
		||||
					emit(comment, OpCode.cbr, ft, isNull, notNull);
 | 
			
		||||
| 
						 | 
				
			
			@ -388,16 +395,18 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
			
		|||
		} catch (Exception e) {
 | 
			
		||||
 | 
			
		||||
			if (tree instanceof ParserRuleContext)
 | 
			
		||||
				logger.severe(getError((ParserRuleContext) tree, "%s: %s", e.getClass().getName(), e.getMessage()));
 | 
			
		||||
				logger.severe(getError((ParserRuleContext) tree, Messages.getString("BoppiGenerator.3"), //$NON-NLS-1$
 | 
			
		||||
						e.getClass().getName(), e.getMessage()));
 | 
			
		||||
			else
 | 
			
		||||
				logger.severe(String.format("Error %s: %s", e.getClass().getName(), e.getMessage()));
 | 
			
		||||
				logger.severe(
 | 
			
		||||
						String.format(Messages.getString("BoppiGenerator.4"), e.getClass().getName(), e.getMessage())); //$NON-NLS-1$
 | 
			
		||||
			return null;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Reg visitAssign(AssignContext ctx) {
 | 
			
		||||
		Reg result = visit(ctx.singleExpr());
 | 
			
		||||
		Reg result = visit(ctx.assignStat() != null ? ctx.assignStat() : ctx.expr());
 | 
			
		||||
 | 
			
		||||
		regPool.blockReg(result, () -> {
 | 
			
		||||
			Type type = an.types.get(ctx);
 | 
			
		||||
| 
						 | 
				
			
			@ -406,7 +415,7 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
			
		|||
			if (type instanceof ReferenceType) {
 | 
			
		||||
				regPool.blockReg(addr, () -> {
 | 
			
		||||
					regPool.withReg((temp) -> {
 | 
			
		||||
						emit("load reference", OpCode.load, addr, temp);
 | 
			
		||||
						emit("load reference", OpCode.load, addr, temp); //$NON-NLS-1$
 | 
			
		||||
						decrementReference(an.types.get(ctx), temp);
 | 
			
		||||
					});
 | 
			
		||||
				});
 | 
			
		||||
| 
						 | 
				
			
			@ -414,12 +423,12 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
			
		|||
 | 
			
		||||
			OpCode op = (SimpleType.CHAR.equals(type)) ? OpCode.cstore : OpCode.store;
 | 
			
		||||
 | 
			
		||||
			emit("to " + ctx.variable().getText(), op, result, addr);
 | 
			
		||||
			emit("to " + ctx.variable().getText(), op, result, addr); //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
			if (type instanceof ReferenceType) {
 | 
			
		||||
				regPool.blockReg(addr, () -> {
 | 
			
		||||
					regPool.withReg((temp) -> {
 | 
			
		||||
						emit("load reference", OpCode.load, addr, temp);
 | 
			
		||||
						emit("load reference", OpCode.load, addr, temp); //$NON-NLS-1$
 | 
			
		||||
						incrementReference(an.types.get(ctx), temp);
 | 
			
		||||
					});
 | 
			
		||||
				});
 | 
			
		||||
| 
						 | 
				
			
			@ -429,29 +438,45 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
			
		|||
		return result;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Call function:
 | 
			
		||||
	 * <ol>
 | 
			
		||||
	 * <li>generate load function reference</li>
 | 
			
		||||
	 * <li>generate allocate AR</li>
 | 
			
		||||
	 * <li>visit parameters and generate store in AR</li>
 | 
			
		||||
	 * <li>generate register save</li>
 | 
			
		||||
	 * <li>generate store AL, AR, return address</li>
 | 
			
		||||
	 * <li>generate increment AR references</li>
 | 
			
		||||
	 * <li>generate set AR</li>
 | 
			
		||||
	 * <li>generate call function</li>
 | 
			
		||||
	 * <li>generate decrement AR references</li>
 | 
			
		||||
	 * <li>generate register restore</li>
 | 
			
		||||
	 * <li>generate restore AR</li>
 | 
			
		||||
	 * </ol>
 | 
			
		||||
	 */
 | 
			
		||||
	@Override
 | 
			
		||||
	public Reg visitCall(CallContext ctx) {
 | 
			
		||||
		String base = "call " + ctx.variable().getText() + " - ";
 | 
			
		||||
		String base = "call " + ctx.variable().getText() + " - "; //$NON-NLS-1$ //$NON-NLS-2$
 | 
			
		||||
		Variable<Type> function = an.variables.get(ctx.variable());
 | 
			
		||||
		TupleType parameters = (TupleType) ((FunctionType) function.getType()).getParameter();
 | 
			
		||||
 | 
			
		||||
		Reg ar = regPool.withReg((tempAR) -> {
 | 
			
		||||
			regPool.withReg((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);
 | 
			
		||||
				emit(base + "load function reference", OpCode.load, addr, rt); //$NON-NLS-1$
 | 
			
		||||
				emit(base + "load AR size", OpCode.loadAI, rt, OFFSET_FUNCREF_ARSIZE, rt); //$NON-NLS-1$
 | 
			
		||||
				malloc(tempAR, rt);
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			emit(base + "shift AR", OpCode.addI, tempAR, new Num(ARBASESIZE), tempAR);
 | 
			
		||||
			emit(base + "shift AR", OpCode.addI, tempAR, new Num(ARBASESIZE), tempAR); //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
			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, tempAR, new Num(parameters.getOffset(i)));
 | 
			
		||||
					emit(base + "store param " + i, OpCode.cstoreAI, exprReg, tempAR, new Num(parameters.getOffset(i))); //$NON-NLS-1$
 | 
			
		||||
				else
 | 
			
		||||
					emit(base + "store param " + i, OpCode.storeAI, exprReg, tempAR, new Num(parameters.getOffset(i)));
 | 
			
		||||
					emit(base + "store param " + i, OpCode.storeAI, exprReg, tempAR, new Num(parameters.getOffset(i))); //$NON-NLS-1$
 | 
			
		||||
			}
 | 
			
		||||
			;
 | 
			
		||||
		});
 | 
			
		||||
| 
						 | 
				
			
			@ -459,26 +484,26 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
			
		|||
		Stack<Reg> inUse = new Stack<>();
 | 
			
		||||
		for (Reg reg : regPool.getInUse()) {
 | 
			
		||||
			inUse.push(reg);
 | 
			
		||||
			emit(base + "register save " + reg.getName(), OpCode.push, reg);
 | 
			
		||||
			emit(base + "register save " + reg.getName(), OpCode.push, reg); //$NON-NLS-1$
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		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);
 | 
			
		||||
				emit(base + "load function reference", OpCode.load, addr, r2); //$NON-NLS-1$
 | 
			
		||||
				emit(base + "link caller ARP", OpCode.storeAI, arp, ar, OFFSET_ARP); //$NON-NLS-1$
 | 
			
		||||
				emit(base + "load AL", OpCode.loadAI, r2, OFFSET_FUNCREF_ARP, r1); //$NON-NLS-1$
 | 
			
		||||
				emit(base + "link AL", OpCode.storeAI, r1, ar, OFFSET_AL); //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
				emit("add ref for callee's AL", OpCode.loadAI, ar, OFFSET_AL, tempArp);
 | 
			
		||||
				emit("add ref for callee's AL", OpCode.loadAI, ar, OFFSET_AL, tempArp); //$NON-NLS-1$
 | 
			
		||||
				incARReferences(r1, tempArp);
 | 
			
		||||
 | 
			
		||||
				emit(base + "load return address", OpCode.loadI, new Num(prog.size() + 5), r1);
 | 
			
		||||
				emit(base + "set return address", OpCode.storeAI, r1, ar, OFFSET_RETURN_ADDR);
 | 
			
		||||
				emit(base + "move ARP", OpCode.i2i, ar, arp);
 | 
			
		||||
				emit(base + "load return address", OpCode.loadI, new Num(prog.size() + 5), r1); //$NON-NLS-1$
 | 
			
		||||
				emit(base + "set return address", OpCode.storeAI, r1, ar, OFFSET_RETURN_ADDR); //$NON-NLS-1$
 | 
			
		||||
				emit(base + "move ARP", OpCode.i2i, ar, arp); //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
				emit(base + "load target address", OpCode.loadAI, r2, OFFSET_FUNCREF_ADDR, r1);
 | 
			
		||||
				emit(base + "execute", OpCode.jump, r1);
 | 
			
		||||
				emit(base + "load target address", OpCode.loadAI, r2, OFFSET_FUNCREF_ADDR, r1); //$NON-NLS-1$
 | 
			
		||||
				emit(base + "execute", OpCode.jump, r1); //$NON-NLS-1$
 | 
			
		||||
			});
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -488,12 +513,12 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
			
		|||
 | 
			
		||||
		while (!inUse.isEmpty()) {
 | 
			
		||||
			Reg reg = inUse.pop();
 | 
			
		||||
			emit(base + "register unsave " + reg.getName(), OpCode.pop, reg);
 | 
			
		||||
			emit(base + "register unsave " + reg.getName(), OpCode.pop, reg); //$NON-NLS-1$
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		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);
 | 
			
		||||
			emit(base + "load result", OpCode.loadAI, arp, OFFSET_RETURN_VAL, res); //$NON-NLS-1$
 | 
			
		||||
			emit(base + "reset ARP", OpCode.loadAI, arp, OFFSET_ARP, arp); //$NON-NLS-1$
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -505,13 +530,13 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
			
		|||
		if (t instanceof ReferenceType) {
 | 
			
		||||
 | 
			
		||||
			regPool.withReg((addr) -> {
 | 
			
		||||
				emit("load var address", OpCode.addI, arp, new Num(var.getOffset()), addr);
 | 
			
		||||
				emit("load var address", OpCode.addI, arp, new Num(var.getOffset()), addr); //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
				if (t instanceof ArrayType) {
 | 
			
		||||
					ArrayType type = (ArrayType) t;
 | 
			
		||||
					regPool.withReg((temp) -> {
 | 
			
		||||
						malloc(temp, type.getReferenceSize());
 | 
			
		||||
						emit("link array", OpCode.store, temp, addr);
 | 
			
		||||
						emit("link array", OpCode.store, temp, addr); //$NON-NLS-1$
 | 
			
		||||
					});
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
| 
						 | 
				
			
			@ -527,6 +552,8 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
			
		|||
	 * <li>generate entry point</li>
 | 
			
		||||
	 * <li>visit function body</li>
 | 
			
		||||
	 * <li>generate return</li>
 | 
			
		||||
	 * <li>generate check whether no more AR references</li>
 | 
			
		||||
	 * <li>if so, generate dereference local variables</li>
 | 
			
		||||
	 * <li>generate allocate function reference</li>
 | 
			
		||||
	 * <li>generate store entry point and AR to function reference</li>
 | 
			
		||||
	 * <li>generate store function reference in variable</li>
 | 
			
		||||
| 
						 | 
				
			
			@ -535,67 +562,64 @@ 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() + " - ";
 | 
			
		||||
		String base = "define " + ctx.IDENTIFIER().getText() + " - "; //$NON-NLS-1$ //$NON-NLS-2$
 | 
			
		||||
 | 
			
		||||
		Label skip = makeLabel("s");
 | 
			
		||||
		emit(base + "jump over body", OpCode.jumpI, skip);
 | 
			
		||||
		int callAddress = emit(base + "entry point", OpCode.nop).getLine();
 | 
			
		||||
		Label skip = makeLabel("s"); //$NON-NLS-1$
 | 
			
		||||
		emit(base + "jump over body", OpCode.jumpI, skip); //$NON-NLS-1$
 | 
			
		||||
		int callAddress = emit(base + "entry point", OpCode.nop).getLine(); //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
		Reg result = visit(ctx.body);
 | 
			
		||||
		emit(base + "move result", OpCode.storeAI, result, arp, OFFSET_RETURN_VAL);
 | 
			
		||||
 | 
			
		||||
		dereferenceLocalVariables(an.function.get(ctx));
 | 
			
		||||
		if (((FunctionType) an.variables.get(ctx).getType()).getReturn() != SimpleType.VOID)
 | 
			
		||||
			emit(base + "move result", OpCode.storeAI, result, arp, OFFSET_RETURN_VAL); //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
		if (PROPER_CLEANUP_INSTEAD_OF_PROPER_CLOSURES)
 | 
			
		||||
			dereferenceLocalVariables(an.function.get(ctx));
 | 
			
		||||
		else {
 | 
			
		||||
			Label cleanup = makeLabel("ycl"), noCleanup = makeLabel("ncl"); //$NON-NLS-1$ //$NON-NLS-2$
 | 
			
		||||
			regPool.withReg((ref, one) -> {
 | 
			
		||||
				emit("load ref count", OpCode.loadAI, arp, OFFSET_REF_COUNT, ref); //$NON-NLS-1$
 | 
			
		||||
				emit("one", OpCode.loadI, new Num(1), one); //$NON-NLS-1$
 | 
			
		||||
				emit("check more than one ref", OpCode.cmp_LE, ref, one, ref); //$NON-NLS-1$
 | 
			
		||||
				emit("remove vars if last reference", OpCode.cbr, ref, cleanup, noCleanup); //$NON-NLS-1$
 | 
			
		||||
			});
 | 
			
		||||
			emit("cleanup target", cleanup, OpCode.nop); //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
			dereferenceLocalVariables(an.function.get(ctx));
 | 
			
		||||
 | 
			
		||||
			emit("no cleanup target", noCleanup, OpCode.nop); //$NON-NLS-1$
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		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 + "load return address", OpCode.loadAI, arp, OFFSET_RETURN_ADDR, r1); //$NON-NLS-1$
 | 
			
		||||
			emit(base + "go to return address", OpCode.jump, r1); //$NON-NLS-1$
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		emit(base + "skip target", skip, OpCode.nop);
 | 
			
		||||
		emit(base + "skip target", skip, OpCode.nop); //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
		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()),
 | 
			
		||||
			emit(base + "load target address", OpCode.loadI, new Num(callAddress), r1); //$NON-NLS-1$
 | 
			
		||||
			emit(base + "set target address", OpCode.storeAI, r1, r2, OFFSET_FUNCREF_ADDR); //$NON-NLS-1$
 | 
			
		||||
			emit(base + "copy ARP", OpCode.storeAI, arp, r2, OFFSET_FUNCREF_ARP); //$NON-NLS-1$
 | 
			
		||||
			emit(base + "load AR size", OpCode.loadI, new Num(ARBASESIZE + an.function.get(ctx).getLocalDataSize()), //$NON-NLS-1$
 | 
			
		||||
					r1);
 | 
			
		||||
			emit(base + "set AR size", OpCode.storeAI, r1, r2, OFFSET_FUNCREF_ARSIZE);
 | 
			
		||||
			emit(base + "set AR size", OpCode.storeAI, r1, r2, OFFSET_FUNCREF_ARSIZE); //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
			emit(base + "set function reference", OpCode.storeAI, r2, arp, new Num(an.variables.get(ctx).getOffset()));
 | 
			
		||||
			emit(base + "set function reference", OpCode.storeAI, r2, arp, new Num(an.variables.get(ctx).getOffset())); //$NON-NLS-1$
 | 
			
		||||
			incARReferences(r2, arp);
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		return null;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Reg visitExpr(ExprContext ctx) {
 | 
			
		||||
 | 
			
		||||
		for (int i = 0; i < ctx.singleExpr().size() - 1; i++) {
 | 
			
		||||
			ParserRuleContext expr = ctx.singleExpr(i);
 | 
			
		||||
			Reg result = visit(expr);
 | 
			
		||||
 | 
			
		||||
			if (result != null)
 | 
			
		||||
				regPool.blockReg(result, () -> {
 | 
			
		||||
					Type type = an.types.get(expr);
 | 
			
		||||
 | 
			
		||||
					if (type instanceof ReferenceType)
 | 
			
		||||
						decrementReference(type, result);
 | 
			
		||||
				});
 | 
			
		||||
		}
 | 
			
		||||
		ParserRuleContext last = ctx.singleExpr(ctx.singleExpr().size() - 1);
 | 
			
		||||
 | 
			
		||||
		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);
 | 
			
		||||
		emit("load address", op, result, result); //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
		if (type instanceof ReferenceType)
 | 
			
		||||
			regPool.blockReg(result, () -> {
 | 
			
		||||
| 
						 | 
				
			
			@ -607,44 +631,48 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
			
		|||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Reg visitIf(IfContext ctx) {
 | 
			
		||||
		Label toTrue = makeLabel("if_t");
 | 
			
		||||
		Label toFalse = makeLabel("if_f");
 | 
			
		||||
		Label toEnd = makeLabel("if_e");
 | 
			
		||||
		Reg cond = visit(ctx.cond);
 | 
			
		||||
		Label toTrue = makeLabel("if_t"); //$NON-NLS-1$
 | 
			
		||||
		Label toFalse = makeLabel("if_f"); //$NON-NLS-1$
 | 
			
		||||
		Label toEnd = makeLabel("if_e"); //$NON-NLS-1$
 | 
			
		||||
		try {
 | 
			
		||||
			Reg cond = visit(ctx.cond);
 | 
			
		||||
 | 
			
		||||
		if (ctx.onFalse == null) {
 | 
			
		||||
			emit("", OpCode.cbr, cond, toTrue, toEnd);
 | 
			
		||||
			if (ctx.onFalse == null) {
 | 
			
		||||
				emit("", OpCode.cbr, cond, toTrue, toEnd); //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
			emit("", toTrue, OpCode.nop);
 | 
			
		||||
			visit(ctx.onTrue);
 | 
			
		||||
				emit("", toTrue, OpCode.nop); //$NON-NLS-1$
 | 
			
		||||
				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("end target", toEnd, OpCode.nop); //$NON-NLS-1$
 | 
			
		||||
				return null;
 | 
			
		||||
			} else if (an.types.get(ctx) == SimpleType.VOID) {
 | 
			
		||||
				emit("", OpCode.cbr, cond, toTrue, toFalse); //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
			emit("", toTrue, OpCode.nop);
 | 
			
		||||
			visit(ctx.onTrue);
 | 
			
		||||
			emit("", OpCode.jumpI, toEnd);
 | 
			
		||||
				emit("", toTrue, OpCode.nop); //$NON-NLS-1$
 | 
			
		||||
				visit(ctx.onTrue);
 | 
			
		||||
				emit("", OpCode.jumpI, toEnd); //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
			emit("", toFalse, OpCode.nop);
 | 
			
		||||
			visit(ctx.onFalse);
 | 
			
		||||
				emit("", toFalse, OpCode.nop); //$NON-NLS-1$
 | 
			
		||||
				visit(ctx.onFalse);
 | 
			
		||||
 | 
			
		||||
			emit("end target", toEnd, OpCode.nop);
 | 
			
		||||
			return null;
 | 
			
		||||
		} else {
 | 
			
		||||
			return regPool.withReg((target) -> {
 | 
			
		||||
				emit("", OpCode.cbr, cond, toTrue, toFalse);
 | 
			
		||||
				emit("end target", toEnd, OpCode.nop); //$NON-NLS-1$
 | 
			
		||||
				return null;
 | 
			
		||||
			} else {
 | 
			
		||||
				return regPool.withReg((target) -> {
 | 
			
		||||
					emit("", OpCode.cbr, cond, toTrue, toFalse); //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
				emit("", toTrue, OpCode.nop);
 | 
			
		||||
				emit("result", OpCode.i2i, visit(ctx.onTrue), target);
 | 
			
		||||
				emit("", OpCode.jumpI, toEnd);
 | 
			
		||||
					emit("", toTrue, OpCode.nop); //$NON-NLS-1$
 | 
			
		||||
					emit("result", OpCode.i2i, visit(ctx.onTrue), target); //$NON-NLS-1$
 | 
			
		||||
					emit("", OpCode.jumpI, toEnd); //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
				emit("", toFalse, OpCode.nop);
 | 
			
		||||
				emit("result", OpCode.i2i, visit(ctx.onFalse), target);
 | 
			
		||||
					emit("", toFalse, OpCode.nop); //$NON-NLS-1$
 | 
			
		||||
					emit("result", OpCode.i2i, visit(ctx.onFalse), target); //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
				emit("end target", toEnd, OpCode.nop);
 | 
			
		||||
			});
 | 
			
		||||
					emit("end target", toEnd, OpCode.nop); //$NON-NLS-1$
 | 
			
		||||
				});
 | 
			
		||||
			}
 | 
			
		||||
		} catch (Throwable e) {
 | 
			
		||||
			throw e;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -680,23 +708,23 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
			
		|||
 | 
			
		||||
				return regPool.withReg((i, temp1) -> {
 | 
			
		||||
					regPool.withReg((temp2) -> {
 | 
			
		||||
						Label cond = makeLabel("aeqc"), loop = makeLabel("aeql"), end = makeLabel("aeqe");
 | 
			
		||||
						Label cond = makeLabel("aeqc"), loop = makeLabel("aeql"), end = makeLabel("aeqe"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 | 
			
		||||
 | 
			
		||||
						emit("iterate from 0", OpCode.loadI, ZERO, i);
 | 
			
		||||
						emit("equality true", OpCode.loadI, new Num(1), temp1);
 | 
			
		||||
						emit("iterate from 0", OpCode.loadI, ZERO, i); //$NON-NLS-1$
 | 
			
		||||
						emit("equality true", OpCode.loadI, new Num(1), temp1); //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
						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 arr size", cond, OpCode.loadAI, lhs, OFFSET_REF_SIZE, temp2); //$NON-NLS-1$
 | 
			
		||||
						emit("check i in bounds", OpCode.cmp_LT, i, temp2, temp2); //$NON-NLS-1$
 | 
			
		||||
						emit("loop", OpCode.cbr, temp2, loop, end); //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
						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("load left", loop, OpCode.loadAO, lhs, i, temp1); //$NON-NLS-1$
 | 
			
		||||
						emit("load right", OpCode.loadAO, rhs, i, temp2); //$NON-NLS-1$
 | 
			
		||||
						emit("increment i", OpCode.addI, i, elementSize, i); //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
						emit("compare elements", OpCode.cmp_EQ, temp1, temp2, temp1);
 | 
			
		||||
						emit("loop or break", OpCode.cbr, temp1, cond, end);
 | 
			
		||||
						emit("compare elements", OpCode.cmp_EQ, temp1, temp2, temp1); //$NON-NLS-1$
 | 
			
		||||
						emit("loop or break", OpCode.cbr, temp1, cond, end); //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
						emit("end equality check", end, OpCode.nop);
 | 
			
		||||
						emit("end equality check", end, OpCode.nop); //$NON-NLS-1$
 | 
			
		||||
					});
 | 
			
		||||
				});
 | 
			
		||||
			});
 | 
			
		||||
| 
						 | 
				
			
			@ -723,6 +751,13 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
			
		|||
		return lhs;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Reg visitLambda(LambdaContext ctx) {
 | 
			
		||||
		emit(ctx.getText(), OpCode.haltI, new Num(768));
 | 
			
		||||
 | 
			
		||||
		return visit(ctx.expr());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Reg visitLiteralBoolean(LiteralBooleanContext ctx) {
 | 
			
		||||
		return regPool.withReg((reg) -> {
 | 
			
		||||
| 
						 | 
				
			
			@ -745,7 +780,7 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
			
		|||
			try {
 | 
			
		||||
				val = Integer.parseInt(ctx.LITERAL10().getText());
 | 
			
		||||
			} catch (NumberFormatException e) {
 | 
			
		||||
				logger.severe(getError(ctx, "Error parsing number '%s'", ctx.LITERAL10().getText()));
 | 
			
		||||
				logger.severe(getError(ctx, Messages.getString("BoppiGenerator.5"), ctx.LITERAL10().getText())); //$NON-NLS-1$
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			emit(ctx.getText(), OpCode.loadI, new Num(val), reg);
 | 
			
		||||
| 
						 | 
				
			
			@ -754,15 +789,15 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
			
		|||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Reg visitPrefix1(Prefix1Context ctx) {
 | 
			
		||||
		Reg reg = visit(ctx.singleExpr());
 | 
			
		||||
		Reg reg = visit(ctx.expr());
 | 
			
		||||
 | 
			
		||||
		switch (ctx.op.getType()) {
 | 
			
		||||
		case BoppiLexer.MINUS:
 | 
			
		||||
			emit("unary -", OpCode.rsubI, reg, ZERO, reg);
 | 
			
		||||
			emit("unary -", OpCode.rsubI, reg, ZERO, reg); //$NON-NLS-1$
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		case BoppiLexer.NOT:
 | 
			
		||||
			emit("not", OpCode.xorI, reg, new Num(1), reg);
 | 
			
		||||
			emit("not", OpCode.xorI, reg, new Num(1), reg); //$NON-NLS-1$
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		return reg;
 | 
			
		||||
| 
						 | 
				
			
			@ -770,15 +805,15 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
			
		|||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Reg visitProgram(ProgramContext ctx) {
 | 
			
		||||
		emit("initialise zero register", OpCode.loadI, ZERO, RegisterPool.ZERO);
 | 
			
		||||
		emit("initialise zero register", OpCode.loadI, ZERO, RegisterPool.ZERO); //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
		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); //$NON-NLS-1$
 | 
			
		||||
		visitChildren(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); //$NON-NLS-1$
 | 
			
		||||
		regPool.withReg((temp) -> {
 | 
			
		||||
			free(temp, arp);
 | 
			
		||||
		});
 | 
			
		||||
| 
						 | 
				
			
			@ -794,37 +829,37 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
			
		|||
 | 
			
		||||
				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("", OpCode.in, new Str(""), temp); //$NON-NLS-1$ //$NON-NLS-2$
 | 
			
		||||
						Reg addr = visit(expr);
 | 
			
		||||
						emit("save to var " + expr.getText(), OpCode.store, temp, addr);
 | 
			
		||||
						emit("save to var " + expr.getText(), OpCode.store, temp, addr); //$NON-NLS-1$
 | 
			
		||||
					});
 | 
			
		||||
				} else if (SimpleType.CHAR.equals(an.types.get(expr))) {
 | 
			
		||||
					// Get input until at least 1 character
 | 
			
		||||
					Label getTarget = makeLabel("lcin_l");
 | 
			
		||||
					Label continueTarget = makeLabel("lcin_e");
 | 
			
		||||
					Label getTarget = makeLabel("lcin_l"); //$NON-NLS-1$
 | 
			
		||||
					Label continueTarget = makeLabel("lcin_e"); //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
					regPool.withReg((temp, count) -> {
 | 
			
		||||
						emit("", getTarget, OpCode.cin, new Str(""));
 | 
			
		||||
						emit("str length", OpCode.pop, count);
 | 
			
		||||
						emit("repeat if 0 length", OpCode.cbr, count, continueTarget, getTarget);
 | 
			
		||||
						emit("", getTarget, OpCode.cin, new Str("")); //$NON-NLS-1$ //$NON-NLS-2$
 | 
			
		||||
						emit("str length", OpCode.pop, count); //$NON-NLS-1$
 | 
			
		||||
						emit("repeat if 0 length", OpCode.cbr, count, continueTarget, getTarget); //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
						// Get character
 | 
			
		||||
						emit("pop whole string", continueTarget, OpCode.cpop, temp);
 | 
			
		||||
						emit("pop whole string", continueTarget, OpCode.cpop, temp); //$NON-NLS-1$
 | 
			
		||||
						Reg addr = visit(expr);
 | 
			
		||||
						emit("save to var " + expr.getText(), OpCode.cstore, temp, addr);
 | 
			
		||||
						emit("save to var " + expr.getText(), OpCode.cstore, temp, addr); //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
						// Pop all remaining characters
 | 
			
		||||
						Label loopTarget = makeLabel("lcpop_l");
 | 
			
		||||
						Label iterTarget = makeLabel("lcpop_c");
 | 
			
		||||
						Label stopTarget = makeLabel("lcpop_e");
 | 
			
		||||
						emit("pop remaining", loopTarget, OpCode.subI, count, new Num(1), temp);
 | 
			
		||||
						emit("pop remaining", OpCode.cbr, count, iterTarget, stopTarget);
 | 
			
		||||
						emit("pop remaining", iterTarget, OpCode.cpop, temp);
 | 
			
		||||
						emit("pop remaining", OpCode.jumpI, loopTarget);
 | 
			
		||||
						emit("pop remaining", stopTarget, OpCode.nop);
 | 
			
		||||
						Label loopTarget = makeLabel("lcpop_l"); //$NON-NLS-1$
 | 
			
		||||
						Label iterTarget = makeLabel("lcpop_c"); //$NON-NLS-1$
 | 
			
		||||
						Label stopTarget = makeLabel("lcpop_e"); //$NON-NLS-1$
 | 
			
		||||
						emit("pop remaining", loopTarget, OpCode.subI, count, new Num(1), temp); //$NON-NLS-1$
 | 
			
		||||
						emit("pop remaining", OpCode.cbr, count, iterTarget, stopTarget); //$NON-NLS-1$
 | 
			
		||||
						emit("pop remaining", iterTarget, OpCode.cpop, temp); //$NON-NLS-1$
 | 
			
		||||
						emit("pop remaining", OpCode.jumpI, loopTarget); //$NON-NLS-1$
 | 
			
		||||
						emit("pop remaining", stopTarget, OpCode.nop); //$NON-NLS-1$
 | 
			
		||||
					});
 | 
			
		||||
				} else {
 | 
			
		||||
					emit("reading unknown type", OpCode.haltI, new Num(0x72656164));
 | 
			
		||||
					emit("reading unknown type", OpCode.haltI, new Num(0x72656164)); //$NON-NLS-1$
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -834,68 +869,88 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
			
		|||
 | 
			
		||||
			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("", OpCode.in, new Str(""), result); //$NON-NLS-1$ //$NON-NLS-2$
 | 
			
		||||
					Reg addr = visit(expr);
 | 
			
		||||
					emit("save to var " + expr.getText(), OpCode.store, result, addr);
 | 
			
		||||
					emit("save to var " + expr.getText(), OpCode.store, result, addr); //$NON-NLS-1$
 | 
			
		||||
				});
 | 
			
		||||
			} else if (SimpleType.CHAR.equals(an.types.get(expr))) {
 | 
			
		||||
				return regPool.withReg((count, result) -> {
 | 
			
		||||
					// Get input until at least 1 character
 | 
			
		||||
					Label getTarget = makeLabel("lcin_l");
 | 
			
		||||
					Label continueTarget = makeLabel("lcin_e");
 | 
			
		||||
					Label getTarget = makeLabel("lcin_l"); //$NON-NLS-1$
 | 
			
		||||
					Label continueTarget = makeLabel("lcin_e"); //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
					emit("", getTarget, OpCode.cin, new Str(""));
 | 
			
		||||
					emit("str length", OpCode.pop, count);
 | 
			
		||||
					emit("repeat if 0 length", OpCode.cbr, count, continueTarget, getTarget);
 | 
			
		||||
					emit("", getTarget, OpCode.cin, new Str("")); //$NON-NLS-1$ //$NON-NLS-2$
 | 
			
		||||
					emit("str length", OpCode.pop, count); //$NON-NLS-1$
 | 
			
		||||
					emit("repeat if 0 length", OpCode.cbr, count, continueTarget, getTarget); //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
					// Get character
 | 
			
		||||
					emit("pop whole string", continueTarget, OpCode.cpop, result);
 | 
			
		||||
					emit("pop whole string", continueTarget, OpCode.cpop, result); //$NON-NLS-1$
 | 
			
		||||
					Reg addr = visit(expr);
 | 
			
		||||
					emit("save to var " + expr.getText(), OpCode.cstore, result, addr);
 | 
			
		||||
					emit("save to var " + expr.getText(), OpCode.cstore, result, addr); //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
					// Pop all remaining characters
 | 
			
		||||
					Label loopTarget = makeLabel("lcpop_l");
 | 
			
		||||
					Label iterTarget = makeLabel("lcpop_c");
 | 
			
		||||
					Label stopTarget = makeLabel("lcpop_e");
 | 
			
		||||
					Label loopTarget = makeLabel("lcpop_l"); //$NON-NLS-1$
 | 
			
		||||
					Label iterTarget = makeLabel("lcpop_c"); //$NON-NLS-1$
 | 
			
		||||
					Label stopTarget = makeLabel("lcpop_e"); //$NON-NLS-1$
 | 
			
		||||
					regPool.withReg((temp) -> {
 | 
			
		||||
						emit("pop remaining", loopTarget, OpCode.subI, count, new Num(1), count);
 | 
			
		||||
						emit("pop remaining", OpCode.cbr, count, iterTarget, stopTarget);
 | 
			
		||||
						emit("pop remaining", iterTarget, OpCode.cpop, temp);
 | 
			
		||||
						emit("pop remaining", OpCode.jumpI, loopTarget);
 | 
			
		||||
						emit("pop remaining", stopTarget, OpCode.nop);
 | 
			
		||||
						emit("pop remaining", loopTarget, OpCode.subI, count, new Num(1), count); //$NON-NLS-1$
 | 
			
		||||
						emit("pop remaining", OpCode.cbr, count, iterTarget, stopTarget); //$NON-NLS-1$
 | 
			
		||||
						emit("pop remaining", iterTarget, OpCode.cpop, temp); //$NON-NLS-1$
 | 
			
		||||
						emit("pop remaining", OpCode.jumpI, loopTarget); //$NON-NLS-1$
 | 
			
		||||
						emit("pop remaining", stopTarget, OpCode.nop); //$NON-NLS-1$
 | 
			
		||||
					});
 | 
			
		||||
				});
 | 
			
		||||
			} else {
 | 
			
		||||
				emit("reading unknown type", OpCode.haltI, new Num(0x72656164));
 | 
			
		||||
				emit("reading unknown type", OpCode.haltI, new Num(0x72656164)); //$NON-NLS-1$
 | 
			
		||||
				return null;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Reg visitStats(StatsContext ctx) {
 | 
			
		||||
 | 
			
		||||
		for (int i = 0; i < ctx.stat().size() - 1; i++) {
 | 
			
		||||
			ParserRuleContext expr = ctx.stat(i);
 | 
			
		||||
			Reg result = visit(expr);
 | 
			
		||||
 | 
			
		||||
			if (result != null)
 | 
			
		||||
				regPool.blockReg(result, () -> {
 | 
			
		||||
					Type type = an.types.get(expr);
 | 
			
		||||
 | 
			
		||||
					if (type instanceof ReferenceType)
 | 
			
		||||
						decrementReference(type, result);
 | 
			
		||||
				});
 | 
			
		||||
		}
 | 
			
		||||
		ParserRuleContext last = ctx.stat(ctx.stat().size() - 1);
 | 
			
		||||
 | 
			
		||||
		return visit(last);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Reg visitVariableArray(VariableArrayContext ctx) {
 | 
			
		||||
		ArrayType type = (ArrayType) an.types.get(ctx.variable());
 | 
			
		||||
		Reg addr = visit(ctx.variable());
 | 
			
		||||
 | 
			
		||||
		emit("get array object", OpCode.load, addr, addr);
 | 
			
		||||
		emit("get array object", OpCode.load, addr, addr); //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
		regPool.blockReg(addr, () -> {
 | 
			
		||||
			Reg offset = visit(ctx.expr());
 | 
			
		||||
			regPool.blockReg(offset, () -> {
 | 
			
		||||
				regPool.withReg((r1, r2) -> {
 | 
			
		||||
					Label outOfBounds = makeLabel("oob"), inBounds = makeLabel("nob");
 | 
			
		||||
					Label outOfBounds = makeLabel("oob"), inBounds = makeLabel("nob"); //$NON-NLS-1$ //$NON-NLS-2$
 | 
			
		||||
 | 
			
		||||
					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));
 | 
			
		||||
					emit("check array index", OpCode.loadAI, addr, OFFSET_REF_SIZE, r1); //$NON-NLS-1$
 | 
			
		||||
					emit("check array index", OpCode.divI, r1, new Num(type.getType().getSize()), r1); //$NON-NLS-1$
 | 
			
		||||
					emit("check array index", OpCode.cmp_LT, offset, r1, r1); //$NON-NLS-1$
 | 
			
		||||
					emit("check array index", OpCode.cmp_GE, offset, RegisterPool.ZERO, r2); //$NON-NLS-1$
 | 
			
		||||
					emit("check array index", OpCode.and, r1, r2, r2); //$NON-NLS-1$
 | 
			
		||||
					emit("check array index", OpCode.cbr, r2, inBounds, outOfBounds); //$NON-NLS-1$
 | 
			
		||||
					emit("array index out of bounds", outOfBounds, OpCode.haltI, new Num(0x616f6f62)); //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
					emit("multiply index by size", inBounds, OpCode.multI, offset, new Num(type.getType().getSize()),
 | 
			
		||||
					emit("multiply index by size", inBounds, OpCode.multI, offset, new Num(type.getType().getSize()), //$NON-NLS-1$
 | 
			
		||||
							offset);
 | 
			
		||||
					emit("get array index address", OpCode.add, addr, offset, addr);
 | 
			
		||||
					emit("get array index address", OpCode.add, addr, offset, addr); //$NON-NLS-1$
 | 
			
		||||
				});
 | 
			
		||||
			});
 | 
			
		||||
		});
 | 
			
		||||
| 
						 | 
				
			
			@ -909,7 +964,7 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
			
		|||
		Reg addr = visit(ctx.variable());
 | 
			
		||||
 | 
			
		||||
		if (innerType instanceof ArrayType) {
 | 
			
		||||
			emit("get size", OpCode.loadAI, addr, OFFSET_REF_SIZE, addr);
 | 
			
		||||
			emit("get size", OpCode.loadAI, addr, OFFSET_REF_SIZE, addr); //$NON-NLS-1$
 | 
			
		||||
			return addr;
 | 
			
		||||
		} else {
 | 
			
		||||
			return addr;
 | 
			
		||||
| 
						 | 
				
			
			@ -925,30 +980,30 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
			
		|||
			int functionDepth = an.function.get(ctx).getFunctionDepth();
 | 
			
		||||
 | 
			
		||||
			if (var.getDepth() < functionDepth) {
 | 
			
		||||
				emit("travelling ALs", OpCode.i2i, ar, tempArp);
 | 
			
		||||
				emit("travelling ALs", OpCode.i2i, ar, tempArp); //$NON-NLS-1$
 | 
			
		||||
				for (int i = var.getDepth(); i < functionDepth; i++)
 | 
			
		||||
					emit(StringUtils.repeat(' ', i) + "\\", OpCode.loadAI, tempArp, OFFSET_AL, tempArp);
 | 
			
		||||
					emit(StringUtils.repeat(' ', i) + "\\", OpCode.loadAI, tempArp, OFFSET_AL, tempArp); //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
				ar = tempArp;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			emit("add offset", OpCode.addI, ar, new Num(var.getOffset()), result);
 | 
			
		||||
			emit("add offset", OpCode.addI, ar, new Num(var.getOffset()), result); //$NON-NLS-1$
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Reg visitWhile(WhileContext ctx) {
 | 
			
		||||
		Label toLoop = makeLabel("while_t");
 | 
			
		||||
		Label toCond = makeLabel("while_f");
 | 
			
		||||
		Label toEnd = makeLabel("while_e");
 | 
			
		||||
		Label toLoop = makeLabel("while_t"); //$NON-NLS-1$
 | 
			
		||||
		Label toCond = makeLabel("while_f"); //$NON-NLS-1$
 | 
			
		||||
		Label toEnd = makeLabel("while_e"); //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
		emit("to condition", OpCode.jumpI, toCond);
 | 
			
		||||
		emit("loop target", toLoop, OpCode.nop);
 | 
			
		||||
		emit("to condition", OpCode.jumpI, toCond); //$NON-NLS-1$
 | 
			
		||||
		emit("loop target", toLoop, OpCode.nop); //$NON-NLS-1$
 | 
			
		||||
		visit(ctx.onTrue);
 | 
			
		||||
 | 
			
		||||
		emit("condition target", toCond, OpCode.nop);
 | 
			
		||||
		emit("", OpCode.cbr, visit(ctx.cond), toLoop, toEnd);
 | 
			
		||||
		emit("end target", toEnd, OpCode.nop);
 | 
			
		||||
		emit("condition target", toCond, OpCode.nop); //$NON-NLS-1$
 | 
			
		||||
		emit("", OpCode.cbr, visit(ctx.cond), toLoop, toEnd); //$NON-NLS-1$
 | 
			
		||||
		emit("end target", toEnd, OpCode.nop); //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
		return null;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -963,16 +1018,16 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
			
		|||
					Type type = an.types.get(expr);
 | 
			
		||||
 | 
			
		||||
					if (SimpleType.BOOL.equals(type) || SimpleType.INT.equals(type)) {
 | 
			
		||||
						emit("", OpCode.out, new Str(""), result);
 | 
			
		||||
						emit("", OpCode.out, new Str(""), result); //$NON-NLS-1$ //$NON-NLS-2$
 | 
			
		||||
					} else if (SimpleType.CHAR.equals(type)) {
 | 
			
		||||
						regPool.withReg((temp) -> {
 | 
			
		||||
							emit("push character", OpCode.cpush, result);
 | 
			
		||||
							emit("load 1", OpCode.loadI, new Num(1), temp);
 | 
			
		||||
							emit("push 1", OpCode.push, temp);
 | 
			
		||||
							emit("print character", OpCode.cout, new Str(""));
 | 
			
		||||
							emit("push character", OpCode.cpush, result); //$NON-NLS-1$
 | 
			
		||||
							emit("load 1", OpCode.loadI, new Num(1), temp); //$NON-NLS-1$
 | 
			
		||||
							emit("push 1", OpCode.push, temp); //$NON-NLS-1$
 | 
			
		||||
							emit("print character", OpCode.cout, new Str("")); //$NON-NLS-1$ //$NON-NLS-2$
 | 
			
		||||
						});
 | 
			
		||||
					} else {
 | 
			
		||||
						emit("writing unknown type", OpCode.haltI, new Num(0x77726974));
 | 
			
		||||
						emit("writing unknown type", OpCode.haltI, new Num(0x77726974)); //$NON-NLS-1$
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					if (!expr.equals(ctx.expr(ctx.expr().size() - 1)))
 | 
			
		||||
| 
						 | 
				
			
			@ -989,18 +1044,18 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
			
		|||
				Type type = an.types.get(expr);
 | 
			
		||||
 | 
			
		||||
				if (SimpleType.BOOL.equals(type) || SimpleType.INT.equals(type)) {
 | 
			
		||||
					emit("", OpCode.out, new Str(""), result);
 | 
			
		||||
					emit("", OpCode.out, new Str(""), result); //$NON-NLS-1$ //$NON-NLS-2$
 | 
			
		||||
					return result;
 | 
			
		||||
				} else if (SimpleType.CHAR.equals(type)) {
 | 
			
		||||
					regPool.withReg((temp) -> {
 | 
			
		||||
						emit("push character", OpCode.cpush, result);
 | 
			
		||||
						emit("load 1", OpCode.loadI, new Num(1), temp);
 | 
			
		||||
						emit("push 1", OpCode.push, temp);
 | 
			
		||||
						emit("print character", OpCode.cout, new Str(""));
 | 
			
		||||
						emit("push character", OpCode.cpush, result); //$NON-NLS-1$
 | 
			
		||||
						emit("load 1", OpCode.loadI, new Num(1), temp); //$NON-NLS-1$
 | 
			
		||||
						emit("push 1", OpCode.push, temp); //$NON-NLS-1$
 | 
			
		||||
						emit("print character", OpCode.cout, new Str("")); //$NON-NLS-1$ //$NON-NLS-2$
 | 
			
		||||
					});
 | 
			
		||||
					return result;
 | 
			
		||||
				} else {
 | 
			
		||||
					emit("writing unknown type", OpCode.haltI, new Num(0x77726974));
 | 
			
		||||
					emit("writing unknown type", OpCode.haltI, new Num(0x77726974)); //$NON-NLS-1$
 | 
			
		||||
					return null;
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -33,19 +33,18 @@ public class CachingSymbolTable<T extends Type> {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Opens a lexical scope.
 | 
			
		||||
	 * Closes a lexical scope for a function declaration, removing all
 | 
			
		||||
	 * declarations within this scope, restoring the local offset and decreasing
 | 
			
		||||
	 * the lookup depth.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @return function scope details
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @throws NoProgramException
 | 
			
		||||
	 *             if no scope is open
 | 
			
		||||
	 */
 | 
			
		||||
	public void openScope() {
 | 
			
		||||
		symbolMapStack.push(new HashMap<>());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Opens a lexical scope for a function declaration, resetting the local
 | 
			
		||||
	 * offset and increasing the lookup depth.
 | 
			
		||||
	 */
 | 
			
		||||
	public void openFunctionScope() {
 | 
			
		||||
		openScope();
 | 
			
		||||
		functionScope.push(new FunctionScope<T>(functionScope.size()));
 | 
			
		||||
	public FunctionScope<T> closeFunctionScope() throws NoProgramException {
 | 
			
		||||
		closeScope();
 | 
			
		||||
		return functionScope.pop();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			@ -69,40 +68,97 @@ public class CachingSymbolTable<T extends Type> {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Closes a lexical scope for a function declaration, removing all
 | 
			
		||||
	 * declarations within this scope, restoring the local offset and decreasing
 | 
			
		||||
	 * the lookup depth.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @return function scope details
 | 
			
		||||
	 * Returns the type of an identifier, if any.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param id
 | 
			
		||||
	 *            the name of the identifier
 | 
			
		||||
	 * @return the type of the identifier
 | 
			
		||||
	 * @throws UndeclaredException
 | 
			
		||||
	 *             if the identifier is not declared
 | 
			
		||||
	 * @throws NoProgramException
 | 
			
		||||
	 *             if no scope is open
 | 
			
		||||
	 */
 | 
			
		||||
	public FunctionScope<T> closeFunctionScope() throws NoProgramException {
 | 
			
		||||
		closeScope();
 | 
			
		||||
		return functionScope.pop();
 | 
			
		||||
	public Variable<T> get(String id) throws UndeclaredException, NoProgramException {
 | 
			
		||||
		if (!this.has(id))
 | 
			
		||||
			throw new UndeclaredException(String.format(Messages.getString("SymbolTable.1"), id)); //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
		return symbolCache.get(id);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Opens a scope, executes the given code and closes the scope. This is a
 | 
			
		||||
	 * helper method to make sure calls to {@link #openScope()} and
 | 
			
		||||
	 * {@link #closeScope()} are balanced. {@code code} must take no arguments
 | 
			
		||||
	 * and must return a value.
 | 
			
		||||
	 * Returns the current function scope, if any.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param <U>
 | 
			
		||||
	 *            the type of the code's return value
 | 
			
		||||
	 * @param code
 | 
			
		||||
	 *            the code to execute within the scope
 | 
			
		||||
	 * @return the return value of the code
 | 
			
		||||
	 * @return the current function scope
 | 
			
		||||
	 * @throws NoProgramException
 | 
			
		||||
	 *             if no scope is open
 | 
			
		||||
	 */
 | 
			
		||||
	public <U> U withScope(Supplier<U> code) {
 | 
			
		||||
	public FunctionScope<T> getFunctionScope() throws NoProgramException {
 | 
			
		||||
		if (symbolMapStack.isEmpty())
 | 
			
		||||
			throw new NoProgramException();
 | 
			
		||||
 | 
			
		||||
		return functionScope.peek();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Returns whether the given identifier is declared.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param id
 | 
			
		||||
	 *            the name of the identifier
 | 
			
		||||
	 * @return true if the identifier has a declared type
 | 
			
		||||
	 * @throws NoProgramException
 | 
			
		||||
	 *             if no scope is open
 | 
			
		||||
	 */
 | 
			
		||||
	public boolean has(String id) throws NoProgramException {
 | 
			
		||||
		if (symbolMapStack.isEmpty())
 | 
			
		||||
			throw new NoProgramException();
 | 
			
		||||
 | 
			
		||||
		return symbolCache.containsKey(id);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Opens a lexical scope for a function declaration, resetting the local
 | 
			
		||||
	 * offset and increasing the lookup depth.
 | 
			
		||||
	 */
 | 
			
		||||
	public void openFunctionScope() {
 | 
			
		||||
		openScope();
 | 
			
		||||
		U result = code.get();
 | 
			
		||||
		try {
 | 
			
		||||
			closeScope();
 | 
			
		||||
		} catch (NoProgramException e) {
 | 
			
		||||
		}
 | 
			
		||||
		return result;
 | 
			
		||||
		functionScope.push(new FunctionScope<T>(functionScope.size()));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Opens a lexical scope.
 | 
			
		||||
	 */
 | 
			
		||||
	public void openScope() {
 | 
			
		||||
		symbolMapStack.push(new HashMap<>());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Associates an identifier with a certain type in the current lexical scope
 | 
			
		||||
	 * and returns the newly made variable instance that belongs to this
 | 
			
		||||
	 * identifier. Throws an exception if the identifier is declared in this
 | 
			
		||||
	 * scope already.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param id
 | 
			
		||||
	 *            the name of the identifier
 | 
			
		||||
	 * @param type
 | 
			
		||||
	 *            the type of the identifier
 | 
			
		||||
	 * @return the type of the identifier
 | 
			
		||||
	 * @throws DeclaredException
 | 
			
		||||
	 *             if the identifier is declared in the current scope already
 | 
			
		||||
	 * @throws NoProgramException
 | 
			
		||||
	 *             if no scope is open
 | 
			
		||||
	 */
 | 
			
		||||
	public Variable<T> put(String id, T type) throws DeclaredException, NoProgramException {
 | 
			
		||||
		if (symbolMapStack.isEmpty())
 | 
			
		||||
			throw new NoProgramException();
 | 
			
		||||
 | 
			
		||||
		if (symbolMapStack.peek().containsKey(id))
 | 
			
		||||
			throw new DeclaredException(String.format(Messages.getString("SymbolTable.0"), id)); //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
		Variable<T> var = functionScope.peek().addVariable(type);
 | 
			
		||||
 | 
			
		||||
		symbolMapStack.peek().put(id, var);
 | 
			
		||||
		symbolCache.put(id, var);
 | 
			
		||||
		return var;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			@ -127,81 +183,25 @@ public class CachingSymbolTable<T extends Type> {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Associates an identifier with a certain type in the current lexical scope
 | 
			
		||||
	 * and returns the newly made variable instance that belongs to this
 | 
			
		||||
	 * identifier. Throws an exception if the identifier is declared in this
 | 
			
		||||
	 * scope already.
 | 
			
		||||
	 * Opens a scope, executes the given code and closes the scope. This is a
 | 
			
		||||
	 * helper method to make sure calls to {@link #openScope()} and
 | 
			
		||||
	 * {@link #closeScope()} are balanced. {@code code} must take no arguments
 | 
			
		||||
	 * and must return a value.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param id
 | 
			
		||||
	 *            the name of the identifier
 | 
			
		||||
	 * @param type
 | 
			
		||||
	 *            the type of the identifier
 | 
			
		||||
	 * @return the type of the identifier
 | 
			
		||||
	 * @throws DeclaredException
 | 
			
		||||
	 *             if the identifier is declared in the current scope already
 | 
			
		||||
	 * @throws NoProgramException
 | 
			
		||||
	 *             if no scope is open
 | 
			
		||||
	 * @param <U>
 | 
			
		||||
	 *            the type of the code's return value
 | 
			
		||||
	 * @param code
 | 
			
		||||
	 *            the code to execute within the scope
 | 
			
		||||
	 * @return the return value of the code
 | 
			
		||||
	 */
 | 
			
		||||
	public Variable<T> put(String id, T type) throws DeclaredException, NoProgramException {
 | 
			
		||||
		if (symbolMapStack.isEmpty())
 | 
			
		||||
			throw new NoProgramException();
 | 
			
		||||
 | 
			
		||||
		if (symbolMapStack.peek().containsKey(id))
 | 
			
		||||
			throw new DeclaredException(String.format("Identifier '%s' already declared in this scope.", id));
 | 
			
		||||
 | 
			
		||||
		Variable<T> var = functionScope.peek().addVariable(type);
 | 
			
		||||
 | 
			
		||||
		symbolMapStack.peek().put(id, var);
 | 
			
		||||
		symbolCache.put(id, var);
 | 
			
		||||
		return var;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Returns whether the given identifier is declared.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param id
 | 
			
		||||
	 *            the name of the identifier
 | 
			
		||||
	 * @return true if the identifier has a declared type
 | 
			
		||||
	 * @throws NoProgramException
 | 
			
		||||
	 *             if no scope is open
 | 
			
		||||
	 */
 | 
			
		||||
	public boolean has(String id) throws NoProgramException {
 | 
			
		||||
		if (symbolMapStack.isEmpty())
 | 
			
		||||
			throw new NoProgramException();
 | 
			
		||||
 | 
			
		||||
		return symbolCache.containsKey(id);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Returns the type of an identifier, if any.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param id
 | 
			
		||||
	 *            the name of the identifier
 | 
			
		||||
	 * @return the type of the identifier
 | 
			
		||||
	 * @throws UndeclaredException
 | 
			
		||||
	 *             if the identifier is not declared
 | 
			
		||||
	 * @throws NoProgramException
 | 
			
		||||
	 *             if no scope is open
 | 
			
		||||
	 */
 | 
			
		||||
	public Variable<T> get(String id) throws UndeclaredException, NoProgramException {
 | 
			
		||||
		if (!this.has(id))
 | 
			
		||||
			throw new UndeclaredException(String.format("Identifier '%s' is undeclared.", id));
 | 
			
		||||
 | 
			
		||||
		return symbolCache.get(id);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Returns the current function scope, if any.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @return the current function scope
 | 
			
		||||
	 * @throws NoProgramException
 | 
			
		||||
	 *             if no scope is open
 | 
			
		||||
	 */
 | 
			
		||||
	public FunctionScope<T> getFunctionScope() throws NoProgramException {
 | 
			
		||||
		if (symbolMapStack.isEmpty())
 | 
			
		||||
			throw new NoProgramException();
 | 
			
		||||
 | 
			
		||||
		return functionScope.peek();
 | 
			
		||||
	public <U> U withScope(Supplier<U> code) {
 | 
			
		||||
		openScope();
 | 
			
		||||
		U result = code.get();
 | 
			
		||||
		try {
 | 
			
		||||
			closeScope();
 | 
			
		||||
		} catch (NoProgramException e) {
 | 
			
		||||
		}
 | 
			
		||||
		return result;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,25 +14,12 @@ import pp.s1184725.boppi.type.*;
 | 
			
		|||
 */
 | 
			
		||||
public class DebugCachingSymbolTable<T extends Type> extends CachingSymbolTable<T> {
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public void openFunctionScope() {
 | 
			
		||||
		super.openFunctionScope();
 | 
			
		||||
		System.out.println(this.getClass().getName() + ": entering scope depth " + functionScope.size());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public FunctionScope<T> closeFunctionScope() throws NoProgramException {
 | 
			
		||||
		System.out.println(this.getClass().getName() + ": leaving scope depth " + functionScope.size());
 | 
			
		||||
		return super.closeFunctionScope();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Variable<T> put(String id, T type) throws DeclaredException, NoProgramException {
 | 
			
		||||
		System.out.println(this.getClass().getName() + ": declaring '" + id + "' (" + type.toString() + ") at scope "
 | 
			
		||||
				+ functionScope.size());
 | 
			
		||||
		return super.put(id, type);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Variable<T> get(String id) throws UndeclaredException, NoProgramException {
 | 
			
		||||
		System.out.println(this.getClass().getName() + ": retrieving '" + id + "' (depth "
 | 
			
		||||
| 
						 | 
				
			
			@ -40,4 +27,17 @@ public class DebugCachingSymbolTable<T extends Type> extends CachingSymbolTable<
 | 
			
		|||
		return super.get(id);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public void openFunctionScope() {
 | 
			
		||||
		super.openFunctionScope();
 | 
			
		||||
		System.out.println(this.getClass().getName() + ": entering scope depth " + functionScope.size());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Variable<T> put(String id, T type) throws DeclaredException, NoProgramException {
 | 
			
		||||
		System.out.println(this.getClass().getName() + ": declaring '" + id + "' (" + type.toString() + ") at scope "
 | 
			
		||||
				+ functionScope.size());
 | 
			
		||||
		return super.put(id, type);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -42,6 +42,15 @@ public class FunctionScope<T extends Type> {
 | 
			
		|||
		return var;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Returns the current lexical scope/function depth.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @return the function depth
 | 
			
		||||
	 */
 | 
			
		||||
	public int getFunctionDepth() {
 | 
			
		||||
		return scopeDepth;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Returns the size, in bytes, required to store all the variables declared
 | 
			
		||||
	 * in the current function scope. Note that this is the size calculated up
 | 
			
		||||
| 
						 | 
				
			
			@ -55,15 +64,6 @@ public class FunctionScope<T extends Type> {
 | 
			
		|||
		return localVars.stream().map(Variable::getType).collect(Collectors.summingInt(Type::getSize));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Returns the current lexical scope/function depth.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @return the function depth
 | 
			
		||||
	 */
 | 
			
		||||
	public int getFunctionDepth() {
 | 
			
		||||
		return scopeDepth;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Returns the variables local to this function scope.
 | 
			
		||||
	 * 
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,31 @@
 | 
			
		|||
package pp.s1184725.boppi;
 | 
			
		||||
 | 
			
		||||
import java.util.MissingResourceException;
 | 
			
		||||
import java.util.ResourceBundle;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Class for externalized messages. Generated by Eclipse.
 | 
			
		||||
 */
 | 
			
		||||
public class Messages {
 | 
			
		||||
	private static final String BUNDLE_NAME = "pp.s1184725.boppi.messages"; //$NON-NLS-1$
 | 
			
		||||
 | 
			
		||||
	private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle.getBundle(BUNDLE_NAME);
 | 
			
		||||
 | 
			
		||||
	private Messages() {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Returns a messages for the given key.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param key
 | 
			
		||||
	 *            the key to lookup
 | 
			
		||||
	 * @return the appropriate message or notification of a missing message
 | 
			
		||||
	 */
 | 
			
		||||
	public static String getString(String key) {
 | 
			
		||||
		try {
 | 
			
		||||
			return RESOURCE_BUNDLE.getString(key);
 | 
			
		||||
		} catch (MissingResourceException e) {
 | 
			
		||||
			return '!' + key + '!';
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -30,142 +30,6 @@ public class ToolChain {
 | 
			
		|||
	 */
 | 
			
		||||
	public static Machine machine;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Opens a file for reading and returns its charstream. Throws an unhandled
 | 
			
		||||
	 * exception if the file could not be read.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param file
 | 
			
		||||
	 *            the file to read
 | 
			
		||||
	 * @return a {@link CharStream} to be used in the lexer phase
 | 
			
		||||
	 */
 | 
			
		||||
	public static CharStream getCharStream(Path file) {
 | 
			
		||||
		try {
 | 
			
		||||
			return CharStreams.fromFileName(file.toString());
 | 
			
		||||
		} catch (IOException e) {
 | 
			
		||||
			throw new RuntimeException(e);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Reads a string as a charstream.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param code
 | 
			
		||||
	 *            the string to read
 | 
			
		||||
	 * @return a {@link CharStream} to be used in the lexer phase
 | 
			
		||||
	 */
 | 
			
		||||
	public static CharStream getCharStream(String code) {
 | 
			
		||||
		return CharStreams.fromString(code);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Creates and initialises a lexer for a given character {@code stream} with
 | 
			
		||||
	 * the given {@code logger} attached to it.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param stream
 | 
			
		||||
	 *            the character stream to read from
 | 
			
		||||
	 * @param logger
 | 
			
		||||
	 *            the logger to attach
 | 
			
		||||
	 * @return the initialised lexer
 | 
			
		||||
	 */
 | 
			
		||||
	public static BoppiLexer getLexer(CharStream stream, Logger logger) {
 | 
			
		||||
		BoppiLexer lexer = new BoppiLexer(stream);
 | 
			
		||||
		lexer.removeErrorListeners();
 | 
			
		||||
		lexer.addErrorListener(new BaseErrorListener() {
 | 
			
		||||
			@Override
 | 
			
		||||
			public void syntaxError(Recognizer<?, ?> r, Object oSym, int l, int c, String msg, RecognitionException e) {
 | 
			
		||||
				logger.severe("" + l + ":" + c + " " + msg);
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		return lexer;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Creates and initialises a parser for a given {@code lexer} with the given
 | 
			
		||||
	 * {@code logger} attached to it.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param lexer
 | 
			
		||||
	 *            the lexer to read from
 | 
			
		||||
	 * @param logger
 | 
			
		||||
	 *            the logger to attach
 | 
			
		||||
	 * @return the initialised parser
 | 
			
		||||
	 */
 | 
			
		||||
	public static BoppiParser getParser(BoppiLexer lexer, Logger logger) {
 | 
			
		||||
		BoppiParser parser = new BoppiParser(new CommonTokenStream(lexer));
 | 
			
		||||
		parser.removeErrorListeners();
 | 
			
		||||
		parser.addErrorListener(new BaseErrorListener() {
 | 
			
		||||
			@Override
 | 
			
		||||
			public void syntaxError(Recognizer<?, ?> r, Object oSym, int l, int c, String msg, RecognitionException e) {
 | 
			
		||||
				logger.severe("" + l + ":" + c + " " + msg);
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		return parser;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Checks a (sub)program with the {@link BoppiChecker} and returns the
 | 
			
		||||
	 * annotations.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param program
 | 
			
		||||
	 *            the parse tree to check
 | 
			
		||||
	 * @param logger
 | 
			
		||||
	 *            the logger to write checker messages to
 | 
			
		||||
	 * @return the annotations made by the checker
 | 
			
		||||
	 */
 | 
			
		||||
	public static Annotations getAnnotations(ParseTree program, Logger logger) {
 | 
			
		||||
		return BoppiChecker.checkProgram(program, logger);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Generates ILOC code for an annotated program.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param program
 | 
			
		||||
	 *            the parse tree to convert to ILOC
 | 
			
		||||
	 * @param annotations
 | 
			
		||||
	 *            the annotations object provided by the checking phase
 | 
			
		||||
	 * @param logger
 | 
			
		||||
	 *            the logger to write checker messages to
 | 
			
		||||
	 * @return an ILOC program
 | 
			
		||||
	 */
 | 
			
		||||
	public static Program getILOC(ParseTree program, Logger logger, Annotations annotations) {
 | 
			
		||||
		return BoppiGenerator.generateProgram(program, annotations, logger);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Replaces all handlers for the given {@code logger} with a handler that
 | 
			
		||||
	 * appends to the list that is returned.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param logger
 | 
			
		||||
	 *            the logger to change
 | 
			
		||||
	 * @return the list to which messages are logged
 | 
			
		||||
	 */
 | 
			
		||||
	public static List<LogRecord> makeListLog(Logger logger) {
 | 
			
		||||
		List<LogRecord> log = new ArrayList<LogRecord>();
 | 
			
		||||
		Handler listLogger = new Handler() {
 | 
			
		||||
			@Override
 | 
			
		||||
			public void close() throws SecurityException {
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			@Override
 | 
			
		||||
			public void flush() {
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			@Override
 | 
			
		||||
			public void publish(LogRecord record) {
 | 
			
		||||
				log.add(record);
 | 
			
		||||
			}
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		for (Handler handler : logger.getHandlers())
 | 
			
		||||
			logger.removeHandler(handler);
 | 
			
		||||
 | 
			
		||||
		logger.setUseParentHandlers(false);
 | 
			
		||||
		logger.addHandler(listLogger);
 | 
			
		||||
 | 
			
		||||
		return log;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Shorthand for the compiler chain from {@link CharStream} to
 | 
			
		||||
	 * {@link Program}.
 | 
			
		||||
| 
						 | 
				
			
			@ -275,21 +139,14 @@ public class ToolChain {
 | 
			
		|||
		sb.append("digraph {\n");
 | 
			
		||||
 | 
			
		||||
		new ParseTreeWalker().walk(new ParseTreeListener() {
 | 
			
		||||
			@Override
 | 
			
		||||
			public void enterEveryRule(ParserRuleContext ctx) {
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			private String escape(String str) {
 | 
			
		||||
				return str.replaceAll("<", "<").replaceAll(">", ">").replaceAll("\"", """);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			@Override
 | 
			
		||||
			public void visitTerminal(TerminalNode node) {
 | 
			
		||||
				sb.append("\tn" + node.hashCode() + "[label=<" + escape(node.getText()) + ">;shape=rect]\n");
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			@Override
 | 
			
		||||
			public void visitErrorNode(ErrorNode node) {
 | 
			
		||||
				sb.append("\tn" + node.hashCode() + "[label=<" + escape(node.getText())
 | 
			
		||||
						+ ">;style=filled;fillcolor=red]\n");
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			@Override
 | 
			
		||||
			public void exitEveryRule(ParserRuleContext ctx) {
 | 
			
		||||
				float hue = (ctx.getClass().hashCode() % 65521) / 65521.0f;
 | 
			
		||||
| 
						 | 
				
			
			@ -310,11 +167,154 @@ public class ToolChain {
 | 
			
		|||
			}
 | 
			
		||||
 | 
			
		||||
			@Override
 | 
			
		||||
			public void enterEveryRule(ParserRuleContext ctx) {
 | 
			
		||||
			public void visitErrorNode(ErrorNode node) {
 | 
			
		||||
				sb.append("\tn" + node.hashCode() + "[label=<" + escape(node.getText())
 | 
			
		||||
						+ ">;style=filled;fillcolor=red]\n");
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			@Override
 | 
			
		||||
			public void visitTerminal(TerminalNode node) {
 | 
			
		||||
				sb.append("\tn" + node.hashCode() + "[label=<" + escape(node.getText()) + ">;shape=rect]\n");
 | 
			
		||||
			}
 | 
			
		||||
		}, tree);
 | 
			
		||||
		sb.append("}\n");
 | 
			
		||||
 | 
			
		||||
		return sb.toString();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Checks a (sub)program with the {@link BoppiChecker} and returns the
 | 
			
		||||
	 * annotations.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param program
 | 
			
		||||
	 *            the parse tree to check
 | 
			
		||||
	 * @param logger
 | 
			
		||||
	 *            the logger to write checker messages to
 | 
			
		||||
	 * @return the annotations made by the checker
 | 
			
		||||
	 */
 | 
			
		||||
	public static Annotations getAnnotations(ParseTree program, Logger logger) {
 | 
			
		||||
		return BoppiChecker.checkProgram(program, logger);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Opens a file for reading and returns its charstream. Throws an unhandled
 | 
			
		||||
	 * exception if the file could not be read.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param file
 | 
			
		||||
	 *            the file to read
 | 
			
		||||
	 * @return a {@link CharStream} to be used in the lexer phase
 | 
			
		||||
	 */
 | 
			
		||||
	public static CharStream getCharStream(Path file) {
 | 
			
		||||
		try {
 | 
			
		||||
			return CharStreams.fromFileName(file.toString());
 | 
			
		||||
		} catch (IOException e) {
 | 
			
		||||
			throw new RuntimeException(e);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Reads a string as a charstream.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param code
 | 
			
		||||
	 *            the string to read
 | 
			
		||||
	 * @return a {@link CharStream} to be used in the lexer phase
 | 
			
		||||
	 */
 | 
			
		||||
	public static CharStream getCharStream(String code) {
 | 
			
		||||
		return CharStreams.fromString(code);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Generates ILOC code for an annotated program.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param program
 | 
			
		||||
	 *            the parse tree to convert to ILOC
 | 
			
		||||
	 * @param annotations
 | 
			
		||||
	 *            the annotations object provided by the checking phase
 | 
			
		||||
	 * @param logger
 | 
			
		||||
	 *            the logger to write checker messages to
 | 
			
		||||
	 * @return an ILOC program
 | 
			
		||||
	 */
 | 
			
		||||
	public static Program getILOC(ParseTree program, Logger logger, Annotations annotations) {
 | 
			
		||||
		return BoppiGenerator.generateProgram(program, annotations, logger);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Creates and initialises a lexer for a given character {@code stream} with
 | 
			
		||||
	 * the given {@code logger} attached to it.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param stream
 | 
			
		||||
	 *            the character stream to read from
 | 
			
		||||
	 * @param logger
 | 
			
		||||
	 *            the logger to attach
 | 
			
		||||
	 * @return the initialised lexer
 | 
			
		||||
	 */
 | 
			
		||||
	public static BoppiLexer getLexer(CharStream stream, Logger logger) {
 | 
			
		||||
		BoppiLexer lexer = new BoppiLexer(stream);
 | 
			
		||||
		lexer.removeErrorListeners();
 | 
			
		||||
		lexer.addErrorListener(new BaseErrorListener() {
 | 
			
		||||
			@Override
 | 
			
		||||
			public void syntaxError(Recognizer<?, ?> r, Object oSym, int l, int c, String msg, RecognitionException e) {
 | 
			
		||||
				logger.severe("" + l + ":" + c + " " + msg);
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		return lexer;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Creates and initialises a parser for a given {@code lexer} with the given
 | 
			
		||||
	 * {@code logger} attached to it.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param lexer
 | 
			
		||||
	 *            the lexer to read from
 | 
			
		||||
	 * @param logger
 | 
			
		||||
	 *            the logger to attach
 | 
			
		||||
	 * @return the initialised parser
 | 
			
		||||
	 */
 | 
			
		||||
	public static BoppiParser getParser(BoppiLexer lexer, Logger logger) {
 | 
			
		||||
		BoppiParser parser = new BoppiParser(new CommonTokenStream(lexer));
 | 
			
		||||
		parser.removeErrorListeners();
 | 
			
		||||
		parser.addErrorListener(new BaseErrorListener() {
 | 
			
		||||
			@Override
 | 
			
		||||
			public void syntaxError(Recognizer<?, ?> r, Object oSym, int l, int c, String msg, RecognitionException e) {
 | 
			
		||||
				logger.severe("" + l + ":" + c + " " + msg);
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		return parser;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Replaces all handlers for the given {@code logger} with a handler that
 | 
			
		||||
	 * appends to the list that is returned.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param logger
 | 
			
		||||
	 *            the logger to change
 | 
			
		||||
	 * @return the list to which messages are logged
 | 
			
		||||
	 */
 | 
			
		||||
	public static List<LogRecord> makeListLog(Logger logger) {
 | 
			
		||||
		List<LogRecord> log = new ArrayList<LogRecord>();
 | 
			
		||||
		Handler listLogger = new Handler() {
 | 
			
		||||
			@Override
 | 
			
		||||
			public void close() throws SecurityException {
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			@Override
 | 
			
		||||
			public void flush() {
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			@Override
 | 
			
		||||
			public void publish(LogRecord record) {
 | 
			
		||||
				log.add(record);
 | 
			
		||||
			}
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		for (Handler handler : logger.getHandlers())
 | 
			
		||||
			logger.removeHandler(handler);
 | 
			
		||||
 | 
			
		||||
		logger.setUseParentHandlers(false);
 | 
			
		||||
		logger.addHandler(listLogger);
 | 
			
		||||
 | 
			
		||||
		return log;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,6 +13,8 @@ public class Variable<T> {
 | 
			
		|||
	private final T type;
 | 
			
		||||
	private final int depth;
 | 
			
		||||
	private final int offset;
 | 
			
		||||
	private boolean constant;
 | 
			
		||||
	private boolean assigned, possiblyAssigned;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Creates a variable with the given type instance and memory offset.
 | 
			
		||||
| 
						 | 
				
			
			@ -28,15 +30,24 @@ public class Variable<T> {
 | 
			
		|||
		this.type = type;
 | 
			
		||||
		this.depth = depth;
 | 
			
		||||
		this.offset = offset;
 | 
			
		||||
		this.assigned = false;
 | 
			
		||||
		this.possiblyAssigned = false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Gets the type of this variable instance.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @return the type
 | 
			
		||||
	 * Marks this variable assigned.
 | 
			
		||||
	 */
 | 
			
		||||
	public T getType() {
 | 
			
		||||
		return type;
 | 
			
		||||
	public void assign() {
 | 
			
		||||
		assigned = true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Gets the lexical depth of this variable instance.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @return the depth
 | 
			
		||||
	 */
 | 
			
		||||
	public int getDepth() {
 | 
			
		||||
		return depth;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			@ -49,16 +60,60 @@ public class Variable<T> {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Gets the lexical depth of this variable instance.
 | 
			
		||||
	 * Gets the type of this variable instance.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @return the depth
 | 
			
		||||
	 * @return the type
 | 
			
		||||
	 */
 | 
			
		||||
	public int getDepth() {
 | 
			
		||||
		return depth;
 | 
			
		||||
	public T getType() {
 | 
			
		||||
		return type;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Returns whether this variable has been assigned.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @return whether this variable has been assigned
 | 
			
		||||
	 */
 | 
			
		||||
	public boolean isAssigned() {
 | 
			
		||||
		return assigned;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Returns whether this variable is a constant.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @return whether this variable is a constant
 | 
			
		||||
	 */
 | 
			
		||||
	public boolean isConstant() {
 | 
			
		||||
		return constant;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Returns whether this variable has been assigned conditionally.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @return whether this variable has been assigned conditionally
 | 
			
		||||
	 */
 | 
			
		||||
	public boolean isPossiblyAssigned() {
 | 
			
		||||
		return possiblyAssigned;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Marks this variable as possibly assigned.
 | 
			
		||||
	 */
 | 
			
		||||
	public void possiblyAssign() {
 | 
			
		||||
		possiblyAssigned = true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Sets the const-ness of this variable.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param constant
 | 
			
		||||
	 *            whether this variable is a constant
 | 
			
		||||
	 */
 | 
			
		||||
	public void setConstant(boolean constant) {
 | 
			
		||||
		this.constant = constant;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public String toString() {
 | 
			
		||||
		return String.format("%s:%X@%d", type.toString(), hashCode(), offset);
 | 
			
		||||
		return String.format("%s:%X@%d", type.toString(), hashCode(), offset); //$NON-NLS-1$
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,33 +1,46 @@
 | 
			
		|||
grammar Boppi;
 | 
			
		||||
import BoppiTokens;
 | 
			
		||||
 | 
			
		||||
program: expr EOF;
 | 
			
		||||
program: stats EOF;
 | 
			
		||||
 | 
			
		||||
expr
 | 
			
		||||
	: singleExpr (COMPOUND singleExpr?)*
 | 
			
		||||
stats
 | 
			
		||||
	: stat (COMPOUND stat?)*
 | 
			
		||||
	;
 | 
			
		||||
 | 
			
		||||
singleExpr
 | 
			
		||||
	: PAROPEN expr PARCLOSE #parens
 | 
			
		||||
	| BRAOPEN expr BRACLOSE #block
 | 
			
		||||
stat
 | 
			
		||||
	: declareStat
 | 
			
		||||
	| assignStat
 | 
			
		||||
	| expr
 | 
			
		||||
	;
 | 
			
		||||
 | 
			
		||||
declareStat
 | 
			
		||||
	: DECLARE CONSTANT? type IDENTIFIER #declare
 | 
			
		||||
	| FUNCTION (result=type)? name=IDENTIFIER PAROPEN parameters? PARCLOSE body=expr #declareFunction
 | 
			
		||||
	| LAMBDA IDENTIFIER+ ARROW expr #lambda
 | 
			
		||||
	;
 | 
			
		||||
 | 
			
		||||
assignStat
 | 
			
		||||
	: variable ASSIGN (assignStat | expr) #assign
 | 
			
		||||
	;
 | 
			
		||||
 | 
			
		||||
expr
 | 
			
		||||
	: PAROPEN stats PARCLOSE #parens
 | 
			
		||||
	| BRAOPEN stats BRACLOSE #block
 | 
			
		||||
	| op=(PLUS|MINUS|NOT) expr #prefix1
 | 
			
		||||
	| lhs=expr op=(MULTIPLY|DIVIDE) rhs=expr #infix1
 | 
			
		||||
	| lhs=expr op=(PLUS|MINUS) rhs=expr #infix2
 | 
			
		||||
	| lhs=expr op=(LT|LEQ|GTE|GT|EQ|NEQ) rhs=expr #infix3
 | 
			
		||||
	| lhs=expr AND rhs=expr #infix4
 | 
			
		||||
	| lhs=expr OR rhs=expr #infix5
 | 
			
		||||
	| IN PAROPEN variable (LISTDELIM variable)* PARCLOSE #read
 | 
			
		||||
	| OUT PAROPEN expr (LISTDELIM expr)* PARCLOSE #write
 | 
			
		||||
	| IFOPEN cond=expr IFTRUE onTrue=expr (IFFALSE onFalse=expr)? IFCLOSE #if
 | 
			
		||||
	| WHILEOPEN cond=expr WHILETRUE onTrue=expr WHILECLOSE #while
 | 
			
		||||
	| op=(PLUS|MINUS|NOT) singleExpr #prefix1
 | 
			
		||||
	| lhs=singleExpr op=(MULTIPLY|DIVIDE) rhs=singleExpr #infix1
 | 
			
		||||
	| lhs=singleExpr op=(PLUS|MINUS) rhs=singleExpr #infix2
 | 
			
		||||
	| lhs=singleExpr op=(LT|LEQ|GTE|GT|EQ|NEQ) rhs=singleExpr #infix3
 | 
			
		||||
	| lhs=singleExpr AND rhs=singleExpr #infix4
 | 
			
		||||
	| lhs=singleExpr OR rhs=singleExpr #infix5
 | 
			
		||||
	| DECLARE type IDENTIFIER #declare
 | 
			
		||||
	| <assoc=right> variable ASSIGN singleExpr #assign
 | 
			
		||||
	| IFOPEN cond=stats IFTRUE onTrue=stats (IFFALSE onFalse=stats)? IFCLOSE #if
 | 
			
		||||
	| WHILEOPEN cond=stats WHILETRUE onTrue=stats WHILECLOSE #while
 | 
			
		||||
	| variable PAROPEN (expr (LISTDELIM expr)*)? PARCLOSE #call
 | 
			
		||||
	| variable #getVariable
 | 
			
		||||
	| LITERAL10 #literalInteger
 | 
			
		||||
	| CHAR #literalCharacter
 | 
			
		||||
	| (TRUE|FALSE) #literalBoolean
 | 
			
		||||
	| FUNCTION result=type name=IDENTIFIER PAROPEN (type IDENTIFIER (LISTDELIM type IDENTIFIER)*)? PARCLOSE body=singleExpr #declareFunction
 | 
			
		||||
	| variable PAROPEN (expr (LISTDELIM expr)*)? PARCLOSE #call
 | 
			
		||||
	;
 | 
			
		||||
 | 
			
		||||
type
 | 
			
		||||
| 
						 | 
				
			
			@ -43,3 +56,7 @@ variable
 | 
			
		|||
	| variable PROP IDENTIFIER #variableProperty
 | 
			
		||||
	| IDENTIFIER #variableSimple
 | 
			
		||||
	;
 | 
			
		||||
 | 
			
		||||
parameters
 | 
			
		||||
	: type IDENTIFIER (LISTDELIM type IDENTIFIER)*
 | 
			
		||||
	;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,24 @@
 | 
			
		|||
BoppiChecker.0=Could not match type %s with type %s.
 | 
			
		||||
BoppiChecker.1=Line %d:%d - %s
 | 
			
		||||
BoppiChecker.10=Record types not implemented.
 | 
			
		||||
BoppiChecker.11=Cannot print argument of type '%s'.
 | 
			
		||||
BoppiChecker.12=Cannot print argument of type '%s'.
 | 
			
		||||
BoppiChecker.13=Constant '%s' may already be assigned.
 | 
			
		||||
BoppiChecker.14=Variable '%s' may not be assigned.
 | 
			
		||||
BoppiChecker.15=Variable '%s' is not assigned.
 | 
			
		||||
BoppiChecker.2=Expected %d arguments but got %d.
 | 
			
		||||
BoppiChecker.3='%s' is not a function.
 | 
			
		||||
BoppiChecker.4=Variable must have a type %s, %s, %s or function.
 | 
			
		||||
BoppiChecker.5=Compound expression ends with declaration.
 | 
			
		||||
BoppiChecker.6=Error parsing number '%s'.
 | 
			
		||||
BoppiChecker.7=Expected array type but got '%s'.
 | 
			
		||||
BoppiChecker.8=Cannot reassign constant '%s'.
 | 
			
		||||
BoppiChecker.9=Unknown array property '%s'.
 | 
			
		||||
BoppiGenerator.0=INTERNAL: trying to increment reference for '%s'
 | 
			
		||||
BoppiGenerator.1=Line %d:%d - %s
 | 
			
		||||
BoppiGenerator.2=INTERNAL: trying to increment reference for '%s'
 | 
			
		||||
BoppiGenerator.3=%s: %s
 | 
			
		||||
BoppiGenerator.4=Error %s: %s
 | 
			
		||||
BoppiGenerator.5=Error parsing number '%s'
 | 
			
		||||
SymbolTable.0=Identifier '%s' already declared in this scope.
 | 
			
		||||
SymbolTable.1=Identifier '%s' is undeclared.
 | 
			
		||||
| 
						 | 
				
			
			@ -70,6 +70,19 @@ public class BoppiTests {
 | 
			
		|||
		logLevel = DEFAULT_LOG_LEVEL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Writes all program output and log messages to the standard output.
 | 
			
		||||
	 */
 | 
			
		||||
	public static void printOutput() {
 | 
			
		||||
		if (out != null)
 | 
			
		||||
			for (String line : out)
 | 
			
		||||
				System.out.println(">>> " + line);
 | 
			
		||||
 | 
			
		||||
		System.out.println();
 | 
			
		||||
		log.forEach((message) -> System.out.println(message.getMessage()));
 | 
			
		||||
		System.out.println();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Parses a string of code, logging to {@link #log}.
 | 
			
		||||
	 * 
 | 
			
		||||
| 
						 | 
				
			
			@ -213,16 +226,22 @@ public class BoppiTests {
 | 
			
		|||
		logger.setLevel(Level.FINEST);
 | 
			
		||||
 | 
			
		||||
		ParseTree ast = ToolChain.getParser(ToolChain.getLexer(stream, logger), logger).program();
 | 
			
		||||
		logger.info("done parsing");
 | 
			
		||||
		Annotations annotations = ToolChain.getAnnotations(ast, logger);
 | 
			
		||||
		logger.info("done checking");
 | 
			
		||||
		Program program = ToolChain.getILOC(ast, logger, annotations);
 | 
			
		||||
 | 
			
		||||
		System.out.println(program.prettyPrint());
 | 
			
		||||
		logger.info("done generating");
 | 
			
		||||
 | 
			
		||||
		out = ToolChain.execute(program, logger, in).split("\n");
 | 
			
		||||
		vm = ToolChain.machine;
 | 
			
		||||
		
 | 
			
		||||
 | 
			
		||||
		System.out.println(program.prettyPrint());
 | 
			
		||||
		System.out.println();
 | 
			
		||||
 | 
			
		||||
		for (String line : out)
 | 
			
		||||
			System.out.println(">>> "+line);
 | 
			
		||||
			System.out.println(">>> " + line);
 | 
			
		||||
 | 
			
		||||
		System.out.println();
 | 
			
		||||
 | 
			
		||||
		log.forEach((entry) -> System.out.println(entry.getMessage()));
 | 
			
		||||
		Simulator.DEBUG = false;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,14 +2,18 @@ package pp.s1184725.boppi.test;
 | 
			
		|||
 | 
			
		||||
import static org.hamcrest.MatcherAssert.assertThat;
 | 
			
		||||
import static org.hamcrest.Matchers.*;
 | 
			
		||||
import org.junit.Test;
 | 
			
		||||
import org.junit.*;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tests for function closures, mostly testing runtime correctness and garbage collection.
 | 
			
		||||
 * Tests for function closures, mostly testing runtime correctness and garbage
 | 
			
		||||
 * collection.
 | 
			
		||||
 * 
 | 
			
		||||
 * @author Frank Wibbelink
 | 
			
		||||
 */
 | 
			
		||||
public class ClosureTest {
 | 
			
		||||
	private int fib(int n) {
 | 
			
		||||
		return (n < 2) ? 1 : fib(n - 1) + fib(n - 2);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Correct closure parsing
 | 
			
		||||
| 
						 | 
				
			
			@ -18,7 +22,7 @@ public class ClosureTest {
 | 
			
		|||
	public void correctClosureParsing() {
 | 
			
		||||
		BoppiTests.parseFile("closure1.boppi");
 | 
			
		||||
		assertThat(BoppiTests.log, is(empty()));
 | 
			
		||||
		
 | 
			
		||||
 | 
			
		||||
		BoppiTests.parseFile("closure2.boppi");
 | 
			
		||||
		assertThat(BoppiTests.log, is(empty()));
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -30,7 +34,7 @@ public class ClosureTest {
 | 
			
		|||
	public void correctClosureChecking() {
 | 
			
		||||
		BoppiTests.checkFile("closure1.boppi");
 | 
			
		||||
		assertThat(BoppiTests.log, is(empty()));
 | 
			
		||||
		
 | 
			
		||||
 | 
			
		||||
		BoppiTests.checkFile("closure2.boppi");
 | 
			
		||||
		assertThat(BoppiTests.log, is(empty()));
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -44,21 +48,29 @@ public class ClosureTest {
 | 
			
		|||
		assertThat(BoppiTests.vm.getInterrupt(), is(0));
 | 
			
		||||
		assertThat(BoppiTests.log, is(empty()));
 | 
			
		||||
		assertThat(BoppiTests.out, is(arrayContaining("8", "9")));
 | 
			
		||||
		
 | 
			
		||||
 | 
			
		||||
		BoppiTests.compileAndRunFile("closure2.boppi");
 | 
			
		||||
		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(BoppiTests.vm.getInterrupt(), is(0));
 | 
			
		||||
		assertThat(BoppiTests.log, is(empty()));
 | 
			
		||||
		assertThat(BoppiTests.out, is(arrayContaining("7", "15", "2")));
 | 
			
		||||
 | 
			
		||||
		BoppiTests.compileAndRunFile("fibonacciRecursive.boppi", "1", "20", "30", "31", "0");
 | 
			
		||||
		assertThat(BoppiTests.vm.getInterrupt(), is(0));
 | 
			
		||||
		assertThat(BoppiTests.log, is(empty()));
 | 
			
		||||
		assertThat(BoppiTests.out, is(arrayContaining(Integer.toString(fib(1)), Integer.toString(fib(20)),
 | 
			
		||||
				Integer.toString(fib(30)), Integer.toString(fib(31)))));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Test garbago collection. Nothing must be allocated at the end of the program.
 | 
			
		||||
	 * Test garbage collection. Nothing must be allocated at the end of the
 | 
			
		||||
	 * program.
 | 
			
		||||
	 */
 | 
			
		||||
	@Ignore("Disabled as long as closures do not nicely clean memory.")
 | 
			
		||||
	@Test
 | 
			
		||||
	public void correctClosureCleanup() {
 | 
			
		||||
		BoppiTests.compileAndRunFile("closure1.boppi");
 | 
			
		||||
| 
						 | 
				
			
			@ -71,5 +83,4 @@ public class ClosureTest {
 | 
			
		|||
		assertThat(BoppiTests.vm.load(0), is(4));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,13 +22,13 @@ public class SimpleFunctionTest {
 | 
			
		|||
		BoppiTests.parseString("function int id(int a) a; id(1)");
 | 
			
		||||
		assertThat(BoppiTests.log, is(empty()));
 | 
			
		||||
 | 
			
		||||
		BoppiTests.parseString("var int a; a := 1; function int const() a; const()");
 | 
			
		||||
		BoppiTests.parseString("var int a; a := 1; function int constant() a; constant()");
 | 
			
		||||
		assertThat(BoppiTests.log, is(empty()));
 | 
			
		||||
 | 
			
		||||
		BoppiTests.parseString("function int add(int a, int b) a+b; print(add(4, 5)+1)");
 | 
			
		||||
		assertThat(BoppiTests.log, is(empty()));
 | 
			
		||||
 | 
			
		||||
		BoppiTests.parseString("function char const(char a, char b) a; print(const('A', 'T'))");
 | 
			
		||||
		BoppiTests.parseString("function char constant(char a, char b) a; print(constant('A', 'T'))");
 | 
			
		||||
		assertThat(BoppiTests.log, is(empty()));
 | 
			
		||||
 | 
			
		||||
		BoppiTests.parseString("function int factorial(int n) {if (n > 1) then n*factorial(n-1) else 1 fi}");
 | 
			
		||||
| 
						 | 
				
			
			@ -40,9 +40,6 @@ public class SimpleFunctionTest {
 | 
			
		|||
	 */
 | 
			
		||||
	@Test
 | 
			
		||||
	public void wrongFunctionParsing() {
 | 
			
		||||
		BoppiTests.parseString("function doNothing(int a, char b) a+b; print(add(4, 'T')+1)");
 | 
			
		||||
		assertThat(BoppiTests.log, hasSize(1));
 | 
			
		||||
 | 
			
		||||
		BoppiTests.parseString("function char add(int a, int b);");
 | 
			
		||||
		assertThat(BoppiTests.log, hasSize(1));
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -61,13 +58,13 @@ public class SimpleFunctionTest {
 | 
			
		|||
		BoppiTests.checkString("function int id(int a) a; id(1); 1");
 | 
			
		||||
		assertThat(BoppiTests.log, is(empty()));
 | 
			
		||||
 | 
			
		||||
		BoppiTests.checkString("var int a; function int const() a; 1");
 | 
			
		||||
		BoppiTests.checkString("var int a; function int constant() a; 1");
 | 
			
		||||
		assertThat(BoppiTests.log, is(empty()));
 | 
			
		||||
 | 
			
		||||
		BoppiTests.checkString("function int add(int a, int b) a+b; print(add(4, 5)+1)");
 | 
			
		||||
		assertThat(BoppiTests.log, is(empty()));
 | 
			
		||||
 | 
			
		||||
		BoppiTests.checkString("function char const(char a, char b) a; print(const('A', 'T'))");
 | 
			
		||||
		BoppiTests.checkString("function char constant(char a, char b) a; print(constant('A', 'T'))");
 | 
			
		||||
		assertThat(BoppiTests.log, is(empty()));
 | 
			
		||||
 | 
			
		||||
		BoppiTests.checkString("function int factorial(int n) {if (n > 1) then n*factorial(n-1) else 1 fi}; 0");
 | 
			
		||||
| 
						 | 
				
			
			@ -90,7 +87,7 @@ public class SimpleFunctionTest {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Correct evaluation of basic functions
 | 
			
		||||
	 * Correct evaluation of basic functions and procedures
 | 
			
		||||
	 */
 | 
			
		||||
	@Test
 | 
			
		||||
	public void correctSimpleFunctionGeneration() {
 | 
			
		||||
| 
						 | 
				
			
			@ -100,10 +97,10 @@ public class SimpleFunctionTest {
 | 
			
		|||
		BoppiTests.compileAndRunString("function int id(int a) a; id(1); 1");
 | 
			
		||||
		assertThat(BoppiTests.log, is(empty()));
 | 
			
		||||
 | 
			
		||||
		BoppiTests.compileAndRunString("var int a; function int const(int b) a; 1");
 | 
			
		||||
		BoppiTests.compileAndRunString("var int a; function int constant(int b) a; 1");
 | 
			
		||||
		assertThat(BoppiTests.log, is(empty()));
 | 
			
		||||
 | 
			
		||||
		BoppiTests.compileAndRunString("var int a; a := 1; function int const(int b) a; print(const(4))");
 | 
			
		||||
		BoppiTests.compileAndRunString("var int a; a := 1; function int constant(int b) a; print(constant(4))");
 | 
			
		||||
		assertThat(BoppiTests.log, is(empty()));
 | 
			
		||||
		assertThat(BoppiTests.out, is(arrayContaining("1")));
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -111,9 +108,13 @@ public class SimpleFunctionTest {
 | 
			
		|||
		assertThat(BoppiTests.log, is(empty()));
 | 
			
		||||
		assertThat(BoppiTests.out, is(arrayContaining("10")));
 | 
			
		||||
 | 
			
		||||
		BoppiTests.compileAndRunString("function char const(char a, char b) a; print(const('A', 'T'))");
 | 
			
		||||
		BoppiTests.compileAndRunString("function char constant(char a, char b) a; print(constant('A', 'T'))");
 | 
			
		||||
		assertThat(BoppiTests.log, is(empty()));
 | 
			
		||||
		assertThat(BoppiTests.out, is(arrayContaining("A")));
 | 
			
		||||
 | 
			
		||||
		BoppiTests.compileAndRunFile("simpleProcedure.boppi", "8");
 | 
			
		||||
		assertThat(BoppiTests.log, is(empty()));
 | 
			
		||||
		assertThat(BoppiTests.out, is(arrayContaining("5", "16")));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,8 +34,14 @@ public class SimpleVariableTest {
 | 
			
		|||
		BoppiTests.parseString("var 'c' varname");
 | 
			
		||||
		assertThat(BoppiTests.log, not(empty()));
 | 
			
		||||
 | 
			
		||||
		BoppiTests.parseString("var bool int; int := true");
 | 
			
		||||
		assertThat(BoppiTests.log, not(empty()));
 | 
			
		||||
 | 
			
		||||
		BoppiTests.parseString("var bool; true true;");
 | 
			
		||||
		assertThat(BoppiTests.log, hasSize(2));
 | 
			
		||||
 | 
			
		||||
		BoppiTests.parseString("var int a; var int b; a := 4 + b := 3;");
 | 
			
		||||
		assertThat(BoppiTests.log, hasSize(1));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			@ -54,10 +60,14 @@ public class SimpleVariableTest {
 | 
			
		|||
	public void correctVariableChecking() {
 | 
			
		||||
		BoppiTests.checkFile("simpleVariable.boppi");
 | 
			
		||||
		assertThat(BoppiTests.log, is(empty()));
 | 
			
		||||
 | 
			
		||||
		BoppiTests.checkString("var const int x; x := 3; var x y; y := x;");
 | 
			
		||||
		assertThat(BoppiTests.log, is(empty()));
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Variable use with type errors
 | 
			
		||||
	 * Variable use with type errors, undefined variables and reassigning constants
 | 
			
		||||
	 */
 | 
			
		||||
	@Test
 | 
			
		||||
	public void wrongVariableChecking() {
 | 
			
		||||
| 
						 | 
				
			
			@ -73,6 +83,15 @@ public class SimpleVariableTest {
 | 
			
		|||
		BoppiTests.checkString("var bool endsWithDeclaration;");
 | 
			
		||||
		assertThat(BoppiTests.log, hasSize(1));
 | 
			
		||||
 | 
			
		||||
		BoppiTests.checkString("var int x; 2+x;");
 | 
			
		||||
		assertThat(BoppiTests.log, hasSize(1));
 | 
			
		||||
 | 
			
		||||
		BoppiTests.checkString("var const int x; 2+x;");
 | 
			
		||||
		assertThat(BoppiTests.log, hasSize(1));
 | 
			
		||||
 | 
			
		||||
		BoppiTests.checkString("var const int x; x := 3; var int y; y := x; x := 2;");
 | 
			
		||||
		assertThat(BoppiTests.log, hasSize(1));
 | 
			
		||||
 | 
			
		||||
		BoppiTests.checkString("var bool var1; var var1 var2; var2 := 'c' ");
 | 
			
		||||
		assertThat(BoppiTests.log, hasSize(1));
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,3 +1,5 @@
 | 
			
		|||
//Currying addition
 | 
			
		||||
 | 
			
		||||
function (int)->int bindAdd(int a) {
 | 
			
		||||
    var int left;
 | 
			
		||||
    left := a;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,3 +1,6 @@
 | 
			
		|||
//"Currying" an arbitrary Int->Int->Int function
 | 
			
		||||
//Logging the number of calls to an Int->Int->Int function by embedding it in a closure
 | 
			
		||||
 | 
			
		||||
function (int)->int bindInt((int,int)->int f, int first) {
 | 
			
		||||
    function int return(int second) f(first,second);
 | 
			
		||||
    return
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,3 +1,6 @@
 | 
			
		|||
//Logging the number of calls to an Int->Int->Int function by embedding it in a closure
 | 
			
		||||
//Wrapping the whole inside a main function to test closure cleanup
 | 
			
		||||
 | 
			
		||||
function int main1() {
 | 
			
		||||
    function int add(int a, int b) a+b;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,6 +2,7 @@ var int n;
 | 
			
		|||
var int i;
 | 
			
		||||
var int current;
 | 
			
		||||
var int previous;
 | 
			
		||||
previous := 0;
 | 
			
		||||
current := 1;
 | 
			
		||||
 | 
			
		||||
read(n);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,44 @@
 | 
			
		|||
//Recursive fibonacci with memoization closure
 | 
			
		||||
 | 
			
		||||
var int n;
 | 
			
		||||
 | 
			
		||||
function int fib(int n) {
 | 
			
		||||
    if n < 2 then
 | 
			
		||||
        1
 | 
			
		||||
    else
 | 
			
		||||
        fib(n-1)+fib(n-2)
 | 
			
		||||
    fi
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function fib memoize(fib f) {
 | 
			
		||||
    var [50]int memo;
 | 
			
		||||
 | 
			
		||||
    var int i; i := 0; while i < 50 do
 | 
			
		||||
        memo[i] := 0;
 | 
			
		||||
        i := i+1;
 | 
			
		||||
    od;
 | 
			
		||||
 | 
			
		||||
    function int memoFib(int n) {
 | 
			
		||||
        if n >= 0 && n < 50 then
 | 
			
		||||
            if memo[n] > 0 then
 | 
			
		||||
                memo[n]
 | 
			
		||||
            else
 | 
			
		||||
                memo[n] := f(n)
 | 
			
		||||
            fi
 | 
			
		||||
        else
 | 
			
		||||
            f(n)
 | 
			
		||||
        fi
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    memoFib
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
fib := memoize(fib);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
read(n);
 | 
			
		||||
 | 
			
		||||
while n > 0 do
 | 
			
		||||
    print(fib(n));
 | 
			
		||||
    read(n)
 | 
			
		||||
od;
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,12 @@
 | 
			
		|||
var int x;
 | 
			
		||||
 | 
			
		||||
function setX(int n) {
 | 
			
		||||
    x := n;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
setX(5);
 | 
			
		||||
print(x);
 | 
			
		||||
 | 
			
		||||
var const int y;
 | 
			
		||||
setX(read(y)*2);
 | 
			
		||||
print(x);
 | 
			
		||||
| 
						 | 
				
			
			@ -1,5 +1,6 @@
 | 
			
		|||
package pp.s1184725.boppi.util;
 | 
			
		||||
 | 
			
		||||
import java.io.ByteArrayOutputStream;
 | 
			
		||||
import java.util.Scanner;
 | 
			
		||||
import java.util.logging.Logger;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -85,7 +86,10 @@ public class InteractivePrompt {
 | 
			
		|||
					System.out.println("--- "+line);
 | 
			
		||||
			}
 | 
			
		||||
			
 | 
			
		||||
			for (String str : ToolChain.execute(program, logger, "").split("\n"))
 | 
			
		||||
			ByteArrayOutputStream out = new ByteArrayOutputStream();
 | 
			
		||||
			ToolChain.execute(program, logger, System.in, out);
 | 
			
		||||
			
 | 
			
		||||
			for (String str : out.toString().split("\r\n|\r"))
 | 
			
		||||
				System.out.println("<<< "+str);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,6 +23,18 @@ public class RegisterPool {
 | 
			
		|||
	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 void blockReg(Reg reg) {
 | 
			
		||||
		if (reg == null)
 | 
			
		||||
			logger.severe("INTERNAL: cannot reserve null register.");
 | 
			
		||||
| 
						 | 
				
			
			@ -34,6 +46,55 @@ public class RegisterPool {
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 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));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Blocks a register while running some code.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param r1
 | 
			
		||||
	 *            the register to reserve
 | 
			
		||||
	 * @param code
 | 
			
		||||
	 *            the code to run
 | 
			
		||||
	 */
 | 
			
		||||
	public void blockReg(Reg r1, Runnable code) {
 | 
			
		||||
		blockReg(r1);
 | 
			
		||||
		code.run();
 | 
			
		||||
		freeReg(r1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Blocks a register while running some code.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param <T>
 | 
			
		||||
	 *            the return type of the code
 | 
			
		||||
	 * @param r1
 | 
			
		||||
	 *            the register to reserve
 | 
			
		||||
	 * @param code
 | 
			
		||||
	 *            the code to run
 | 
			
		||||
	 * @return the return value of the code
 | 
			
		||||
	 */
 | 
			
		||||
	public <T> T blockReg(Reg r1, Supplier<T> code) {
 | 
			
		||||
		blockReg(r1);
 | 
			
		||||
		T result = code.get();
 | 
			
		||||
		freeReg(r1);
 | 
			
		||||
		return result;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private void freeReg(Reg reg) {
 | 
			
		||||
		if (reg == null) {
 | 
			
		||||
			logger.severe("INTERNAL: cannot free null register.");
 | 
			
		||||
| 
						 | 
				
			
			@ -45,6 +106,15 @@ public class RegisterPool {
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Returns the list of registers currently in use.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @return the list of registers in use
 | 
			
		||||
	 */
 | 
			
		||||
	public List<Reg> getInUse() {
 | 
			
		||||
		return regInUse;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private Reg makeReg() {
 | 
			
		||||
		return makeRegThatIsNot(null);
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -65,24 +135,14 @@ public class RegisterPool {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Creates a new register pool
 | 
			
		||||
	 * Runs some code with two registers allocated.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param logger
 | 
			
		||||
	 *            the logger to use
 | 
			
		||||
	 * @param code
 | 
			
		||||
	 *            the code to run
 | 
			
		||||
	 * @return the second register used in the code
 | 
			
		||||
	 */
 | 
			
		||||
	public RegisterPool(Logger logger) {
 | 
			
		||||
		regFree = new ArrayList<>();
 | 
			
		||||
		regInUse = new ArrayList<>();
 | 
			
		||||
		this.logger = logger;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Returns the list of registers currently in use.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @return the list of registers in use
 | 
			
		||||
	 */
 | 
			
		||||
	public List<Reg> getInUse() {
 | 
			
		||||
		return regInUse;
 | 
			
		||||
	public Reg withReg(BiConsumer<Reg, Reg> code) {
 | 
			
		||||
		return withReg((r2) -> withReg((r1) -> code.accept(r1, r2)));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			@ -99,64 +159,4 @@ public class RegisterPool {
 | 
			
		|||
		return r;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Runs some code with two registers allocated.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param code
 | 
			
		||||
	 *            the code to run
 | 
			
		||||
	 * @return the second register used in the code
 | 
			
		||||
	 */
 | 
			
		||||
	public Reg withReg(BiConsumer<Reg, Reg> code) {
 | 
			
		||||
		return withReg((r2) -> withReg((r1) -> code.accept(r1, r2)));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Blocks a register while running some code.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param <T>
 | 
			
		||||
	 *            the return type of the code
 | 
			
		||||
	 * @param r1
 | 
			
		||||
	 *            the register to reserve
 | 
			
		||||
	 * @param code
 | 
			
		||||
	 *            the code to run
 | 
			
		||||
	 * @return the return value of the code
 | 
			
		||||
	 */
 | 
			
		||||
	public <T> T blockReg(Reg r1, Supplier<T> code) {
 | 
			
		||||
		blockReg(r1);
 | 
			
		||||
		T result = code.get();
 | 
			
		||||
		freeReg(r1);
 | 
			
		||||
		return result;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Blocks a register while running some code.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param r1
 | 
			
		||||
	 *            the register to reserve
 | 
			
		||||
	 * @param code
 | 
			
		||||
	 *            the code to run
 | 
			
		||||
	 */
 | 
			
		||||
	public void blockReg(Reg r1, Runnable code) {
 | 
			
		||||
		blockReg(r1);
 | 
			
		||||
		code.run();
 | 
			
		||||
		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