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.Op;
 | 
				
			||||||
import pp.iloc.model.OpClaz;
 | 
					import pp.iloc.model.OpClaz;
 | 
				
			||||||
import pp.iloc.model.OpCode;
 | 
					import pp.iloc.model.OpCode;
 | 
				
			||||||
 | 
					import pp.iloc.model.Operand.Type;
 | 
				
			||||||
import pp.iloc.model.Program;
 | 
					import pp.iloc.model.Program;
 | 
				
			||||||
import pp.iloc.parse.FormatException;
 | 
					import pp.iloc.parse.FormatException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -101,10 +102,8 @@ public class Simulator {
 | 
				
			||||||
		Op o = this.prg.getOpAt(this.vm.getPC());
 | 
							Op o = this.prg.getOpAt(this.vm.getPC());
 | 
				
			||||||
		OPContext c = new OPContext(o);
 | 
							OPContext c = new OPContext(o);
 | 
				
			||||||
		Machine vm = this.vm;
 | 
							Machine vm = this.vm;
 | 
				
			||||||
		if (DEBUG) {
 | 
							String pre = c.toPreString();
 | 
				
			||||||
			System.out.printf("Op %d: %s%n", vm.getPC(), o);
 | 
							
 | 
				
			||||||
			System.out.println(vm);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		switch (o.getOpCode()) {
 | 
							switch (o.getOpCode()) {
 | 
				
			||||||
		case nop:
 | 
							case nop:
 | 
				
			||||||
			// do nothing
 | 
								// do nothing
 | 
				
			||||||
| 
						 | 
					@ -328,6 +327,13 @@ public class Simulator {
 | 
				
			||||||
			this.vm.incPC();
 | 
								this.vm.incPC();
 | 
				
			||||||
			return;
 | 
								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) {
 | 
							if (o.getClaz() != OpClaz.CONTROL) {
 | 
				
			||||||
			this.vm.incPC();
 | 
								this.vm.incPC();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -474,5 +480,76 @@ public class Simulator {
 | 
				
			||||||
		public int label(int ix) {
 | 
							public int label(int ix) {
 | 
				
			||||||
			return getProgram().getLine(this.op.label(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.HashMap;
 | 
				
			||||||
import java.util.Map;
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					import java.util.stream.Collectors;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import pp.iloc.model.Num;
 | 
					import pp.iloc.model.Num;
 | 
				
			||||||
import pp.iloc.model.Reg;
 | 
					import pp.iloc.model.Reg;
 | 
				
			||||||
| 
						 | 
					@ -292,11 +293,10 @@ public class Machine {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Sets the interrupt value of the machine.
 | 
						 * Clears the interrupt value of the machine.
 | 
				
			||||||
	 * @param interruptCode the new interrupt value
 | 
					 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public void setInterrupt(int interruptCode) {
 | 
						public void clearInterrupt() {
 | 
				
			||||||
		this.interrupt = interruptCode;
 | 
							this.interrupt = 0;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
| 
						 | 
					@ -307,6 +307,14 @@ public class Machine {
 | 
				
			||||||
		return interrupt;
 | 
							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. */
 | 
						/** Clears the registers, constants, memory, PC and interrupt. */
 | 
				
			||||||
	public void clear() {
 | 
						public void clear() {
 | 
				
			||||||
		this.registers.clear();
 | 
							this.registers.clear();
 | 
				
			||||||
| 
						 | 
					@ -321,7 +329,9 @@ public class Machine {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public String toString() {
 | 
						public String toString() {
 | 
				
			||||||
		return String.format("Registers: %s%nConstants: %s%nMemory: %s%n",
 | 
							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(", ", "{", "}"));
 | 
				
			||||||
				this.registers, this.symbMap, this.memory);
 | 
							
 | 
				
			||||||
 | 
							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 java.util.Arrays;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import pp.s1184725.boppi.ToolChain;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Simulated memory. */
 | 
					/** Simulated memory. */
 | 
				
			||||||
public class Memory {
 | 
					public class Memory {
 | 
				
			||||||
	/** The default size of the memory, in number of bytes. */
 | 
						/** The default size of the memory, in number of bytes. */
 | 
				
			||||||
| 
						 | 
					@ -40,17 +42,76 @@ public class Memory {
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public String toString() {
 | 
						public String toString() {
 | 
				
			||||||
		StringBuilder result = new StringBuilder();
 | 
							StringBuilder result = new StringBuilder();
 | 
				
			||||||
		for (int i = 0; i < size(); i++) {
 | 
					//		boolean wasEmpty = false;
 | 
				
			||||||
			if (get(i) == 0) {
 | 
					//		for (int i = 0; i < size(); i++) {
 | 
				
			||||||
				continue;
 | 
					////			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(size > 40 ? "...\n" : "\n");
 | 
				
			||||||
				result.append(", ");
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			result.append(i);
 | 
								loc += size+8;
 | 
				
			||||||
			result.append(":");
 | 
					 | 
				
			||||||
			result.append(String.format("%02X", get(i) & 0xFF));
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return result.toString();
 | 
							return result.toString();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,8 @@
 | 
				
			||||||
package pp.s1184725.boppi;
 | 
					package pp.s1184725.boppi;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.antlr.v4.runtime.ParserRuleContext;
 | 
				
			||||||
import org.antlr.v4.runtime.tree.ParseTreeProperty;
 | 
					import org.antlr.v4.runtime.tree.ParseTreeProperty;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import pp.iloc.model.Reg;
 | 
					import pp.iloc.model.Reg;
 | 
				
			||||||
| 
						 | 
					@ -31,6 +34,10 @@ public class Annotations {
 | 
				
			||||||
	 * A symbol table
 | 
						 * A symbol table
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public CachingSymbolTable<Type> symbols;
 | 
						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.
 | 
						 * Creates a new annotations object with empty maps.
 | 
				
			||||||
| 
						 | 
					@ -41,5 +48,6 @@ public class Annotations {
 | 
				
			||||||
		function = new ParseTreeProperty<>();
 | 
							function = new ParseTreeProperty<>();
 | 
				
			||||||
		variables = new ParseTreeProperty<>();
 | 
							variables = new ParseTreeProperty<>();
 | 
				
			||||||
		symbols = new CachingSymbolTable<>();
 | 
							symbols = new CachingSymbolTable<>();
 | 
				
			||||||
 | 
							variableRoot = new HashMap<>();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -20,6 +20,7 @@ import pp.s1184725.boppi.type.*;
 | 
				
			||||||
public class BoppiChecker extends BoppiBaseVisitor<Type> {
 | 
					public class BoppiChecker extends BoppiBaseVisitor<Type> {
 | 
				
			||||||
	private Annotations an;
 | 
						private Annotations an;
 | 
				
			||||||
	private Logger log;
 | 
						private Logger log;
 | 
				
			||||||
 | 
						private boolean inLhs, inType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Checks and annotates a program. Problems are reported to the given
 | 
						 * 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) {
 | 
						protected BoppiChecker(Logger logger, Annotations annotations) {
 | 
				
			||||||
		an = annotations;
 | 
							an = annotations;
 | 
				
			||||||
		log = logger;
 | 
							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) {
 | 
						private void checkConstraint(Type type1, Type type2, ParserRuleContext node) {
 | 
				
			||||||
		if (!type2.equals(type1))
 | 
							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) {
 | 
						private String getError(ParserRuleContext node, String message, Object... args) {
 | 
				
			||||||
		int line = node.getStart().getLine();
 | 
							int line = node.getStart().getLine();
 | 
				
			||||||
		int column = node.getStart().getCharPositionInLine();
 | 
							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
 | 
						@Override
 | 
				
			||||||
| 
						 | 
					@ -82,15 +111,17 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public Type visitAssign(AssignContext ctx) {
 | 
						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());
 | 
							Type var = visit(ctx.variable());
 | 
				
			||||||
 | 
							inLhs = false;
 | 
				
			||||||
		checkConstraint(expr, var, ctx);
 | 
							checkConstraint(expr, var, ctx);
 | 
				
			||||||
		return expr;
 | 
							return expr;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public Type visitBlock(BlockContext ctx) {
 | 
						public Type visitBlock(BlockContext ctx) {
 | 
				
			||||||
		return an.symbols.withScope(() -> visit(ctx.expr()));
 | 
							return an.symbols.withScope(() -> visit(ctx.stats()));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
| 
						 | 
					@ -101,14 +132,14 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
 | 
				
			||||||
			FunctionType type = (FunctionType) t;
 | 
								FunctionType type = (FunctionType) t;
 | 
				
			||||||
			TupleType parameters = (TupleType) type.getParameter();
 | 
								TupleType parameters = (TupleType) type.getParameter();
 | 
				
			||||||
			if (parameters.size() != ctx.expr().size())
 | 
								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++)
 | 
								for (int i = 0; i < ctx.expr().size() && i < parameters.size(); i++)
 | 
				
			||||||
				checkConstraint(visit(ctx.expr(i)), parameters.get(i), ctx.expr(i));
 | 
									checkConstraint(visit(ctx.expr(i)), parameters.get(i), ctx.expr(i));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			return type.getReturn();
 | 
								return type.getReturn();
 | 
				
			||||||
		} else {
 | 
							} 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;
 | 
								return SimpleType.VOID;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -117,16 +148,18 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
 | 
				
			||||||
	public Type visitDeclare(DeclareContext ctx) {
 | 
						public Type visitDeclare(DeclareContext ctx) {
 | 
				
			||||||
		try {
 | 
							try {
 | 
				
			||||||
			Variable<Type> var = an.symbols.put(ctx.IDENTIFIER().getText(), visit(ctx.type()));
 | 
								Variable<Type> var = an.symbols.put(ctx.IDENTIFIER().getText(), visit(ctx.type()));
 | 
				
			||||||
 | 
								var.setConstant(ctx.CONSTANT() != null);
 | 
				
			||||||
			an.variables.put(ctx, var);
 | 
								an.variables.put(ctx, var);
 | 
				
			||||||
 | 
								an.variableRoot.put(var, ctx.getParent());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (var.getType() instanceof TupleType)
 | 
								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.BOOLTYPE),
 | 
				
			||||||
						BoppiLexer.VOCABULARY.getLiteralName(BoppiLexer.CHARTYPE),
 | 
											BoppiLexer.VOCABULARY.getLiteralName(BoppiLexer.CHARTYPE),
 | 
				
			||||||
						BoppiLexer.VOCABULARY.getLiteralName(BoppiLexer.INTTYPE)));
 | 
											BoppiLexer.VOCABULARY.getLiteralName(BoppiLexer.INTTYPE)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (!(var.getType() instanceof SimpleType))
 | 
								if (var.getType() instanceof ReferenceType)
 | 
				
			||||||
				log.warning("Be careful only to pass pure functions outside their scope.");
 | 
									var.assign();
 | 
				
			||||||
		} catch (SymbolTableException e) {
 | 
							} catch (SymbolTableException e) {
 | 
				
			||||||
			log.severe(getError(ctx, e.getMessage()));
 | 
								log.severe(getError(ctx, e.getMessage()));
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -136,21 +169,28 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public Type visitDeclareFunction(DeclareFunctionContext ctx) {
 | 
						public Type visitDeclareFunction(DeclareFunctionContext ctx) {
 | 
				
			||||||
		try {
 | 
							try {
 | 
				
			||||||
			TupleType parameterTypes = new TupleType(
 | 
								TupleType parameterTypes;
 | 
				
			||||||
					ctx.type().stream().skip(1).map(this::visit).collect(Collectors.toList()));
 | 
								if (ctx.parameters() != null)
 | 
				
			||||||
			FunctionType type = new FunctionType(visit(ctx.result), parameterTypes);
 | 
									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);
 | 
								Variable<Type> func = an.symbols.put(ctx.name.getText(), type);
 | 
				
			||||||
 | 
								func.assign();
 | 
				
			||||||
			an.variables.put(ctx, func);
 | 
								an.variables.put(ctx, func);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			an.function.put(ctx, an.symbols.withFunctionScope(() -> {
 | 
								an.function.put(ctx, an.symbols.withFunctionScope(() -> {
 | 
				
			||||||
				for (int i = 1; i < ctx.type().size(); i++)
 | 
									if (ctx.parameters() != null)
 | 
				
			||||||
					try {
 | 
										visit(ctx.parameters());
 | 
				
			||||||
						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);
 | 
									Type resultType = an.symbols.withScope(() -> visit(ctx.body));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if (returnType != SimpleType.VOID)
 | 
				
			||||||
 | 
										checkConstraint(resultType, returnType, ctx);
 | 
				
			||||||
			}));
 | 
								}));
 | 
				
			||||||
		} catch (SymbolTableException e) {
 | 
							} catch (SymbolTableException e) {
 | 
				
			||||||
			log.severe(getError(ctx, e.getMessage()));
 | 
								log.severe(getError(ctx, e.getMessage()));
 | 
				
			||||||
| 
						 | 
					@ -159,15 +199,6 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
 | 
				
			||||||
		return SimpleType.VOID;
 | 
							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
 | 
						@Override
 | 
				
			||||||
	public Type visitGetVariable(GetVariableContext ctx) {
 | 
						public Type visitGetVariable(GetVariableContext ctx) {
 | 
				
			||||||
		return visit(ctx.variable());
 | 
							return visit(ctx.variable());
 | 
				
			||||||
| 
						 | 
					@ -238,6 +269,36 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
 | 
				
			||||||
		return SimpleType.BOOL;
 | 
							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
 | 
						@Override
 | 
				
			||||||
	public Type visitLiteralBoolean(LiteralBooleanContext ctx) {
 | 
						public Type visitLiteralBoolean(LiteralBooleanContext ctx) {
 | 
				
			||||||
		return SimpleType.BOOL;
 | 
							return SimpleType.BOOL;
 | 
				
			||||||
| 
						 | 
					@ -255,27 +316,38 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public Type visitParens(ParensContext ctx) {
 | 
						public Type visitParens(ParensContext ctx) {
 | 
				
			||||||
		return visit(ctx.expr());
 | 
							return visit(ctx.stats());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public Type visitPrefix1(Prefix1Context ctx) {
 | 
						public Type visitPrefix1(Prefix1Context ctx) {
 | 
				
			||||||
		Type type = visit(ctx.singleExpr());
 | 
							Type type = visit(ctx.expr());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		switch (ctx.op.getType()) {
 | 
							switch (ctx.op.getType()) {
 | 
				
			||||||
		case BoppiLexer.NOT:
 | 
							case BoppiLexer.NOT:
 | 
				
			||||||
			checkConstraint(type, SimpleType.BOOL, ctx.singleExpr());
 | 
								checkConstraint(type, SimpleType.BOOL, ctx.expr());
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		case BoppiLexer.PLUS:
 | 
							case BoppiLexer.PLUS:
 | 
				
			||||||
		case BoppiLexer.MINUS:
 | 
							case BoppiLexer.MINUS:
 | 
				
			||||||
			checkConstraint(type, SimpleType.INT, ctx.singleExpr());
 | 
								checkConstraint(type, SimpleType.INT, ctx.expr());
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return type;
 | 
							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
 | 
						@Override
 | 
				
			||||||
	public Type visitProgram(ProgramContext ctx) {
 | 
						public Type visitProgram(ProgramContext ctx) {
 | 
				
			||||||
		an.function.put(ctx, an.symbols.withFunctionScope(() -> super.visitProgram(ctx)));
 | 
							an.function.put(ctx, an.symbols.withFunctionScope(() -> super.visitProgram(ctx)));
 | 
				
			||||||
| 
						 | 
					@ -284,24 +356,46 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public Type visitRead(ReadContext ctx) {
 | 
						public Type visitRead(ReadContext ctx) {
 | 
				
			||||||
 | 
							Type result;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							inLhs = true;
 | 
				
			||||||
		if (ctx.variable().size() == 1) {
 | 
							if (ctx.variable().size() == 1) {
 | 
				
			||||||
			return visit(ctx.variable(0));
 | 
								result = visit(ctx.variable(0));
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			ctx.variable().forEach(this::visit);
 | 
								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
 | 
						@Override
 | 
				
			||||||
	public Type visitTypeArray(TypeArrayContext ctx) {
 | 
						public Type visitTypeArray(TypeArrayContext ctx) {
 | 
				
			||||||
		int size = 0;
 | 
							int size = 0;
 | 
				
			||||||
		
 | 
					
 | 
				
			||||||
		try {
 | 
							try {
 | 
				
			||||||
			size = Integer.parseInt(ctx.LITERAL10().getText());
 | 
								size = Integer.parseInt(ctx.LITERAL10().getText());
 | 
				
			||||||
		}catch (NumberFormatException e) {
 | 
							} catch (NumberFormatException e) {
 | 
				
			||||||
			log.severe(getError(ctx, "Error parsing number '%s'", ctx.LITERAL10().getText()));
 | 
								log.severe(getError(ctx, Messages.getString("BoppiChecker.6"), ctx.LITERAL10().getText())); //$NON-NLS-1$
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		
 | 
					
 | 
				
			||||||
		return new ArrayType(size, visit(ctx.type()));
 | 
							return new ArrayType(size, visit(ctx.type()));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -322,40 +416,44 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public Type visitTypeVariable(TypeVariableContext ctx) {
 | 
						public Type visitTypeVariable(TypeVariableContext ctx) {
 | 
				
			||||||
		return visit(ctx.variable());
 | 
							inType = true;
 | 
				
			||||||
 | 
							Type type = visit(ctx.variable());
 | 
				
			||||||
 | 
							inType = false;
 | 
				
			||||||
 | 
							return type;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public Type visitVariableArray(VariableArrayContext ctx) {
 | 
						public Type visitVariableArray(VariableArrayContext ctx) {
 | 
				
			||||||
		Type t = visit(ctx.variable());
 | 
							boolean wasOnLhs = inLhs;
 | 
				
			||||||
 | 
							inLhs = false;
 | 
				
			||||||
		checkConstraint(visit(ctx.expr()), SimpleType.INT, ctx);
 | 
							checkConstraint(visit(ctx.expr()), SimpleType.INT, ctx);
 | 
				
			||||||
 | 
							inLhs = wasOnLhs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							Type t = visit(ctx.variable());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (t instanceof ArrayType) {
 | 
							if (t instanceof ArrayType) {
 | 
				
			||||||
			ArrayType arrayType = (ArrayType) t;
 | 
								ArrayType arrayType = (ArrayType) t;
 | 
				
			||||||
			return arrayType.getType();
 | 
								return arrayType.getType();
 | 
				
			||||||
		} else {
 | 
							} 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;
 | 
								return SimpleType.VOID;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public Type visitVariableProperty(VariablePropertyContext ctx) {
 | 
						public Type visitVariableProperty(VariablePropertyContext ctx) {
 | 
				
			||||||
		Type varType = visit(ctx.variable());
 | 
							Type varType = visit(ctx.variable());
 | 
				
			||||||
		String prop = ctx.IDENTIFIER().getText();
 | 
							String prop = ctx.IDENTIFIER().getText();
 | 
				
			||||||
		
 | 
					
 | 
				
			||||||
		if (varType instanceof ArrayType) {
 | 
							if (varType instanceof ArrayType) {
 | 
				
			||||||
			if (prop.equals("length")) {
 | 
								if (prop.equals("length")) { //$NON-NLS-1$
 | 
				
			||||||
				return SimpleType.INT;
 | 
									return SimpleType.INT;
 | 
				
			||||||
			}
 | 
								} else {
 | 
				
			||||||
			else {
 | 
									log.severe(getError(ctx, Messages.getString("BoppiChecker.9"), prop)); //$NON-NLS-1$
 | 
				
			||||||
				log.severe(getError(ctx, "Unknown array property '%s'.", prop));
 | 
					 | 
				
			||||||
				return SimpleType.VOID;
 | 
									return SimpleType.VOID;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							} else {
 | 
				
			||||||
		else {
 | 
								log.severe(getError(ctx, Messages.getString("BoppiChecker.10"))); //$NON-NLS-1$
 | 
				
			||||||
			log.severe(getError(ctx, "Record types not implemented."));
 | 
					 | 
				
			||||||
			return SimpleType.VOID;
 | 
								return SimpleType.VOID;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -365,6 +463,34 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
 | 
				
			||||||
		try {
 | 
							try {
 | 
				
			||||||
			Variable<Type> var = an.symbols.get(ctx.getText());
 | 
								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 {
 | 
								try {
 | 
				
			||||||
				an.function.put(ctx, an.symbols.getFunctionScope());
 | 
									an.function.put(ctx, an.symbols.getFunctionScope());
 | 
				
			||||||
			} catch (NoProgramException e) {
 | 
								} catch (NoProgramException e) {
 | 
				
			||||||
| 
						 | 
					@ -393,13 +519,13 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
 | 
				
			||||||
		if (ctx.expr().size() == 1) {
 | 
							if (ctx.expr().size() == 1) {
 | 
				
			||||||
			Type type = visit(ctx.expr(0));
 | 
								Type type = visit(ctx.expr(0));
 | 
				
			||||||
			if (SimpleType.VOID.equals(type))
 | 
								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;
 | 
								return type;
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			ctx.expr().stream().map(this::visit).forEach((type) -> {
 | 
								ctx.expr().stream().map(this::visit).forEach((type) -> {
 | 
				
			||||||
				if (SimpleType.VOID.equals(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;
 | 
								return SimpleType.VOID;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -22,6 +22,13 @@ import pp.s1184725.boppi.util.RegisterPool;
 | 
				
			||||||
 * @author Frank Wibbelink
 | 
					 * @author Frank Wibbelink
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
					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 int ARBASESIZE = 16;
 | 
				
			||||||
	private static final Num ZERO = new Num(0);
 | 
						private static final Num ZERO = new Num(0);
 | 
				
			||||||
	private static final Num OFFSET_ARP = new Num(-4), OFFSET_RETURN_ADDR = new Num(-8),
 | 
						private static final Num OFFSET_ARP = new Num(-4), OFFSET_RETURN_ADDR = new Num(-8),
 | 
				
			||||||
| 
						 | 
					@ -31,7 +38,7 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private Program prog;
 | 
						private Program prog;
 | 
				
			||||||
	private Annotations an;
 | 
						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 Logger logger;
 | 
				
			||||||
	private RegisterPool regPool;
 | 
						private RegisterPool regPool;
 | 
				
			||||||
	private int labels = 0;
 | 
						private int labels = 0;
 | 
				
			||||||
| 
						 | 
					@ -78,7 +85,7 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	protected BoppiGenerator(Annotations annotations, Logger logger) {
 | 
						protected BoppiGenerator(Annotations annotations, Logger logger) {
 | 
				
			||||||
		try {
 | 
							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);
 | 
								prog = Assembler.instance().assemble(memlib);
 | 
				
			||||||
		} catch (Exception e) {
 | 
							} catch (Exception e) {
 | 
				
			||||||
			throw new RuntimeException(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) {
 | 
						private Op emit(String comment, Label label, OpCode opCode, Operand... args) {
 | 
				
			||||||
		Op result = new Op(label, opCode, 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);
 | 
							this.prog.addInstr(result);
 | 
				
			||||||
		return result;
 | 
							return result;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -117,8 +124,8 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
				
			||||||
	 *            register containing the top-level AR
 | 
						 *            register containing the top-level AR
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	private void incARReferences(Reg temp, Reg ar) {
 | 
						private void incARReferences(Reg temp, Reg ar) {
 | 
				
			||||||
		String comment = "AR incRef";
 | 
							String comment = "AR incRef"; //$NON-NLS-1$
 | 
				
			||||||
		Label loop = makeLabel("aril"), done = makeLabel("arid");
 | 
							Label loop = makeLabel("aril"), done = makeLabel("arid"); //$NON-NLS-1$ //$NON-NLS-2$
 | 
				
			||||||
		emit(comment, OpCode.i2i, ar, tempArp);
 | 
							emit(comment, OpCode.i2i, ar, tempArp);
 | 
				
			||||||
		emit(comment, OpCode.cmp_NE, tempArp, RegisterPool.ZERO, temp);
 | 
							emit(comment, OpCode.cmp_NE, tempArp, RegisterPool.ZERO, temp);
 | 
				
			||||||
		emit(comment, OpCode.cbr, temp, loop, done);
 | 
							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.push, temp);
 | 
				
			||||||
		emit(comment, OpCode.subI, tempArp, new Num(ARBASESIZE), temp);
 | 
							emit(comment, OpCode.subI, tempArp, new Num(ARBASESIZE), temp);
 | 
				
			||||||
		emit(comment, OpCode.push, 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.loadAI, tempArp, OFFSET_AL, tempArp);
 | 
				
			||||||
		emit(comment, OpCode.cmp_NE, tempArp, RegisterPool.ZERO, temp);
 | 
							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
 | 
						 *            register containing the top-level AR
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	private void decARReferences(Reg temp, Reg ar) {
 | 
						private void decARReferences(Reg temp, Reg ar) {
 | 
				
			||||||
		String comment = "AR decRef";
 | 
							String comment = "AR decRef"; //$NON-NLS-1$
 | 
				
			||||||
		Label loop = makeLabel("ardl"), done = makeLabel("ardd");
 | 
							Label loop = makeLabel("ardl"), done = makeLabel("ardd"); //$NON-NLS-1$ //$NON-NLS-2$
 | 
				
			||||||
		emit(comment, OpCode.i2i, ar, tempArp);
 | 
							emit(comment, OpCode.i2i, ar, tempArp);
 | 
				
			||||||
		emit(comment, OpCode.cmp_NE, tempArp, RegisterPool.ZERO, temp);
 | 
							emit(comment, OpCode.cmp_NE, tempArp, RegisterPool.ZERO, temp);
 | 
				
			||||||
		emit(comment, OpCode.cbr, temp, loop, done);
 | 
							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.push, temp);
 | 
				
			||||||
		emit(comment, OpCode.subI, tempArp, new Num(ARBASESIZE), temp);
 | 
							emit(comment, OpCode.subI, tempArp, new Num(ARBASESIZE), temp);
 | 
				
			||||||
		emit(comment, OpCode.push, 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.loadAI, tempArp, OFFSET_AL, tempArp);
 | 
				
			||||||
		emit(comment, OpCode.cmp_NE, tempArp, RegisterPool.ZERO, temp);
 | 
							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
 | 
						 *            the size of the memory chunk
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	private void malloc(Reg temp, int size) {
 | 
						private void malloc(Reg temp, int size) {
 | 
				
			||||||
		emit("malloc", OpCode.loadI, new Num(prog.size() + 5), temp);
 | 
							emit("malloc", OpCode.loadI, new Num(prog.size() + 5), temp); //$NON-NLS-1$
 | 
				
			||||||
		emit("malloc", OpCode.push, temp);
 | 
							emit("malloc", OpCode.push, temp); //$NON-NLS-1$
 | 
				
			||||||
		emit("malloc", OpCode.loadI, new Num(size), temp);
 | 
							emit("malloc", OpCode.loadI, new Num(size), temp); //$NON-NLS-1$
 | 
				
			||||||
		emit("malloc", OpCode.push, temp);
 | 
							emit("malloc", OpCode.push, temp); //$NON-NLS-1$
 | 
				
			||||||
		emit("malloc", OpCode.jumpI, new Label("memalloc"));
 | 
							emit("malloc", OpCode.jumpI, new Label("memalloc")); //$NON-NLS-1$ //$NON-NLS-2$
 | 
				
			||||||
		emit("malloc", OpCode.pop, temp);
 | 
							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
 | 
						 *            the register containing the size of the memory chunk
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	private void malloc(Reg temp, Reg size) {
 | 
						private void malloc(Reg temp, Reg size) {
 | 
				
			||||||
		emit("malloc", OpCode.loadI, new Num(prog.size() + 4), temp);
 | 
							emit("malloc", OpCode.loadI, new Num(prog.size() + 4), temp); //$NON-NLS-1$
 | 
				
			||||||
		emit("malloc", OpCode.push, temp);
 | 
							emit("malloc", OpCode.push, temp); //$NON-NLS-1$
 | 
				
			||||||
		emit("malloc", OpCode.push, size);
 | 
							emit("malloc", OpCode.push, size); //$NON-NLS-1$
 | 
				
			||||||
		emit("malloc", OpCode.jumpI, new Label("memalloc"));
 | 
							emit("malloc", OpCode.jumpI, new Label("memalloc")); //$NON-NLS-1$ //$NON-NLS-2$
 | 
				
			||||||
		emit("malloc", OpCode.pop, temp);
 | 
							emit("malloc", OpCode.pop, temp); //$NON-NLS-1$
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
| 
						 | 
					@ -204,10 +211,10 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
				
			||||||
	 *            the register containing the memory pointer
 | 
						 *            the register containing the memory pointer
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	private void free(Reg temp, Reg ptr) {
 | 
						private void free(Reg temp, Reg ptr) {
 | 
				
			||||||
		emit("free", OpCode.loadI, new Num(prog.size() + 4), temp);
 | 
							emit("free", OpCode.loadI, new Num(prog.size() + 4), temp); //$NON-NLS-1$
 | 
				
			||||||
		emit("free", OpCode.push, temp);
 | 
							emit("free", OpCode.push, temp); //$NON-NLS-1$
 | 
				
			||||||
		emit("free", OpCode.push, ptr);
 | 
							emit("free", OpCode.push, ptr); //$NON-NLS-1$
 | 
				
			||||||
		emit("free", OpCode.jumpI, new Label("memfree"));
 | 
							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
 | 
						 *            the register containing the memory pointer
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	private void incRef(Reg temp, Reg ptr) {
 | 
						private void incRef(Reg temp, Reg ptr) {
 | 
				
			||||||
		emit("memaddref", OpCode.loadI, new Num(prog.size() + 4), temp);
 | 
							emit("memaddref", OpCode.loadI, new Num(prog.size() + 4), temp); //$NON-NLS-1$
 | 
				
			||||||
		emit("memaddref", OpCode.push, temp);
 | 
							emit("memaddref", OpCode.push, temp); //$NON-NLS-1$
 | 
				
			||||||
		emit("memaddref", OpCode.push, ptr);
 | 
							emit("memaddref", OpCode.push, ptr); //$NON-NLS-1$
 | 
				
			||||||
		emit("memaddref", OpCode.jumpI, new Label("memaddref"));
 | 
							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) {
 | 
						private String getError(ParserRuleContext node, String message, Object... args) {
 | 
				
			||||||
		int line = node.getStart().getLine();
 | 
							int line = node.getStart().getLine();
 | 
				
			||||||
		int column = node.getStart().getCharPositionInLine();
 | 
							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) {
 | 
						private void incrementReference(Type type, Reg addr) {
 | 
				
			||||||
		if (!(type instanceof ReferenceType))
 | 
							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) -> {
 | 
							regPool.withReg((temp, ar) -> {
 | 
				
			||||||
			String comment = "add new reference";
 | 
								String comment = "add new reference"; //$NON-NLS-1$
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			incRef(temp, addr);
 | 
								incRef(temp, addr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -292,11 +299,11 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	private void decrementReference(Type type, Reg addr) {
 | 
						private void decrementReference(Type type, Reg addr) {
 | 
				
			||||||
		if (!(type instanceof ReferenceType))
 | 
							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) -> {
 | 
							regPool.withReg((temp) -> {
 | 
				
			||||||
			Label isNull = makeLabel("ynul"), notNull = makeLabel("nnul");
 | 
								Label isNull = makeLabel("ynul"), notNull = makeLabel("nnul"); //$NON-NLS-1$ //$NON-NLS-2$
 | 
				
			||||||
			String comment = "remove old reference";
 | 
								String comment = "remove old reference"; //$NON-NLS-1$
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			emit(comment, OpCode.cmp_EQ, addr, RegisterPool.ZERO, temp);
 | 
								emit(comment, OpCode.cmp_EQ, addr, RegisterPool.ZERO, temp);
 | 
				
			||||||
			emit(comment, OpCode.cbr, temp, isNull, notNull);
 | 
								emit(comment, OpCode.cbr, temp, isNull, notNull);
 | 
				
			||||||
| 
						 | 
					@ -328,9 +335,9 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
				
			||||||
				Type type = var.getType();
 | 
									Type type = var.getType();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if (type instanceof ReferenceType) {
 | 
									if (type instanceof ReferenceType) {
 | 
				
			||||||
					Label isNull = makeLabel("ynul"), notNull = makeLabel("nnul");
 | 
										Label isNull = makeLabel("ynul"), notNull = makeLabel("nnul"); //$NON-NLS-1$ //$NON-NLS-2$
 | 
				
			||||||
					String comment = "remove reference";
 | 
										String comment = "remove reference"; //$NON-NLS-1$
 | 
				
			||||||
					emit(comment + " get var", OpCode.loadAI, arp, new Num(var.getOffset()), addr);
 | 
										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.cmp_EQ, addr, RegisterPool.ZERO, ft);
 | 
				
			||||||
					emit(comment, OpCode.cbr, ft, isNull, notNull);
 | 
										emit(comment, OpCode.cbr, ft, isNull, notNull);
 | 
				
			||||||
| 
						 | 
					@ -388,16 +395,18 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
				
			||||||
		} catch (Exception e) {
 | 
							} catch (Exception e) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (tree instanceof ParserRuleContext)
 | 
								if (tree instanceof ParserRuleContext)
 | 
				
			||||||
				logger.severe(getError((ParserRuleContext) tree, "%s: %s", e.getClass().getName(), e.getMessage()));
 | 
									logger.severe(getError((ParserRuleContext) tree, Messages.getString("BoppiGenerator.3"), //$NON-NLS-1$
 | 
				
			||||||
 | 
											e.getClass().getName(), e.getMessage()));
 | 
				
			||||||
			else
 | 
								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;
 | 
								return null;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public Reg visitAssign(AssignContext ctx) {
 | 
						public Reg visitAssign(AssignContext ctx) {
 | 
				
			||||||
		Reg result = visit(ctx.singleExpr());
 | 
							Reg result = visit(ctx.assignStat() != null ? ctx.assignStat() : ctx.expr());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		regPool.blockReg(result, () -> {
 | 
							regPool.blockReg(result, () -> {
 | 
				
			||||||
			Type type = an.types.get(ctx);
 | 
								Type type = an.types.get(ctx);
 | 
				
			||||||
| 
						 | 
					@ -406,7 +415,7 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
				
			||||||
			if (type instanceof ReferenceType) {
 | 
								if (type instanceof ReferenceType) {
 | 
				
			||||||
				regPool.blockReg(addr, () -> {
 | 
									regPool.blockReg(addr, () -> {
 | 
				
			||||||
					regPool.withReg((temp) -> {
 | 
										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);
 | 
											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;
 | 
								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) {
 | 
								if (type instanceof ReferenceType) {
 | 
				
			||||||
				regPool.blockReg(addr, () -> {
 | 
									regPool.blockReg(addr, () -> {
 | 
				
			||||||
					regPool.withReg((temp) -> {
 | 
										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);
 | 
											incrementReference(an.types.get(ctx), temp);
 | 
				
			||||||
					});
 | 
										});
 | 
				
			||||||
				});
 | 
									});
 | 
				
			||||||
| 
						 | 
					@ -429,29 +438,45 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
				
			||||||
		return result;
 | 
							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
 | 
						@Override
 | 
				
			||||||
	public Reg visitCall(CallContext ctx) {
 | 
						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());
 | 
							Variable<Type> function = an.variables.get(ctx.variable());
 | 
				
			||||||
		TupleType parameters = (TupleType) ((FunctionType) function.getType()).getParameter();
 | 
							TupleType parameters = (TupleType) ((FunctionType) function.getType()).getParameter();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		Reg ar = regPool.withReg((tempAR) -> {
 | 
							Reg ar = regPool.withReg((tempAR) -> {
 | 
				
			||||||
			regPool.withReg((rt) -> {
 | 
								regPool.withReg((rt) -> {
 | 
				
			||||||
				Reg addr = visit(ctx.variable());
 | 
									Reg addr = visit(ctx.variable());
 | 
				
			||||||
				emit(base + "load function reference", OpCode.load, addr, 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);
 | 
									emit(base + "load AR size", OpCode.loadAI, rt, OFFSET_FUNCREF_ARSIZE, rt); //$NON-NLS-1$
 | 
				
			||||||
				malloc(tempAR, rt);
 | 
									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++) {
 | 
								for (int i = 0; i < ctx.expr().size(); i++) {
 | 
				
			||||||
				Reg exprReg = visit(ctx.expr(i));
 | 
									Reg exprReg = visit(ctx.expr(i));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if (SimpleType.CHAR.equals(parameters.get(i)))
 | 
									if (SimpleType.CHAR.equals(parameters.get(i)))
 | 
				
			||||||
					emit(base + "store param " + i, OpCode.cstoreAI, exprReg, tempAR, new Num(parameters.getOffset(i)));
 | 
										emit(base + "store param " + i, OpCode.cstoreAI, exprReg, tempAR, new Num(parameters.getOffset(i))); //$NON-NLS-1$
 | 
				
			||||||
				else
 | 
									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<>();
 | 
							Stack<Reg> inUse = new Stack<>();
 | 
				
			||||||
		for (Reg reg : regPool.getInUse()) {
 | 
							for (Reg reg : regPool.getInUse()) {
 | 
				
			||||||
			inUse.push(reg);
 | 
								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.blockReg(ar, () -> {
 | 
				
			||||||
			regPool.withReg((r1, r2) -> {
 | 
								regPool.withReg((r1, r2) -> {
 | 
				
			||||||
				Reg addr = visit(ctx.variable());
 | 
									Reg addr = visit(ctx.variable());
 | 
				
			||||||
				emit(base + "load function reference", OpCode.load, addr, r2);
 | 
									emit(base + "load function reference", OpCode.load, addr, r2); //$NON-NLS-1$
 | 
				
			||||||
				emit(base + "link caller ARP", OpCode.storeAI, arp, ar, OFFSET_ARP);
 | 
									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);
 | 
									emit(base + "load AL", OpCode.loadAI, r2, OFFSET_FUNCREF_ARP, r1); //$NON-NLS-1$
 | 
				
			||||||
				emit(base + "link AL", OpCode.storeAI, r1, ar, OFFSET_AL);
 | 
									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);
 | 
									incARReferences(r1, tempArp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				emit(base + "load return address", OpCode.loadI, new Num(prog.size() + 5), r1);
 | 
									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);
 | 
									emit(base + "set return address", OpCode.storeAI, r1, ar, OFFSET_RETURN_ADDR); //$NON-NLS-1$
 | 
				
			||||||
				emit(base + "move ARP", OpCode.i2i, ar, arp);
 | 
									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 + "load target address", OpCode.loadAI, r2, OFFSET_FUNCREF_ADDR, r1); //$NON-NLS-1$
 | 
				
			||||||
				emit(base + "execute", OpCode.jump, r1);
 | 
									emit(base + "execute", OpCode.jump, r1); //$NON-NLS-1$
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -488,12 +513,12 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		while (!inUse.isEmpty()) {
 | 
							while (!inUse.isEmpty()) {
 | 
				
			||||||
			Reg reg = inUse.pop();
 | 
								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) -> {
 | 
							return regPool.withReg((res) -> {
 | 
				
			||||||
			emit(base + "load result", OpCode.loadAI, arp, OFFSET_RETURN_VAL, res);
 | 
								emit(base + "load result", OpCode.loadAI, arp, OFFSET_RETURN_VAL, res); //$NON-NLS-1$
 | 
				
			||||||
			emit(base + "reset ARP", OpCode.loadAI, arp, OFFSET_ARP, arp);
 | 
								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) {
 | 
							if (t instanceof ReferenceType) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			regPool.withReg((addr) -> {
 | 
								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) {
 | 
									if (t instanceof ArrayType) {
 | 
				
			||||||
					ArrayType type = (ArrayType) t;
 | 
										ArrayType type = (ArrayType) t;
 | 
				
			||||||
					regPool.withReg((temp) -> {
 | 
										regPool.withReg((temp) -> {
 | 
				
			||||||
						malloc(temp, type.getReferenceSize());
 | 
											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>generate entry point</li>
 | 
				
			||||||
	 * <li>visit function body</li>
 | 
						 * <li>visit function body</li>
 | 
				
			||||||
	 * <li>generate return</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 allocate function reference</li>
 | 
				
			||||||
	 * <li>generate store entry point and AR to function reference</li>
 | 
						 * <li>generate store entry point and AR to function reference</li>
 | 
				
			||||||
	 * <li>generate store function reference in variable</li>
 | 
						 * <li>generate store function reference in variable</li>
 | 
				
			||||||
| 
						 | 
					@ -535,67 +562,64 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public Reg visitDeclareFunction(DeclareFunctionContext ctx) {
 | 
						public Reg visitDeclareFunction(DeclareFunctionContext ctx) {
 | 
				
			||||||
		ReferenceType type = (ReferenceType) an.variables.get(ctx).getType();
 | 
							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");
 | 
							Label skip = makeLabel("s"); //$NON-NLS-1$
 | 
				
			||||||
		emit(base + "jump over body", OpCode.jumpI, skip);
 | 
							emit(base + "jump over body", OpCode.jumpI, skip); //$NON-NLS-1$
 | 
				
			||||||
		int callAddress = emit(base + "entry point", OpCode.nop).getLine();
 | 
							int callAddress = emit(base + "entry point", OpCode.nop).getLine(); //$NON-NLS-1$
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		Reg result = visit(ctx.body);
 | 
							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) -> {
 | 
							regPool.withReg((r1) -> {
 | 
				
			||||||
			emit(base + "load return address", OpCode.loadAI, arp, OFFSET_RETURN_ADDR, r1);
 | 
								emit(base + "load return address", OpCode.loadAI, arp, OFFSET_RETURN_ADDR, r1); //$NON-NLS-1$
 | 
				
			||||||
			emit(base + "go to return address", OpCode.jump, r1);
 | 
								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) -> {
 | 
							regPool.withReg((r1, r2) -> {
 | 
				
			||||||
			malloc(r2, type.getReferenceSize());
 | 
								malloc(r2, type.getReferenceSize());
 | 
				
			||||||
			emit(base + "load target address", OpCode.loadI, new Num(callAddress), r1);
 | 
								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);
 | 
								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);
 | 
								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()),
 | 
								emit(base + "load AR size", OpCode.loadI, new Num(ARBASESIZE + an.function.get(ctx).getLocalDataSize()), //$NON-NLS-1$
 | 
				
			||||||
					r1);
 | 
										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);
 | 
								incARReferences(r2, arp);
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return null;
 | 
							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
 | 
						@Override
 | 
				
			||||||
	public Reg visitGetVariable(GetVariableContext ctx) {
 | 
						public Reg visitGetVariable(GetVariableContext ctx) {
 | 
				
			||||||
		Type type = an.types.get(ctx);
 | 
							Type type = an.types.get(ctx);
 | 
				
			||||||
		Reg result = visit(ctx.variable());
 | 
							Reg result = visit(ctx.variable());
 | 
				
			||||||
		OpCode op = SimpleType.CHAR.equals(an.types.get(ctx)) ? OpCode.cload : OpCode.load;
 | 
							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)
 | 
							if (type instanceof ReferenceType)
 | 
				
			||||||
			regPool.blockReg(result, () -> {
 | 
								regPool.blockReg(result, () -> {
 | 
				
			||||||
| 
						 | 
					@ -607,44 +631,48 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public Reg visitIf(IfContext ctx) {
 | 
						public Reg visitIf(IfContext ctx) {
 | 
				
			||||||
		Label toTrue = makeLabel("if_t");
 | 
							Label toTrue = makeLabel("if_t"); //$NON-NLS-1$
 | 
				
			||||||
		Label toFalse = makeLabel("if_f");
 | 
							Label toFalse = makeLabel("if_f"); //$NON-NLS-1$
 | 
				
			||||||
		Label toEnd = makeLabel("if_e");
 | 
							Label toEnd = makeLabel("if_e"); //$NON-NLS-1$
 | 
				
			||||||
		Reg cond = visit(ctx.cond);
 | 
							try {
 | 
				
			||||||
 | 
								Reg cond = visit(ctx.cond);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (ctx.onFalse == null) {
 | 
								if (ctx.onFalse == null) {
 | 
				
			||||||
			emit("", OpCode.cbr, cond, toTrue, toEnd);
 | 
									emit("", OpCode.cbr, cond, toTrue, toEnd); //$NON-NLS-1$
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			emit("", toTrue, OpCode.nop);
 | 
									emit("", toTrue, OpCode.nop); //$NON-NLS-1$
 | 
				
			||||||
			visit(ctx.onTrue);
 | 
									visit(ctx.onTrue);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			emit("end target", toEnd, OpCode.nop);
 | 
									emit("end target", toEnd, OpCode.nop); //$NON-NLS-1$
 | 
				
			||||||
			return null;
 | 
									return null;
 | 
				
			||||||
		} else if (an.types.get(ctx) == SimpleType.VOID) {
 | 
								} else if (an.types.get(ctx) == SimpleType.VOID) {
 | 
				
			||||||
			emit("", OpCode.cbr, cond, toTrue, toFalse);
 | 
									emit("", OpCode.cbr, cond, toTrue, toFalse); //$NON-NLS-1$
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			emit("", toTrue, OpCode.nop);
 | 
									emit("", toTrue, OpCode.nop); //$NON-NLS-1$
 | 
				
			||||||
			visit(ctx.onTrue);
 | 
									visit(ctx.onTrue);
 | 
				
			||||||
			emit("", OpCode.jumpI, toEnd);
 | 
									emit("", OpCode.jumpI, toEnd); //$NON-NLS-1$
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			emit("", toFalse, OpCode.nop);
 | 
									emit("", toFalse, OpCode.nop); //$NON-NLS-1$
 | 
				
			||||||
			visit(ctx.onFalse);
 | 
									visit(ctx.onFalse);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			emit("end target", toEnd, OpCode.nop);
 | 
									emit("end target", toEnd, OpCode.nop); //$NON-NLS-1$
 | 
				
			||||||
			return null;
 | 
									return null;
 | 
				
			||||||
		} else {
 | 
								} else {
 | 
				
			||||||
			return regPool.withReg((target) -> {
 | 
									return regPool.withReg((target) -> {
 | 
				
			||||||
				emit("", OpCode.cbr, cond, toTrue, toFalse);
 | 
										emit("", OpCode.cbr, cond, toTrue, toFalse); //$NON-NLS-1$
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				emit("", toTrue, OpCode.nop);
 | 
										emit("", toTrue, OpCode.nop); //$NON-NLS-1$
 | 
				
			||||||
				emit("result", OpCode.i2i, visit(ctx.onTrue), target);
 | 
										emit("result", OpCode.i2i, visit(ctx.onTrue), target); //$NON-NLS-1$
 | 
				
			||||||
				emit("", OpCode.jumpI, toEnd);
 | 
										emit("", OpCode.jumpI, toEnd); //$NON-NLS-1$
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				emit("", toFalse, OpCode.nop);
 | 
										emit("", toFalse, OpCode.nop); //$NON-NLS-1$
 | 
				
			||||||
				emit("result", OpCode.i2i, visit(ctx.onFalse), target);
 | 
										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) -> {
 | 
									return regPool.withReg((i, temp1) -> {
 | 
				
			||||||
					regPool.withReg((temp2) -> {
 | 
										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("iterate from 0", OpCode.loadI, ZERO, i); //$NON-NLS-1$
 | 
				
			||||||
						emit("equality true", OpCode.loadI, new Num(1), temp1);
 | 
											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("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);
 | 
											emit("check i in bounds", OpCode.cmp_LT, i, temp2, temp2); //$NON-NLS-1$
 | 
				
			||||||
						emit("loop", OpCode.cbr, temp2, loop, end);
 | 
											emit("loop", OpCode.cbr, temp2, loop, end); //$NON-NLS-1$
 | 
				
			||||||
 | 
					
 | 
				
			||||||
						emit("load left", loop, OpCode.loadAO, lhs, i, temp1);
 | 
											emit("load left", loop, OpCode.loadAO, lhs, i, temp1); //$NON-NLS-1$
 | 
				
			||||||
						emit("load right", OpCode.loadAO, rhs, i, temp2);
 | 
											emit("load right", OpCode.loadAO, rhs, i, temp2); //$NON-NLS-1$
 | 
				
			||||||
						emit("increment i", OpCode.addI, i, elementSize, i);
 | 
											emit("increment i", OpCode.addI, i, elementSize, i); //$NON-NLS-1$
 | 
				
			||||||
 | 
					
 | 
				
			||||||
						emit("compare elements", OpCode.cmp_EQ, temp1, temp2, temp1);
 | 
											emit("compare elements", OpCode.cmp_EQ, temp1, temp2, temp1); //$NON-NLS-1$
 | 
				
			||||||
						emit("loop or break", OpCode.cbr, temp1, cond, end);
 | 
											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;
 | 
							return lhs;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public Reg visitLambda(LambdaContext ctx) {
 | 
				
			||||||
 | 
							emit(ctx.getText(), OpCode.haltI, new Num(768));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return visit(ctx.expr());
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public Reg visitLiteralBoolean(LiteralBooleanContext ctx) {
 | 
						public Reg visitLiteralBoolean(LiteralBooleanContext ctx) {
 | 
				
			||||||
		return regPool.withReg((reg) -> {
 | 
							return regPool.withReg((reg) -> {
 | 
				
			||||||
| 
						 | 
					@ -745,7 +780,7 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
				
			||||||
			try {
 | 
								try {
 | 
				
			||||||
				val = Integer.parseInt(ctx.LITERAL10().getText());
 | 
									val = Integer.parseInt(ctx.LITERAL10().getText());
 | 
				
			||||||
			} catch (NumberFormatException e) {
 | 
								} 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);
 | 
								emit(ctx.getText(), OpCode.loadI, new Num(val), reg);
 | 
				
			||||||
| 
						 | 
					@ -754,15 +789,15 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public Reg visitPrefix1(Prefix1Context ctx) {
 | 
						public Reg visitPrefix1(Prefix1Context ctx) {
 | 
				
			||||||
		Reg reg = visit(ctx.singleExpr());
 | 
							Reg reg = visit(ctx.expr());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		switch (ctx.op.getType()) {
 | 
							switch (ctx.op.getType()) {
 | 
				
			||||||
		case BoppiLexer.MINUS:
 | 
							case BoppiLexer.MINUS:
 | 
				
			||||||
			emit("unary -", OpCode.rsubI, reg, ZERO, reg);
 | 
								emit("unary -", OpCode.rsubI, reg, ZERO, reg); //$NON-NLS-1$
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		case BoppiLexer.NOT:
 | 
							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;
 | 
								break;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return reg;
 | 
							return reg;
 | 
				
			||||||
| 
						 | 
					@ -770,15 +805,15 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public Reg visitProgram(ProgramContext ctx) {
 | 
						public Reg visitProgram(ProgramContext ctx) {
 | 
				
			||||||
		emit("initialise zero register", OpCode.loadI, ZERO, RegisterPool.ZERO);
 | 
							emit("initialise zero register", OpCode.loadI, ZERO, RegisterPool.ZERO); //$NON-NLS-1$
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		malloc(arp, an.function.get(ctx).getLocalDataSize() + ARBASESIZE);
 | 
							malloc(arp, an.function.get(ctx).getLocalDataSize() + ARBASESIZE);
 | 
				
			||||||
		emit("construct main AR", OpCode.addI, arp, new Num(ARBASESIZE), arp);
 | 
							emit("construct main AR", OpCode.addI, arp, new Num(ARBASESIZE), arp); //$NON-NLS-1$
 | 
				
			||||||
		visitChildren(ctx);
 | 
							visitChildren(ctx);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		dereferenceLocalVariables(an.function.get(ctx));
 | 
							dereferenceLocalVariables(an.function.get(ctx));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		emit("deconstruct main AR", OpCode.subI, arp, new Num(ARBASESIZE), arp);
 | 
							emit("deconstruct main AR", OpCode.subI, arp, new Num(ARBASESIZE), arp); //$NON-NLS-1$
 | 
				
			||||||
		regPool.withReg((temp) -> {
 | 
							regPool.withReg((temp) -> {
 | 
				
			||||||
			free(temp, arp);
 | 
								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))) {
 | 
									if (SimpleType.BOOL.equals(an.types.get(expr)) || SimpleType.INT.equals(an.types.get(expr))) {
 | 
				
			||||||
					regPool.withReg((temp) -> {
 | 
										regPool.withReg((temp) -> {
 | 
				
			||||||
						emit("", OpCode.in, new Str(""), temp);
 | 
											emit("", OpCode.in, new Str(""), temp); //$NON-NLS-1$ //$NON-NLS-2$
 | 
				
			||||||
						Reg addr = visit(expr);
 | 
											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))) {
 | 
									} else if (SimpleType.CHAR.equals(an.types.get(expr))) {
 | 
				
			||||||
					// Get input until at least 1 character
 | 
										// Get input until at least 1 character
 | 
				
			||||||
					Label getTarget = makeLabel("lcin_l");
 | 
										Label getTarget = makeLabel("lcin_l"); //$NON-NLS-1$
 | 
				
			||||||
					Label continueTarget = makeLabel("lcin_e");
 | 
										Label continueTarget = makeLabel("lcin_e"); //$NON-NLS-1$
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					regPool.withReg((temp, count) -> {
 | 
										regPool.withReg((temp, count) -> {
 | 
				
			||||||
						emit("", getTarget, OpCode.cin, new Str(""));
 | 
											emit("", getTarget, OpCode.cin, new Str("")); //$NON-NLS-1$ //$NON-NLS-2$
 | 
				
			||||||
						emit("str length", OpCode.pop, count);
 | 
											emit("str length", OpCode.pop, count); //$NON-NLS-1$
 | 
				
			||||||
						emit("repeat if 0 length", OpCode.cbr, count, continueTarget, getTarget);
 | 
											emit("repeat if 0 length", OpCode.cbr, count, continueTarget, getTarget); //$NON-NLS-1$
 | 
				
			||||||
 | 
					
 | 
				
			||||||
						// Get character
 | 
											// 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);
 | 
											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
 | 
											// Pop all remaining characters
 | 
				
			||||||
						Label loopTarget = makeLabel("lcpop_l");
 | 
											Label loopTarget = makeLabel("lcpop_l"); //$NON-NLS-1$
 | 
				
			||||||
						Label iterTarget = makeLabel("lcpop_c");
 | 
											Label iterTarget = makeLabel("lcpop_c"); //$NON-NLS-1$
 | 
				
			||||||
						Label stopTarget = makeLabel("lcpop_e");
 | 
											Label stopTarget = makeLabel("lcpop_e"); //$NON-NLS-1$
 | 
				
			||||||
						emit("pop remaining", loopTarget, OpCode.subI, count, new Num(1), temp);
 | 
											emit("pop remaining", loopTarget, OpCode.subI, count, new Num(1), temp); //$NON-NLS-1$
 | 
				
			||||||
						emit("pop remaining", OpCode.cbr, count, iterTarget, stopTarget);
 | 
											emit("pop remaining", OpCode.cbr, count, iterTarget, stopTarget); //$NON-NLS-1$
 | 
				
			||||||
						emit("pop remaining", iterTarget, OpCode.cpop, temp);
 | 
											emit("pop remaining", iterTarget, OpCode.cpop, temp); //$NON-NLS-1$
 | 
				
			||||||
						emit("pop remaining", OpCode.jumpI, loopTarget);
 | 
											emit("pop remaining", OpCode.jumpI, loopTarget); //$NON-NLS-1$
 | 
				
			||||||
						emit("pop remaining", stopTarget, OpCode.nop);
 | 
											emit("pop remaining", stopTarget, OpCode.nop); //$NON-NLS-1$
 | 
				
			||||||
					});
 | 
										});
 | 
				
			||||||
				} else {
 | 
									} 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))) {
 | 
								if (SimpleType.BOOL.equals(an.types.get(expr)) || SimpleType.INT.equals(an.types.get(expr))) {
 | 
				
			||||||
				return regPool.withReg((result) -> {
 | 
									return regPool.withReg((result) -> {
 | 
				
			||||||
					emit("", OpCode.in, new Str(""), result);
 | 
										emit("", OpCode.in, new Str(""), result); //$NON-NLS-1$ //$NON-NLS-2$
 | 
				
			||||||
					Reg addr = visit(expr);
 | 
										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))) {
 | 
								} else if (SimpleType.CHAR.equals(an.types.get(expr))) {
 | 
				
			||||||
				return regPool.withReg((count, result) -> {
 | 
									return regPool.withReg((count, result) -> {
 | 
				
			||||||
					// Get input until at least 1 character
 | 
										// Get input until at least 1 character
 | 
				
			||||||
					Label getTarget = makeLabel("lcin_l");
 | 
										Label getTarget = makeLabel("lcin_l"); //$NON-NLS-1$
 | 
				
			||||||
					Label continueTarget = makeLabel("lcin_e");
 | 
										Label continueTarget = makeLabel("lcin_e"); //$NON-NLS-1$
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					emit("", getTarget, OpCode.cin, new Str(""));
 | 
										emit("", getTarget, OpCode.cin, new Str("")); //$NON-NLS-1$ //$NON-NLS-2$
 | 
				
			||||||
					emit("str length", OpCode.pop, count);
 | 
										emit("str length", OpCode.pop, count); //$NON-NLS-1$
 | 
				
			||||||
					emit("repeat if 0 length", OpCode.cbr, count, continueTarget, getTarget);
 | 
										emit("repeat if 0 length", OpCode.cbr, count, continueTarget, getTarget); //$NON-NLS-1$
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					// Get character
 | 
										// 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);
 | 
										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
 | 
										// Pop all remaining characters
 | 
				
			||||||
					Label loopTarget = makeLabel("lcpop_l");
 | 
										Label loopTarget = makeLabel("lcpop_l"); //$NON-NLS-1$
 | 
				
			||||||
					Label iterTarget = makeLabel("lcpop_c");
 | 
										Label iterTarget = makeLabel("lcpop_c"); //$NON-NLS-1$
 | 
				
			||||||
					Label stopTarget = makeLabel("lcpop_e");
 | 
										Label stopTarget = makeLabel("lcpop_e"); //$NON-NLS-1$
 | 
				
			||||||
					regPool.withReg((temp) -> {
 | 
										regPool.withReg((temp) -> {
 | 
				
			||||||
						emit("pop remaining", loopTarget, OpCode.subI, count, new Num(1), count);
 | 
											emit("pop remaining", loopTarget, OpCode.subI, count, new Num(1), count); //$NON-NLS-1$
 | 
				
			||||||
						emit("pop remaining", OpCode.cbr, count, iterTarget, stopTarget);
 | 
											emit("pop remaining", OpCode.cbr, count, iterTarget, stopTarget); //$NON-NLS-1$
 | 
				
			||||||
						emit("pop remaining", iterTarget, OpCode.cpop, temp);
 | 
											emit("pop remaining", iterTarget, OpCode.cpop, temp); //$NON-NLS-1$
 | 
				
			||||||
						emit("pop remaining", OpCode.jumpI, loopTarget);
 | 
											emit("pop remaining", OpCode.jumpI, loopTarget); //$NON-NLS-1$
 | 
				
			||||||
						emit("pop remaining", stopTarget, OpCode.nop);
 | 
											emit("pop remaining", stopTarget, OpCode.nop); //$NON-NLS-1$
 | 
				
			||||||
					});
 | 
										});
 | 
				
			||||||
				});
 | 
									});
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				emit("reading unknown type", OpCode.haltI, new Num(0x72656164));
 | 
									emit("reading unknown type", OpCode.haltI, new Num(0x72656164)); //$NON-NLS-1$
 | 
				
			||||||
				return null;
 | 
									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
 | 
						@Override
 | 
				
			||||||
	public Reg visitVariableArray(VariableArrayContext ctx) {
 | 
						public Reg visitVariableArray(VariableArrayContext ctx) {
 | 
				
			||||||
		ArrayType type = (ArrayType) an.types.get(ctx.variable());
 | 
							ArrayType type = (ArrayType) an.types.get(ctx.variable());
 | 
				
			||||||
		Reg addr = visit(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, () -> {
 | 
							regPool.blockReg(addr, () -> {
 | 
				
			||||||
			Reg offset = visit(ctx.expr());
 | 
								Reg offset = visit(ctx.expr());
 | 
				
			||||||
			regPool.blockReg(offset, () -> {
 | 
								regPool.blockReg(offset, () -> {
 | 
				
			||||||
				regPool.withReg((r1, r2) -> {
 | 
									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.loadAI, addr, OFFSET_REF_SIZE, r1); //$NON-NLS-1$
 | 
				
			||||||
					emit("check array index", OpCode.divI, r1, new Num(type.getType().getSize()), r1);
 | 
										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);
 | 
										emit("check array index", OpCode.cmp_LT, offset, r1, r1); //$NON-NLS-1$
 | 
				
			||||||
					emit("check array index", OpCode.cmp_GE, offset, RegisterPool.ZERO, r2);
 | 
										emit("check array index", OpCode.cmp_GE, offset, RegisterPool.ZERO, r2); //$NON-NLS-1$
 | 
				
			||||||
					emit("check array index", OpCode.and, r1, r2, r2);
 | 
										emit("check array index", OpCode.and, r1, r2, r2); //$NON-NLS-1$
 | 
				
			||||||
					emit("check array index", OpCode.cbr, r2, inBounds, outOfBounds);
 | 
										emit("check array index", OpCode.cbr, r2, inBounds, outOfBounds); //$NON-NLS-1$
 | 
				
			||||||
					emit("array index out of bounds", outOfBounds, OpCode.haltI, new Num(0x616f6f62));
 | 
										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);
 | 
												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());
 | 
							Reg addr = visit(ctx.variable());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (innerType instanceof ArrayType) {
 | 
							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;
 | 
								return addr;
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			return addr;
 | 
								return addr;
 | 
				
			||||||
| 
						 | 
					@ -925,30 +980,30 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
				
			||||||
			int functionDepth = an.function.get(ctx).getFunctionDepth();
 | 
								int functionDepth = an.function.get(ctx).getFunctionDepth();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (var.getDepth() < functionDepth) {
 | 
								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++)
 | 
									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;
 | 
									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
 | 
						@Override
 | 
				
			||||||
	public Reg visitWhile(WhileContext ctx) {
 | 
						public Reg visitWhile(WhileContext ctx) {
 | 
				
			||||||
		Label toLoop = makeLabel("while_t");
 | 
							Label toLoop = makeLabel("while_t"); //$NON-NLS-1$
 | 
				
			||||||
		Label toCond = makeLabel("while_f");
 | 
							Label toCond = makeLabel("while_f"); //$NON-NLS-1$
 | 
				
			||||||
		Label toEnd = makeLabel("while_e");
 | 
							Label toEnd = makeLabel("while_e"); //$NON-NLS-1$
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		emit("to condition", OpCode.jumpI, toCond);
 | 
							emit("to condition", OpCode.jumpI, toCond); //$NON-NLS-1$
 | 
				
			||||||
		emit("loop target", toLoop, OpCode.nop);
 | 
							emit("loop target", toLoop, OpCode.nop); //$NON-NLS-1$
 | 
				
			||||||
		visit(ctx.onTrue);
 | 
							visit(ctx.onTrue);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		emit("condition target", toCond, OpCode.nop);
 | 
							emit("condition target", toCond, OpCode.nop); //$NON-NLS-1$
 | 
				
			||||||
		emit("", OpCode.cbr, visit(ctx.cond), toLoop, toEnd);
 | 
							emit("", OpCode.cbr, visit(ctx.cond), toLoop, toEnd); //$NON-NLS-1$
 | 
				
			||||||
		emit("end target", toEnd, OpCode.nop);
 | 
							emit("end target", toEnd, OpCode.nop); //$NON-NLS-1$
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return null;
 | 
							return null;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -963,16 +1018,16 @@ public class BoppiGenerator extends BoppiBaseVisitor<Reg> {
 | 
				
			||||||
					Type type = an.types.get(expr);
 | 
										Type type = an.types.get(expr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					if (SimpleType.BOOL.equals(type) || SimpleType.INT.equals(type)) {
 | 
										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)) {
 | 
										} else if (SimpleType.CHAR.equals(type)) {
 | 
				
			||||||
						regPool.withReg((temp) -> {
 | 
											regPool.withReg((temp) -> {
 | 
				
			||||||
							emit("push character", OpCode.cpush, result);
 | 
												emit("push character", OpCode.cpush, result); //$NON-NLS-1$
 | 
				
			||||||
							emit("load 1", OpCode.loadI, new Num(1), temp);
 | 
												emit("load 1", OpCode.loadI, new Num(1), temp); //$NON-NLS-1$
 | 
				
			||||||
							emit("push 1", OpCode.push, temp);
 | 
												emit("push 1", OpCode.push, temp); //$NON-NLS-1$
 | 
				
			||||||
							emit("print character", OpCode.cout, new Str(""));
 | 
												emit("print character", OpCode.cout, new Str("")); //$NON-NLS-1$ //$NON-NLS-2$
 | 
				
			||||||
						});
 | 
											});
 | 
				
			||||||
					} else {
 | 
										} 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)))
 | 
										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);
 | 
									Type type = an.types.get(expr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if (SimpleType.BOOL.equals(type) || SimpleType.INT.equals(type)) {
 | 
									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;
 | 
										return result;
 | 
				
			||||||
				} else if (SimpleType.CHAR.equals(type)) {
 | 
									} else if (SimpleType.CHAR.equals(type)) {
 | 
				
			||||||
					regPool.withReg((temp) -> {
 | 
										regPool.withReg((temp) -> {
 | 
				
			||||||
						emit("push character", OpCode.cpush, result);
 | 
											emit("push character", OpCode.cpush, result); //$NON-NLS-1$
 | 
				
			||||||
						emit("load 1", OpCode.loadI, new Num(1), temp);
 | 
											emit("load 1", OpCode.loadI, new Num(1), temp); //$NON-NLS-1$
 | 
				
			||||||
						emit("push 1", OpCode.push, temp);
 | 
											emit("push 1", OpCode.push, temp); //$NON-NLS-1$
 | 
				
			||||||
						emit("print character", OpCode.cout, new Str(""));
 | 
											emit("print character", OpCode.cout, new Str("")); //$NON-NLS-1$ //$NON-NLS-2$
 | 
				
			||||||
					});
 | 
										});
 | 
				
			||||||
					return result;
 | 
										return result;
 | 
				
			||||||
				} else {
 | 
									} else {
 | 
				
			||||||
					emit("writing unknown type", OpCode.haltI, new Num(0x77726974));
 | 
										emit("writing unknown type", OpCode.haltI, new Num(0x77726974)); //$NON-NLS-1$
 | 
				
			||||||
					return null;
 | 
										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() {
 | 
						public FunctionScope<T> closeFunctionScope() throws NoProgramException {
 | 
				
			||||||
		symbolMapStack.push(new HashMap<>());
 | 
							closeScope();
 | 
				
			||||||
	}
 | 
							return functionScope.pop();
 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * 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()));
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
| 
						 | 
					@ -69,40 +68,97 @@ public class CachingSymbolTable<T extends Type> {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Closes a lexical scope for a function declaration, removing all
 | 
						 * Returns the type of an identifier, if any.
 | 
				
			||||||
	 * declarations within this scope, restoring the local offset and decreasing
 | 
					 | 
				
			||||||
	 * the lookup depth.
 | 
					 | 
				
			||||||
	 * 
 | 
					 | 
				
			||||||
	 * @return function scope details
 | 
					 | 
				
			||||||
	 * 
 | 
						 * 
 | 
				
			||||||
 | 
						 * @param id
 | 
				
			||||||
 | 
						 *            the name of the identifier
 | 
				
			||||||
 | 
						 * @return the type of the identifier
 | 
				
			||||||
 | 
						 * @throws UndeclaredException
 | 
				
			||||||
 | 
						 *             if the identifier is not declared
 | 
				
			||||||
	 * @throws NoProgramException
 | 
						 * @throws NoProgramException
 | 
				
			||||||
	 *             if no scope is open
 | 
						 *             if no scope is open
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public FunctionScope<T> closeFunctionScope() throws NoProgramException {
 | 
						public Variable<T> get(String id) throws UndeclaredException, NoProgramException {
 | 
				
			||||||
		closeScope();
 | 
							if (!this.has(id))
 | 
				
			||||||
		return functionScope.pop();
 | 
								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
 | 
						 * Returns the current function scope, if any.
 | 
				
			||||||
	 * 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 <U>
 | 
						 * @return the current function scope
 | 
				
			||||||
	 *            the type of the code's return value
 | 
						 * @throws NoProgramException
 | 
				
			||||||
	 * @param code
 | 
						 *             if no scope is open
 | 
				
			||||||
	 *            the code to execute within the scope
 | 
					 | 
				
			||||||
	 * @return the return value of the code
 | 
					 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	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();
 | 
							openScope();
 | 
				
			||||||
		U result = code.get();
 | 
							functionScope.push(new FunctionScope<T>(functionScope.size()));
 | 
				
			||||||
		try {
 | 
						}
 | 
				
			||||||
			closeScope();
 | 
					
 | 
				
			||||||
		} catch (NoProgramException e) {
 | 
						/**
 | 
				
			||||||
		}
 | 
						 * Opens a lexical scope.
 | 
				
			||||||
		return result;
 | 
						 */
 | 
				
			||||||
 | 
						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
 | 
						 * Opens a scope, executes the given code and closes the scope. This is a
 | 
				
			||||||
	 * and returns the newly made variable instance that belongs to this
 | 
						 * helper method to make sure calls to {@link #openScope()} and
 | 
				
			||||||
	 * identifier. Throws an exception if the identifier is declared in this
 | 
						 * {@link #closeScope()} are balanced. {@code code} must take no arguments
 | 
				
			||||||
	 * scope already.
 | 
						 * and must return a value.
 | 
				
			||||||
	 * 
 | 
						 * 
 | 
				
			||||||
	 * @param id
 | 
						 * @param <U>
 | 
				
			||||||
	 *            the name of the identifier
 | 
						 *            the type of the code's return value
 | 
				
			||||||
	 * @param type
 | 
						 * @param code
 | 
				
			||||||
	 *            the type of the identifier
 | 
						 *            the code to execute within the scope
 | 
				
			||||||
	 * @return the type of the identifier
 | 
						 * @return the return value of the code
 | 
				
			||||||
	 * @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 {
 | 
						public <U> U withScope(Supplier<U> code) {
 | 
				
			||||||
		if (symbolMapStack.isEmpty())
 | 
							openScope();
 | 
				
			||||||
			throw new NoProgramException();
 | 
							U result = code.get();
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
		if (symbolMapStack.peek().containsKey(id))
 | 
								closeScope();
 | 
				
			||||||
			throw new DeclaredException(String.format("Identifier '%s' already declared in this scope.", id));
 | 
							} catch (NoProgramException e) {
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		Variable<T> var = functionScope.peek().addVariable(type);
 | 
							return result;
 | 
				
			||||||
 | 
					 | 
				
			||||||
		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();
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,25 +14,12 @@ import pp.s1184725.boppi.type.*;
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public class DebugCachingSymbolTable<T extends Type> extends CachingSymbolTable<T> {
 | 
					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
 | 
						@Override
 | 
				
			||||||
	public FunctionScope<T> closeFunctionScope() throws NoProgramException {
 | 
						public FunctionScope<T> closeFunctionScope() throws NoProgramException {
 | 
				
			||||||
		System.out.println(this.getClass().getName() + ": leaving scope depth " + functionScope.size());
 | 
							System.out.println(this.getClass().getName() + ": leaving scope depth " + functionScope.size());
 | 
				
			||||||
		return super.closeFunctionScope();
 | 
							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
 | 
						@Override
 | 
				
			||||||
	public Variable<T> get(String id) throws UndeclaredException, NoProgramException {
 | 
						public Variable<T> get(String id) throws UndeclaredException, NoProgramException {
 | 
				
			||||||
		System.out.println(this.getClass().getName() + ": retrieving '" + id + "' (depth "
 | 
							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);
 | 
							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;
 | 
							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
 | 
						 * 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
 | 
						 * 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));
 | 
							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.
 | 
						 * 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;
 | 
						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
 | 
						 * Shorthand for the compiler chain from {@link CharStream} to
 | 
				
			||||||
	 * {@link Program}.
 | 
						 * {@link Program}.
 | 
				
			||||||
| 
						 | 
					@ -275,21 +139,14 @@ public class ToolChain {
 | 
				
			||||||
		sb.append("digraph {\n");
 | 
							sb.append("digraph {\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		new ParseTreeWalker().walk(new ParseTreeListener() {
 | 
							new ParseTreeWalker().walk(new ParseTreeListener() {
 | 
				
			||||||
 | 
								@Override
 | 
				
			||||||
 | 
								public void enterEveryRule(ParserRuleContext ctx) {
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			private String escape(String str) {
 | 
								private String escape(String str) {
 | 
				
			||||||
				return str.replaceAll("<", "<").replaceAll(">", ">").replaceAll("\"", """);
 | 
									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
 | 
								@Override
 | 
				
			||||||
			public void exitEveryRule(ParserRuleContext ctx) {
 | 
								public void exitEveryRule(ParserRuleContext ctx) {
 | 
				
			||||||
				float hue = (ctx.getClass().hashCode() % 65521) / 65521.0f;
 | 
									float hue = (ctx.getClass().hashCode() % 65521) / 65521.0f;
 | 
				
			||||||
| 
						 | 
					@ -310,11 +167,154 @@ public class ToolChain {
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			@Override
 | 
								@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);
 | 
							}, tree);
 | 
				
			||||||
		sb.append("}\n");
 | 
							sb.append("}\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return sb.toString();
 | 
							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 T type;
 | 
				
			||||||
	private final int depth;
 | 
						private final int depth;
 | 
				
			||||||
	private final int offset;
 | 
						private final int offset;
 | 
				
			||||||
 | 
						private boolean constant;
 | 
				
			||||||
 | 
						private boolean assigned, possiblyAssigned;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Creates a variable with the given type instance and memory offset.
 | 
						 * Creates a variable with the given type instance and memory offset.
 | 
				
			||||||
| 
						 | 
					@ -28,15 +30,24 @@ public class Variable<T> {
 | 
				
			||||||
		this.type = type;
 | 
							this.type = type;
 | 
				
			||||||
		this.depth = depth;
 | 
							this.depth = depth;
 | 
				
			||||||
		this.offset = offset;
 | 
							this.offset = offset;
 | 
				
			||||||
 | 
							this.assigned = false;
 | 
				
			||||||
 | 
							this.possiblyAssigned = false;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Gets the type of this variable instance.
 | 
						 * Marks this variable assigned.
 | 
				
			||||||
	 * 
 | 
					 | 
				
			||||||
	 * @return the type
 | 
					 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public T getType() {
 | 
						public void assign() {
 | 
				
			||||||
		return type;
 | 
							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() {
 | 
						public T getType() {
 | 
				
			||||||
		return depth;
 | 
							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
 | 
						@Override
 | 
				
			||||||
	public String toString() {
 | 
						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;
 | 
					grammar Boppi;
 | 
				
			||||||
import BoppiTokens;
 | 
					import BoppiTokens;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
program: expr EOF;
 | 
					program: stats EOF;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
expr
 | 
					stats
 | 
				
			||||||
	: singleExpr (COMPOUND singleExpr?)*
 | 
						: stat (COMPOUND stat?)*
 | 
				
			||||||
	;
 | 
						;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
singleExpr
 | 
					stat
 | 
				
			||||||
	: PAROPEN expr PARCLOSE #parens
 | 
						: declareStat
 | 
				
			||||||
	| BRAOPEN expr BRACLOSE #block
 | 
						| 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
 | 
						| IN PAROPEN variable (LISTDELIM variable)* PARCLOSE #read
 | 
				
			||||||
	| OUT PAROPEN expr (LISTDELIM expr)* PARCLOSE #write
 | 
						| OUT PAROPEN expr (LISTDELIM expr)* PARCLOSE #write
 | 
				
			||||||
	| IFOPEN cond=expr IFTRUE onTrue=expr (IFFALSE onFalse=expr)? IFCLOSE #if
 | 
						| IFOPEN cond=stats IFTRUE onTrue=stats (IFFALSE onFalse=stats)? IFCLOSE #if
 | 
				
			||||||
	| WHILEOPEN cond=expr WHILETRUE onTrue=expr WHILECLOSE #while
 | 
						| WHILEOPEN cond=stats WHILETRUE onTrue=stats WHILECLOSE #while
 | 
				
			||||||
	| op=(PLUS|MINUS|NOT) singleExpr #prefix1
 | 
						| variable PAROPEN (expr (LISTDELIM expr)*)? PARCLOSE #call
 | 
				
			||||||
	| 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
 | 
					 | 
				
			||||||
	| variable #getVariable
 | 
						| variable #getVariable
 | 
				
			||||||
	| LITERAL10 #literalInteger
 | 
						| LITERAL10 #literalInteger
 | 
				
			||||||
	| CHAR #literalCharacter
 | 
						| CHAR #literalCharacter
 | 
				
			||||||
	| (TRUE|FALSE) #literalBoolean
 | 
						| (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
 | 
					type
 | 
				
			||||||
| 
						 | 
					@ -43,3 +56,7 @@ variable
 | 
				
			||||||
	| variable PROP IDENTIFIER #variableProperty
 | 
						| variable PROP IDENTIFIER #variableProperty
 | 
				
			||||||
	| IDENTIFIER #variableSimple
 | 
						| 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;
 | 
							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}.
 | 
						 * Parses a string of code, logging to {@link #log}.
 | 
				
			||||||
	 * 
 | 
						 * 
 | 
				
			||||||
| 
						 | 
					@ -213,16 +226,22 @@ public class BoppiTests {
 | 
				
			||||||
		logger.setLevel(Level.FINEST);
 | 
							logger.setLevel(Level.FINEST);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ParseTree ast = ToolChain.getParser(ToolChain.getLexer(stream, logger), logger).program();
 | 
							ParseTree ast = ToolChain.getParser(ToolChain.getLexer(stream, logger), logger).program();
 | 
				
			||||||
 | 
							logger.info("done parsing");
 | 
				
			||||||
		Annotations annotations = ToolChain.getAnnotations(ast, logger);
 | 
							Annotations annotations = ToolChain.getAnnotations(ast, logger);
 | 
				
			||||||
 | 
							logger.info("done checking");
 | 
				
			||||||
		Program program = ToolChain.getILOC(ast, logger, annotations);
 | 
							Program program = ToolChain.getILOC(ast, logger, annotations);
 | 
				
			||||||
 | 
							logger.info("done generating");
 | 
				
			||||||
		System.out.println(program.prettyPrint());
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		out = ToolChain.execute(program, logger, in).split("\n");
 | 
							out = ToolChain.execute(program, logger, in).split("\n");
 | 
				
			||||||
		vm = ToolChain.machine;
 | 
							vm = ToolChain.machine;
 | 
				
			||||||
		
 | 
					
 | 
				
			||||||
 | 
							System.out.println(program.prettyPrint());
 | 
				
			||||||
 | 
							System.out.println();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for (String line : out)
 | 
							for (String line : out)
 | 
				
			||||||
			System.out.println(">>> "+line);
 | 
								System.out.println(">>> " + line);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							System.out.println();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		log.forEach((entry) -> System.out.println(entry.getMessage()));
 | 
							log.forEach((entry) -> System.out.println(entry.getMessage()));
 | 
				
			||||||
		Simulator.DEBUG = false;
 | 
							Simulator.DEBUG = false;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,14 +2,18 @@ package pp.s1184725.boppi.test;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import static org.hamcrest.MatcherAssert.assertThat;
 | 
					import static org.hamcrest.MatcherAssert.assertThat;
 | 
				
			||||||
import static org.hamcrest.Matchers.*;
 | 
					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
 | 
					 * @author Frank Wibbelink
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public class ClosureTest {
 | 
					public class ClosureTest {
 | 
				
			||||||
 | 
						private int fib(int n) {
 | 
				
			||||||
 | 
							return (n < 2) ? 1 : fib(n - 1) + fib(n - 2);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Correct closure parsing
 | 
						 * Correct closure parsing
 | 
				
			||||||
| 
						 | 
					@ -18,7 +22,7 @@ public class ClosureTest {
 | 
				
			||||||
	public void correctClosureParsing() {
 | 
						public void correctClosureParsing() {
 | 
				
			||||||
		BoppiTests.parseFile("closure1.boppi");
 | 
							BoppiTests.parseFile("closure1.boppi");
 | 
				
			||||||
		assertThat(BoppiTests.log, is(empty()));
 | 
							assertThat(BoppiTests.log, is(empty()));
 | 
				
			||||||
		
 | 
					
 | 
				
			||||||
		BoppiTests.parseFile("closure2.boppi");
 | 
							BoppiTests.parseFile("closure2.boppi");
 | 
				
			||||||
		assertThat(BoppiTests.log, is(empty()));
 | 
							assertThat(BoppiTests.log, is(empty()));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -30,7 +34,7 @@ public class ClosureTest {
 | 
				
			||||||
	public void correctClosureChecking() {
 | 
						public void correctClosureChecking() {
 | 
				
			||||||
		BoppiTests.checkFile("closure1.boppi");
 | 
							BoppiTests.checkFile("closure1.boppi");
 | 
				
			||||||
		assertThat(BoppiTests.log, is(empty()));
 | 
							assertThat(BoppiTests.log, is(empty()));
 | 
				
			||||||
		
 | 
					
 | 
				
			||||||
		BoppiTests.checkFile("closure2.boppi");
 | 
							BoppiTests.checkFile("closure2.boppi");
 | 
				
			||||||
		assertThat(BoppiTests.log, is(empty()));
 | 
							assertThat(BoppiTests.log, is(empty()));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -44,21 +48,29 @@ public class ClosureTest {
 | 
				
			||||||
		assertThat(BoppiTests.vm.getInterrupt(), is(0));
 | 
							assertThat(BoppiTests.vm.getInterrupt(), is(0));
 | 
				
			||||||
		assertThat(BoppiTests.log, is(empty()));
 | 
							assertThat(BoppiTests.log, is(empty()));
 | 
				
			||||||
		assertThat(BoppiTests.out, is(arrayContaining("8", "9")));
 | 
							assertThat(BoppiTests.out, is(arrayContaining("8", "9")));
 | 
				
			||||||
		
 | 
					
 | 
				
			||||||
		BoppiTests.compileAndRunFile("closure2.boppi");
 | 
							BoppiTests.compileAndRunFile("closure2.boppi");
 | 
				
			||||||
		assertThat(BoppiTests.vm.getInterrupt(), is(0));
 | 
							assertThat(BoppiTests.vm.getInterrupt(), is(0));
 | 
				
			||||||
		assertThat(BoppiTests.log, is(empty()));
 | 
							assertThat(BoppiTests.log, is(empty()));
 | 
				
			||||||
		assertThat(BoppiTests.out, is(arrayContaining("8", "7", "15", "2")));
 | 
							assertThat(BoppiTests.out, is(arrayContaining("8", "7", "15", "2")));
 | 
				
			||||||
		
 | 
					
 | 
				
			||||||
		BoppiTests.compileAndRunFile("closure3.boppi");
 | 
							BoppiTests.compileAndRunFile("closure3.boppi");
 | 
				
			||||||
		assertThat(BoppiTests.vm.getInterrupt(), is(0));
 | 
							assertThat(BoppiTests.vm.getInterrupt(), is(0));
 | 
				
			||||||
		assertThat(BoppiTests.log, is(empty()));
 | 
							assertThat(BoppiTests.log, is(empty()));
 | 
				
			||||||
		assertThat(BoppiTests.out, is(arrayContaining("7", "15", "2")));
 | 
							assertThat(BoppiTests.out, is(arrayContaining("7", "15", "2")));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							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
 | 
						@Test
 | 
				
			||||||
	public void correctClosureCleanup() {
 | 
						public void correctClosureCleanup() {
 | 
				
			||||||
		BoppiTests.compileAndRunFile("closure1.boppi");
 | 
							BoppiTests.compileAndRunFile("closure1.boppi");
 | 
				
			||||||
| 
						 | 
					@ -71,5 +83,4 @@ public class ClosureTest {
 | 
				
			||||||
		assertThat(BoppiTests.vm.load(0), is(4));
 | 
							assertThat(BoppiTests.vm.load(0), is(4));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -22,13 +22,13 @@ public class SimpleFunctionTest {
 | 
				
			||||||
		BoppiTests.parseString("function int id(int a) a; id(1)");
 | 
							BoppiTests.parseString("function int id(int a) a; id(1)");
 | 
				
			||||||
		assertThat(BoppiTests.log, is(empty()));
 | 
							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()));
 | 
							assertThat(BoppiTests.log, is(empty()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		BoppiTests.parseString("function int add(int a, int b) a+b; print(add(4, 5)+1)");
 | 
							BoppiTests.parseString("function int add(int a, int b) a+b; print(add(4, 5)+1)");
 | 
				
			||||||
		assertThat(BoppiTests.log, is(empty()));
 | 
							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()));
 | 
							assertThat(BoppiTests.log, is(empty()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		BoppiTests.parseString("function int factorial(int n) {if (n > 1) then n*factorial(n-1) else 1 fi}");
 | 
							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
 | 
						@Test
 | 
				
			||||||
	public void wrongFunctionParsing() {
 | 
						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);");
 | 
							BoppiTests.parseString("function char add(int a, int b);");
 | 
				
			||||||
		assertThat(BoppiTests.log, hasSize(1));
 | 
							assertThat(BoppiTests.log, hasSize(1));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -61,13 +58,13 @@ public class SimpleFunctionTest {
 | 
				
			||||||
		BoppiTests.checkString("function int id(int a) a; id(1); 1");
 | 
							BoppiTests.checkString("function int id(int a) a; id(1); 1");
 | 
				
			||||||
		assertThat(BoppiTests.log, is(empty()));
 | 
							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()));
 | 
							assertThat(BoppiTests.log, is(empty()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		BoppiTests.checkString("function int add(int a, int b) a+b; print(add(4, 5)+1)");
 | 
							BoppiTests.checkString("function int add(int a, int b) a+b; print(add(4, 5)+1)");
 | 
				
			||||||
		assertThat(BoppiTests.log, is(empty()));
 | 
							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()));
 | 
							assertThat(BoppiTests.log, is(empty()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		BoppiTests.checkString("function int factorial(int n) {if (n > 1) then n*factorial(n-1) else 1 fi}; 0");
 | 
							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
 | 
						@Test
 | 
				
			||||||
	public void correctSimpleFunctionGeneration() {
 | 
						public void correctSimpleFunctionGeneration() {
 | 
				
			||||||
| 
						 | 
					@ -100,10 +97,10 @@ public class SimpleFunctionTest {
 | 
				
			||||||
		BoppiTests.compileAndRunString("function int id(int a) a; id(1); 1");
 | 
							BoppiTests.compileAndRunString("function int id(int a) a; id(1); 1");
 | 
				
			||||||
		assertThat(BoppiTests.log, is(empty()));
 | 
							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()));
 | 
							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.log, is(empty()));
 | 
				
			||||||
		assertThat(BoppiTests.out, is(arrayContaining("1")));
 | 
							assertThat(BoppiTests.out, is(arrayContaining("1")));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -111,9 +108,13 @@ public class SimpleFunctionTest {
 | 
				
			||||||
		assertThat(BoppiTests.log, is(empty()));
 | 
							assertThat(BoppiTests.log, is(empty()));
 | 
				
			||||||
		assertThat(BoppiTests.out, is(arrayContaining("10")));
 | 
							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.log, is(empty()));
 | 
				
			||||||
		assertThat(BoppiTests.out, is(arrayContaining("A")));
 | 
							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");
 | 
							BoppiTests.parseString("var 'c' varname");
 | 
				
			||||||
		assertThat(BoppiTests.log, not(empty()));
 | 
							assertThat(BoppiTests.log, not(empty()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							BoppiTests.parseString("var bool int; int := true");
 | 
				
			||||||
 | 
							assertThat(BoppiTests.log, not(empty()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		BoppiTests.parseString("var bool; true true;");
 | 
							BoppiTests.parseString("var bool; true true;");
 | 
				
			||||||
		assertThat(BoppiTests.log, hasSize(2));
 | 
							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() {
 | 
						public void correctVariableChecking() {
 | 
				
			||||||
		BoppiTests.checkFile("simpleVariable.boppi");
 | 
							BoppiTests.checkFile("simpleVariable.boppi");
 | 
				
			||||||
		assertThat(BoppiTests.log, is(empty()));
 | 
							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
 | 
						@Test
 | 
				
			||||||
	public void wrongVariableChecking() {
 | 
						public void wrongVariableChecking() {
 | 
				
			||||||
| 
						 | 
					@ -73,6 +83,15 @@ public class SimpleVariableTest {
 | 
				
			||||||
		BoppiTests.checkString("var bool endsWithDeclaration;");
 | 
							BoppiTests.checkString("var bool endsWithDeclaration;");
 | 
				
			||||||
		assertThat(BoppiTests.log, hasSize(1));
 | 
							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' ");
 | 
							BoppiTests.checkString("var bool var1; var var1 var2; var2 := 'c' ");
 | 
				
			||||||
		assertThat(BoppiTests.log, hasSize(1));
 | 
							assertThat(BoppiTests.log, hasSize(1));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,3 +1,5 @@
 | 
				
			||||||
 | 
					//Currying addition
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function (int)->int bindAdd(int a) {
 | 
					function (int)->int bindAdd(int a) {
 | 
				
			||||||
    var int left;
 | 
					    var int left;
 | 
				
			||||||
    left := a;
 | 
					    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)->int bindInt((int,int)->int f, int first) {
 | 
				
			||||||
    function int return(int second) f(first,second);
 | 
					    function int return(int second) f(first,second);
 | 
				
			||||||
    return
 | 
					    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 main1() {
 | 
				
			||||||
    function int add(int a, int b) a+b;
 | 
					    function int add(int a, int b) a+b;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,6 +2,7 @@ var int n;
 | 
				
			||||||
var int i;
 | 
					var int i;
 | 
				
			||||||
var int current;
 | 
					var int current;
 | 
				
			||||||
var int previous;
 | 
					var int previous;
 | 
				
			||||||
 | 
					previous := 0;
 | 
				
			||||||
current := 1;
 | 
					current := 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
read(n);
 | 
					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;
 | 
					package pp.s1184725.boppi.util;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.ByteArrayOutputStream;
 | 
				
			||||||
import java.util.Scanner;
 | 
					import java.util.Scanner;
 | 
				
			||||||
import java.util.logging.Logger;
 | 
					import java.util.logging.Logger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -85,7 +86,10 @@ public class InteractivePrompt {
 | 
				
			||||||
					System.out.println("--- "+line);
 | 
										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);
 | 
									System.out.println("<<< "+str);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -23,6 +23,18 @@ public class RegisterPool {
 | 
				
			||||||
	private Logger logger;
 | 
						private Logger logger;
 | 
				
			||||||
	private List<Reg> regFree, regInUse;
 | 
						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) {
 | 
						private void blockReg(Reg reg) {
 | 
				
			||||||
		if (reg == null)
 | 
							if (reg == null)
 | 
				
			||||||
			logger.severe("INTERNAL: cannot reserve null register.");
 | 
								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) {
 | 
						private void freeReg(Reg reg) {
 | 
				
			||||||
		if (reg == null) {
 | 
							if (reg == null) {
 | 
				
			||||||
			logger.severe("INTERNAL: cannot free null register.");
 | 
								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() {
 | 
						private Reg makeReg() {
 | 
				
			||||||
		return makeRegThatIsNot(null);
 | 
							return makeRegThatIsNot(null);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -65,24 +135,14 @@ public class RegisterPool {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Creates a new register pool
 | 
						 * Runs some code with two registers allocated.
 | 
				
			||||||
	 * 
 | 
						 * 
 | 
				
			||||||
	 * @param logger
 | 
						 * @param code
 | 
				
			||||||
	 *            the logger to use
 | 
						 *            the code to run
 | 
				
			||||||
 | 
						 * @return the second register used in the code
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public RegisterPool(Logger logger) {
 | 
						public Reg withReg(BiConsumer<Reg, Reg> code) {
 | 
				
			||||||
		regFree = new ArrayList<>();
 | 
							return withReg((r2) -> withReg((r1) -> code.accept(r1, r2)));
 | 
				
			||||||
		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;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
| 
						 | 
					@ -99,64 +159,4 @@ public class RegisterPool {
 | 
				
			||||||
		return r;
 | 
							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