TopchetoEU/revert-ES5 #31
@ -1,64 +1,90 @@
|
||||
package me.topchetoeu.jscript.compilation.scope;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
||||
import me.topchetoeu.jscript.common.parsing.Location;
|
||||
|
||||
public class FunctionScope extends Scope {
|
||||
public final class FunctionScope {
|
||||
protected final VariableList locals = new VariableList(VariableIndex.IndexType.LOCALS);
|
||||
protected final VariableList catches = new VariableList(VariableIndex.IndexType.LOCALS, this.locals);
|
||||
protected final VariableList capturables = new VariableList(VariableIndex.IndexType.CAPTURABLES, this.catches);
|
||||
private final VariableList captures = new VariableList(VariableIndex.IndexType.CAPTURES);
|
||||
|
||||
private final HashMap<String, Variable> specialVarMap = new HashMap<>();
|
||||
private final HashMap<String, Variable> functionVarMap = new HashMap<>();
|
||||
private final HashMap<String, Variable> localsMap = new HashMap<>();
|
||||
private final HashMap<String, Variable> capturesMap = new HashMap<>();
|
||||
private final HashSet<String> blacklistNames = new HashSet<>();
|
||||
private final ArrayList<Variable> catchesMap = new ArrayList<>();
|
||||
|
||||
private final HashMap<Variable, Variable> childToParent = new HashMap<>();
|
||||
private final HashMap<Variable, Variable> parentToChild = new HashMap<>();
|
||||
|
||||
private final Scope captureParent;
|
||||
public final FunctionScope parent;
|
||||
public final boolean passthrough;
|
||||
|
||||
public final boolean passtrough;
|
||||
|
||||
@Override public boolean hasNonStrict(String name) {
|
||||
if (functionVarMap.containsKey(name)) return true;
|
||||
if (blacklistNames.contains(name)) return true;
|
||||
|
||||
return false;
|
||||
private Variable addCaptured(Variable var, boolean captured) {
|
||||
if (captured && !this.capturables.has(var)) this.capturables.add(var);
|
||||
return var;
|
||||
}
|
||||
|
||||
@Override public Variable define(Variable var, Location loc) {
|
||||
checkNotEnded();
|
||||
if (strictVarMap.containsKey(var.name)) throw alreadyDefinedErr(loc, var.name);
|
||||
private Variable getCatchVar(String name) {
|
||||
for (var el : catchesMap) {
|
||||
if (el.name.equals(name)) return el;
|
||||
}
|
||||
|
||||
if (passtrough) {
|
||||
blacklistNames.add(var.name);
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns If a variable with the same name exists, the old variable. Otherwise, the given variable
|
||||
*/
|
||||
public Variable define(Variable var) {
|
||||
if (passthrough) return null;
|
||||
else {
|
||||
functionVarMap.put(var.name, var);
|
||||
var old = get(var.name, false);
|
||||
if (old != null) return old;
|
||||
|
||||
localsMap.put(var.name, var);
|
||||
return locals.add(var);
|
||||
}
|
||||
}
|
||||
public Variable defineSpecial(Variable var, Location loc) {
|
||||
checkNotEnded();
|
||||
if (strictVarMap.containsKey(var.name)) throw alreadyDefinedErr(loc, var.name);
|
||||
|
||||
specialVarMap.put(var.name, var);
|
||||
return locals.add(var);
|
||||
/**
|
||||
* @returns A variable with the given name, or null if a global variable
|
||||
*/
|
||||
public Variable define(String name) {
|
||||
return define(new Variable(name, false));
|
||||
}
|
||||
|
||||
@Override public Variable get(String name, boolean capture) {
|
||||
var superRes = super.get(name, capture);
|
||||
if (superRes != null) return superRes;
|
||||
/**
|
||||
* Creates a catch variable and returns it
|
||||
*/
|
||||
public Variable defineCatch(String name) {
|
||||
var var = new Variable(name, false);
|
||||
this.catches.add(var);
|
||||
this.catchesMap.add(var);
|
||||
return var;
|
||||
}
|
||||
/**
|
||||
* Removes the last catch variable.
|
||||
* NOTE: the variable is still in the internal list. It just won't be findable by its name
|
||||
*/
|
||||
public void undefineCatch() {
|
||||
this.catchesMap.remove(this.catchesMap.size() - 1);
|
||||
}
|
||||
|
||||
if (specialVarMap.containsKey(name)) return addCaptured(specialVarMap.get(name), capture);
|
||||
if (functionVarMap.containsKey(name)) return addCaptured(functionVarMap.get(name), capture);
|
||||
/**
|
||||
* Gets the index supplier of the given variable name, or null if it is a global
|
||||
*
|
||||
* @param capture If true, the variable is being captured by a function
|
||||
*/
|
||||
public Variable get(String name, boolean capture) {
|
||||
var catchVar = getCatchVar(name);
|
||||
|
||||
if (catchVar != null) return addCaptured(catchVar, capture);
|
||||
if (localsMap.containsKey(name)) return addCaptured(localsMap.get(name), capture);
|
||||
if (capturesMap.containsKey(name)) return addCaptured(capturesMap.get(name), capture);
|
||||
|
||||
if (captureParent == null) return null;
|
||||
if (parent == null) return null;
|
||||
|
||||
var parentVar = captureParent.get(name, true);
|
||||
var parentVar = parent.get(name, true);
|
||||
if (parentVar == null) return null;
|
||||
|
||||
var childVar = captures.add(parentVar.clone());
|
||||
@ -69,49 +95,67 @@ public class FunctionScope extends Scope {
|
||||
return childVar;
|
||||
}
|
||||
|
||||
@Override public Variable get(Variable var, boolean capture) {
|
||||
if (parentToChild.containsKey(var)) return addCaptured(parentToChild.get(var), capture);
|
||||
/**
|
||||
* If the variable given is contained in this function, just returns the variable itself.
|
||||
* However, this function is important to handle cases in which you might want to access
|
||||
* a captured variable. In such cases, this function will return a capture to the given variable.
|
||||
*
|
||||
* @param capture Whether or not to execute this capturing logic
|
||||
*/
|
||||
public Variable get(Variable var, boolean capture) {
|
||||
if (catches.has(var)) return addCaptured(var, capture);
|
||||
if (captures.has(var)) return addCaptured(var, capture);
|
||||
if (locals.has(var)) return addCaptured(var, capture);
|
||||
|
||||
if (captureParent == null) return null;
|
||||
|
||||
var parentVar = captureParent.get(var, true);
|
||||
if (parentVar == null) return null;
|
||||
|
||||
var childVar = captures.add(parentVar.clone());
|
||||
childToParent.put(childVar, parentVar);
|
||||
parentToChild.put(parentVar, childVar);
|
||||
|
||||
return childVar;
|
||||
if (capture) {
|
||||
if (parentToChild.containsKey(var)) return addCaptured(parentToChild.get(var), capture);
|
||||
|
||||
if (parent == null) return null;
|
||||
|
||||
var parentVar = parent.get(var, true);
|
||||
if (parentVar == null) return null;
|
||||
|
||||
var childVar = captures.add(parentVar.clone());
|
||||
childToParent.put(childVar, parentVar);
|
||||
parentToChild.put(parentVar, childVar);
|
||||
|
||||
return childVar;
|
||||
}
|
||||
else return null;
|
||||
}
|
||||
|
||||
@Override public boolean has(String name, boolean capture) {
|
||||
if (functionVarMap.containsKey(name)) return true;
|
||||
if (specialVarMap.containsKey(name)) return true;
|
||||
/**
|
||||
* Checks if the given variable name is accessible
|
||||
*
|
||||
* @param capture If true, will check beyond this function's scope
|
||||
*/
|
||||
public boolean has(String name, boolean capture) {
|
||||
if (localsMap.containsKey(name)) return true;
|
||||
// if (specialVarMap.containsKey(name)) return true;
|
||||
|
||||
if (capture) {
|
||||
if (capturesMap.containsKey(name)) return true;
|
||||
if (captureParent != null) return captureParent.has(name, true);
|
||||
if (parent != null) return parent.has(name, true);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override protected void onFinish() {
|
||||
captures.freeze();
|
||||
super.onFinish();
|
||||
public int localsCount() {
|
||||
return locals.size();
|
||||
}
|
||||
|
||||
@Override public int capturesCount() {
|
||||
public int capturesCount() {
|
||||
return captures.size();
|
||||
}
|
||||
public int capturablesCount() {
|
||||
return capturables.size();
|
||||
}
|
||||
|
||||
public int[] getCaptureIndices() {
|
||||
var res = new int[captures.size()];
|
||||
var i = 0;
|
||||
|
||||
for (var el : captures.all()) {
|
||||
for (var el : captures) {
|
||||
assert childToParent.containsKey(el);
|
||||
res[i] = childToParent.get(el).index().toCaptureIndex();
|
||||
i++;
|
||||
@ -120,17 +164,43 @@ public class FunctionScope extends Scope {
|
||||
return res;
|
||||
}
|
||||
|
||||
public FunctionScope(Scope parent) {
|
||||
super();
|
||||
if (parent.finished()) throw new RuntimeException("Parent is finished");
|
||||
this.captureParent = parent;
|
||||
this.passtrough = false;
|
||||
this.singleEntry = false;
|
||||
public Iterable<Variable> capturables() {
|
||||
return capturables;
|
||||
}
|
||||
public FunctionScope(boolean passtrough) {
|
||||
super();
|
||||
this.captureParent = null;
|
||||
this.passtrough = passtrough;
|
||||
this.singleEntry = false;
|
||||
public Iterable<Variable> locals() {
|
||||
return locals;
|
||||
}
|
||||
|
||||
public String[] captureNames() {
|
||||
var res = new String[this.captures.size()];
|
||||
var i = 0;
|
||||
|
||||
for (var el : this.captures) {
|
||||
res[i++] = el.name;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
public String[] localNames() {
|
||||
var res = new String[this.locals.size() + this.capturables.size()];
|
||||
var i = 0;
|
||||
|
||||
for (var el : this.locals) {
|
||||
res[i++] = el.name;
|
||||
}
|
||||
for (var el : this.capturables) {
|
||||
res[i++] = el.name;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public FunctionScope(FunctionScope parent) {
|
||||
this.parent = parent;
|
||||
this.passthrough = false;
|
||||
}
|
||||
public FunctionScope(boolean passthrough) {
|
||||
this.parent = null;
|
||||
this.passthrough = passthrough;
|
||||
}
|
||||
}
|
||||
|
@ -1,249 +0,0 @@
|
||||
package me.topchetoeu.jscript.compilation.scope;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import me.topchetoeu.jscript.common.SyntaxException;
|
||||
import me.topchetoeu.jscript.common.parsing.Location;
|
||||
import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType;
|
||||
|
||||
public class Scope {
|
||||
protected final HashMap<String, Variable> strictVarMap = new HashMap<>();
|
||||
|
||||
protected final VariableList locals = new VariableList(VariableIndex.IndexType.LOCALS, this::variableOffset);
|
||||
protected final VariableList capturables = new VariableList(VariableIndex.IndexType.CAPTURABLES, this::capturablesOffset);
|
||||
|
||||
private boolean ended = false;
|
||||
private boolean finished = false;
|
||||
private Scope child;
|
||||
private LinkedList<Scope> children = new LinkedList<>();
|
||||
|
||||
public final Scope parent;
|
||||
|
||||
/**
|
||||
* Wether or not the scope is going to be entered multiple times.
|
||||
* If set to true, captured variables will be kept as allocations, otherwise will be converted to locals
|
||||
*/
|
||||
public boolean singleEntry = true;
|
||||
|
||||
|
||||
protected void transferCaptured(Variable var) {
|
||||
if (!singleEntry) {
|
||||
if (!this.capturables.has(var)) this.capturables.add(var);
|
||||
}
|
||||
else if (parent != null) {
|
||||
parent.transferCaptured(var);
|
||||
}
|
||||
else throw new IllegalStateException("Couldn't transfer captured variable");
|
||||
}
|
||||
|
||||
protected final Variable addCaptured(Variable var, boolean captured) {
|
||||
if (captured) transferCaptured(var);
|
||||
return var;
|
||||
}
|
||||
|
||||
protected final SyntaxException alreadyDefinedErr(Location loc, String name) {
|
||||
return new SyntaxException(loc, String.format("Identifier '%s' has already been declared", name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws if the scope is ended
|
||||
*/
|
||||
protected final void checkNotEnded() {
|
||||
if (ended) throw new IllegalStateException("Cannot define in an ended scope");
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a nameless variable for holding intermediate temporary values
|
||||
*
|
||||
* @throws RuntimeException If the scope is finalized or has an active child
|
||||
*/
|
||||
public Variable defineTemp() {
|
||||
checkNotEnded();
|
||||
return this.locals.add(new Variable("<temp>", false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines an ES5-style variable
|
||||
*
|
||||
* @returns The index supplier of the variable if it is a local, or null if it is a global
|
||||
* @throws SyntaxException If an ES2015-style variable with the same name exists anywhere from the current function to the current scope
|
||||
* @throws RuntimeException If the scope is finalized or has an active child
|
||||
*/
|
||||
public Variable define(Variable var, Location loc) {
|
||||
checkNotEnded();
|
||||
if (strictVarMap.containsKey(var.name)) throw alreadyDefinedErr(loc, var.name);
|
||||
if (parent != null) return parent.define(var, loc);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this scope's function parent has a non-strict variable of the given name
|
||||
*/
|
||||
public boolean hasNonStrict(String name) { return false; }
|
||||
|
||||
/**
|
||||
* Defines an ES2015-style variable
|
||||
* @param readonly True if const, false if let
|
||||
* @return The index supplier of the variable
|
||||
* @throws SyntaxException If any variable with the same name exists in the current scope
|
||||
* @throws RuntimeException If the scope is finalized or has an active child
|
||||
*/
|
||||
public Variable defineStrict(Variable var, Location loc) {
|
||||
checkNotEnded();
|
||||
if (strictVarMap.containsKey(var.name)) throw alreadyDefinedErr(loc, var.name);
|
||||
if (hasNonStrict(var.name)) throw alreadyDefinedErr(loc, var.name);
|
||||
|
||||
strictVarMap.put(var.name, var);
|
||||
return locals.add(var);
|
||||
}
|
||||
/**
|
||||
* Gets the index supplier of the given variable name, or null if it is a global
|
||||
*
|
||||
* @param capture If true, the variable is being captured by a function
|
||||
*/
|
||||
public Variable get(String name, boolean capture) {
|
||||
var res = strictVarMap.get(name);
|
||||
|
||||
if (res != null) return addCaptured(res, capture);
|
||||
if (parent != null) return parent.get(name, capture);
|
||||
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Gets the index supplier of the given variable, or null if it isn't in this scope chain.
|
||||
* This will also execute capturing logic.
|
||||
*
|
||||
* @param capture If true, the variable is being captured by a function
|
||||
*/
|
||||
public Variable get(Variable var, boolean capture) {
|
||||
if (locals.has(var)) return addCaptured(var, capture);
|
||||
if (parent != null) return parent.get(var, capture);
|
||||
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Checks if the given variable name is accessible
|
||||
*
|
||||
* @param capture If true, will check beyond this function's scope
|
||||
*/
|
||||
public boolean has(String name, boolean capture) {
|
||||
if (strictVarMap.containsKey(name)) return true;
|
||||
if (parent != null) return parent.has(name, capture);
|
||||
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Gets the index offset from this scope to its children
|
||||
*/
|
||||
public final int variableOffset() {
|
||||
var res = 0;
|
||||
|
||||
for (var curr = parent; curr != null; curr = curr.parent) {
|
||||
res += curr.locals.size();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
public final int capturablesOffset() {
|
||||
var res = 0;
|
||||
|
||||
for (var curr = this; curr != null; curr = curr.parent) {
|
||||
if (curr != this) res += curr.capturables.size();
|
||||
if (curr.parent == null) res += curr.localsCount();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public final Variable define(DeclarationType type, String name, Location loc) {
|
||||
if (type.strict) return defineStrict(new Variable(name, type.readonly), loc);
|
||||
else return define(new Variable(name, type.readonly), loc);
|
||||
}
|
||||
|
||||
public int localsCount() {
|
||||
var res = 0;
|
||||
for (var child : children) {
|
||||
var childN = child.localsCount();
|
||||
if (res < childN) res = childN;
|
||||
}
|
||||
|
||||
return res + locals.size();
|
||||
}
|
||||
public int capturesCount() { return 0; }
|
||||
public int allocCount() {
|
||||
var res = capturables.size();
|
||||
return res;
|
||||
}
|
||||
public int capturablesCount() {
|
||||
var res = capturables.size();
|
||||
for (var child : children) res += child.capturablesCount();
|
||||
return res;
|
||||
}
|
||||
|
||||
public Iterable<Variable> capturables() {
|
||||
return capturables.all();
|
||||
}
|
||||
public Iterable<Variable> locals() {
|
||||
return locals.all();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends this scope. This will make it possible for another child to take its place
|
||||
*/
|
||||
public boolean end() {
|
||||
if (ended) return false;
|
||||
|
||||
this.ended = true;
|
||||
|
||||
if (this.parent != null) {
|
||||
assert this.parent.child == this;
|
||||
this.parent.child = null;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void onFinish() {
|
||||
this.locals.freeze();
|
||||
this.capturables.freeze();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalizes this scope. The scope will become immutable after this call
|
||||
* @return
|
||||
*/
|
||||
public final boolean finish() {
|
||||
if (finished) return false;
|
||||
|
||||
if (parent != null && parent.finished) throw new IllegalStateException("Tried to finish a child after the parent was finished");
|
||||
this.onFinish();
|
||||
|
||||
for (var child : children) child.finish();
|
||||
|
||||
this.finished = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public final boolean ended() { return ended; }
|
||||
public final boolean finished() { return finished; }
|
||||
public final Scope child() { return child; }
|
||||
|
||||
public Scope() {
|
||||
this(null);
|
||||
}
|
||||
public Scope(Scope parent) {
|
||||
if (parent != null) {
|
||||
if (parent.ended) throw new RuntimeException("Parent is not active");
|
||||
if (parent.finished) throw new RuntimeException("Parent is finished");
|
||||
if (parent.child != null) throw new RuntimeException("Parent has an active child");
|
||||
|
||||
this.parent = parent;
|
||||
this.parent.child = this;
|
||||
this.parent.children.add(this);
|
||||
}
|
||||
else this.parent = null;
|
||||
}
|
||||
}
|
@ -4,20 +4,14 @@ import java.util.function.Supplier;
|
||||
|
||||
public final class Variable {
|
||||
private Supplier<VariableIndex> indexSupplier;
|
||||
private boolean frozen;
|
||||
|
||||
public final boolean readonly;
|
||||
public final String name;
|
||||
|
||||
public final VariableIndex index() {
|
||||
if (!frozen) throw new IllegalStateException("Tried to access the index of a variable before it was finalized");
|
||||
return indexSupplier.get();
|
||||
}
|
||||
|
||||
public final void freeze() {
|
||||
this.frozen = true;
|
||||
}
|
||||
|
||||
public final Variable setIndexSupplier(Supplier<VariableIndex> index) {
|
||||
this.indexSupplier = index;
|
||||
return this;
|
||||
|
@ -4,12 +4,21 @@ import me.topchetoeu.jscript.common.Instruction;
|
||||
|
||||
public final class VariableIndex {
|
||||
public static enum IndexType {
|
||||
/**
|
||||
* A simple variable that is only ever used within the function
|
||||
*/
|
||||
LOCALS,
|
||||
/**
|
||||
* A variable that has the ability to be captured by children functions
|
||||
*/
|
||||
CAPTURABLES,
|
||||
/**
|
||||
* A variable that has been captured from the parent function
|
||||
*/
|
||||
CAPTURES,
|
||||
}
|
||||
|
||||
public final VariableIndex.IndexType type;
|
||||
public final IndexType type;
|
||||
public final int index;
|
||||
|
||||
public final int toCaptureIndex() {
|
||||
@ -44,14 +53,6 @@ public final class VariableIndex {
|
||||
default: throw new UnsupportedOperationException("Unknown index type " + type);
|
||||
}
|
||||
}
|
||||
public final Instruction toUndefinedInit(boolean force) {
|
||||
switch (type) {
|
||||
case CAPTURES: throw new UnsupportedOperationException("Unknown index type " + type);
|
||||
case CAPTURABLES: return Instruction.varInit(index, force);
|
||||
case LOCALS: return Instruction.varInit(index, force);
|
||||
default: throw new UnsupportedOperationException("Unknown index type " + type);
|
||||
}
|
||||
}
|
||||
|
||||
public VariableIndex(VariableIndex.IndexType type, int index) {
|
||||
this.type = type;
|
||||
|
@ -1,48 +1,36 @@
|
||||
package me.topchetoeu.jscript.compilation.scope;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.function.IntSupplier;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public final class VariableList {
|
||||
import me.topchetoeu.jscript.compilation.scope.VariableIndex.IndexType;
|
||||
|
||||
public final class VariableList implements Iterable<Variable> {
|
||||
private final class VariableNode implements Supplier<VariableIndex> {
|
||||
public Variable var;
|
||||
public VariableNode next;
|
||||
public VariableNode prev;
|
||||
public boolean frozen;
|
||||
public int index;
|
||||
public int indexIteration;
|
||||
|
||||
public VariableList list() { return VariableList.this; }
|
||||
|
||||
private int getIndex() {
|
||||
if (this.indexIteration != VariableList.this.indexIteration) {
|
||||
this.indexIteration = VariableList.this.indexIteration;
|
||||
|
||||
if (prev == null) this.index = 0;
|
||||
else this.index = prev.getIndex() + 1;
|
||||
}
|
||||
|
||||
return this.index;
|
||||
}
|
||||
|
||||
@Override public VariableIndex get() {
|
||||
if (frozen) {
|
||||
if (offset == null) return new VariableIndex(indexType, index);
|
||||
else new VariableIndex(indexType, index + offset.getAsInt());
|
||||
}
|
||||
|
||||
var res = 0;
|
||||
if (offset != null) res = offset.getAsInt();
|
||||
|
||||
for (var it = prev; it != null; it = it.prev) {
|
||||
res++;
|
||||
}
|
||||
|
||||
return new VariableIndex(indexType, res);
|
||||
}
|
||||
|
||||
public void freeze() {
|
||||
if (frozen) return;
|
||||
this.frozen = true;
|
||||
this.next = null;
|
||||
this.var.freeze();
|
||||
if (prev == null) return;
|
||||
|
||||
this.index = prev.index + 1;
|
||||
this.next = null;
|
||||
|
||||
return;
|
||||
if (offset == null) return new VariableIndex(indexType, this.getIndex());
|
||||
else return new VariableIndex(indexType, offset.getAsInt() + this.getIndex());
|
||||
}
|
||||
|
||||
public VariableNode(Variable var, VariableNode next, VariableNode prev) {
|
||||
@ -54,32 +42,25 @@ public final class VariableList {
|
||||
|
||||
private VariableNode first, last;
|
||||
|
||||
private ArrayList<VariableNode> frozenList = null;
|
||||
private HashMap<Variable, VariableNode> varMap = new HashMap<>();
|
||||
|
||||
private final IntSupplier offset;
|
||||
/**
|
||||
* Increased when indices need recalculation. VariableNode will check if
|
||||
* its internal indexIteration is up to date with this, and if not, will
|
||||
* recalculate its index
|
||||
*/
|
||||
private int indexIteration = 0;
|
||||
|
||||
public final VariableIndex.IndexType indexType;
|
||||
|
||||
public boolean frozen() {
|
||||
if (frozenList != null) {
|
||||
assert frozenList != null;
|
||||
assert varMap != null;
|
||||
assert first == null;
|
||||
assert last == null;
|
||||
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
assert frozenList == null;
|
||||
assert varMap != null;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given variable to this list. If it already exists, does nothing
|
||||
* @return val
|
||||
*/
|
||||
public Variable add(Variable val) {
|
||||
if (frozen()) throw new RuntimeException("The scope has been frozen");
|
||||
if (this.varMap.containsKey(val)) return val;
|
||||
this.indexIteration++;
|
||||
|
||||
if (val.indexSupplier() instanceof VariableNode prevNode) {
|
||||
prevNode.list().remove(val);
|
||||
@ -105,14 +86,18 @@ public final class VariableList {
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the variable is not in the list, does nothing. Otherwise, removes the variable from the list
|
||||
* @return null if nothing was done, else the deleted variable (should be var)
|
||||
*/
|
||||
public Variable remove(Variable var) {
|
||||
if (frozen()) throw new RuntimeException("The scope has been frozen");
|
||||
|
||||
if (var == null) return null;
|
||||
|
||||
var node = varMap.get(var);
|
||||
if (node == null) return null;
|
||||
|
||||
this.indexIteration++;
|
||||
|
||||
if (node.prev != null) {
|
||||
assert node != first;
|
||||
node.prev.next = node.next;
|
||||
@ -139,38 +124,26 @@ public final class VariableList {
|
||||
return node.var;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the list has the given variable
|
||||
*/
|
||||
public boolean has(Variable var) {
|
||||
return varMap.containsKey(var);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an indexer for the given variable
|
||||
*/
|
||||
public Supplier<VariableIndex> indexer(Variable var) {
|
||||
return varMap.get(var);
|
||||
}
|
||||
|
||||
public int size() {
|
||||
if (frozen()) return frozenList.size();
|
||||
else return varMap.size();
|
||||
return varMap.size();
|
||||
}
|
||||
|
||||
public void freeze() {
|
||||
if (frozen()) return;
|
||||
|
||||
frozenList = new ArrayList<>();
|
||||
|
||||
for (var node = first; node != null; ) {
|
||||
frozenList.add(node);
|
||||
|
||||
var tmp = node;
|
||||
node = node.next;
|
||||
tmp.freeze();
|
||||
}
|
||||
|
||||
first = last = null;
|
||||
}
|
||||
|
||||
public Iterable<Variable> all() {
|
||||
if (frozen()) return () -> frozenList.stream().map(v -> v.var).iterator();
|
||||
else return () -> new Iterator<Variable>() {
|
||||
public Iterator<Variable> iterator() {
|
||||
return new Iterator<Variable>() {
|
||||
private VariableNode curr = first;
|
||||
|
||||
@Override public boolean hasNext() {
|
||||
@ -186,19 +159,28 @@ public final class VariableList {
|
||||
};
|
||||
}
|
||||
|
||||
public VariableList(VariableIndex.IndexType type, IntSupplier offset) {
|
||||
/**
|
||||
* @param offset Will offset the indices by the given amount from the supplier
|
||||
*/
|
||||
public VariableList(IndexType type, IntSupplier offset) {
|
||||
this.indexType = type;
|
||||
this.offset = offset;
|
||||
}
|
||||
public VariableList(VariableIndex.IndexType type, int offset) {
|
||||
/**
|
||||
* @param offset Will offset the indices by the given amount
|
||||
*/
|
||||
public VariableList(IndexType type, int offset) {
|
||||
this.indexType = type;
|
||||
this.offset = () -> offset;
|
||||
}
|
||||
public VariableList(VariableIndex.IndexType type, VariableList prev) {
|
||||
/**
|
||||
* @param offset Will offset the indices by the size of the given list
|
||||
*/
|
||||
public VariableList(IndexType type, VariableList prev) {
|
||||
this.indexType = type;
|
||||
this.offset = prev::size;
|
||||
}
|
||||
public VariableList(VariableIndex.IndexType type) {
|
||||
public VariableList(IndexType type) {
|
||||
this.indexType = type;
|
||||
this.offset = null;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user