ES6 Support Groundwork + Fixes (OLD ONE DON'T LOOK AT ME!!!) #22
@ -13,17 +13,21 @@ import me.topchetoeu.jscript.common.parsing.Source;
|
||||
|
||||
public class CompoundNode extends Node {
|
||||
public final Node[] statements;
|
||||
public final boolean hasScope;
|
||||
public Location end;
|
||||
|
||||
@Override public void resolve(CompileResult target) {
|
||||
for (var stm : statements) stm.resolve(target);
|
||||
}
|
||||
|
||||
public void compile(CompileResult target, boolean pollute, boolean alloc, BreakpointType type) {
|
||||
public void compile(CompileResult target, boolean pollute, boolean singleEntry, BreakpointType type) {
|
||||
List<Node> statements = new ArrayList<Node>();
|
||||
|
||||
var subtarget = alloc ? target.subtarget() : target;
|
||||
if (alloc) subtarget.add(i -> Instruction.stackAlloc(subtarget.scope.allocCount()));
|
||||
var subtarget = hasScope ? target.subtarget() : target;
|
||||
if (hasScope) {
|
||||
subtarget.add(i -> Instruction.stackAlloc(subtarget.scope.allocCount()));
|
||||
subtarget.scope.singleEntry = singleEntry;
|
||||
}
|
||||
|
||||
for (var stm : this.statements) {
|
||||
if (stm instanceof FunctionStatementNode func) {
|
||||
@ -41,7 +45,7 @@ public class CompoundNode extends Node {
|
||||
else stm.compile(subtarget, polluted = pollute, BreakpointType.STEP_OVER);
|
||||
}
|
||||
|
||||
if (alloc) {
|
||||
if (hasScope) {
|
||||
subtarget.scope.end();
|
||||
subtarget.add(_i -> Instruction.stackFree(subtarget.scope.allocCount()));
|
||||
}
|
||||
@ -60,11 +64,21 @@ public class CompoundNode extends Node {
|
||||
return this;
|
||||
}
|
||||
|
||||
public CompoundNode(Location loc, Node ...statements) {
|
||||
public CompoundNode(Location loc, boolean hasScope, Node ...statements) {
|
||||
super(loc);
|
||||
this.hasScope = hasScope;
|
||||
this.statements = statements;
|
||||
}
|
||||
|
||||
public static void compileMultiEntry(Node node, CompileResult target, boolean pollute, BreakpointType type) {
|
||||
if (node instanceof CompoundNode comp) {
|
||||
comp.compile(target, pollute, false, type);
|
||||
}
|
||||
else {
|
||||
node.compile(target, pollute, type);
|
||||
}
|
||||
}
|
||||
|
||||
public static ParseRes<CompoundNode> parseComma(Source src, int i, Node prev, int precedence) {
|
||||
if (precedence > 1) return ParseRes.failed();
|
||||
|
||||
@ -78,14 +92,14 @@ public class CompoundNode extends Node {
|
||||
if (!curr.isSuccess()) return curr.chainError(src.loc(i + n), "Expected a value after the comma");
|
||||
n += curr.n;
|
||||
|
||||
if (prev instanceof CompoundNode) {
|
||||
if (prev instanceof CompoundNode comp) {
|
||||
var children = new ArrayList<Node>();
|
||||
children.addAll(List.of(((CompoundNode)prev).statements));
|
||||
children.addAll(List.of(comp.statements));
|
||||
children.add(curr.result);
|
||||
|
||||
return ParseRes.res(new CompoundNode(loc, children.toArray(Node[]::new)), n);
|
||||
return ParseRes.res(new CompoundNode(loc, comp.hasScope, children.toArray(Node[]::new)), n);
|
||||
}
|
||||
else return ParseRes.res(new CompoundNode(loc, prev, curr.result), n);
|
||||
else return ParseRes.res(new CompoundNode(loc, false, prev, curr.result), n);
|
||||
}
|
||||
public static ParseRes<CompoundNode> parse(Source src, int i) {
|
||||
var n = Parsing.skipEmpty(src, i);
|
||||
@ -115,6 +129,6 @@ public class CompoundNode extends Node {
|
||||
statements.add(res.result);
|
||||
}
|
||||
|
||||
return ParseRes.res(new CompoundNode(loc, statements.toArray(Node[]::new)).setEnd(src.loc(i + n - 1)), n);
|
||||
return ParseRes.res(new CompoundNode(loc, true, statements.toArray(Node[]::new)).setEnd(src.loc(i + n - 1)), n);
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ public class FunctionArrowNode extends FunctionNode {
|
||||
|
||||
private static final CompoundNode expToBody(Node node) {
|
||||
if (node instanceof CompoundNode res) return res;
|
||||
else return new CompoundNode(node.loc(), new ReturnNode(node.loc(), node));
|
||||
else return new CompoundNode(node.loc(), false, new ReturnNode(node.loc(), node));
|
||||
}
|
||||
|
||||
public static ParseRes<FunctionArrowNode> parse(Source src, int i) {
|
||||
|
@ -84,7 +84,7 @@ public abstract class FunctionNode extends Node {
|
||||
}
|
||||
|
||||
body.resolve(target);
|
||||
body.compile(target, lastReturn, false, BreakpointType.NONE);
|
||||
body.compile(target, lastReturn, BreakpointType.NONE);
|
||||
|
||||
scope.end();
|
||||
|
||||
|
@ -323,7 +323,7 @@ public final class JavaScript {
|
||||
}
|
||||
|
||||
public static CompileResult compile(Environment env, Node ...statements) {
|
||||
var func = new FunctionValueNode(null, null, new Parameters(List.of()), new CompoundNode(null, statements), null);
|
||||
var func = new FunctionValueNode(null, null, new Parameters(List.of()), new CompoundNode(null, true, statements), null);
|
||||
var res = func.compileBody(env, new FunctionScope(true), true, null, null);
|
||||
res.buildTask.run();
|
||||
return res;
|
||||
|
@ -46,7 +46,7 @@ public class VariableDeclareNode extends Node {
|
||||
target.add(VariableNode.toSet(target, entry.location, entry.name, false, true));
|
||||
}
|
||||
else target.add(_i -> {
|
||||
var i = target.scope.get(entry.name, true);
|
||||
var i = target.scope.get(entry.name, false);
|
||||
|
||||
if (i == null) return Instruction.globDef(entry.name);
|
||||
else return Instruction.nop();
|
||||
|
@ -8,6 +8,7 @@ import me.topchetoeu.jscript.common.parsing.ParseRes;
|
||||
import me.topchetoeu.jscript.common.parsing.Parsing;
|
||||
import me.topchetoeu.jscript.common.parsing.Source;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.CompoundNode;
|
||||
import me.topchetoeu.jscript.compilation.DeferredIntSupplier;
|
||||
import me.topchetoeu.jscript.compilation.JavaScript;
|
||||
import me.topchetoeu.jscript.compilation.LabelContext;
|
||||
@ -47,7 +48,7 @@ public class ForInNode extends Node {
|
||||
var end = new DeferredIntSupplier();
|
||||
|
||||
LabelContext.pushLoop(target.env, loc(), label, end, start);
|
||||
body.compile(target, false, BreakpointType.STEP_OVER);
|
||||
CompoundNode.compileMultiEntry(body, target, false, BreakpointType.STEP_OVER);
|
||||
LabelContext.popLoop(target.env, label);
|
||||
|
||||
int endI = target.size();
|
||||
|
@ -7,6 +7,7 @@ import me.topchetoeu.jscript.common.parsing.ParseRes;
|
||||
import me.topchetoeu.jscript.common.parsing.Parsing;
|
||||
import me.topchetoeu.jscript.common.parsing.Source;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.CompoundNode;
|
||||
import me.topchetoeu.jscript.compilation.DeferredIntSupplier;
|
||||
import me.topchetoeu.jscript.compilation.JavaScript;
|
||||
import me.topchetoeu.jscript.compilation.LabelContext;
|
||||
@ -29,16 +30,16 @@ public class ForNode extends Node {
|
||||
declaration.compile(subtarget, false, BreakpointType.STEP_OVER);
|
||||
|
||||
int start = subtarget.size();
|
||||
condition.compile(subtarget, true, BreakpointType.STEP_OVER);
|
||||
CompoundNode.compileMultiEntry(condition, subtarget, true, BreakpointType.STEP_OVER);
|
||||
int mid = subtarget.temp();
|
||||
|
||||
var end = new DeferredIntSupplier();
|
||||
|
||||
LabelContext.pushLoop(subtarget.env, loc(), label, end, start);
|
||||
body.compile(subtarget, false, BreakpointType.STEP_OVER);
|
||||
CompoundNode.compileMultiEntry(body, subtarget, false, BreakpointType.STEP_OVER);
|
||||
LabelContext.popLoop(subtarget.env, label);
|
||||
|
||||
assignment.compile(subtarget, false, BreakpointType.STEP_OVER);
|
||||
CompoundNode.compileMultiEntry(assignment, subtarget, false, BreakpointType.STEP_OVER);
|
||||
int endI = subtarget.size();
|
||||
end.set(endI);
|
||||
|
||||
|
@ -7,6 +7,7 @@ import me.topchetoeu.jscript.common.parsing.ParseRes;
|
||||
import me.topchetoeu.jscript.common.parsing.Parsing;
|
||||
import me.topchetoeu.jscript.common.parsing.Source;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.CompoundNode;
|
||||
import me.topchetoeu.jscript.compilation.DeferredIntSupplier;
|
||||
import me.topchetoeu.jscript.compilation.JavaScript;
|
||||
import me.topchetoeu.jscript.compilation.LabelContext;
|
||||
@ -51,7 +52,7 @@ public class ForOfNode extends Node {
|
||||
var end = new DeferredIntSupplier();
|
||||
|
||||
LabelContext.pushLoop(target.env, loc(), label, end, start);
|
||||
body.compile(target, false, BreakpointType.STEP_OVER);
|
||||
CompoundNode.compileMultiEntry(body, target, false, BreakpointType.STEP_OVER);
|
||||
LabelContext.popLoop(target.env, label);
|
||||
|
||||
int endI = target.size();
|
||||
|
@ -7,6 +7,7 @@ import me.topchetoeu.jscript.common.parsing.ParseRes;
|
||||
import me.topchetoeu.jscript.common.parsing.Parsing;
|
||||
import me.topchetoeu.jscript.common.parsing.Source;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.CompoundNode;
|
||||
import me.topchetoeu.jscript.compilation.DeferredIntSupplier;
|
||||
import me.topchetoeu.jscript.compilation.JavaScript;
|
||||
import me.topchetoeu.jscript.compilation.LabelContext;
|
||||
@ -28,7 +29,7 @@ public class WhileNode extends Node {
|
||||
|
||||
|
||||
LabelContext.pushLoop(target.env, loc(), label, end, start);
|
||||
body.compile(target, false, BreakpointType.STEP_OVER);
|
||||
CompoundNode.compileMultiEntry(body, target, false, BreakpointType.STEP_OVER);
|
||||
LabelContext.popLoop(target.env, label);
|
||||
|
||||
var endI = target.size();
|
||||
|
@ -60,9 +60,10 @@ public class FunctionScope extends Scope {
|
||||
var superRes = super.get(name, capture);
|
||||
if (superRes != null) return superRes;
|
||||
|
||||
if (specials.has(name)) return specials.get(name);
|
||||
if (locals.has(name)) return locals.get(name);
|
||||
if (captures.has(name)) return captures.get(name);
|
||||
if (specials.has(name)) return addCaptured(specials.get(name), capture);
|
||||
if (locals.has(name)) return addCaptured(locals.get(name), capture);
|
||||
if (captures.has(name)) return addCaptured(captures.get(name), capture);
|
||||
|
||||
if (captureParent == null) return null;
|
||||
|
||||
var parentVar = captureParent.get(name, true);
|
||||
|
@ -18,6 +18,11 @@ public class Scope {
|
||||
public final Scope parent;
|
||||
public final HashSet<Variable> captured = new HashSet<>();
|
||||
|
||||
protected final Variable addCaptured(Variable var, boolean captured) {
|
||||
if (captured) this.captured.add(var);
|
||||
return var;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -76,7 +81,7 @@ public class Scope {
|
||||
*/
|
||||
public Variable get(String name, boolean capture) {
|
||||
var res = variables.get(name);
|
||||
if (res != null) return res;
|
||||
if (res != null) return addCaptured(res, capture);
|
||||
if (parent != null) return parent.get(name, capture);
|
||||
|
||||
return null;
|
||||
@ -107,9 +112,15 @@ public class Scope {
|
||||
* @return Whether or not the request was actually fuliflled
|
||||
*/
|
||||
public boolean flattenVariable(Variable variable, boolean capturable) {
|
||||
if (singleEntry || !capturable) {
|
||||
if (parent == null) return false;
|
||||
return parent.flattenVariable(variable, capturable);
|
||||
}
|
||||
else {
|
||||
variables.overlay(variable);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public int localsCount() { return 0; }
|
||||
public int capturesCount() { return 0; }
|
||||
@ -123,15 +134,6 @@ public class Scope {
|
||||
|
||||
this.ended = true;
|
||||
|
||||
for (var v : variables.all()) {
|
||||
if (captured.contains(v)) {
|
||||
if (singleEntry) this.flattenVariable(v, true);
|
||||
}
|
||||
else {
|
||||
this.flattenVariable(v, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.parent != null) {
|
||||
assert this.parent.child == this;
|
||||
this.parent.child = null;
|
||||
@ -146,13 +148,30 @@ public class Scope {
|
||||
*/
|
||||
public boolean finish() {
|
||||
if (finished) return false;
|
||||
if (parent != null && !parent.finished) throw new IllegalStateException("Tried to finish a child before the parent was finished");
|
||||
if (parent != null && parent.finished) throw new IllegalStateException("Tried to finish a child after the parent was finished");
|
||||
|
||||
for (var child : prevChildren) child.finish();
|
||||
|
||||
var captured = new HashSet<Variable>();
|
||||
var normal = new HashSet<Variable>();
|
||||
|
||||
for (var v : variables.all()) {
|
||||
if (this.captured.contains(v)) {
|
||||
if (singleEntry) captured.add(v);
|
||||
}
|
||||
else normal.add(v);
|
||||
}
|
||||
|
||||
for (var v : captured) variables.remove(v);
|
||||
for (var v : normal) variables.remove(v);
|
||||
|
||||
for (var v : captured) flattenVariable(v, true);
|
||||
for (var v : normal) flattenVariable(v, false);
|
||||
|
||||
|
||||
this.variables.freeze();
|
||||
this.finished = true;
|
||||
|
||||
for (var child : prevChildren) child.finish();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -61,6 +61,7 @@ public final class VariableList {
|
||||
|
||||
private final HashMap<String, Node> map = new HashMap<>();
|
||||
private ArrayList<Node> frozenList = null;
|
||||
private HashMap<Variable, Node> varMap = new HashMap<>();
|
||||
|
||||
private final IntSupplier offset;
|
||||
private IntUnaryOperator indexConverter = null;
|
||||
@ -105,6 +106,7 @@ public final class VariableList {
|
||||
}
|
||||
|
||||
map.put(val.name, node);
|
||||
varMap.put(val, node);
|
||||
val.setIndexSupplier(node);
|
||||
|
||||
return val;
|
||||
@ -117,9 +119,15 @@ public final class VariableList {
|
||||
return this.add(val, true);
|
||||
}
|
||||
public Variable remove(String key) {
|
||||
var res = map.get(key);
|
||||
if (res != null) return remove(res.var);
|
||||
else return null;
|
||||
}
|
||||
public Variable remove(Variable var) {
|
||||
if (var == null) return null;
|
||||
if (frozen()) throw new RuntimeException("The scope has been frozen");
|
||||
|
||||
var node = map.get(key);
|
||||
var node = varMap.get(var);
|
||||
if (node == null) return null;
|
||||
|
||||
if (node.prev != null) {
|
||||
@ -143,6 +151,9 @@ public final class VariableList {
|
||||
node.next = null;
|
||||
node.prev = null;
|
||||
|
||||
map.remove(node.var.name);
|
||||
varMap.remove(node.var);
|
||||
|
||||
return node.var;
|
||||
}
|
||||
|
||||
@ -178,6 +189,7 @@ public final class VariableList {
|
||||
}
|
||||
|
||||
first = last = null;
|
||||
varMap = null;
|
||||
}
|
||||
|
||||
public Iterable<Variable> all() {
|
||||
|
@ -24,7 +24,7 @@ public class VariableNode extends Node implements AssignableNode {
|
||||
}
|
||||
|
||||
@Override public void compile(CompileResult target, boolean pollute) {
|
||||
var i = target.scope.get(name, true);
|
||||
var i = target.scope.get(name, false);
|
||||
|
||||
if (i == null) {
|
||||
target.add(_i -> {
|
||||
@ -40,7 +40,7 @@ public class VariableNode extends Node implements AssignableNode {
|
||||
}
|
||||
|
||||
public static IntFunction<Instruction> toGet(CompileResult target, Location loc, String name, Supplier<Instruction> onGlobal) {
|
||||
var i = target.scope.get(name, true);
|
||||
var i = target.scope.get(name, false);
|
||||
|
||||
if (i == null) return _i -> {
|
||||
if (target.scope.has(name, false)) return Instruction.throwSyntax(loc, String.format("Cannot access '%s' before initialization", name));
|
||||
@ -54,7 +54,7 @@ public class VariableNode extends Node implements AssignableNode {
|
||||
|
||||
|
||||
public static IntFunction<Instruction> toSet(CompileResult target, Location loc, String name, boolean keep, boolean define) {
|
||||
var i = target.scope.get(name, true);
|
||||
var i = target.scope.get(name, false);
|
||||
|
||||
if (i == null) return _i -> {
|
||||
if (target.scope.has(name, false)) return Instruction.throwSyntax(loc, String.format("Cannot access '%s' before initialization", name));
|
||||
|
Loading…
Reference in New Issue
Block a user