implemented closures with correct cleanup
This commit is contained in:
		
							parent
							
								
									9baff2d080
								
							
						
					
					
						commit
						5a48e93674
					
				| 
						 | 
					@ -1,7 +1,5 @@
 | 
				
			||||||
package pp.s1184725.boppi;
 | 
					package pp.s1184725.boppi;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.Stack;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import org.antlr.v4.runtime.tree.ParseTreeProperty;
 | 
					import org.antlr.v4.runtime.tree.ParseTreeProperty;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import pp.iloc.model.Reg;
 | 
					import pp.iloc.model.Reg;
 | 
				
			||||||
| 
						 | 
					@ -28,11 +26,7 @@ public class Annotations {
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Maps nodes to their function scope.
 | 
						 * Maps nodes to their function scope.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public ParseTreeProperty<Variable<Type>> function;
 | 
						public ParseTreeProperty<FunctionScope<Type>> function;
 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * A stack with the current function scope on top.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	public Stack<Variable<Type>> currentFunction;
 | 
					 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * A symbol table
 | 
						 * A symbol table
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
| 
						 | 
					@ -46,7 +40,6 @@ public class Annotations {
 | 
				
			||||||
		types = new ParseTreeProperty<>();
 | 
							types = new ParseTreeProperty<>();
 | 
				
			||||||
		function = new ParseTreeProperty<>();
 | 
							function = new ParseTreeProperty<>();
 | 
				
			||||||
		variables = new ParseTreeProperty<>();
 | 
							variables = new ParseTreeProperty<>();
 | 
				
			||||||
		currentFunction = new Stack<>();
 | 
					 | 
				
			||||||
		symbols = new CachingSymbolTable<>();
 | 
							symbols = new CachingSymbolTable<>();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,5 @@
 | 
				
			||||||
package pp.s1184725.boppi;
 | 
					package pp.s1184725.boppi;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.*;
 | 
					 | 
				
			||||||
import java.util.logging.Logger;
 | 
					import java.util.logging.Logger;
 | 
				
			||||||
import java.util.stream.Collectors;
 | 
					import java.util.stream.Collectors;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,6 +8,7 @@ import org.antlr.v4.runtime.tree.ParseTree;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import pp.s1184725.boppi.antlr.*;
 | 
					import pp.s1184725.boppi.antlr.*;
 | 
				
			||||||
import pp.s1184725.boppi.antlr.BoppiParser.*;
 | 
					import pp.s1184725.boppi.antlr.BoppiParser.*;
 | 
				
			||||||
 | 
					import pp.s1184725.boppi.exception.*;
 | 
				
			||||||
import pp.s1184725.boppi.type.*;
 | 
					import pp.s1184725.boppi.type.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
| 
						 | 
					@ -77,8 +77,6 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
 | 
				
			||||||
	public Type visit(ParseTree tree) {
 | 
						public Type visit(ParseTree tree) {
 | 
				
			||||||
		Type type = super.visit(tree);
 | 
							Type type = super.visit(tree);
 | 
				
			||||||
		an.types.put(tree, type);
 | 
							an.types.put(tree, type);
 | 
				
			||||||
		if (an.function.get(tree) == null)
 | 
					 | 
				
			||||||
			an.function.put(tree, an.currentFunction.peek());
 | 
					 | 
				
			||||||
		return type;
 | 
							return type;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -129,9 +127,7 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (!(var.getType() instanceof SimpleType))
 | 
								if (!(var.getType() instanceof SimpleType))
 | 
				
			||||||
				log.warning("Be careful only to pass pure functions outside their scope.");
 | 
									log.warning("Be careful only to pass pure functions outside their scope.");
 | 
				
			||||||
		} catch (EmptyStackException e) {
 | 
							} catch (SymbolTableException e) {
 | 
				
			||||||
			log.severe(getError(ctx, "Declaring variable outside a program."));
 | 
					 | 
				
			||||||
		} catch (Exception e) {
 | 
					 | 
				
			||||||
			log.severe(getError(ctx, e.getMessage()));
 | 
								log.severe(getError(ctx, e.getMessage()));
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return SimpleType.VOID;
 | 
							return SimpleType.VOID;
 | 
				
			||||||
| 
						 | 
					@ -145,23 +141,21 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
 | 
				
			||||||
			FunctionType type = new FunctionType(visit(ctx.result), parameterTypes);
 | 
								FunctionType type = new FunctionType(visit(ctx.result), parameterTypes);
 | 
				
			||||||
			Variable<Type> func = an.symbols.put(ctx.name.getText(), type);
 | 
								Variable<Type> func = an.symbols.put(ctx.name.getText(), type);
 | 
				
			||||||
			an.variables.put(ctx, func);
 | 
								an.variables.put(ctx, func);
 | 
				
			||||||
			an.currentFunction.push(func);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			type.setLocalDataSize(an.symbols.withFunctionScope(() -> {
 | 
								an.function.put(ctx, an.symbols.withFunctionScope(() -> {
 | 
				
			||||||
				for (int i = 1; i < ctx.type().size(); i++)
 | 
									for (int i = 1; i < ctx.type().size(); i++)
 | 
				
			||||||
					try {
 | 
										try {
 | 
				
			||||||
						an.symbols.put(ctx.IDENTIFIER(i).getText(), an.types.get(ctx.type(i)));
 | 
											an.symbols.put(ctx.IDENTIFIER(i).getText(), an.types.get(ctx.type(i)));
 | 
				
			||||||
					} catch (Exception e) {
 | 
										} catch (SymbolTableException e) {
 | 
				
			||||||
						log.severe(getError(ctx, e.getMessage()));
 | 
											log.severe(getError(ctx, e.getMessage()));
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				checkConstraint(an.symbols.withScope(() -> visit(ctx.body)), an.types.get(ctx.result), ctx);
 | 
									checkConstraint(an.symbols.withScope(() -> visit(ctx.body)), an.types.get(ctx.result), ctx);
 | 
				
			||||||
			}));
 | 
								}));
 | 
				
			||||||
		} catch (EmptyStackException e) {
 | 
							} catch (SymbolTableException e) {
 | 
				
			||||||
			log.severe(getError(ctx, "Declaring function outside a program."));
 | 
					 | 
				
			||||||
		} catch (Exception e) {
 | 
					 | 
				
			||||||
			log.severe(getError(ctx, e.getMessage()));
 | 
								log.severe(getError(ctx, e.getMessage()));
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		an.currentFunction.pop();
 | 
					
 | 
				
			||||||
		return SimpleType.VOID;
 | 
							return SimpleType.VOID;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -279,9 +273,7 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public Type visitProgram(ProgramContext ctx) {
 | 
						public Type visitProgram(ProgramContext ctx) {
 | 
				
			||||||
		FunctionType main = new FunctionType(TupleType.UNIT, TupleType.UNIT);
 | 
							an.function.put(ctx, an.symbols.withFunctionScope(() -> super.visitProgram(ctx)));
 | 
				
			||||||
		an.currentFunction.push(new Variable<Type>(main, 0, 0));
 | 
					 | 
				
			||||||
		main.setLocalDataSize(an.symbols.withFunctionScope(() -> super.visitProgram(ctx)));
 | 
					 | 
				
			||||||
		return null;
 | 
							return null;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -319,12 +311,17 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
 | 
				
			||||||
	public Type visitVariable(VariableContext ctx) {
 | 
						public Type visitVariable(VariableContext ctx) {
 | 
				
			||||||
		try {
 | 
							try {
 | 
				
			||||||
			Variable<Type> var = an.symbols.get(ctx.getText());
 | 
								Variable<Type> var = an.symbols.get(ctx.getText());
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
								try {
 | 
				
			||||||
 | 
									an.function.put(ctx, an.symbols.getFunctionScope());
 | 
				
			||||||
 | 
								} catch (NoProgramException e) {
 | 
				
			||||||
 | 
									log.severe(getError(ctx, e.getMessage()));
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								
 | 
				
			||||||
			an.variables.put(ctx, var);
 | 
								an.variables.put(ctx, var);
 | 
				
			||||||
			return var.getType();
 | 
								return var.getType();
 | 
				
			||||||
		} catch (EmptyStackException e) {
 | 
							} catch (SymbolTableException e) {
 | 
				
			||||||
			log.severe(getError(ctx, "Using variable outside a program."));
 | 
								log.severe(getError(ctx, e.getMessage()));
 | 
				
			||||||
		} catch (Exception e) {
 | 
					 | 
				
			||||||
			log.severe(getError(ctx, e.getMessage() != null ? e.getMessage() : "unknown error"));
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return SimpleType.VOID;
 | 
							return SimpleType.VOID;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| 
						 | 
					@ -3,6 +3,7 @@ package pp.s1184725.boppi;
 | 
				
			||||||
import java.util.*;
 | 
					import java.util.*;
 | 
				
			||||||
import java.util.function.Supplier;
 | 
					import java.util.function.Supplier;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import pp.s1184725.boppi.exception.*;
 | 
				
			||||||
import pp.s1184725.boppi.type.*;
 | 
					import pp.s1184725.boppi.type.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
| 
						 | 
					@ -19,12 +20,8 @@ import pp.s1184725.boppi.type.*;
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public class CachingSymbolTable<T extends Type> {
 | 
					public class CachingSymbolTable<T extends Type> {
 | 
				
			||||||
	protected Stack<Map<String, Variable<T>>> symbolMapStack;
 | 
						protected Stack<Map<String, Variable<T>>> symbolMapStack;
 | 
				
			||||||
	protected Stack<Integer> offsets;
 | 
					 | 
				
			||||||
	protected Stack<Integer> functionSizes;
 | 
					 | 
				
			||||||
	protected Map<String, Variable<T>> symbolCache;
 | 
						protected Map<String, Variable<T>> symbolCache;
 | 
				
			||||||
	protected int offset;
 | 
						protected Stack<FunctionScope<T>> functionScope;
 | 
				
			||||||
	protected int functionDepth;
 | 
					 | 
				
			||||||
	protected int functionSize;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Creates an empty symbol table with no open scope.
 | 
						 * Creates an empty symbol table with no open scope.
 | 
				
			||||||
| 
						 | 
					@ -32,11 +29,7 @@ public class CachingSymbolTable<T extends Type> {
 | 
				
			||||||
	public CachingSymbolTable() {
 | 
						public CachingSymbolTable() {
 | 
				
			||||||
		symbolMapStack = new Stack<>();
 | 
							symbolMapStack = new Stack<>();
 | 
				
			||||||
		symbolCache = new HashMap<>();
 | 
							symbolCache = new HashMap<>();
 | 
				
			||||||
		offsets = new Stack<>();
 | 
							functionScope = new Stack<>();
 | 
				
			||||||
		functionSizes = new Stack<>();
 | 
					 | 
				
			||||||
		offset = 0;
 | 
					 | 
				
			||||||
		functionDepth = 0;
 | 
					 | 
				
			||||||
		functionSize = 0;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
| 
						 | 
					@ -44,7 +37,6 @@ public class CachingSymbolTable<T extends Type> {
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public void openScope() {
 | 
						public void openScope() {
 | 
				
			||||||
		symbolMapStack.push(new HashMap<>());
 | 
							symbolMapStack.push(new HashMap<>());
 | 
				
			||||||
		offsets.push(offset);
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
| 
						 | 
					@ -53,28 +45,27 @@ public class CachingSymbolTable<T extends Type> {
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public void openFunctionScope() {
 | 
						public void openFunctionScope() {
 | 
				
			||||||
		openScope();
 | 
							openScope();
 | 
				
			||||||
		functionSizes.push(functionSize);
 | 
							functionScope.push(new FunctionScope<T>(functionScope.size()));
 | 
				
			||||||
		functionDepth++;
 | 
					 | 
				
			||||||
		functionSize = 0;
 | 
					 | 
				
			||||||
		offset = 0;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Closes a lexical scope, removing all declarations within this scope.
 | 
						 * Closes a lexical scope, removing all declarations within this scope. Runs
 | 
				
			||||||
 | 
						 * in {@code O(k log k log n)} time, with {@code k} the number of
 | 
				
			||||||
 | 
						 * identifiers going out of scope, when the stream is parallelized.
 | 
				
			||||||
	 * 
 | 
						 * 
 | 
				
			||||||
	 * @throws EmptyStackException
 | 
						 * @throws NoProgramException
 | 
				
			||||||
	 *             if no scope is open
 | 
						 *             if no scope is open
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public void closeScope() throws EmptyStackException {
 | 
						public void closeScope() throws NoProgramException {
 | 
				
			||||||
		Map<String, Variable<T>> deletions = symbolMapStack.pop();
 | 
							Map<String, Variable<T>> deletions = symbolMapStack.pop();
 | 
				
			||||||
		for (String identifier : deletions.keySet()) {
 | 
							for (String identifier : deletions.keySet()) {
 | 
				
			||||||
			Optional<Variable<T>> maybeType = symbolMapStack.stream().filter((map) -> map.containsKey(identifier))
 | 
								Optional<Variable<T>> maybeType = symbolMapStack.parallelStream()
 | 
				
			||||||
					.map((map) -> map.get(identifier)).reduce((__, snd) -> snd);
 | 
										.filter((map) -> map.containsKey(identifier)).map((map) -> map.get(identifier))
 | 
				
			||||||
 | 
										.reduce((__, snd) -> snd);
 | 
				
			||||||
			maybeType.ifPresent((var) -> symbolCache.replace(identifier, var));
 | 
								maybeType.ifPresent((var) -> symbolCache.replace(identifier, var));
 | 
				
			||||||
			if (!maybeType.isPresent())
 | 
								if (!maybeType.isPresent())
 | 
				
			||||||
				symbolCache.remove(identifier);
 | 
									symbolCache.remove(identifier);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		offset = offsets.pop();
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
| 
						 | 
					@ -82,13 +73,14 @@ public class CachingSymbolTable<T extends Type> {
 | 
				
			||||||
	 * declarations within this scope, restoring the local offset and decreasing
 | 
						 * declarations within this scope, restoring the local offset and decreasing
 | 
				
			||||||
	 * the lookup depth.
 | 
						 * the lookup depth.
 | 
				
			||||||
	 * 
 | 
						 * 
 | 
				
			||||||
	 * @throws EmptyStackException
 | 
						 * @return function scope details
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @throws NoProgramException
 | 
				
			||||||
	 *             if no scope is open
 | 
						 *             if no scope is open
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public void closeFunctionScope() throws EmptyStackException {
 | 
						public FunctionScope<T> closeFunctionScope() throws NoProgramException {
 | 
				
			||||||
		closeScope();
 | 
							closeScope();
 | 
				
			||||||
		functionSize = functionSizes.pop();
 | 
							return functionScope.pop();
 | 
				
			||||||
		functionDepth--;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
| 
						 | 
					@ -106,27 +98,32 @@ public class CachingSymbolTable<T extends Type> {
 | 
				
			||||||
	public <U> U withScope(Supplier<U> code) {
 | 
						public <U> U withScope(Supplier<U> code) {
 | 
				
			||||||
		openScope();
 | 
							openScope();
 | 
				
			||||||
		U result = code.get();
 | 
							U result = code.get();
 | 
				
			||||||
		closeScope();
 | 
							try {
 | 
				
			||||||
 | 
								closeScope();
 | 
				
			||||||
 | 
							} catch (NoProgramException e) {
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		return result;
 | 
							return result;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Opens a function scope, executes the given code and closes it. This is a
 | 
						 * Opens a function scope, executes the given code and closes it. This is a
 | 
				
			||||||
	 * helper method to make sure calls to {@link #openFunctionScope()} and
 | 
						 * helper method to make sure calls to {@link #openFunctionScope()} and
 | 
				
			||||||
	 * {@link #closeFunctionScope()} are balanced and returns the local data
 | 
						 * {@link #closeFunctionScope()} are balanced and returns the function scope
 | 
				
			||||||
	 * size (in bytes) of the function. {@code code} must take no arguments and
 | 
						 * details. {@code code} must take no arguments and must not return a value.
 | 
				
			||||||
	 * must not return a value.
 | 
					 | 
				
			||||||
	 * 
 | 
						 * 
 | 
				
			||||||
	 * @param code
 | 
						 * @param code
 | 
				
			||||||
	 *            the code to execute within the scope
 | 
						 *            the code to execute within the scope
 | 
				
			||||||
	 * @return the return value of the code
 | 
						 * @return the function scope details
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public int withFunctionScope(Runnable code) {
 | 
						public FunctionScope<T> withFunctionScope(Runnable code) {
 | 
				
			||||||
		openFunctionScope();
 | 
							openFunctionScope();
 | 
				
			||||||
		code.run();
 | 
							code.run();
 | 
				
			||||||
		int result = functionSize;
 | 
					
 | 
				
			||||||
		closeFunctionScope();
 | 
							try {
 | 
				
			||||||
		return result;
 | 
								return closeFunctionScope();
 | 
				
			||||||
 | 
							} catch (NoProgramException e) {
 | 
				
			||||||
 | 
								return null;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
| 
						 | 
					@ -140,18 +137,19 @@ public class CachingSymbolTable<T extends Type> {
 | 
				
			||||||
	 * @param type
 | 
						 * @param type
 | 
				
			||||||
	 *            the type of the identifier
 | 
						 *            the type of the identifier
 | 
				
			||||||
	 * @return the type of the identifier
 | 
						 * @return the type of the identifier
 | 
				
			||||||
	 * @throws Exception
 | 
						 * @throws DeclaredException
 | 
				
			||||||
	 *             if the identifier is declared in the current scope already
 | 
						 *             if the identifier is declared in the current scope already
 | 
				
			||||||
	 * @throws EmptyStackException
 | 
						 * @throws NoProgramException
 | 
				
			||||||
	 *             if no scope is open
 | 
						 *             if no scope is open
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public Variable<T> put(String id, T type) throws Exception, EmptyStackException {
 | 
						public Variable<T> put(String id, T type) throws DeclaredException, NoProgramException {
 | 
				
			||||||
		if (symbolMapStack.peek().containsKey(id))
 | 
							if (symbolMapStack.isEmpty())
 | 
				
			||||||
			throw new Exception(String.format("Identifier '%s' already declared in this scope.", id));
 | 
								throw new NoProgramException();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		Variable<T> var = new Variable<>(type, functionDepth, offset);
 | 
							if (symbolMapStack.peek().containsKey(id))
 | 
				
			||||||
		offset += type.getSize();
 | 
								throw new DeclaredException(String.format("Identifier '%s' already declared in this scope.", id));
 | 
				
			||||||
		functionSize = Math.max(functionSize, offset);
 | 
					
 | 
				
			||||||
 | 
							Variable<T> var = functionScope.peek().addVariable(type);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		symbolMapStack.peek().put(id, var);
 | 
							symbolMapStack.peek().put(id, var);
 | 
				
			||||||
		symbolCache.put(id, var);
 | 
							symbolCache.put(id, var);
 | 
				
			||||||
| 
						 | 
					@ -164,53 +162,46 @@ public class CachingSymbolTable<T extends Type> {
 | 
				
			||||||
	 * @param id
 | 
						 * @param id
 | 
				
			||||||
	 *            the name of the identifier
 | 
						 *            the name of the identifier
 | 
				
			||||||
	 * @return true if the identifier has a declared type
 | 
						 * @return true if the identifier has a declared type
 | 
				
			||||||
	 * @throws EmptyStackException
 | 
						 * @throws NoProgramException
 | 
				
			||||||
	 *             if no scope is open
 | 
						 *             if no scope is open
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public boolean has(String id) throws EmptyStackException {
 | 
						public boolean has(String id) throws NoProgramException {
 | 
				
			||||||
		if (symbolMapStack.isEmpty())
 | 
							if (symbolMapStack.isEmpty())
 | 
				
			||||||
			throw new EmptyStackException();
 | 
								throw new NoProgramException();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return symbolCache.containsKey(id);
 | 
							return symbolCache.containsKey(id);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Retrieves the type of an identifier, if any.
 | 
						 * Returns the type of an identifier, if any.
 | 
				
			||||||
	 * 
 | 
						 * 
 | 
				
			||||||
	 * @param id
 | 
						 * @param id
 | 
				
			||||||
	 *            the name of the identifier
 | 
						 *            the name of the identifier
 | 
				
			||||||
	 * @return the type of the identifier
 | 
						 * @return the type of the identifier
 | 
				
			||||||
	 * @throws Exception
 | 
						 * @throws UndeclaredException
 | 
				
			||||||
	 *             if the identifier is not declared
 | 
						 *             if the identifier is not declared
 | 
				
			||||||
	 * @throws EmptyStackException
 | 
						 * @throws NoProgramException
 | 
				
			||||||
	 *             if no scope is open
 | 
						 *             if no scope is open
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public Variable<T> get(String id) throws Exception, EmptyStackException {
 | 
						public Variable<T> get(String id) throws UndeclaredException, NoProgramException {
 | 
				
			||||||
		if (!this.has(id))
 | 
							if (!this.has(id))
 | 
				
			||||||
			throw new Exception(String.format("Identifier '%s' is undeclared.", id));
 | 
								throw new UndeclaredException(String.format("Identifier '%s' is undeclared.", id));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return symbolCache.get(id);
 | 
							return symbolCache.get(id);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Returns the size, in bytes, required to store all the variables declared
 | 
						 * Returns the current function scope, if any.
 | 
				
			||||||
	 * in the current function scope. Note that this is the size calculated up
 | 
					 | 
				
			||||||
	 * and including the last variable declaration, so make sure to call it
 | 
					 | 
				
			||||||
	 * right before leaving a function scope.
 | 
					 | 
				
			||||||
	 * 
 | 
						 * 
 | 
				
			||||||
	 * @return the number of bytes required to store all variables local to a
 | 
						 * @return the current function scope
 | 
				
			||||||
	 *         function
 | 
						 * @throws NoProgramException
 | 
				
			||||||
 | 
						 *             if no scope is open
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public int getLocalDataSize() {
 | 
						public FunctionScope<T> getFunctionScope() throws NoProgramException {
 | 
				
			||||||
		return functionSize;
 | 
							if (symbolMapStack.isEmpty())
 | 
				
			||||||
 | 
								throw new NoProgramException();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return functionScope.peek();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Returns the current lexical scope/function depth.
 | 
					 | 
				
			||||||
	 * 
 | 
					 | 
				
			||||||
	 * @return the function depth
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	public int getFunctionDepth() {
 | 
					 | 
				
			||||||
		return functionDepth;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,6 @@
 | 
				
			||||||
package pp.s1184725.boppi;
 | 
					package pp.s1184725.boppi;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.EmptyStackException;
 | 
					import pp.s1184725.boppi.exception.*;
 | 
				
			||||||
 | 
					 | 
				
			||||||
import pp.s1184725.boppi.type.*;
 | 
					import pp.s1184725.boppi.type.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
| 
						 | 
					@ -18,26 +17,26 @@ public class DebugCachingSymbolTable<T extends Type> extends CachingSymbolTable<
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public void openFunctionScope() {
 | 
						public void openFunctionScope() {
 | 
				
			||||||
		super.openFunctionScope();
 | 
							super.openFunctionScope();
 | 
				
			||||||
		System.out.println(this.getClass().getName() + ": entering scope depth " + functionDepth);
 | 
							System.out.println(this.getClass().getName() + ": entering scope depth " + functionScope.size());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public void closeFunctionScope() throws EmptyStackException {
 | 
						public FunctionScope<T> closeFunctionScope() throws NoProgramException {
 | 
				
			||||||
		System.out.println(this.getClass().getName() + ": leaving scope depth " + functionDepth);
 | 
							System.out.println(this.getClass().getName() + ": leaving scope depth " + functionScope.size());
 | 
				
			||||||
		super.closeFunctionScope();
 | 
							return super.closeFunctionScope();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public Variable<T> put(String id, T type) throws Exception, EmptyStackException {
 | 
						public Variable<T> put(String id, T type) throws DeclaredException, NoProgramException {
 | 
				
			||||||
		System.out.println(this.getClass().getName() + ": declaring '" + id + "' (" + type.toString() + ") at scope "
 | 
							System.out.println(this.getClass().getName() + ": declaring '" + id + "' (" + type.toString() + ") at scope "
 | 
				
			||||||
				+ functionDepth);
 | 
									+ functionScope.size());
 | 
				
			||||||
		return super.put(id, type);
 | 
							return super.put(id, type);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public Variable<T> get(String id) throws Exception, EmptyStackException {
 | 
						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 "
 | 
				
			||||||
				+ symbolCache.get(id).getDepth() + ") at scope " + functionDepth);
 | 
									+ symbolCache.get(id).getDepth() + ") at scope " + functionScope.size());
 | 
				
			||||||
		return super.get(id);
 | 
							return super.get(id);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,76 @@
 | 
				
			||||||
 | 
					package pp.s1184725.boppi;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.*;
 | 
				
			||||||
 | 
					import java.util.stream.Collectors;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import pp.s1184725.boppi.type.Type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Keeps track of local data for functions.
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 * @author Frank Wibbelink
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 * @param <T>
 | 
				
			||||||
 | 
					 *            the type system to use
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class FunctionScope<T extends Type> {
 | 
				
			||||||
 | 
						private final int scopeDepth;
 | 
				
			||||||
 | 
						private List<Variable<T>> localVars;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Creates a function scope with the given depth.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @param depth
 | 
				
			||||||
 | 
						 *            the lexical depth of this function
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public FunctionScope(int depth) {
 | 
				
			||||||
 | 
							scopeDepth = depth;
 | 
				
			||||||
 | 
							localVars = new ArrayList<>();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Creates a new variable in this function scope and returns it.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @param type
 | 
				
			||||||
 | 
						 *            the type of variable to create
 | 
				
			||||||
 | 
						 * @return the variable created
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public Variable<T> addVariable(T type) {
 | 
				
			||||||
 | 
							Variable<T> var = new Variable<T>(type, scopeDepth, getLocalDataSize());
 | 
				
			||||||
 | 
							localVars.add(var);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return var;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * 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
 | 
				
			||||||
 | 
						 * and including the last variable declaration, so make sure to call it
 | 
				
			||||||
 | 
						 * right before leaving a function scope.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @return the number of bytes required to store all variables local to a
 | 
				
			||||||
 | 
						 *         function
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public int getLocalDataSize() {
 | 
				
			||||||
 | 
							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.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @return a list of variables
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public List<Variable<T>> getVars() {
 | 
				
			||||||
 | 
							return localVars;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,7 @@ package pp.s1184725.boppi;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.io.*;
 | 
					import java.io.*;
 | 
				
			||||||
import java.nio.charset.StandardCharsets;
 | 
					import java.nio.charset.StandardCharsets;
 | 
				
			||||||
import java.nio.file.Path;
 | 
					import java.nio.file.*;
 | 
				
			||||||
import java.util.*;
 | 
					import java.util.*;
 | 
				
			||||||
import java.util.logging.*;
 | 
					import java.util.logging.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,6 +11,7 @@ import org.antlr.v4.runtime.tree.*;
 | 
				
			||||||
import org.apache.commons.lang3.tuple.Pair;
 | 
					import org.apache.commons.lang3.tuple.Pair;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import pp.iloc.Simulator;
 | 
					import pp.iloc.Simulator;
 | 
				
			||||||
 | 
					import pp.iloc.eval.Machine;
 | 
				
			||||||
import pp.iloc.model.Program;
 | 
					import pp.iloc.model.Program;
 | 
				
			||||||
import pp.s1184725.boppi.antlr.*;
 | 
					import pp.s1184725.boppi.antlr.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -20,6 +21,14 @@ import pp.s1184725.boppi.antlr.*;
 | 
				
			||||||
 * @author Frank Wibbelink
 | 
					 * @author Frank Wibbelink
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public class ToolChain {
 | 
					public class ToolChain {
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * The file system path of this class
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public static final Path PATH = Paths.get("src/" + ToolChain.class.getPackage().getName().replaceAll("\\.", "/"));
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * The last virtual machine used for executing a program
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public static Machine machine;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Opens a file for reading and returns its charstream. Throws an unhandled
 | 
						 * Opens a file for reading and returns its charstream. Throws an unhandled
 | 
				
			||||||
| 
						 | 
					@ -190,7 +199,8 @@ public class ToolChain {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Runs a program using the given input and output streams. Run errors will
 | 
						 * Runs a program using the given input and output streams. Run errors will
 | 
				
			||||||
	 * be logged as {@link Level#SEVERE}.
 | 
						 * be logged as {@link Level#SEVERE}. Instantiates a fresh machine and
 | 
				
			||||||
 | 
						 * simulator.
 | 
				
			||||||
	 * 
 | 
						 * 
 | 
				
			||||||
	 * @param program
 | 
						 * @param program
 | 
				
			||||||
	 *            the program to run
 | 
						 *            the program to run
 | 
				
			||||||
| 
						 | 
					@ -202,7 +212,27 @@ public class ToolChain {
 | 
				
			||||||
	 *            the output stream the program can write to
 | 
						 *            the output stream the program can write to
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public static void execute(Program program, Logger logger, InputStream in, OutputStream out) {
 | 
						public static void execute(Program program, Logger logger, InputStream in, OutputStream out) {
 | 
				
			||||||
		Simulator s = new Simulator(program);
 | 
							execute(program, logger, in, out, new Machine());
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Runs a program using the given input and output streams. Run errors will
 | 
				
			||||||
 | 
						 * be logged as {@link Level#SEVERE}.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @param program
 | 
				
			||||||
 | 
						 *            the program to run
 | 
				
			||||||
 | 
						 * @param logger
 | 
				
			||||||
 | 
						 *            the logger to write runtime errors to
 | 
				
			||||||
 | 
						 * @param in
 | 
				
			||||||
 | 
						 *            the input stream the program can read from
 | 
				
			||||||
 | 
						 * @param out
 | 
				
			||||||
 | 
						 *            the output stream the program can write to
 | 
				
			||||||
 | 
						 * @param vm
 | 
				
			||||||
 | 
						 *            the virtual machine to use
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public static void execute(Program program, Logger logger, InputStream in, OutputStream out, Machine vm) {
 | 
				
			||||||
 | 
							machine = vm;
 | 
				
			||||||
 | 
							Simulator s = new Simulator(program, machine);
 | 
				
			||||||
		s.setIn(in);
 | 
							s.setIn(in);
 | 
				
			||||||
		s.setOut(out);
 | 
							s.setOut(out);
 | 
				
			||||||
		try {
 | 
							try {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,23 @@
 | 
				
			||||||
 | 
					package pp.s1184725.boppi.exception;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Exception thrown when declaring a variable a second time in the same scope.
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 * @author Frank Wibbelink
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class DeclaredException extends SymbolTableException {
 | 
				
			||||||
 | 
						private static final long serialVersionUID = 5769561320967096410L;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Constructs a new exception with the specified detail message. The cause
 | 
				
			||||||
 | 
						 * is not initialized, and may subsequently be initialized by a call to
 | 
				
			||||||
 | 
						 * {@link #initCause}.
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @param message
 | 
				
			||||||
 | 
						 *            the detail message. The detail message is saved for later
 | 
				
			||||||
 | 
						 *            retrieval by the {@link #getMessage()} method.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public DeclaredException(String message) {
 | 
				
			||||||
 | 
							super(message);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,19 @@
 | 
				
			||||||
 | 
					package pp.s1184725.boppi.exception;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Exception thrown when trying to use a SymbolTable while no scope is open.
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 * @author Frank Wibbelink
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class NoProgramException extends SymbolTableException {
 | 
				
			||||||
 | 
						private static final long serialVersionUID = 214588214046135357L;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Constructs a new exception with {@code "No scope open."} as its detail
 | 
				
			||||||
 | 
						 * message. The cause is not initialized, and may subsequently be
 | 
				
			||||||
 | 
						 * initialized by a call to {@link #initCause}.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public NoProgramException() {
 | 
				
			||||||
 | 
							super("No scope open.");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,23 @@
 | 
				
			||||||
 | 
					package pp.s1184725.boppi.exception;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import pp.s1184725.boppi.CachingSymbolTable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Exceptions that may be thrown by the {@link CachingSymbolTable}.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public abstract class SymbolTableException extends Exception {
 | 
				
			||||||
 | 
						private static final long serialVersionUID = 2390361610733507914L;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Constructs a new exception with the specified detail message. The cause
 | 
				
			||||||
 | 
						 * is not initialized, and may subsequently be initialized by a call to
 | 
				
			||||||
 | 
						 * {@link #initCause}.
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @param message
 | 
				
			||||||
 | 
						 *            the detail message. The detail message is saved for later
 | 
				
			||||||
 | 
						 *            retrieval by the {@link #getMessage()} method.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public SymbolTableException(String message) {
 | 
				
			||||||
 | 
							super(message);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,23 @@
 | 
				
			||||||
 | 
					package pp.s1184725.boppi.exception;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Exception thrown when declaring a variable a second time in the same scope.
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 * @author Frank Wibbelink
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class UndeclaredException extends SymbolTableException {
 | 
				
			||||||
 | 
						private static final long serialVersionUID = -5333137316309067383L;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Constructs a new exception with the specified detail message. The cause
 | 
				
			||||||
 | 
						 * is not initialized, and may subsequently be initialized by a call to
 | 
				
			||||||
 | 
						 * {@link #initCause}.
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @param message
 | 
				
			||||||
 | 
						 *            the detail message. The detail message is saved for later
 | 
				
			||||||
 | 
						 *            retrieval by the {@link #getMessage()} method.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public UndeclaredException(String message) {
 | 
				
			||||||
 | 
							super(message);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -100,8 +100,8 @@ memfree: loadI    0 => m_0
 | 
				
			||||||
mf_ynul: haltI    1865442925
 | 
					mf_ynul: haltI    1865442925
 | 
				
			||||||
mf_nnul: loadAI   m_n,@off_oref => m_1
 | 
					mf_nnul: loadAI   m_n,@off_oref => m_1
 | 
				
			||||||
         subI     m_1,1 => m_1
 | 
					         subI     m_1,1 => m_1
 | 
				
			||||||
         cmp_GT   m_1,m_0 => m_1
 | 
					         cmp_GT   m_1,m_0 => m_2
 | 
				
			||||||
         cbr      m_1 -> mf_exit,mf_free
 | 
					         cbr      m_2 -> mf_exit,mf_free
 | 
				
			||||||
 | 
					
 | 
				
			||||||
mf_exit: storeAI  m_1 => m_n,@off_oref
 | 
					mf_exit: storeAI  m_1 => m_n,@off_oref
 | 
				
			||||||
         pop      => m_1                         // load return address
 | 
					         pop      => m_1                         // load return address
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -22,7 +22,7 @@ import pp.s1184725.boppi.*;
 | 
				
			||||||
 * @author Frank Wibbelink
 | 
					 * @author Frank Wibbelink
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@RunWith(Suite.class)
 | 
					@RunWith(Suite.class)
 | 
				
			||||||
@SuiteClasses({ ExpressionTest.class, SimpleVariableTest.class, ConditionalTest.class, SimpleFunctionTest.class })
 | 
					@SuiteClasses({ ExpressionTest.class, SimpleVariableTest.class, ConditionalTest.class, SimpleFunctionTest.class, ClosureTest.class })
 | 
				
			||||||
public class BoppiTests {
 | 
					public class BoppiTests {
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * The path for test programs
 | 
						 * The path for test programs
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,77 @@
 | 
				
			||||||
 | 
					package pp.s1184725.boppi.test;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static org.hamcrest.MatcherAssert.assertThat;
 | 
				
			||||||
 | 
					import static org.hamcrest.Matchers.*;
 | 
				
			||||||
 | 
					import org.junit.Test;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import pp.s1184725.boppi.ToolChain;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Tests for function closures, mostly testing runtime correctness and garbage collection.
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 * @author Frank Wibbelink
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class ClosureTest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Correct closure parsing
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void correctClosureParsing() {
 | 
				
			||||||
 | 
							BoppiTests.parseFile("closure1.boppi");
 | 
				
			||||||
 | 
							assertThat(BoppiTests.log, is(empty()));
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							BoppiTests.parseFile("closure2.boppi");
 | 
				
			||||||
 | 
							assertThat(BoppiTests.log, is(empty()));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Correct closure checking
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void correctClosureChecking() {
 | 
				
			||||||
 | 
							BoppiTests.checkFile("closure1.boppi");
 | 
				
			||||||
 | 
							assertThat(BoppiTests.log, is(empty()));
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							BoppiTests.checkFile("closure2.boppi");
 | 
				
			||||||
 | 
							assertThat(BoppiTests.log, is(empty()));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Correct closure use
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void correctClosureGeneration() {
 | 
				
			||||||
 | 
							BoppiTests.compileAndRunFile("closure1.boppi");
 | 
				
			||||||
 | 
							assertThat(ToolChain.machine.getInterrupt(), is(0));
 | 
				
			||||||
 | 
							assertThat(BoppiTests.log, is(empty()));
 | 
				
			||||||
 | 
							assertThat(BoppiTests.out, is(arrayContaining("8", "9")));
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							BoppiTests.compileAndRunFile("closure2.boppi");
 | 
				
			||||||
 | 
							assertThat(ToolChain.machine.getInterrupt(), is(0));
 | 
				
			||||||
 | 
							assertThat(BoppiTests.log, is(empty()));
 | 
				
			||||||
 | 
							assertThat(BoppiTests.out, is(arrayContaining("8", "7", "15", "2")));
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							BoppiTests.compileAndRunFile("closure3.boppi");
 | 
				
			||||||
 | 
							assertThat(ToolChain.machine.getInterrupt(), is(0));
 | 
				
			||||||
 | 
							assertThat(BoppiTests.log, is(empty()));
 | 
				
			||||||
 | 
							assertThat(BoppiTests.out, is(arrayContaining("7", "15", "2")));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Test garbago collection. Nothing must be allocated at the end of the program.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void correctClosureCleanup() {
 | 
				
			||||||
 | 
							BoppiTests.compileAndRunFile("closure1.boppi");
 | 
				
			||||||
 | 
							assertThat(ToolChain.machine.load(0), is(4));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							BoppiTests.compileAndRunFile("closure2.boppi");
 | 
				
			||||||
 | 
							assertThat(ToolChain.machine.load(0), is(4));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							BoppiTests.compileAndRunFile("closure3.boppi");
 | 
				
			||||||
 | 
							assertThat(ToolChain.machine.load(0), is(4));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -107,6 +107,7 @@ public class SimpleVariableTest {
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void correctVariableGeneration() {
 | 
						public void correctVariableGeneration() {
 | 
				
			||||||
		BoppiTests.compileAndRunFile("simpleVariable.boppi");
 | 
							BoppiTests.compileAndRunFile("simpleVariable.boppi");
 | 
				
			||||||
 | 
							BoppiTests.log.forEach((l)->System.out.println(l.getMessage()));
 | 
				
			||||||
		assertThat(BoppiTests.log, is(empty()));
 | 
							assertThat(BoppiTests.log, is(empty()));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,12 @@
 | 
				
			||||||
 | 
					function (int)->int bindAdd(int a) {
 | 
				
			||||||
 | 
					    var int left;
 | 
				
			||||||
 | 
					    left := a;
 | 
				
			||||||
 | 
					    function int return(int right) left+right;
 | 
				
			||||||
 | 
					    return
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (int)->int add5;
 | 
				
			||||||
 | 
					add5 := bindAdd(5);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					print(add5(3), add5(4));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,31 @@
 | 
				
			||||||
 | 
					function (int)->int bindInt((int,int)->int f, int first) {
 | 
				
			||||||
 | 
					    function int return(int second) f(first,second);
 | 
				
			||||||
 | 
					    return
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function int add(int a, int b) a+b;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (int)->int add5;
 | 
				
			||||||
 | 
					add5 := bindInt(add, 5);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					print(add5(3));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var ()->int getCalls;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function (int,int)->int logCalls((int,int)->int f) {
 | 
				
			||||||
 | 
					    var int calls;
 | 
				
			||||||
 | 
					    calls := 0;
 | 
				
			||||||
 | 
					    function int getCalls2() calls;
 | 
				
			||||||
 | 
					    getCalls := getCalls2;
 | 
				
			||||||
 | 
					    function int return(int first, int second) {
 | 
				
			||||||
 | 
					        calls := calls + 1;
 | 
				
			||||||
 | 
					        f(first,second)
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    return
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					add := logCalls(add);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					print(add(1,6), add(10,5));
 | 
				
			||||||
 | 
					print(getCalls());
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,26 @@
 | 
				
			||||||
 | 
					function int main1() {
 | 
				
			||||||
 | 
					    function int add(int a, int b) a+b;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var (int)->int add5;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    var ()->int getCalls;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function (int,int)->int logCalls((int,int)->int f) {
 | 
				
			||||||
 | 
					        var int calls;
 | 
				
			||||||
 | 
					        calls := 0;
 | 
				
			||||||
 | 
					        function int getCalls2() calls;
 | 
				
			||||||
 | 
					        getCalls := getCalls2;
 | 
				
			||||||
 | 
					        function int return(int first, int second) {
 | 
				
			||||||
 | 
					            calls := calls + 1;
 | 
				
			||||||
 | 
					            f(first,second)
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    add := logCalls(add);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    print(add(1,6), add(10,5));
 | 
				
			||||||
 | 
					    print(getCalls());
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					main1();
 | 
				
			||||||
| 
						 | 
					@ -7,9 +7,8 @@ import pp.iloc.eval.Machine;
 | 
				
			||||||
 * 
 | 
					 * 
 | 
				
			||||||
 * @author Frank Wibbelink
 | 
					 * @author Frank Wibbelink
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public class FunctionType implements Type {
 | 
					public class FunctionType implements ReferenceType {
 | 
				
			||||||
	private Type argument, result;
 | 
						private Type argument, result;
 | 
				
			||||||
	private int localDataSize;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Creates a new function type from the given parameter and return types.
 | 
						 * Creates a new function type from the given parameter and return types.
 | 
				
			||||||
| 
						 | 
					@ -43,25 +42,6 @@ public class FunctionType implements Type {
 | 
				
			||||||
		return argument.toString() + "->" + result.toString();
 | 
							return argument.toString() + "->" + result.toString();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Sets the size (in bytes) of the local data segment for this function.
 | 
					 | 
				
			||||||
	 * 
 | 
					 | 
				
			||||||
	 * @param newSize
 | 
					 | 
				
			||||||
	 *            the size (in bytes) of the local data segment
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	public void setLocalDataSize(int newSize) {
 | 
					 | 
				
			||||||
		localDataSize = newSize;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Returns the size (in bytes) of the local data segment for this function.
 | 
					 | 
				
			||||||
	 * 
 | 
					 | 
				
			||||||
	 * @return the size (in bytes) of the local data segment
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	public int getLocalDataSize() {
 | 
					 | 
				
			||||||
		return localDataSize;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Returns the result type of this function.
 | 
						 * Returns the result type of this function.
 | 
				
			||||||
	 * 
 | 
						 * 
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,10 @@
 | 
				
			||||||
 | 
					package pp.s1184725.boppi.type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * An interface for types that are stored in dynamic memory.
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 * @author Frank Wibbelink
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public interface ReferenceType extends Type {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,196 @@
 | 
				
			||||||
 | 
					package pp.s1184725.boppi.util;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.ArrayList;
 | 
				
			||||||
 | 
					import java.util.*;
 | 
				
			||||||
 | 
					import java.util.function.*;
 | 
				
			||||||
 | 
					import java.util.logging.Logger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import pp.iloc.model.Reg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Simple class to manage registers.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class RegisterPool {
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Recommended number of registers to use before throwing a warning.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public static int RECOMMENDED_REGISTER_COUNT = 10;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private Logger logger;
 | 
				
			||||||
 | 
						private List<Reg> regFree, regInUse;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Creates a new register pool
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @param logger
 | 
				
			||||||
 | 
						 *            the logger to use
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public RegisterPool(Logger logger) {
 | 
				
			||||||
 | 
							regFree = new ArrayList<>();
 | 
				
			||||||
 | 
							regInUse = new ArrayList<>();
 | 
				
			||||||
 | 
							this.logger = logger;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private Reg makeReg() {
 | 
				
			||||||
 | 
							return makeRegThatIsNot(null);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private Reg makeRegThatIsNot(Reg r1) {
 | 
				
			||||||
 | 
							Reg reg = regFree.stream().filter((r2) -> !r2.equals(r1)).findAny().orElseGet(() -> {
 | 
				
			||||||
 | 
								if (regInUse.size() == RECOMMENDED_REGISTER_COUNT + 1)
 | 
				
			||||||
 | 
									logger.warning(String.format("Using more than %d registers. Consider rebalancing your expressions.",
 | 
				
			||||||
 | 
											RECOMMENDED_REGISTER_COUNT));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return new Reg("__" + (regInUse.size() + 1));
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							regFree.remove(reg);
 | 
				
			||||||
 | 
							regInUse.add(reg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return reg;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private void freeReg(Reg reg) {
 | 
				
			||||||
 | 
							if (reg == null) {
 | 
				
			||||||
 | 
								logger.severe("INTERNAL: cannot free null register.");
 | 
				
			||||||
 | 
							} else if (!regInUse.contains(reg)) {
 | 
				
			||||||
 | 
								logger.severe(String.format("INTERNAL: cannot free register %s because it is not in use.", reg.getName()));
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								regInUse.remove(reg);
 | 
				
			||||||
 | 
								regFree.add(reg);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private void blockReg(Reg reg) {
 | 
				
			||||||
 | 
							if (reg == null)
 | 
				
			||||||
 | 
								logger.severe("INTERNAL: cannot reserve null register.");
 | 
				
			||||||
 | 
							else if (!regFree.contains(reg)) {
 | 
				
			||||||
 | 
								logger.severe(String.format("INTERNAL: cannot reserve register %s because it is in use.", reg.getName()));
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								regFree.remove(reg);
 | 
				
			||||||
 | 
								regInUse.add(reg);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Returns the list of registers currently in use.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @return the list of registers in use
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public List<Reg> getInUse() {
 | 
				
			||||||
 | 
							return regInUse;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Runs some code with a single register allocated.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @param <T>
 | 
				
			||||||
 | 
						 *            the return type of the code
 | 
				
			||||||
 | 
						 * @param code
 | 
				
			||||||
 | 
						 *            the code to run
 | 
				
			||||||
 | 
						 * @return the return value of the code
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public <T> T withReg(Function<Reg, T> code) {
 | 
				
			||||||
 | 
							Reg r = makeReg();
 | 
				
			||||||
 | 
							T result = code.apply(r);
 | 
				
			||||||
 | 
							freeReg(r);
 | 
				
			||||||
 | 
							return result;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Runs some code with a single register allocated.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @param code
 | 
				
			||||||
 | 
						 *            the code to run
 | 
				
			||||||
 | 
						 * @return the register used in the code
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public Reg withReg(Consumer<Reg> code) {
 | 
				
			||||||
 | 
							Reg r = makeReg();
 | 
				
			||||||
 | 
							code.accept(r);
 | 
				
			||||||
 | 
							freeReg(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) {
 | 
				
			||||||
 | 
							Reg r1 = makeReg();
 | 
				
			||||||
 | 
							Reg r2 = makeReg();
 | 
				
			||||||
 | 
							code.accept(r1, r2);
 | 
				
			||||||
 | 
							freeReg(r1);
 | 
				
			||||||
 | 
							freeReg(r2);
 | 
				
			||||||
 | 
							return r2;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Runs some code with one register given and one allocated.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @param r1
 | 
				
			||||||
 | 
						 *            the first register to use
 | 
				
			||||||
 | 
						 * @param code
 | 
				
			||||||
 | 
						 *            the code to run
 | 
				
			||||||
 | 
						 * @return the register used in the code
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public Reg withReg(Reg r1, Consumer<Reg> code) {
 | 
				
			||||||
 | 
							Reg r = makeRegThatIsNot(r1);
 | 
				
			||||||
 | 
							code.accept(r);
 | 
				
			||||||
 | 
							freeReg(r);
 | 
				
			||||||
 | 
							return r;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Runs some code with one register given and two allocated.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @param r1
 | 
				
			||||||
 | 
						 *            the first register to use
 | 
				
			||||||
 | 
						 * @param code
 | 
				
			||||||
 | 
						 *            the code to run
 | 
				
			||||||
 | 
						 * @return the second register used in the code
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public Reg withReg(Reg r1, BiConsumer<Reg, Reg> code) {
 | 
				
			||||||
 | 
							Reg r2 = makeRegThatIsNot(r1);
 | 
				
			||||||
 | 
							Reg r3 = makeRegThatIsNot(r1);
 | 
				
			||||||
 | 
							code.accept(r2, r3);
 | 
				
			||||||
 | 
							freeReg(r2);
 | 
				
			||||||
 | 
							freeReg(r3);
 | 
				
			||||||
 | 
							return r3;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * 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);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue