implemented closures with correct cleanup
This commit is contained in:
		
							parent
							
								
									9baff2d080
								
							
						
					
					
						commit
						5a48e93674
					
				| 
						 | 
				
			
			@ -1,7 +1,5 @@
 | 
			
		|||
package pp.s1184725.boppi;
 | 
			
		||||
 | 
			
		||||
import java.util.Stack;
 | 
			
		||||
 | 
			
		||||
import org.antlr.v4.runtime.tree.ParseTreeProperty;
 | 
			
		||||
 | 
			
		||||
import pp.iloc.model.Reg;
 | 
			
		||||
| 
						 | 
				
			
			@ -28,11 +26,7 @@ public class Annotations {
 | 
			
		|||
	/**
 | 
			
		||||
	 * Maps nodes to their function scope.
 | 
			
		||||
	 */
 | 
			
		||||
	public ParseTreeProperty<Variable<Type>> function;
 | 
			
		||||
	/**
 | 
			
		||||
	 * A stack with the current function scope on top.
 | 
			
		||||
	 */
 | 
			
		||||
	public Stack<Variable<Type>> currentFunction;
 | 
			
		||||
	public ParseTreeProperty<FunctionScope<Type>> function;
 | 
			
		||||
	/**
 | 
			
		||||
	 * A symbol table
 | 
			
		||||
	 */
 | 
			
		||||
| 
						 | 
				
			
			@ -46,7 +40,6 @@ public class Annotations {
 | 
			
		|||
		types = new ParseTreeProperty<>();
 | 
			
		||||
		function = new ParseTreeProperty<>();
 | 
			
		||||
		variables = new ParseTreeProperty<>();
 | 
			
		||||
		currentFunction = new Stack<>();
 | 
			
		||||
		symbols = new CachingSymbolTable<>();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,5 @@
 | 
			
		|||
package pp.s1184725.boppi;
 | 
			
		||||
 | 
			
		||||
import java.util.*;
 | 
			
		||||
import java.util.logging.Logger;
 | 
			
		||||
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.BoppiParser.*;
 | 
			
		||||
import pp.s1184725.boppi.exception.*;
 | 
			
		||||
import pp.s1184725.boppi.type.*;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -77,8 +77,6 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
 | 
			
		|||
	public Type visit(ParseTree tree) {
 | 
			
		||||
		Type type = super.visit(tree);
 | 
			
		||||
		an.types.put(tree, type);
 | 
			
		||||
		if (an.function.get(tree) == null)
 | 
			
		||||
			an.function.put(tree, an.currentFunction.peek());
 | 
			
		||||
		return type;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -129,9 +127,7 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
 | 
			
		|||
 | 
			
		||||
			if (!(var.getType() instanceof SimpleType))
 | 
			
		||||
				log.warning("Be careful only to pass pure functions outside their scope.");
 | 
			
		||||
		} catch (EmptyStackException e) {
 | 
			
		||||
			log.severe(getError(ctx, "Declaring variable outside a program."));
 | 
			
		||||
		} catch (Exception e) {
 | 
			
		||||
		} catch (SymbolTableException e) {
 | 
			
		||||
			log.severe(getError(ctx, e.getMessage()));
 | 
			
		||||
		}
 | 
			
		||||
		return SimpleType.VOID;
 | 
			
		||||
| 
						 | 
				
			
			@ -145,23 +141,21 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
 | 
			
		|||
			FunctionType type = new FunctionType(visit(ctx.result), parameterTypes);
 | 
			
		||||
			Variable<Type> func = an.symbols.put(ctx.name.getText(), type);
 | 
			
		||||
			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++)
 | 
			
		||||
					try {
 | 
			
		||||
						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()));
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
				checkConstraint(an.symbols.withScope(() -> visit(ctx.body)), an.types.get(ctx.result), ctx);
 | 
			
		||||
			}));
 | 
			
		||||
		} catch (EmptyStackException e) {
 | 
			
		||||
			log.severe(getError(ctx, "Declaring function outside a program."));
 | 
			
		||||
		} catch (Exception e) {
 | 
			
		||||
		} catch (SymbolTableException e) {
 | 
			
		||||
			log.severe(getError(ctx, e.getMessage()));
 | 
			
		||||
		}
 | 
			
		||||
		an.currentFunction.pop();
 | 
			
		||||
 | 
			
		||||
		return SimpleType.VOID;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -279,9 +273,7 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
 | 
			
		|||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Type visitProgram(ProgramContext ctx) {
 | 
			
		||||
		FunctionType main = new FunctionType(TupleType.UNIT, TupleType.UNIT);
 | 
			
		||||
		an.currentFunction.push(new Variable<Type>(main, 0, 0));
 | 
			
		||||
		main.setLocalDataSize(an.symbols.withFunctionScope(() -> super.visitProgram(ctx)));
 | 
			
		||||
		an.function.put(ctx, an.symbols.withFunctionScope(() -> super.visitProgram(ctx)));
 | 
			
		||||
		return null;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -319,12 +311,17 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
 | 
			
		|||
	public Type visitVariable(VariableContext ctx) {
 | 
			
		||||
		try {
 | 
			
		||||
			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);
 | 
			
		||||
			return var.getType();
 | 
			
		||||
		} catch (EmptyStackException e) {
 | 
			
		||||
			log.severe(getError(ctx, "Using variable outside a program."));
 | 
			
		||||
		} catch (Exception e) {
 | 
			
		||||
			log.severe(getError(ctx, e.getMessage() != null ? e.getMessage() : "unknown error"));
 | 
			
		||||
		} catch (SymbolTableException e) {
 | 
			
		||||
			log.severe(getError(ctx, e.getMessage()));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		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.function.Supplier;
 | 
			
		||||
 | 
			
		||||
import pp.s1184725.boppi.exception.*;
 | 
			
		||||
import pp.s1184725.boppi.type.*;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -19,12 +20,8 @@ import pp.s1184725.boppi.type.*;
 | 
			
		|||
 */
 | 
			
		||||
public class CachingSymbolTable<T extends Type> {
 | 
			
		||||
	protected Stack<Map<String, Variable<T>>> symbolMapStack;
 | 
			
		||||
	protected Stack<Integer> offsets;
 | 
			
		||||
	protected Stack<Integer> functionSizes;
 | 
			
		||||
	protected Map<String, Variable<T>> symbolCache;
 | 
			
		||||
	protected int offset;
 | 
			
		||||
	protected int functionDepth;
 | 
			
		||||
	protected int functionSize;
 | 
			
		||||
	protected Stack<FunctionScope<T>> functionScope;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Creates an empty symbol table with no open scope.
 | 
			
		||||
| 
						 | 
				
			
			@ -32,11 +29,7 @@ public class CachingSymbolTable<T extends Type> {
 | 
			
		|||
	public CachingSymbolTable() {
 | 
			
		||||
		symbolMapStack = new Stack<>();
 | 
			
		||||
		symbolCache = new HashMap<>();
 | 
			
		||||
		offsets = new Stack<>();
 | 
			
		||||
		functionSizes = new Stack<>();
 | 
			
		||||
		offset = 0;
 | 
			
		||||
		functionDepth = 0;
 | 
			
		||||
		functionSize = 0;
 | 
			
		||||
		functionScope = new Stack<>();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			@ -44,7 +37,6 @@ public class CachingSymbolTable<T extends Type> {
 | 
			
		|||
	 */
 | 
			
		||||
	public void openScope() {
 | 
			
		||||
		symbolMapStack.push(new HashMap<>());
 | 
			
		||||
		offsets.push(offset);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			@ -53,28 +45,27 @@ public class CachingSymbolTable<T extends Type> {
 | 
			
		|||
	 */
 | 
			
		||||
	public void openFunctionScope() {
 | 
			
		||||
		openScope();
 | 
			
		||||
		functionSizes.push(functionSize);
 | 
			
		||||
		functionDepth++;
 | 
			
		||||
		functionSize = 0;
 | 
			
		||||
		offset = 0;
 | 
			
		||||
		functionScope.push(new FunctionScope<T>(functionScope.size()));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 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
 | 
			
		||||
	 */
 | 
			
		||||
	public void closeScope() throws EmptyStackException {
 | 
			
		||||
	public void closeScope() throws NoProgramException {
 | 
			
		||||
		Map<String, Variable<T>> deletions = symbolMapStack.pop();
 | 
			
		||||
		for (String identifier : deletions.keySet()) {
 | 
			
		||||
			Optional<Variable<T>> maybeType = symbolMapStack.stream().filter((map) -> map.containsKey(identifier))
 | 
			
		||||
					.map((map) -> map.get(identifier)).reduce((__, snd) -> snd);
 | 
			
		||||
			Optional<Variable<T>> maybeType = symbolMapStack.parallelStream()
 | 
			
		||||
					.filter((map) -> map.containsKey(identifier)).map((map) -> map.get(identifier))
 | 
			
		||||
					.reduce((__, snd) -> snd);
 | 
			
		||||
			maybeType.ifPresent((var) -> symbolCache.replace(identifier, var));
 | 
			
		||||
			if (!maybeType.isPresent())
 | 
			
		||||
				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
 | 
			
		||||
	 * the lookup depth.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @throws EmptyStackException
 | 
			
		||||
	 * @return function scope details
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @throws NoProgramException
 | 
			
		||||
	 *             if no scope is open
 | 
			
		||||
	 */
 | 
			
		||||
	public void closeFunctionScope() throws EmptyStackException {
 | 
			
		||||
	public FunctionScope<T> closeFunctionScope() throws NoProgramException {
 | 
			
		||||
		closeScope();
 | 
			
		||||
		functionSize = functionSizes.pop();
 | 
			
		||||
		functionDepth--;
 | 
			
		||||
		return functionScope.pop();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			@ -106,27 +98,32 @@ public class CachingSymbolTable<T extends Type> {
 | 
			
		|||
	public <U> U withScope(Supplier<U> code) {
 | 
			
		||||
		openScope();
 | 
			
		||||
		U result = code.get();
 | 
			
		||||
		closeScope();
 | 
			
		||||
		try {
 | 
			
		||||
			closeScope();
 | 
			
		||||
		} catch (NoProgramException e) {
 | 
			
		||||
		}
 | 
			
		||||
		return result;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Opens a function scope, executes the given code and closes it. This is a
 | 
			
		||||
	 * helper method to make sure calls to {@link #openFunctionScope()} and
 | 
			
		||||
	 * {@link #closeFunctionScope()} are balanced and returns the local data
 | 
			
		||||
	 * size (in bytes) of the function. {@code code} must take no arguments and
 | 
			
		||||
	 * must not return a value.
 | 
			
		||||
	 * {@link #closeFunctionScope()} are balanced and returns the function scope
 | 
			
		||||
	 * details. {@code code} must take no arguments and must not return a value.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param code
 | 
			
		||||
	 *            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();
 | 
			
		||||
		code.run();
 | 
			
		||||
		int result = functionSize;
 | 
			
		||||
		closeFunctionScope();
 | 
			
		||||
		return result;
 | 
			
		||||
 | 
			
		||||
		try {
 | 
			
		||||
			return closeFunctionScope();
 | 
			
		||||
		} catch (NoProgramException e) {
 | 
			
		||||
			return null;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			@ -140,18 +137,19 @@ public class CachingSymbolTable<T extends Type> {
 | 
			
		|||
	 * @param type
 | 
			
		||||
	 *            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
 | 
			
		||||
	 * @throws EmptyStackException
 | 
			
		||||
	 * @throws NoProgramException
 | 
			
		||||
	 *             if no scope is open
 | 
			
		||||
	 */
 | 
			
		||||
	public Variable<T> put(String id, T type) throws Exception, EmptyStackException {
 | 
			
		||||
		if (symbolMapStack.peek().containsKey(id))
 | 
			
		||||
			throw new Exception(String.format("Identifier '%s' already declared in this scope.", id));
 | 
			
		||||
	public Variable<T> put(String id, T type) throws DeclaredException, NoProgramException {
 | 
			
		||||
		if (symbolMapStack.isEmpty())
 | 
			
		||||
			throw new NoProgramException();
 | 
			
		||||
 | 
			
		||||
		Variable<T> var = new Variable<>(type, functionDepth, offset);
 | 
			
		||||
		offset += type.getSize();
 | 
			
		||||
		functionSize = Math.max(functionSize, offset);
 | 
			
		||||
		if (symbolMapStack.peek().containsKey(id))
 | 
			
		||||
			throw new DeclaredException(String.format("Identifier '%s' already declared in this scope.", id));
 | 
			
		||||
 | 
			
		||||
		Variable<T> var = functionScope.peek().addVariable(type);
 | 
			
		||||
 | 
			
		||||
		symbolMapStack.peek().put(id, var);
 | 
			
		||||
		symbolCache.put(id, var);
 | 
			
		||||
| 
						 | 
				
			
			@ -164,53 +162,46 @@ public class CachingSymbolTable<T extends Type> {
 | 
			
		|||
	 * @param id
 | 
			
		||||
	 *            the name of the identifier
 | 
			
		||||
	 * @return true if the identifier has a declared type
 | 
			
		||||
	 * @throws EmptyStackException
 | 
			
		||||
	 * @throws NoProgramException
 | 
			
		||||
	 *             if no scope is open
 | 
			
		||||
	 */
 | 
			
		||||
	public boolean has(String id) throws EmptyStackException {
 | 
			
		||||
	public boolean has(String id) throws NoProgramException {
 | 
			
		||||
		if (symbolMapStack.isEmpty())
 | 
			
		||||
			throw new EmptyStackException();
 | 
			
		||||
			throw new NoProgramException();
 | 
			
		||||
 | 
			
		||||
		return symbolCache.containsKey(id);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Retrieves the type of an identifier, if any.
 | 
			
		||||
	 * Returns the type of an identifier, if any.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param id
 | 
			
		||||
	 *            the name of the identifier
 | 
			
		||||
	 * @return the type of the identifier
 | 
			
		||||
	 * @throws Exception
 | 
			
		||||
	 * @throws UndeclaredException
 | 
			
		||||
	 *             if the identifier is not declared
 | 
			
		||||
	 * @throws EmptyStackException
 | 
			
		||||
	 * @throws NoProgramException
 | 
			
		||||
	 *             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))
 | 
			
		||||
			throw new Exception(String.format("Identifier '%s' is undeclared.", id));
 | 
			
		||||
			throw new UndeclaredException(String.format("Identifier '%s' is undeclared.", id));
 | 
			
		||||
 | 
			
		||||
		return symbolCache.get(id);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 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.
 | 
			
		||||
	 * Returns the current function scope, if any.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @return the number of bytes required to store all variables local to a
 | 
			
		||||
	 *         function
 | 
			
		||||
	 * @return the current function scope
 | 
			
		||||
	 * @throws NoProgramException
 | 
			
		||||
	 *             if no scope is open
 | 
			
		||||
	 */
 | 
			
		||||
	public int getLocalDataSize() {
 | 
			
		||||
		return functionSize;
 | 
			
		||||
	public FunctionScope<T> getFunctionScope() throws NoProgramException {
 | 
			
		||||
		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;
 | 
			
		||||
 | 
			
		||||
import java.util.EmptyStackException;
 | 
			
		||||
 | 
			
		||||
import pp.s1184725.boppi.exception.*;
 | 
			
		||||
import pp.s1184725.boppi.type.*;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -18,26 +17,26 @@ public class DebugCachingSymbolTable<T extends Type> extends CachingSymbolTable<
 | 
			
		|||
	@Override
 | 
			
		||||
	public void 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
 | 
			
		||||
	public void closeFunctionScope() throws EmptyStackException {
 | 
			
		||||
		System.out.println(this.getClass().getName() + ": leaving scope depth " + functionDepth);
 | 
			
		||||
		super.closeFunctionScope();
 | 
			
		||||
	public FunctionScope<T> closeFunctionScope() throws NoProgramException {
 | 
			
		||||
		System.out.println(this.getClass().getName() + ": leaving scope depth " + functionScope.size());
 | 
			
		||||
		return super.closeFunctionScope();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Variable<T> put(String id, T type) throws 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 "
 | 
			
		||||
				+ functionDepth);
 | 
			
		||||
				+ functionScope.size());
 | 
			
		||||
		return super.put(id, type);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@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 "
 | 
			
		||||
				+ symbolCache.get(id).getDepth() + ") at scope " + functionDepth);
 | 
			
		||||
				+ symbolCache.get(id).getDepth() + ") at scope " + functionScope.size());
 | 
			
		||||
		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.nio.charset.StandardCharsets;
 | 
			
		||||
import java.nio.file.Path;
 | 
			
		||||
import java.nio.file.*;
 | 
			
		||||
import java.util.*;
 | 
			
		||||
import java.util.logging.*;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -11,6 +11,7 @@ import org.antlr.v4.runtime.tree.*;
 | 
			
		|||
import org.apache.commons.lang3.tuple.Pair;
 | 
			
		||||
 | 
			
		||||
import pp.iloc.Simulator;
 | 
			
		||||
import pp.iloc.eval.Machine;
 | 
			
		||||
import pp.iloc.model.Program;
 | 
			
		||||
import pp.s1184725.boppi.antlr.*;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -20,6 +21,14 @@ import pp.s1184725.boppi.antlr.*;
 | 
			
		|||
 * @author Frank Wibbelink
 | 
			
		||||
 */
 | 
			
		||||
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
 | 
			
		||||
| 
						 | 
				
			
			@ -190,7 +199,8 @@ public class ToolChain {
 | 
			
		|||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 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
 | 
			
		||||
	 *            the program to run
 | 
			
		||||
| 
						 | 
				
			
			@ -202,7 +212,27 @@ public class ToolChain {
 | 
			
		|||
	 *            the output stream the program can write to
 | 
			
		||||
	 */
 | 
			
		||||
	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.setOut(out);
 | 
			
		||||
		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_nnul: loadAI   m_n,@off_oref => m_1
 | 
			
		||||
         subI     m_1,1 => m_1
 | 
			
		||||
         cmp_GT   m_1,m_0 => m_1
 | 
			
		||||
         cbr      m_1 -> mf_exit,mf_free
 | 
			
		||||
         cmp_GT   m_1,m_0 => m_2
 | 
			
		||||
         cbr      m_2 -> mf_exit,mf_free
 | 
			
		||||
 | 
			
		||||
mf_exit: storeAI  m_1 => m_n,@off_oref
 | 
			
		||||
         pop      => m_1                         // load return address
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,7 +22,7 @@ import pp.s1184725.boppi.*;
 | 
			
		|||
 * @author Frank Wibbelink
 | 
			
		||||
 */
 | 
			
		||||
@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 {
 | 
			
		||||
	/**
 | 
			
		||||
	 * 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
 | 
			
		||||
	public void correctVariableGeneration() {
 | 
			
		||||
		BoppiTests.compileAndRunFile("simpleVariable.boppi");
 | 
			
		||||
		BoppiTests.log.forEach((l)->System.out.println(l.getMessage()));
 | 
			
		||||
		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
 | 
			
		||||
 */
 | 
			
		||||
public class FunctionType implements Type {
 | 
			
		||||
public class FunctionType implements ReferenceType {
 | 
			
		||||
	private Type argument, result;
 | 
			
		||||
	private int localDataSize;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 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();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 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.
 | 
			
		||||
	 * 
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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