regress: remove infrastructure needed for ES6 stuff, simplify loops
This commit is contained in:
parent
754648fbf6
commit
14e4aade35
@ -3,8 +3,6 @@ package me.topchetoeu.jscript.compilation;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.function.IntFunction;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.FunctionBody;
|
import me.topchetoeu.jscript.common.FunctionBody;
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
import me.topchetoeu.jscript.common.Instruction;
|
||||||
@ -14,7 +12,7 @@ import me.topchetoeu.jscript.common.environment.Key;
|
|||||||
import me.topchetoeu.jscript.common.mapping.FunctionMap;
|
import me.topchetoeu.jscript.common.mapping.FunctionMap;
|
||||||
import me.topchetoeu.jscript.common.mapping.FunctionMap.FunctionMapBuilder;
|
import me.topchetoeu.jscript.common.mapping.FunctionMap.FunctionMapBuilder;
|
||||||
import me.topchetoeu.jscript.common.parsing.Location;
|
import me.topchetoeu.jscript.common.parsing.Location;
|
||||||
import me.topchetoeu.jscript.compilation.scope.Scope;
|
import me.topchetoeu.jscript.compilation.scope.FunctionScope;
|
||||||
|
|
||||||
public final class CompileResult {
|
public final class CompileResult {
|
||||||
public static final class ChildData {
|
public static final class ChildData {
|
||||||
@ -27,15 +25,12 @@ public final class CompileResult {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final List<IntFunction<Instruction>> instructions;
|
public final List<Instruction> instructions;
|
||||||
public final List<CompileResult> children;
|
public final List<CompileResult> children;
|
||||||
public final FunctionMapBuilder map;
|
public final FunctionMapBuilder map;
|
||||||
public final Environment env;
|
public final Environment env;
|
||||||
public int length;
|
public int length;
|
||||||
public Runnable buildTask = () -> {
|
public final FunctionScope scope;
|
||||||
throw new IllegalStateException("Compile result is not ready to be built");
|
|
||||||
};
|
|
||||||
public final Scope scope;
|
|
||||||
|
|
||||||
public int temp() {
|
public int temp() {
|
||||||
instructions.add(null);
|
instructions.add(null);
|
||||||
@ -43,21 +38,14 @@ public final class CompileResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public CompileResult add(Instruction instr) {
|
public CompileResult add(Instruction instr) {
|
||||||
instructions.add(i -> instr);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
public CompileResult add(IntFunction<Instruction> instr) {
|
|
||||||
instructions.add(instr);
|
instructions.add(instr);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
public CompileResult set(int i, Instruction instr) {
|
public CompileResult set(int i, Instruction instr) {
|
||||||
instructions.set(i, _i -> instr);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
public CompileResult set(int i, IntFunction<Instruction>instr) {
|
|
||||||
instructions.set(i, instr);
|
instructions.set(i, instr);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int size() { return instructions.size(); }
|
public int size() { return instructions.size(); }
|
||||||
|
|
||||||
public void setDebug(Location loc, BreakpointType type) {
|
public void setDebug(Location loc, BreakpointType type) {
|
||||||
@ -79,62 +67,25 @@ public final class CompileResult {
|
|||||||
setLocationAndDebug(instructions.size() - 1, loc, type);
|
setLocationAndDebug(instructions.size() - 1, loc, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void beginScope() {
|
|
||||||
// for (var cap : scope.capturables()) {
|
|
||||||
// add(_i -> Instruction.capInit(cap.index().index));
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
public void reallocScope() {
|
|
||||||
for (var cap : scope.capturables()) {
|
|
||||||
add(_i -> cap.index().toGet());
|
|
||||||
add(_i -> Instruction.capFree(cap.index().index));
|
|
||||||
add(_i -> cap.index().toInit());
|
|
||||||
}
|
|
||||||
|
|
||||||
scope.end();
|
|
||||||
}
|
|
||||||
public void endScope() {
|
|
||||||
for (var cap : scope.capturables()) {
|
|
||||||
add(_i -> Instruction.capFree(cap.index().index));
|
|
||||||
}
|
|
||||||
for (var var : scope.locals()) {
|
|
||||||
add(_i -> Instruction.varFree(var.index().index));
|
|
||||||
}
|
|
||||||
|
|
||||||
scope.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int addChild(CompileResult res) {
|
public int addChild(CompileResult res) {
|
||||||
this.children.add(res);
|
this.children.add(res);
|
||||||
return this.children.size() - 1;
|
return this.children.size() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Instruction[] instructions() {
|
public Instruction[] instructions() {
|
||||||
var res = new Instruction[instructions.size()];
|
return instructions.toArray(new Instruction[0]);
|
||||||
var i = 0;
|
|
||||||
for (var suppl : instructions) {
|
|
||||||
res[i] = suppl.apply(i);
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public FunctionMap map() {
|
public FunctionMap map() {
|
||||||
return map.build(scope);
|
return map.build(scope.localNames(), scope.captureNames());
|
||||||
}
|
}
|
||||||
public FunctionBody body() {
|
public FunctionBody body() {
|
||||||
var builtChildren = new FunctionBody[children.size()];
|
var builtChildren = new FunctionBody[children.size()];
|
||||||
|
|
||||||
for (var i = 0; i < children.size(); i++) builtChildren[i] = children.get(i).body();
|
for (var i = 0; i < children.size(); i++) builtChildren[i] = children.get(i).body();
|
||||||
|
|
||||||
var instrRes = new Instruction[instructions.size()];
|
var instrRes = instructions();
|
||||||
var i = 0;
|
|
||||||
|
|
||||||
for (var suppl : instructions) {
|
for (var instr : instrRes) System.out.println(instr);
|
||||||
instrRes[i] = suppl.apply(i);
|
|
||||||
// System.out.println(instrRes[i]);
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new FunctionBody(
|
return new FunctionBody(
|
||||||
scope.localsCount(), scope.capturablesCount(), scope.capturesCount(),
|
scope.localsCount(), scope.capturablesCount(), scope.capturesCount(),
|
||||||
@ -143,7 +94,7 @@ public final class CompileResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public CompileResult subtarget() {
|
public CompileResult subtarget() {
|
||||||
return new CompileResult(env, new Scope(scope), this);
|
return new CompileResult(env, new FunctionScope(scope), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompileResult setEnvironment(Environment env) {
|
public CompileResult setEnvironment(Environment env) {
|
||||||
@ -160,16 +111,15 @@ public final class CompileResult {
|
|||||||
return new CompileResult(env.child(), scope, this);
|
return new CompileResult(env.child(), scope, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompileResult(Environment env, Scope scope, int length, Consumer<CompileResult> task) {
|
public CompileResult(Environment env, FunctionScope scope, int length) {
|
||||||
this.scope = scope;
|
this.scope = scope;
|
||||||
this.instructions = new ArrayList<>();
|
this.instructions = new ArrayList<>();
|
||||||
this.children = new LinkedList<>();
|
this.children = new LinkedList<>();
|
||||||
this.map = FunctionMap.builder();
|
this.map = FunctionMap.builder();
|
||||||
this.env = env;
|
this.env = env;
|
||||||
this.length = length;
|
this.length = length;
|
||||||
this.buildTask = () -> task.accept(this);
|
|
||||||
}
|
}
|
||||||
private CompileResult(Environment env, Scope scope, CompileResult parent) {
|
private CompileResult(Environment env, FunctionScope scope, CompileResult parent) {
|
||||||
this.scope = scope;
|
this.scope = scope;
|
||||||
this.instructions = parent.instructions;
|
this.instructions = parent.instructions;
|
||||||
this.children = parent.children;
|
this.children = parent.children;
|
||||||
|
@ -14,7 +14,6 @@ import me.topchetoeu.jscript.common.parsing.Source;
|
|||||||
|
|
||||||
public class CompoundNode extends Node {
|
public class CompoundNode extends Node {
|
||||||
public final Node[] statements;
|
public final Node[] statements;
|
||||||
public boolean hasScope;
|
|
||||||
public Location end;
|
public Location end;
|
||||||
|
|
||||||
@Override public void resolve(CompileResult target) {
|
@Override public void resolve(CompileResult target) {
|
||||||
@ -24,12 +23,9 @@ public class CompoundNode extends Node {
|
|||||||
public void compile(CompileResult target, boolean pollute, boolean singleEntry, BreakpointType type) {
|
public void compile(CompileResult target, boolean pollute, boolean singleEntry, BreakpointType type) {
|
||||||
List<Node> statements = new ArrayList<Node>();
|
List<Node> statements = new ArrayList<Node>();
|
||||||
|
|
||||||
var subtarget = hasScope ? target.subtarget() : target;
|
|
||||||
if (hasScope) subtarget.beginScope();
|
|
||||||
|
|
||||||
for (var stm : this.statements) {
|
for (var stm : this.statements) {
|
||||||
if (stm instanceof FunctionStatementNode func) {
|
if (stm instanceof FunctionStatementNode func) {
|
||||||
func.compile(subtarget, false);
|
func.compile(target, false);
|
||||||
}
|
}
|
||||||
else statements.add(stm);
|
else statements.add(stm);
|
||||||
}
|
}
|
||||||
@ -39,12 +35,10 @@ public class CompoundNode extends Node {
|
|||||||
for (var i = 0; i < statements.size(); i++) {
|
for (var i = 0; i < statements.size(); i++) {
|
||||||
var stm = statements.get(i);
|
var stm = statements.get(i);
|
||||||
|
|
||||||
if (i != statements.size() - 1) stm.compile(subtarget, false, BreakpointType.STEP_OVER);
|
if (i != statements.size() - 1) stm.compile(target, false, BreakpointType.STEP_OVER);
|
||||||
else stm.compile(subtarget, polluted = pollute, BreakpointType.STEP_OVER);
|
else stm.compile(target, polluted = pollute, BreakpointType.STEP_OVER);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasScope) subtarget.endScope();
|
|
||||||
|
|
||||||
if (!polluted && pollute) {
|
if (!polluted && pollute) {
|
||||||
target.add(Instruction.pushUndefined());
|
target.add(Instruction.pushUndefined());
|
||||||
}
|
}
|
||||||
@ -59,9 +53,8 @@ public class CompoundNode extends Node {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompoundNode(Location loc, boolean hasScope, Node ...statements) {
|
public CompoundNode(Location loc, Node ...statements) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.hasScope = hasScope;
|
|
||||||
this.statements = statements;
|
this.statements = statements;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,9 +85,9 @@ public class CompoundNode extends Node {
|
|||||||
children.addAll(Arrays.asList(comp.statements));
|
children.addAll(Arrays.asList(comp.statements));
|
||||||
children.add(curr.result);
|
children.add(curr.result);
|
||||||
|
|
||||||
return ParseRes.res(new CompoundNode(loc, comp.hasScope, children.toArray(new Node[0])), n);
|
return ParseRes.res(new CompoundNode(loc, children.toArray(new Node[0])), n);
|
||||||
}
|
}
|
||||||
else return ParseRes.res(new CompoundNode(loc, false, prev, curr.result), n);
|
else return ParseRes.res(new CompoundNode(loc, prev, curr.result), n);
|
||||||
}
|
}
|
||||||
public static ParseRes<CompoundNode> parse(Source src, int i) {
|
public static ParseRes<CompoundNode> parse(Source src, int i) {
|
||||||
var n = Parsing.skipEmpty(src, i);
|
var n = Parsing.skipEmpty(src, i);
|
||||||
@ -124,6 +117,6 @@ public class CompoundNode extends Node {
|
|||||||
statements.add(res.result);
|
statements.add(res.result);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ParseRes.res(new CompoundNode(loc, true, statements.toArray(new Node[0])).setEnd(src.loc(i + n - 1)), n);
|
return ParseRes.res(new CompoundNode(loc, statements.toArray(new Node[0])).setEnd(src.loc(i + n - 1)), n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package me.topchetoeu.jscript.compilation;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.SyntaxException;
|
import me.topchetoeu.jscript.common.SyntaxException;
|
||||||
@ -17,9 +18,7 @@ import me.topchetoeu.jscript.compilation.control.ContinueNode;
|
|||||||
import me.topchetoeu.jscript.compilation.control.DebugNode;
|
import me.topchetoeu.jscript.compilation.control.DebugNode;
|
||||||
import me.topchetoeu.jscript.compilation.control.DeleteNode;
|
import me.topchetoeu.jscript.compilation.control.DeleteNode;
|
||||||
import me.topchetoeu.jscript.compilation.control.DoWhileNode;
|
import me.topchetoeu.jscript.compilation.control.DoWhileNode;
|
||||||
import me.topchetoeu.jscript.compilation.control.ForInNode;
|
|
||||||
import me.topchetoeu.jscript.compilation.control.ForNode;
|
import me.topchetoeu.jscript.compilation.control.ForNode;
|
||||||
import me.topchetoeu.jscript.compilation.control.ForOfNode;
|
|
||||||
import me.topchetoeu.jscript.compilation.control.IfNode;
|
import me.topchetoeu.jscript.compilation.control.IfNode;
|
||||||
import me.topchetoeu.jscript.compilation.control.ReturnNode;
|
import me.topchetoeu.jscript.compilation.control.ReturnNode;
|
||||||
import me.topchetoeu.jscript.compilation.control.SwitchNode;
|
import me.topchetoeu.jscript.compilation.control.SwitchNode;
|
||||||
@ -29,7 +28,6 @@ import me.topchetoeu.jscript.compilation.control.WhileNode;
|
|||||||
import me.topchetoeu.jscript.compilation.scope.FunctionScope;
|
import me.topchetoeu.jscript.compilation.scope.FunctionScope;
|
||||||
import me.topchetoeu.jscript.compilation.values.ArgumentsNode;
|
import me.topchetoeu.jscript.compilation.values.ArgumentsNode;
|
||||||
import me.topchetoeu.jscript.compilation.values.ArrayNode;
|
import me.topchetoeu.jscript.compilation.values.ArrayNode;
|
||||||
import me.topchetoeu.jscript.compilation.values.ClassValueNode;
|
|
||||||
import me.topchetoeu.jscript.compilation.values.ObjectNode;
|
import me.topchetoeu.jscript.compilation.values.ObjectNode;
|
||||||
import me.topchetoeu.jscript.compilation.values.RegexNode;
|
import me.topchetoeu.jscript.compilation.values.RegexNode;
|
||||||
import me.topchetoeu.jscript.compilation.values.SuperNode;
|
import me.topchetoeu.jscript.compilation.values.SuperNode;
|
||||||
@ -49,16 +47,8 @@ import me.topchetoeu.jscript.compilation.values.operations.TypeofNode;
|
|||||||
|
|
||||||
public final class JavaScript {
|
public final class JavaScript {
|
||||||
public static enum DeclarationType {
|
public static enum DeclarationType {
|
||||||
VAR(false, false),
|
@Deprecated
|
||||||
CONST(true, true),
|
VAR;
|
||||||
LET(true, false);
|
|
||||||
|
|
||||||
public final boolean strict, readonly;
|
|
||||||
|
|
||||||
private DeclarationType(boolean strict, boolean readonly) {
|
|
||||||
this.strict = strict;
|
|
||||||
this.readonly = readonly;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final Key<Environment> COMPILE_ROOT = Key.of();
|
public static final Key<Environment> COMPILE_ROOT = Key.of();
|
||||||
@ -93,7 +83,6 @@ public final class JavaScript {
|
|||||||
return ParseRes.first(src, i,
|
return ParseRes.first(src, i,
|
||||||
(s, j) -> statement ? ParseRes.failed() : ObjectNode.parse(s, j),
|
(s, j) -> statement ? ParseRes.failed() : ObjectNode.parse(s, j),
|
||||||
(s, j) -> statement ? ParseRes.failed() : FunctionNode.parseFunction(s, j, false),
|
(s, j) -> statement ? ParseRes.failed() : FunctionNode.parseFunction(s, j, false),
|
||||||
(s, j) -> statement ? ParseRes.failed() : ClassValueNode.parse(s, j),
|
|
||||||
JavaScript::parseLiteral,
|
JavaScript::parseLiteral,
|
||||||
StringNode::parse,
|
StringNode::parse,
|
||||||
RegexNode::parse,
|
RegexNode::parse,
|
||||||
@ -102,7 +91,6 @@ public final class JavaScript {
|
|||||||
ChangeNode::parsePrefixIncrease,
|
ChangeNode::parsePrefixIncrease,
|
||||||
OperationNode::parsePrefix,
|
OperationNode::parsePrefix,
|
||||||
ArrayNode::parse,
|
ArrayNode::parse,
|
||||||
(s, j) -> statement ? ParseRes.failed() : FunctionArrowNode.parse(s, j),
|
|
||||||
JavaScript::parseParens,
|
JavaScript::parseParens,
|
||||||
CallNode::parseNew,
|
CallNode::parseNew,
|
||||||
TypeofNode::parse,
|
TypeofNode::parse,
|
||||||
@ -188,14 +176,13 @@ public final class JavaScript {
|
|||||||
return res.addN(end.n);
|
return res.addN(end.n);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ParseRes<? extends Node> parseStatement(Source src, int i) {
|
public static ParseRes<Node> parseStatement(Source src, int i) {
|
||||||
var n = Parsing.skipEmpty(src, i);
|
var n = Parsing.skipEmpty(src, i);
|
||||||
|
|
||||||
if (src.is(i + n, ";")) return ParseRes.res(new DiscardNode(src.loc(i+ n), null), n + 1);
|
if (src.is(i + n, ";")) return ParseRes.res(new DiscardNode(src.loc(i+ n), null), n + 1);
|
||||||
if (Parsing.isIdentifier(src, i + n, "with")) return ParseRes.error(src.loc(i + n), "'with' statements are not allowed.");
|
if (Parsing.isIdentifier(src, i + n, "with")) return ParseRes.error(src.loc(i + n), "'with' statements are not allowed.");
|
||||||
|
|
||||||
ParseRes<? extends Node> res = ParseRes.first(src, i + n,
|
ParseRes<Node> res = ParseRes.first(src, i + n,
|
||||||
ClassStatementNode::parse,
|
|
||||||
VariableDeclareNode::parse,
|
VariableDeclareNode::parse,
|
||||||
ReturnNode::parse,
|
ReturnNode::parse,
|
||||||
ThrowNode::parse,
|
ThrowNode::parse,
|
||||||
@ -206,8 +193,6 @@ public final class JavaScript {
|
|||||||
WhileNode::parse,
|
WhileNode::parse,
|
||||||
SwitchNode::parse,
|
SwitchNode::parse,
|
||||||
ForNode::parse,
|
ForNode::parse,
|
||||||
ForInNode::parse,
|
|
||||||
ForOfNode::parse,
|
|
||||||
DoWhileNode::parse,
|
DoWhileNode::parse,
|
||||||
TryNode::parse,
|
TryNode::parse,
|
||||||
CompoundNode::parse,
|
CompoundNode::parse,
|
||||||
@ -231,13 +216,11 @@ public final class JavaScript {
|
|||||||
return ParseRes.failed();
|
return ParseRes.failed();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ParseRes<DeclarationType> parseDeclarationType(Source src, int i) {
|
public static ParseRes<Boolean> parseDeclarationType(Source src, int i) {
|
||||||
var res = Parsing.parseIdentifier(src, i);
|
var res = Parsing.parseIdentifier(src, i);
|
||||||
if (!res.isSuccess()) return res.chainError();
|
if (!res.isSuccess()) return res.chainError();
|
||||||
|
|
||||||
if (res.result.equals("var")) return ParseRes.res(DeclarationType.VAR, res.n);
|
if (res.result.equals("var")) return ParseRes.res(true, res.n);
|
||||||
if (res.result.equals("let")) return ParseRes.res(DeclarationType.LET, res.n);
|
|
||||||
if (res.result.equals("const")) return ParseRes.res(DeclarationType.CONST, res.n);
|
|
||||||
|
|
||||||
return ParseRes.failed();
|
return ParseRes.failed();
|
||||||
}
|
}
|
||||||
@ -272,9 +255,8 @@ public final class JavaScript {
|
|||||||
env = env.child();
|
env = env.child();
|
||||||
env.add(COMPILE_ROOT, env);
|
env.add(COMPILE_ROOT, env);
|
||||||
|
|
||||||
var func = new FunctionValueNode(null, null, new Parameters(Arrays.asList()), new CompoundNode(null, true, statements), null);
|
var func = new FunctionValueNode(null, null, Arrays.asList(), new CompoundNode(null, statements), null);
|
||||||
var res = func.compileBody(env, new FunctionScope(true), true, null, null);
|
var res = func.compileBody(env, new FunctionScope(true), true, null, null);
|
||||||
res.buildTask.run();
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -299,4 +281,42 @@ public final class JavaScript {
|
|||||||
|
|
||||||
return ParseRes.res(nameRes.result, n);
|
return ParseRes.res(nameRes.result, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ParseRes<List<VariableNode>> parseParameters(Source src, int i) {
|
||||||
|
var n = Parsing.skipEmpty(src, i);
|
||||||
|
|
||||||
|
var openParen = Parsing.parseOperator(src, i + n, "(");
|
||||||
|
if (!openParen.isSuccess()) return openParen.chainError(src.loc(i + n), "Expected a parameter list");
|
||||||
|
n += openParen.n;
|
||||||
|
|
||||||
|
var params = new ArrayList<VariableNode>();
|
||||||
|
|
||||||
|
var closeParen = Parsing.parseOperator(src, i + n, ")");
|
||||||
|
n += closeParen.n;
|
||||||
|
|
||||||
|
if (!closeParen.isSuccess()) {
|
||||||
|
while (true) {
|
||||||
|
n += Parsing.skipEmpty(src, i + n);
|
||||||
|
|
||||||
|
var param = VariableNode.parse(src, i + n);
|
||||||
|
if (!param.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a parameter or a closing brace");
|
||||||
|
n += param.n;
|
||||||
|
n += Parsing.skipEmpty(src, i + n);
|
||||||
|
|
||||||
|
params.add(param.result);
|
||||||
|
|
||||||
|
if (src.is(i + n, ",")) {
|
||||||
|
n++;
|
||||||
|
n += Parsing.skipEmpty(src, i + n);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (src.is(i + n, ")")) {
|
||||||
|
n++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ParseRes.res(params, n);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
package me.topchetoeu.jscript.compilation;
|
package me.topchetoeu.jscript.compilation;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.function.IntFunction;
|
import java.util.Stack;
|
||||||
import java.util.function.IntSupplier;
|
import java.util.function.IntSupplier;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
import me.topchetoeu.jscript.common.Instruction;
|
||||||
@ -18,6 +19,8 @@ public class LabelContext {
|
|||||||
private final LinkedList<IntSupplier> list = new LinkedList<>();
|
private final LinkedList<IntSupplier> list = new LinkedList<>();
|
||||||
private final HashMap<String, IntSupplier> map = new HashMap<>();
|
private final HashMap<String, IntSupplier> map = new HashMap<>();
|
||||||
|
|
||||||
|
private final Stack<ArrayList<Runnable>> deferredAdders = new Stack<>();
|
||||||
|
|
||||||
public IntSupplier get() {
|
public IntSupplier get() {
|
||||||
return list.peekLast();
|
return list.peekLast();
|
||||||
}
|
}
|
||||||
@ -25,15 +28,31 @@ public class LabelContext {
|
|||||||
return map.get(name);
|
return map.get(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IntFunction<Instruction> getJump() {
|
public void flushAdders() {
|
||||||
|
for (var adder : deferredAdders.peek()) {
|
||||||
|
adder.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
deferredAdders.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean jump(CompileResult target) {
|
||||||
var res = get();
|
var res = get();
|
||||||
if (res == null) return null;
|
if (res != null) {
|
||||||
else return i -> Instruction.jmp(res.getAsInt() - i);
|
var tmp = target.temp();
|
||||||
|
this.deferredAdders.peek().add(() -> target.set(tmp, Instruction.jmp(res.getAsInt() - tmp)));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else return false;
|
||||||
}
|
}
|
||||||
public IntFunction<Instruction> getJump(String name) {
|
public boolean jump(CompileResult target, String name) {
|
||||||
var res = get(name);
|
var res = get(name);
|
||||||
if (res == null) return null;
|
if (res != null) {
|
||||||
else return i -> Instruction.jmp(res.getAsInt() - i);
|
var tmp = target.temp();
|
||||||
|
this.deferredAdders.peek().add(() -> target.set(tmp, Instruction.jmp(res.getAsInt() - tmp)));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void push(IntSupplier jumpTarget) {
|
public void push(IntSupplier jumpTarget) {
|
||||||
@ -48,6 +67,7 @@ public class LabelContext {
|
|||||||
public void pushLoop(Location loc, String name, IntSupplier jumpTarget) {
|
public void pushLoop(Location loc, String name, IntSupplier jumpTarget) {
|
||||||
push(jumpTarget);
|
push(jumpTarget);
|
||||||
push(loc, name, jumpTarget);
|
push(loc, name, jumpTarget);
|
||||||
|
deferredAdders.push(new ArrayList<>());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void pop() {
|
public void pop() {
|
||||||
@ -61,6 +81,7 @@ public class LabelContext {
|
|||||||
public void popLoop(String name) {
|
public void popLoop(String name) {
|
||||||
pop();
|
pop();
|
||||||
pop(name);
|
pop(name);
|
||||||
|
flushAdders();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LabelContext getBreak(Environment env) {
|
public static LabelContext getBreak(Environment env) {
|
||||||
|
@ -15,12 +15,10 @@ public class BreakNode extends Node {
|
|||||||
public final String label;
|
public final String label;
|
||||||
|
|
||||||
@Override public void compile(CompileResult target, boolean pollute) {
|
@Override public void compile(CompileResult target, boolean pollute) {
|
||||||
var res = LabelContext.getBreak(target.env).getJump();
|
if (!LabelContext.getBreak(target.env).jump(target)) {
|
||||||
if (res == null) {
|
|
||||||
if (label != null) throw new SyntaxException(loc(), String.format("Undefined label '%s'", label));
|
if (label != null) throw new SyntaxException(loc(), String.format("Undefined label '%s'", label));
|
||||||
else throw new SyntaxException(loc(), "Illegal break statement");
|
else throw new SyntaxException(loc(), "Illegal break statement");
|
||||||
}
|
}
|
||||||
target.add(res);
|
|
||||||
|
|
||||||
if (pollute) target.add(Instruction.pushUndefined());
|
if (pollute) target.add(Instruction.pushUndefined());
|
||||||
}
|
}
|
||||||
|
@ -15,12 +15,10 @@ public class ContinueNode extends Node {
|
|||||||
public final String label;
|
public final String label;
|
||||||
|
|
||||||
@Override public void compile(CompileResult target, boolean pollute) {
|
@Override public void compile(CompileResult target, boolean pollute) {
|
||||||
var res = LabelContext.getCont(target.env).getJump();
|
if (!LabelContext.getCont(target.env).jump(target)) {
|
||||||
if (res == null) {
|
|
||||||
if (label != null) throw new SyntaxException(loc(), String.format("Undefined label '%s'", label));
|
if (label != null) throw new SyntaxException(loc(), String.format("Undefined label '%s'", label));
|
||||||
else throw new SyntaxException(loc(), "Illegal continue statement");
|
else throw new SyntaxException(loc(), "Illegal continue statement");
|
||||||
}
|
}
|
||||||
target.add(res);
|
|
||||||
|
|
||||||
if (pollute) target.add(Instruction.pushUndefined());
|
if (pollute) target.add(Instruction.pushUndefined());
|
||||||
}
|
}
|
||||||
|
@ -27,13 +27,14 @@ public class DoWhileNode extends Node {
|
|||||||
|
|
||||||
LabelContext.pushLoop(target.env, loc(), label, end, start);
|
LabelContext.pushLoop(target.env, loc(), label, end, start);
|
||||||
body.compile(target, false, BreakpointType.STEP_OVER);
|
body.compile(target, false, BreakpointType.STEP_OVER);
|
||||||
LabelContext.popLoop(target.env, label);
|
|
||||||
|
|
||||||
mid.set(target.size());
|
mid.set(target.size());
|
||||||
condition.compile(target, true, BreakpointType.STEP_OVER);
|
condition.compile(target, true, BreakpointType.STEP_OVER);
|
||||||
int endI = target.size();
|
int endI = target.size();
|
||||||
end.set(endI + 1);
|
end.set(endI + 1);
|
||||||
|
|
||||||
|
LabelContext.popLoop(target.env, label);
|
||||||
|
|
||||||
target.add(Instruction.jmpIf(start - endI));
|
target.add(Instruction.jmpIf(start - endI));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,14 +8,15 @@ import me.topchetoeu.jscript.common.parsing.Parsing;
|
|||||||
import me.topchetoeu.jscript.common.parsing.Source;
|
import me.topchetoeu.jscript.common.parsing.Source;
|
||||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||||
import me.topchetoeu.jscript.compilation.CompoundNode;
|
import me.topchetoeu.jscript.compilation.CompoundNode;
|
||||||
import me.topchetoeu.jscript.compilation.DeferredIntSupplier;
|
|
||||||
import me.topchetoeu.jscript.compilation.JavaScript;
|
import me.topchetoeu.jscript.compilation.JavaScript;
|
||||||
import me.topchetoeu.jscript.compilation.LabelContext;
|
import me.topchetoeu.jscript.compilation.LabelContext;
|
||||||
|
import me.topchetoeu.jscript.compilation.DeferredIntSupplier;
|
||||||
import me.topchetoeu.jscript.compilation.Node;
|
import me.topchetoeu.jscript.compilation.Node;
|
||||||
import me.topchetoeu.jscript.compilation.patterns.Binding;
|
import me.topchetoeu.jscript.compilation.values.VariableNode;
|
||||||
|
|
||||||
public class ForInNode extends Node {
|
public class ForInNode extends Node {
|
||||||
public final Binding binding;
|
public final boolean isDecl;
|
||||||
|
public final VariableNode binding;
|
||||||
public final Node object, body;
|
public final Node object, body;
|
||||||
public final String label;
|
public final String label;
|
||||||
|
|
||||||
@ -25,8 +26,6 @@ public class ForInNode extends Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override public void compile(CompileResult target, boolean pollute) {
|
@Override public void compile(CompileResult target, boolean pollute) {
|
||||||
binding.declareLateInit(target);
|
|
||||||
|
|
||||||
object.compile(target, true, BreakpointType.STEP_OVER);
|
object.compile(target, true, BreakpointType.STEP_OVER);
|
||||||
target.add(Instruction.keys(false, true));
|
target.add(Instruction.keys(false, true));
|
||||||
|
|
||||||
@ -35,27 +34,32 @@ public class ForInNode extends Node {
|
|||||||
int mid = target.temp();
|
int mid = target.temp();
|
||||||
|
|
||||||
target.add(Instruction.loadMember("value")).setLocation(binding.loc());
|
target.add(Instruction.loadMember("value")).setLocation(binding.loc());
|
||||||
binding.assign(target, false);
|
target.add(VariableNode.toInit(target, loc(), binding.name));
|
||||||
|
|
||||||
target.setLocationAndDebug(object.loc(), BreakpointType.STEP_OVER);
|
target.setLocationAndDebug(object.loc(), BreakpointType.STEP_OVER);
|
||||||
|
|
||||||
var end = new DeferredIntSupplier();
|
var end = new DeferredIntSupplier();
|
||||||
|
|
||||||
LabelContext.pushLoop(target.env, loc(), label, end, start);
|
LabelContext.pushLoop(target.env, loc(), label, end, start);
|
||||||
CompoundNode.compileMultiEntry(body, target, false, BreakpointType.STEP_OVER);
|
CompoundNode.compileMultiEntry(body, target, false, BreakpointType.STEP_OVER);
|
||||||
LabelContext.popLoop(target.env, label);
|
|
||||||
|
|
||||||
int endI = target.size();
|
int endI = target.size();
|
||||||
|
|
||||||
target.add(Instruction.jmp(start - endI));
|
target.add(Instruction.jmp(start - endI));
|
||||||
target.add(Instruction.discard());
|
target.add(Instruction.discard());
|
||||||
target.set(mid, Instruction.jmpIfNot(endI - mid + 1));
|
target.set(mid, Instruction.jmpIfNot(endI - mid + 1));
|
||||||
|
|
||||||
|
end.set(endI);
|
||||||
|
LabelContext.popLoop(target.env, label);
|
||||||
|
|
||||||
if (pollute) target.add(Instruction.pushUndefined());
|
if (pollute) target.add(Instruction.pushUndefined());
|
||||||
}
|
}
|
||||||
|
|
||||||
public ForInNode(Location loc, String label, Binding binding, Node object, Node body) {
|
public ForInNode(Location loc, String label, VariableNode binding, boolean isDecl, Node object, Node body) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.label = label;
|
this.label = label;
|
||||||
this.binding = binding;
|
this.binding = binding;
|
||||||
|
this.isDecl = isDecl;
|
||||||
this.object = object;
|
this.object = object;
|
||||||
this.body = body;
|
this.body = body;
|
||||||
}
|
}
|
||||||
@ -76,9 +80,15 @@ public class ForInNode extends Node {
|
|||||||
n++;
|
n++;
|
||||||
n += Parsing.skipEmpty(src, i + n);
|
n += Parsing.skipEmpty(src, i + n);
|
||||||
|
|
||||||
var binding = Binding.parse(src, i + n);
|
var varKw = JavaScript.parseDeclarationType(src, i + n);
|
||||||
if (!binding.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a binding in for-in loop");
|
n += varKw.n;
|
||||||
n += binding.n;
|
n += Parsing.skipEmpty(src, i + n);
|
||||||
|
|
||||||
|
var bindingLoc = src.loc(i + n);
|
||||||
|
|
||||||
|
var name = Parsing.parseIdentifier(src, i + n);
|
||||||
|
if (!name.isSuccess()) return name.chainError(src.loc(i + n), "Expected a variable name");
|
||||||
|
n += name.n;
|
||||||
n += Parsing.skipEmpty(src, i + n);
|
n += Parsing.skipEmpty(src, i + n);
|
||||||
|
|
||||||
if (!Parsing.isIdentifier(src, i + n, "in")) return ParseRes.error(src.loc(i + n), "Expected 'in' keyword after variable declaration");
|
if (!Parsing.isIdentifier(src, i + n, "in")) return ParseRes.error(src.loc(i + n), "Expected 'in' keyword after variable declaration");
|
||||||
@ -96,6 +106,6 @@ public class ForInNode extends Node {
|
|||||||
if (!bodyRes.isSuccess()) return bodyRes.chainError(src.loc(i + n), "Expected a for-in body");
|
if (!bodyRes.isSuccess()) return bodyRes.chainError(src.loc(i + n), "Expected a for-in body");
|
||||||
n += bodyRes.n;
|
n += bodyRes.n;
|
||||||
|
|
||||||
return ParseRes.res(new ForInNode(loc, label.result, binding.result, obj.result, bodyRes.result), n);
|
return ParseRes.res(new ForInNode(loc, label.result, new VariableNode(bindingLoc, name.result), varKw.isSuccess(), obj.result, bodyRes.result), n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,8 +25,6 @@ public class ForNode extends Node {
|
|||||||
}
|
}
|
||||||
@Override public void compile(CompileResult target, boolean pollute) {
|
@Override public void compile(CompileResult target, boolean pollute) {
|
||||||
var subtarget = target.subtarget();
|
var subtarget = target.subtarget();
|
||||||
subtarget.scope.singleEntry = false;
|
|
||||||
subtarget.beginScope();
|
|
||||||
|
|
||||||
declaration.compile(subtarget, false, BreakpointType.STEP_OVER);
|
declaration.compile(subtarget, false, BreakpointType.STEP_OVER);
|
||||||
|
|
||||||
@ -38,20 +36,16 @@ public class ForNode extends Node {
|
|||||||
|
|
||||||
LabelContext.pushLoop(subtarget.env, loc(), label, end, start);
|
LabelContext.pushLoop(subtarget.env, loc(), label, end, start);
|
||||||
CompoundNode.compileMultiEntry(body, subtarget, false, BreakpointType.STEP_OVER);
|
CompoundNode.compileMultiEntry(body, subtarget, false, BreakpointType.STEP_OVER);
|
||||||
LabelContext.popLoop(subtarget.env, label);
|
|
||||||
|
|
||||||
subtarget.reallocScope();
|
|
||||||
|
|
||||||
CompoundNode.compileMultiEntry(assignment, subtarget, false, BreakpointType.STEP_OVER);
|
CompoundNode.compileMultiEntry(assignment, subtarget, false, BreakpointType.STEP_OVER);
|
||||||
int endI = subtarget.size();
|
int endI = subtarget.size();
|
||||||
|
|
||||||
end.set(endI);
|
end.set(endI);
|
||||||
|
LabelContext.popLoop(subtarget.env, label);
|
||||||
|
|
||||||
subtarget.add(Instruction.jmp(start - endI));
|
subtarget.add(Instruction.jmp(start - endI));
|
||||||
subtarget.set(mid, Instruction.jmpIfNot(endI - mid + 1));
|
subtarget.set(mid, Instruction.jmpIfNot(endI - mid + 1));
|
||||||
if (pollute) subtarget.add(Instruction.pushUndefined());
|
if (pollute) subtarget.add(Instruction.pushUndefined());
|
||||||
|
|
||||||
subtarget.endScope();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ForNode(Location loc, String label, Node declaration, Node condition, Node assignment, Node body) {
|
public ForNode(Location loc, String label, Node declaration, Node condition, Node assignment, Node body) {
|
||||||
|
@ -43,41 +43,37 @@ public class SwitchNode extends Node {
|
|||||||
|
|
||||||
value.compile(target, true, BreakpointType.STEP_OVER);
|
value.compile(target, true, BreakpointType.STEP_OVER);
|
||||||
|
|
||||||
var subtarget = target.subtarget();
|
|
||||||
subtarget.beginScope();
|
|
||||||
|
|
||||||
// TODO: create a jump map
|
// TODO: create a jump map
|
||||||
for (var ccase : cases) {
|
for (var ccase : cases) {
|
||||||
subtarget.add(Instruction.dup());
|
target.add(Instruction.dup());
|
||||||
ccase.value.compile(subtarget, true);
|
ccase.value.compile(target, true);
|
||||||
subtarget.add(Instruction.operation(Operation.EQUALS));
|
target.add(Instruction.operation(Operation.EQUALS));
|
||||||
caseToStatement.put(subtarget.temp(), ccase.statementI);
|
caseToStatement.put(target.temp(), ccase.statementI);
|
||||||
}
|
}
|
||||||
|
|
||||||
int start = subtarget.temp();
|
int start = target.temp();
|
||||||
var end = new DeferredIntSupplier();
|
var end = new DeferredIntSupplier();
|
||||||
|
|
||||||
LabelContext.getBreak(target.env).push(loc(), label, end);
|
LabelContext.getBreak(target.env).push(loc(), label, end);
|
||||||
for (var stm : body) {
|
for (var stm : body) {
|
||||||
statementToIndex.put(statementToIndex.size(), subtarget.size());
|
statementToIndex.put(statementToIndex.size(), target.size());
|
||||||
stm.compile(subtarget, false, BreakpointType.STEP_OVER);
|
stm.compile(target, false, BreakpointType.STEP_OVER);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int endI = target.size();
|
||||||
|
end.set(endI);
|
||||||
LabelContext.getBreak(target.env).pop(label);
|
LabelContext.getBreak(target.env).pop(label);
|
||||||
|
|
||||||
subtarget.endScope();
|
target.add(Instruction.discard());
|
||||||
|
if (pollute) target.add(Instruction.pushUndefined());
|
||||||
|
|
||||||
int endI = subtarget.size();
|
if (defaultI < 0 || defaultI >= body.length) target.set(start, Instruction.jmp(endI - start));
|
||||||
end.set(endI);
|
else target.set(start, Instruction.jmp(statementToIndex.get(defaultI) - start));
|
||||||
subtarget.add(Instruction.discard());
|
|
||||||
if (pollute) subtarget.add(Instruction.pushUndefined());
|
|
||||||
|
|
||||||
if (defaultI < 0 || defaultI >= body.length) subtarget.set(start, Instruction.jmp(endI - start));
|
|
||||||
else subtarget.set(start, Instruction.jmp(statementToIndex.get(defaultI) - start));
|
|
||||||
|
|
||||||
for (var el : caseToStatement.entrySet()) {
|
for (var el : caseToStatement.entrySet()) {
|
||||||
var i = statementToIndex.get(el.getValue());
|
var i = statementToIndex.get(el.getValue());
|
||||||
if (i == null) i = endI;
|
if (i == null) i = endI;
|
||||||
subtarget.set(el.getKey(), Instruction.jmpIf(i - el.getKey()));
|
target.set(el.getKey(), Instruction.jmpIf(i - el.getKey()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,6 @@ import me.topchetoeu.jscript.compilation.DeferredIntSupplier;
|
|||||||
import me.topchetoeu.jscript.compilation.JavaScript;
|
import me.topchetoeu.jscript.compilation.JavaScript;
|
||||||
import me.topchetoeu.jscript.compilation.LabelContext;
|
import me.topchetoeu.jscript.compilation.LabelContext;
|
||||||
import me.topchetoeu.jscript.compilation.Node;
|
import me.topchetoeu.jscript.compilation.Node;
|
||||||
import me.topchetoeu.jscript.compilation.scope.Variable;
|
|
||||||
|
|
||||||
public class TryNode extends Node {
|
public class TryNode extends Node {
|
||||||
public final CompoundNode tryBody;
|
public final CompoundNode tryBody;
|
||||||
@ -42,17 +41,11 @@ public class TryNode extends Node {
|
|||||||
catchStart = target.size() - start;
|
catchStart = target.size() - start;
|
||||||
|
|
||||||
if (captureName != null) {
|
if (captureName != null) {
|
||||||
var subtarget = target.subtarget();
|
var catchVar = target.scope.defineCatch(captureName);
|
||||||
subtarget.beginScope();
|
target.add(Instruction.loadError());
|
||||||
subtarget.scope.singleEntry = true;
|
target.add(catchVar.index().toInit());
|
||||||
|
catchBody.compile(target, false);
|
||||||
var catchVar = subtarget.scope.defineStrict(new Variable(captureName, false), catchBody.loc());
|
target.scope.undefineCatch();
|
||||||
subtarget.add(Instruction.loadError());
|
|
||||||
subtarget.add(catchVar.index().toInit());
|
|
||||||
catchBody.compile(subtarget, false);
|
|
||||||
subtarget.endScope();
|
|
||||||
|
|
||||||
subtarget.scope.end();
|
|
||||||
}
|
}
|
||||||
else catchBody.compile(target, false);
|
else catchBody.compile(target, false);
|
||||||
|
|
||||||
|
@ -30,10 +30,10 @@ public class WhileNode extends Node {
|
|||||||
|
|
||||||
LabelContext.pushLoop(target.env, loc(), label, end, start);
|
LabelContext.pushLoop(target.env, loc(), label, end, start);
|
||||||
CompoundNode.compileMultiEntry(body, target, false, BreakpointType.STEP_OVER);
|
CompoundNode.compileMultiEntry(body, target, false, BreakpointType.STEP_OVER);
|
||||||
LabelContext.popLoop(target.env, label);
|
|
||||||
|
|
||||||
var endI = target.size();
|
var endI = target.size();
|
||||||
end.set(endI + 1);
|
end.set(endI + 1);
|
||||||
|
LabelContext.popLoop(target.env, label);
|
||||||
|
|
||||||
target.add(Instruction.jmp(start - end.getAsInt()));
|
target.add(Instruction.jmp(start - end.getAsInt()));
|
||||||
target.set(mid, Instruction.jmpIfNot(end.getAsInt() - mid + 1));
|
target.set(mid, Instruction.jmpIfNot(end.getAsInt() - mid + 1));
|
||||||
|
Loading…
Reference in New Issue
Block a user