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