feat: implement ES6 variables and rest args
This commit is contained in:
parent
4cbc108686
commit
506726fd76
@ -3,12 +3,11 @@ package me.topchetoeu.jscript.common;
|
|||||||
public class FunctionBody {
|
public class FunctionBody {
|
||||||
public final FunctionBody[] children;
|
public final FunctionBody[] children;
|
||||||
public final Instruction[] instructions;
|
public final Instruction[] instructions;
|
||||||
public final int localsN, capturesN, argsN, length;
|
public final int localsN, capturesN, length;
|
||||||
|
|
||||||
public FunctionBody(int localsN, int capturesN, int length, int argsN, Instruction[] instructions, FunctionBody[] children) {
|
public FunctionBody(int localsN, int capturesN, int length, Instruction[] instructions, FunctionBody[] children) {
|
||||||
this.children = children;
|
this.children = children;
|
||||||
this.length = length;
|
this.length = length;
|
||||||
this.argsN = argsN;
|
|
||||||
this.localsN = localsN;
|
this.localsN = localsN;
|
||||||
this.capturesN = capturesN;
|
this.capturesN = capturesN;
|
||||||
this.instructions = instructions;
|
this.instructions = instructions;
|
||||||
|
@ -1,16 +1,15 @@
|
|||||||
package me.topchetoeu.jscript.common;
|
package me.topchetoeu.jscript.common;
|
||||||
|
|
||||||
// import java.io.DataInputStream;
|
|
||||||
// import java.io.DataOutputStream;
|
|
||||||
// import java.io.IOException;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.function.IntFunction;
|
||||||
|
import java.util.function.IntSupplier;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
|
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
|
||||||
|
|
||||||
public class Instruction {
|
public class Instruction {
|
||||||
public static enum Type {
|
public static enum Type {
|
||||||
NOP(0x00),
|
RETURN(0x00),
|
||||||
RETURN(0x01),
|
NOP(0x01),
|
||||||
THROW(0x02),
|
THROW(0x02),
|
||||||
THROW_SYNTAX(0x03),
|
THROW_SYNTAX(0x03),
|
||||||
DELETE(0x04),
|
DELETE(0x04),
|
||||||
@ -41,10 +40,18 @@ public class Instruction {
|
|||||||
|
|
||||||
LOAD_VAR(0x40),
|
LOAD_VAR(0x40),
|
||||||
LOAD_MEMBER(0x41),
|
LOAD_MEMBER(0x41),
|
||||||
LOAD_ARGS(0x42),
|
LOAD_MEMBER_INT(0x42),
|
||||||
LOAD_THIS(0x43),
|
LOAD_MEMBER_STR(0x43),
|
||||||
|
|
||||||
|
LOAD_ARGS(0x44),
|
||||||
|
LOAD_REST_ARGS(0x45),
|
||||||
|
LOAD_CALLEE(0x46),
|
||||||
|
LOAD_THIS(0x47),
|
||||||
|
|
||||||
STORE_VAR(0x48),
|
STORE_VAR(0x48),
|
||||||
STORE_MEMBER(0x49),
|
STORE_MEMBER(0x49),
|
||||||
|
STORE_MEMBER_INT(0x4A),
|
||||||
|
STORE_MEMBER_STR(0x4B),
|
||||||
|
|
||||||
DEF_PROP(0x50),
|
DEF_PROP(0x50),
|
||||||
KEYS(0x51),
|
KEYS(0x51),
|
||||||
@ -286,6 +293,7 @@ public class Instruction {
|
|||||||
public static Instruction callNew(int argn) {
|
public static Instruction callNew(int argn) {
|
||||||
return new Instruction(Type.CALL_NEW, argn, "");
|
return new Instruction(Type.CALL_NEW, argn, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Instruction jmp(int offset) {
|
public static Instruction jmp(int offset) {
|
||||||
return new Instruction(Type.JMP, offset);
|
return new Instruction(Type.JMP, offset);
|
||||||
}
|
}
|
||||||
@ -296,6 +304,17 @@ public class Instruction {
|
|||||||
return new Instruction(Type.JMP_IFN, offset);
|
return new Instruction(Type.JMP_IFN, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IntFunction<Instruction> jmp(IntSupplier pos) {
|
||||||
|
return i -> new Instruction(Type.JMP, pos.getAsInt() - i);
|
||||||
|
}
|
||||||
|
public static IntFunction<Instruction> jmpIf(IntSupplier pos) {
|
||||||
|
return i -> new Instruction(Type.JMP_IF, pos.getAsInt() - i);
|
||||||
|
}
|
||||||
|
public static IntFunction<Instruction> jmpIfNot(IntSupplier pos) {
|
||||||
|
return i -> new Instruction(Type.JMP_IFN, pos.getAsInt() - i);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static Instruction pushUndefined() {
|
public static Instruction pushUndefined() {
|
||||||
return new Instruction(Type.PUSH_UNDEFINED);
|
return new Instruction(Type.PUSH_UNDEFINED);
|
||||||
}
|
}
|
||||||
@ -313,7 +332,7 @@ public class Instruction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Instruction globDef(String name) {
|
public static Instruction globDef(String name) {
|
||||||
return new Instruction(Type.GLOB_GET, name);
|
return new Instruction(Type.GLOB_DEF, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Instruction globGet(String name) {
|
public static Instruction globGet(String name) {
|
||||||
@ -332,6 +351,12 @@ public class Instruction {
|
|||||||
public static Instruction loadArgs() {
|
public static Instruction loadArgs() {
|
||||||
return new Instruction(Type.LOAD_ARGS);
|
return new Instruction(Type.LOAD_ARGS);
|
||||||
}
|
}
|
||||||
|
public static Instruction loadRestArgs(int offset) {
|
||||||
|
return new Instruction(Type.LOAD_REST_ARGS, offset);
|
||||||
|
}
|
||||||
|
public static Instruction loadCallee() {
|
||||||
|
return new Instruction(Type.LOAD_CALLEE);
|
||||||
|
}
|
||||||
public static Instruction loadGlob() {
|
public static Instruction loadGlob() {
|
||||||
return new Instruction(Type.LOAD_GLOB);
|
return new Instruction(Type.LOAD_GLOB);
|
||||||
}
|
}
|
||||||
@ -341,17 +366,26 @@ public class Instruction {
|
|||||||
public static Instruction loadMember() {
|
public static Instruction loadMember() {
|
||||||
return new Instruction(Type.LOAD_MEMBER);
|
return new Instruction(Type.LOAD_MEMBER);
|
||||||
}
|
}
|
||||||
|
public static Instruction loadMember(int member) {
|
||||||
|
return new Instruction(Type.LOAD_MEMBER_INT, member);
|
||||||
|
}
|
||||||
|
public static Instruction loadMember(String member) {
|
||||||
|
return new Instruction(Type.LOAD_MEMBER_STR, member);
|
||||||
|
}
|
||||||
|
|
||||||
public static Instruction loadRegex(String pattern, String flags) {
|
public static Instruction loadRegex(String pattern, String flags) {
|
||||||
return new Instruction(Type.LOAD_REGEX, pattern, flags);
|
return new Instruction(Type.LOAD_REGEX, pattern, flags);
|
||||||
}
|
}
|
||||||
public static Instruction loadFunc(int id, String name, int[] captures) {
|
public static Instruction loadFunc(int id, boolean callable, boolean constructible, boolean captureThis, String name, int[] captures) {
|
||||||
if (name == null) name = "";
|
if (name == null) name = "";
|
||||||
|
|
||||||
var args = new Object[2 + captures.length];
|
var args = new Object[5 + captures.length];
|
||||||
args[0] = id;
|
args[0] = id;
|
||||||
args[1] = name;
|
args[1] = name;
|
||||||
for (var i = 0; i < captures.length; i++) args[i + 2] = captures[i];
|
args[2] = callable;
|
||||||
|
args[3] = constructible;
|
||||||
|
args[4] = captureThis;
|
||||||
|
for (var i = 0; i < captures.length; i++) args[i + 5] = captures[i];
|
||||||
return new Instruction(Type.LOAD_FUNC, args);
|
return new Instruction(Type.LOAD_FUNC, args);
|
||||||
}
|
}
|
||||||
public static Instruction loadObj() {
|
public static Instruction loadObj() {
|
||||||
@ -373,12 +407,28 @@ public class Instruction {
|
|||||||
public static Instruction storeVar(int i, boolean keep) {
|
public static Instruction storeVar(int i, boolean keep) {
|
||||||
return new Instruction(Type.STORE_VAR, i, keep);
|
return new Instruction(Type.STORE_VAR, i, keep);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Instruction storeMember() {
|
public static Instruction storeMember() {
|
||||||
return new Instruction(Type.STORE_MEMBER, false);
|
return new Instruction(Type.STORE_MEMBER, false);
|
||||||
}
|
}
|
||||||
public static Instruction storeMember(boolean keep) {
|
public static Instruction storeMember(boolean keep) {
|
||||||
return new Instruction(Type.STORE_MEMBER, keep);
|
return new Instruction(Type.STORE_MEMBER, keep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Instruction storeMember(String key) {
|
||||||
|
return new Instruction(Type.STORE_MEMBER_STR, key, false);
|
||||||
|
}
|
||||||
|
public static Instruction storeMember(String key, boolean keep) {
|
||||||
|
return new Instruction(Type.STORE_MEMBER_STR, key, keep);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Instruction storeMember(int key) {
|
||||||
|
return new Instruction(Type.STORE_MEMBER_INT, key, false);
|
||||||
|
}
|
||||||
|
public static Instruction storeMember(int key, boolean keep) {
|
||||||
|
return new Instruction(Type.STORE_MEMBER_STR, key, keep);
|
||||||
|
}
|
||||||
|
|
||||||
public static Instruction discard() {
|
public static Instruction discard() {
|
||||||
return new Instruction(Type.DISCARD);
|
return new Instruction(Type.DISCARD);
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ public class CompoundNode extends Node {
|
|||||||
public void compile(CompileResult target, boolean pollute, boolean alloc, BreakpointType type) {
|
public void compile(CompileResult target, boolean pollute, boolean alloc, BreakpointType type) {
|
||||||
List<Node> statements = new ArrayList<Node>();
|
List<Node> statements = new ArrayList<Node>();
|
||||||
|
|
||||||
var subtarget = target.subtarget();
|
var subtarget = alloc ? target.subtarget() : target;
|
||||||
if (alloc) subtarget.add(i -> Instruction.stackAlloc(subtarget.scope.allocCount()));
|
if (alloc) subtarget.add(i -> Instruction.stackAlloc(subtarget.scope.allocCount()));
|
||||||
|
|
||||||
for (var stm : this.statements) {
|
for (var stm : this.statements) {
|
||||||
@ -41,8 +41,10 @@ public class CompoundNode extends Node {
|
|||||||
else stm.compile(subtarget, polluted = pollute, BreakpointType.STEP_OVER);
|
else stm.compile(subtarget, polluted = pollute, BreakpointType.STEP_OVER);
|
||||||
}
|
}
|
||||||
|
|
||||||
subtarget.scope.end();
|
if (alloc) {
|
||||||
if (alloc) subtarget.add(Instruction.stackFree(subtarget.scope.allocCount()));
|
subtarget.scope.end();
|
||||||
|
subtarget.add(Instruction.stackFree(subtarget.scope.allocCount()));
|
||||||
|
}
|
||||||
|
|
||||||
if (!polluted && pollute) {
|
if (!polluted && pollute) {
|
||||||
target.add(Instruction.pushUndefined());
|
target.add(Instruction.pushUndefined());
|
||||||
|
@ -0,0 +1,73 @@
|
|||||||
|
package me.topchetoeu.jscript.compilation;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Instruction;
|
||||||
|
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
||||||
|
import me.topchetoeu.jscript.common.parsing.Location;
|
||||||
|
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.control.ReturnNode;
|
||||||
|
|
||||||
|
public class FunctionArrowNode extends FunctionNode {
|
||||||
|
@Override public String name() { return null; }
|
||||||
|
|
||||||
|
@Override protected void compileLoadFunc(CompileResult target, int id, int[] captures, String name) {
|
||||||
|
target.add(Instruction.loadFunc(id, true, false, true, null, captures));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
|
||||||
|
compile(target, pollute, false, name, null, bp);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FunctionArrowNode(Location loc, Location end, Parameters params, Node body) {
|
||||||
|
super(loc, end, params, expToBody(body));
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ParseRes<FunctionArrowNode> parse(Source src, int i) {
|
||||||
|
var n = Parsing.skipEmpty(src, i);
|
||||||
|
var loc = src.loc(i + n);
|
||||||
|
|
||||||
|
Parameters params;
|
||||||
|
|
||||||
|
if (src.is(i + n, "(")) {
|
||||||
|
var paramsRes = JavaScript.parseParameters(src, i + n);
|
||||||
|
if (!paramsRes.isSuccess()) return paramsRes.chainError();
|
||||||
|
n += paramsRes.n;
|
||||||
|
n += Parsing.skipEmpty(src, i + n);
|
||||||
|
|
||||||
|
params = paramsRes.result;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var singleParam = Parsing.parseIdentifier(src, i + n);
|
||||||
|
if (!singleParam.isSuccess()) return ParseRes.failed();
|
||||||
|
|
||||||
|
var paramLoc = src.loc(i + n);
|
||||||
|
n += singleParam.n;
|
||||||
|
n += Parsing.skipEmpty(src, i + n);
|
||||||
|
|
||||||
|
params = new Parameters(List.of(new Parameter(paramLoc, singleParam.result, null)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!src.is(i + n, "=>")) return ParseRes.failed();
|
||||||
|
n += 2;
|
||||||
|
|
||||||
|
ParseRes<Node> body = ParseRes.first(src, i + n,
|
||||||
|
(s, j) -> JavaScript.parseExpression(s, j, 2),
|
||||||
|
CompoundNode::parse
|
||||||
|
);
|
||||||
|
if (!body.isSuccess()) return body.chainError(src.loc(i + n), "Expected an expression or a compount statement after '=>'");
|
||||||
|
n += body.n;
|
||||||
|
|
||||||
|
return ParseRes.res(new FunctionArrowNode(
|
||||||
|
loc, src.loc(i + n - 1),
|
||||||
|
params, body.result
|
||||||
|
), n);
|
||||||
|
}
|
||||||
|
}
|
@ -35,11 +35,11 @@ public abstract class FunctionNode extends Node {
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
protected void compileLoadFunc(CompileResult target, int[] captures, String name) {
|
protected void compileLoadFunc(CompileResult target, int id, int[] captures, String name) {
|
||||||
target.add(Instruction.loadFunc(target.children.size(), name, captures));
|
target.add(Instruction.loadFunc(id, true, true, false, name, captures));
|
||||||
}
|
}
|
||||||
|
|
||||||
private CompileResult compileBody(CompileResult target, String name, boolean storeSelf, boolean pollute, BreakpointType bp) {
|
private CompileResult compileBody(CompileResult target, boolean hasArgs, String name, String selfName, boolean pollute, BreakpointType bp) {
|
||||||
var env = target.env.child()
|
var env = target.env.child()
|
||||||
.remove(LabelContext.BREAK_CTX)
|
.remove(LabelContext.BREAK_CTX)
|
||||||
.remove(LabelContext.CONTINUE_CTX);
|
.remove(LabelContext.CONTINUE_CTX);
|
||||||
@ -47,43 +47,74 @@ public abstract class FunctionNode extends Node {
|
|||||||
var funcScope = new FunctionScope(target.scope);
|
var funcScope = new FunctionScope(target.scope);
|
||||||
var subtarget = new CompileResult(env, new LocalScope(funcScope));
|
var subtarget = new CompileResult(env, new LocalScope(funcScope));
|
||||||
|
|
||||||
for (var param : params.params) {
|
subtarget.length = params.params.size();
|
||||||
// TODO: Implement default values
|
|
||||||
// TODO: Implement argument location
|
|
||||||
if (funcScope.hasArg(param.name)) throw new SyntaxException(param.loc, "Duplicate parameter name not allowed");
|
|
||||||
var i = funcScope.defineParam(param.name, param.loc);
|
|
||||||
|
|
||||||
if (param.node != null) {
|
if (hasArgs || params.params.size() > 0) subtarget.add(Instruction.loadArgs());
|
||||||
var end = new DeferredIntSupplier();
|
|
||||||
|
|
||||||
subtarget.add(_i -> Instruction.loadVar(i.index()));
|
if (hasArgs) {
|
||||||
subtarget.add(Instruction.pushUndefined());
|
var argsVar = funcScope.defineParam("arguments", true, loc());
|
||||||
subtarget.add(Instruction.operation(Operation.EQUALS));
|
subtarget.add(_i -> Instruction.storeVar(argsVar.index(), params.params.size() > 0));
|
||||||
subtarget.add(_i -> Instruction.jmpIfNot(end.getAsInt() - _i));
|
}
|
||||||
param.node.compile(subtarget, pollute);
|
|
||||||
subtarget.add(_i -> Instruction.storeVar(i.index()));
|
|
||||||
|
|
||||||
end.set(subtarget.size());
|
if (params.params.size() > 0) {
|
||||||
|
if (params.params.size() > 1) subtarget.add(Instruction.dup(params.params.size() - 1));
|
||||||
|
var i = 0;
|
||||||
|
|
||||||
|
for (var param : params.params) {
|
||||||
|
if (funcScope.hasArg(param.name)) throw new SyntaxException(param.loc, "Duplicate parameter name not allowed");
|
||||||
|
if (!JavaScript.checkVarName(param.name)) {
|
||||||
|
throw new SyntaxException(param.loc, String.format("Unexpected identifier '%s'", param.name));
|
||||||
|
}
|
||||||
|
var varI = funcScope.defineParam(param.name, false, param.loc);
|
||||||
|
|
||||||
|
subtarget.add(Instruction.loadMember(i++));
|
||||||
|
|
||||||
|
if (param.node != null) {
|
||||||
|
var end = new DeferredIntSupplier();
|
||||||
|
|
||||||
|
subtarget.add(Instruction.dup());
|
||||||
|
subtarget.add(Instruction.pushUndefined());
|
||||||
|
subtarget.add(Instruction.operation(Operation.EQUALS));
|
||||||
|
subtarget.add(Instruction.jmpIfNot(end));
|
||||||
|
subtarget.add(Instruction.discard());
|
||||||
|
param.node.compile(subtarget, pollute);
|
||||||
|
|
||||||
|
end.set(subtarget.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
subtarget.add(Instruction.storeVar(varI.index()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (params.restName != null) {
|
||||||
|
if (funcScope.hasArg(params.restName)) throw new SyntaxException(params.restLocation, "Duplicate parameter name not allowed");
|
||||||
|
var restVar = funcScope.defineParam(params.restName, true, params.restLocation);
|
||||||
|
subtarget.add(Instruction.loadRestArgs(params.params.size()));
|
||||||
|
subtarget.add(_i -> Instruction.storeVar(restVar.index()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selfName != null && !funcScope.hasArg(name)) {
|
||||||
|
var i = funcScope.defineParam(selfName, true, end);
|
||||||
|
|
||||||
|
subtarget.add(Instruction.loadCallee());
|
||||||
|
subtarget.add(_i -> Instruction.storeVar(i.index(), false));
|
||||||
|
}
|
||||||
|
|
||||||
body.resolve(subtarget);
|
body.resolve(subtarget);
|
||||||
body.compile(subtarget, false, false, BreakpointType.NONE);
|
body.compile(subtarget, false, false, BreakpointType.NONE);
|
||||||
|
|
||||||
subtarget.length = params.length;
|
|
||||||
subtarget.assignN = params.params.size();
|
|
||||||
subtarget.scope.end();
|
subtarget.scope.end();
|
||||||
funcScope.end();
|
funcScope.end();
|
||||||
|
|
||||||
if (pollute) compileLoadFunc(target, funcScope.getCaptureIndices(), name);
|
if (pollute) compileLoadFunc(target, target.children.size(), funcScope.getCaptureIndices(), name);
|
||||||
|
|
||||||
return target.addChild(subtarget);
|
return target.addChild(subtarget);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void compile(CompileResult target, boolean pollute, boolean storeSelf, String name, BreakpointType bp) {
|
public void compile(CompileResult target, boolean pollute, boolean hasArgs, String name, String selfName, BreakpointType bp) {
|
||||||
if (this.name() != null) name = this.name();
|
if (this.name() != null) name = this.name();
|
||||||
|
|
||||||
compileBody(target, name, storeSelf, pollute, bp);
|
compileBody(target, hasArgs, name, selfName, pollute, bp);
|
||||||
}
|
}
|
||||||
public abstract void compile(CompileResult target, boolean pollute, String name, BreakpointType bp);
|
public abstract void compile(CompileResult target, boolean pollute, String name, BreakpointType bp);
|
||||||
public void compile(CompileResult target, boolean pollute, String name) {
|
public void compile(CompileResult target, boolean pollute, String name) {
|
||||||
|
@ -14,7 +14,7 @@ public class FunctionStatementNode extends FunctionNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
|
@Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
|
||||||
compile(target, true, false, this.name, bp);
|
compile(target, true, true, name, this.name, bp);
|
||||||
target.add(VariableNode.toSet(target, end, this.name, pollute, true));
|
target.add(VariableNode.toSet(target, end, this.name, pollute, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ public class FunctionValueNode extends FunctionNode {
|
|||||||
@Override public String name() { return name; }
|
@Override public String name() { return name; }
|
||||||
|
|
||||||
@Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
|
@Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
|
||||||
compile(target, pollute, true, name, bp);
|
compile(target, pollute, true, name, null, bp);
|
||||||
}
|
}
|
||||||
|
|
||||||
public FunctionValueNode(Location loc, Location end, Parameters params, CompoundNode body, String name) {
|
public FunctionValueNode(Location loc, Location end, Parameters params, CompoundNode body, String name) {
|
||||||
|
@ -26,9 +26,7 @@ import me.topchetoeu.jscript.compilation.control.TryNode;
|
|||||||
import me.topchetoeu.jscript.compilation.control.WhileNode;
|
import me.topchetoeu.jscript.compilation.control.WhileNode;
|
||||||
import me.topchetoeu.jscript.compilation.scope.GlobalScope;
|
import me.topchetoeu.jscript.compilation.scope.GlobalScope;
|
||||||
import me.topchetoeu.jscript.compilation.scope.LocalScope;
|
import me.topchetoeu.jscript.compilation.scope.LocalScope;
|
||||||
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.GlobalThisNode;
|
|
||||||
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.ThisNode;
|
import me.topchetoeu.jscript.compilation.values.ThisNode;
|
||||||
@ -45,7 +43,20 @@ import me.topchetoeu.jscript.compilation.values.operations.OperationNode;
|
|||||||
import me.topchetoeu.jscript.compilation.values.operations.TypeofNode;
|
import me.topchetoeu.jscript.compilation.values.operations.TypeofNode;
|
||||||
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
|
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
|
||||||
|
|
||||||
public class JavaScript {
|
public final class JavaScript {
|
||||||
|
public static enum DeclarationType {
|
||||||
|
VAR(false, false),
|
||||||
|
CONST(true, true),
|
||||||
|
LET(true, false);
|
||||||
|
|
||||||
|
public final boolean strict, readonly;
|
||||||
|
|
||||||
|
private DeclarationType(boolean strict, boolean readonly) {
|
||||||
|
this.strict = strict;
|
||||||
|
this.readonly = readonly;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static final Set<String> reserved = Set.of(
|
static final Set<String> reserved = Set.of(
|
||||||
"true", "false", "void", "null", "this", "if", "else", "try", "catch",
|
"true", "false", "void", "null", "this", "if", "else", "try", "catch",
|
||||||
"finally", "for", "do", "while", "switch", "case", "default", "new",
|
"finally", "for", "do", "while", "switch", "case", "default", "new",
|
||||||
@ -84,6 +95,7 @@ public class JavaScript {
|
|||||||
ChangeNode::parsePrefixIncrease,
|
ChangeNode::parsePrefixIncrease,
|
||||||
OperationNode::parsePrefix,
|
OperationNode::parsePrefix,
|
||||||
ArrayNode::parse,
|
ArrayNode::parse,
|
||||||
|
FunctionArrowNode::parse,
|
||||||
JavaScript::parseParens,
|
JavaScript::parseParens,
|
||||||
CallNode::parseNew,
|
CallNode::parseNew,
|
||||||
TypeofNode::parse,
|
TypeofNode::parse,
|
||||||
@ -103,11 +115,11 @@ public class JavaScript {
|
|||||||
|
|
||||||
if (id.result.equals("true")) return ParseRes.res(new BoolNode(loc, true), n);
|
if (id.result.equals("true")) return ParseRes.res(new BoolNode(loc, true), n);
|
||||||
if (id.result.equals("false")) return ParseRes.res(new BoolNode(loc, false), n);
|
if (id.result.equals("false")) return ParseRes.res(new BoolNode(loc, false), n);
|
||||||
if (id.result.equals("undefined")) return ParseRes.res(new DiscardNode(loc, null), n);
|
// if (id.result.equals("undefined")) return ParseRes.res(new DiscardNode(loc, null), n);
|
||||||
if (id.result.equals("null")) return ParseRes.res(new NullNode(loc), n);
|
if (id.result.equals("null")) return ParseRes.res(new NullNode(loc), n);
|
||||||
if (id.result.equals("this")) return ParseRes.res(new ThisNode(loc), n);
|
if (id.result.equals("this")) return ParseRes.res(new ThisNode(loc), n);
|
||||||
if (id.result.equals("arguments")) return ParseRes.res(new ArgumentsNode(loc), n);
|
// if (id.result.equals("arguments")) return ParseRes.res(new ArgumentsNode(loc), n);
|
||||||
if (id.result.equals("globalThis")) return ParseRes.res(new GlobalThisNode(loc), n);
|
// if (id.result.equals("globalThis")) return ParseRes.res(new GlobalThisNode(loc), n);
|
||||||
|
|
||||||
return ParseRes.failed();
|
return ParseRes.failed();
|
||||||
}
|
}
|
||||||
@ -228,10 +240,25 @@ public class JavaScript {
|
|||||||
while (true) {
|
while (true) {
|
||||||
n += Parsing.skipEmpty(src, i + n);
|
n += Parsing.skipEmpty(src, i + n);
|
||||||
|
|
||||||
|
if (src.is(i + n, "...")) {
|
||||||
|
n += 3;
|
||||||
|
var restLoc = src.loc(i);
|
||||||
|
|
||||||
|
var restName = Parsing.parseIdentifier(src, i + n);
|
||||||
|
if (!restName.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a rest parameter");
|
||||||
|
n += restName.n;
|
||||||
|
n += Parsing.skipEmpty(src, i + n);
|
||||||
|
|
||||||
|
if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected an end of parameters list after rest parameter");
|
||||||
|
n++;
|
||||||
|
|
||||||
|
return ParseRes.res(new Parameters(params, restName.result, restLoc), n);
|
||||||
|
}
|
||||||
|
|
||||||
var paramLoc = src.loc(i);
|
var paramLoc = src.loc(i);
|
||||||
|
|
||||||
var name = Parsing.parseIdentifier(src, i + n);
|
var name = Parsing.parseIdentifier(src, i + n);
|
||||||
if (!name.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected an argument or a closing brace");
|
if (!name.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a parameter or a closing brace");
|
||||||
n += name.n;
|
n += name.n;
|
||||||
n += Parsing.skipEmpty(src, i + n);
|
n += Parsing.skipEmpty(src, i + n);
|
||||||
|
|
||||||
@ -239,7 +266,7 @@ public class JavaScript {
|
|||||||
n++;
|
n++;
|
||||||
|
|
||||||
var val = parseExpression(src, i + n, 2);
|
var val = parseExpression(src, i + n, 2);
|
||||||
if (!val.isSuccess()) return openParen.chainError(src.loc(i + n), "Expected a default value");
|
if (!val.isSuccess()) return openParen.chainError(src.loc(i + n), "Expected a parameter default value");
|
||||||
n += val.n;
|
n += val.n;
|
||||||
n += Parsing.skipEmpty(src, i + n);
|
n += Parsing.skipEmpty(src, i + n);
|
||||||
|
|
||||||
@ -251,6 +278,7 @@ public class JavaScript {
|
|||||||
n++;
|
n++;
|
||||||
n += Parsing.skipEmpty(src, i + n);
|
n += Parsing.skipEmpty(src, i + n);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (src.is(i + n, ")")) {
|
if (src.is(i + n, ")")) {
|
||||||
n++;
|
n++;
|
||||||
break;
|
break;
|
||||||
@ -261,6 +289,17 @@ public class JavaScript {
|
|||||||
return ParseRes.res(new Parameters(params), n);
|
return ParseRes.res(new Parameters(params), n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ParseRes<DeclarationType> parseDeclarationType(Source src, int i) {
|
||||||
|
var res = Parsing.parseIdentifier(src, i);
|
||||||
|
if (!res.isSuccess()) return res.chainError();
|
||||||
|
|
||||||
|
if (res.result.equals("var")) return ParseRes.res(DeclarationType.VAR, 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();
|
||||||
|
}
|
||||||
|
|
||||||
public static Node[] parse(Environment env, Filename filename, String raw) {
|
public static Node[] parse(Environment env, Filename filename, String raw) {
|
||||||
var src = new Source(env, filename, raw);
|
var src = new Source(env, filename, raw);
|
||||||
var list = new ArrayList<Node>();
|
var list = new ArrayList<Node>();
|
||||||
@ -289,17 +328,20 @@ public class JavaScript {
|
|||||||
public static CompileResult compile(Environment env, Node ...statements) {
|
public static CompileResult compile(Environment env, Node ...statements) {
|
||||||
var target = new CompileResult(env, new LocalScope(new GlobalScope()));
|
var target = new CompileResult(env, new LocalScope(new GlobalScope()));
|
||||||
var stm = new CompoundNode(null, statements);
|
var stm = new CompoundNode(null, statements);
|
||||||
|
var argsI = target.scope.defineStrict("arguments", true, null);
|
||||||
|
target.add(Instruction.loadArgs());
|
||||||
|
target.add(_i -> Instruction.storeVar(argsI.index()));
|
||||||
|
|
||||||
try {
|
// try {
|
||||||
stm.resolve(target);
|
stm.resolve(target);
|
||||||
stm.compile(target, true, false, BreakpointType.NONE);
|
stm.compile(target, true, false, BreakpointType.NONE);
|
||||||
// FunctionNode.checkBreakAndCont(target, 0);
|
// FunctionNode.checkBreakAndCont(target, 0);
|
||||||
}
|
// }
|
||||||
catch (SyntaxException e) {
|
// catch (SyntaxException e) {
|
||||||
target = new CompileResult(env, new LocalScope(new GlobalScope()));
|
// target = new CompileResult(env, new LocalScope(new GlobalScope()));
|
||||||
|
|
||||||
target.add(Instruction.throwSyntax(e)).setLocation(stm.loc());
|
// target.add(Instruction.throwSyntax(e)).setLocation(stm.loc());
|
||||||
}
|
// }
|
||||||
|
|
||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
package me.topchetoeu.jscript.compilation;
|
package me.topchetoeu.jscript.compilation;
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
|
||||||
|
import me.topchetoeu.jscript.common.parsing.Location;
|
||||||
|
|
||||||
public final class Parameters {
|
public final class Parameters {
|
||||||
public final int length;
|
public final int length;
|
||||||
public final List<Parameter> params;
|
public final List<Parameter> params;
|
||||||
public final Set<String> names;
|
public final String restName;
|
||||||
|
public final Location restLocation;
|
||||||
|
|
||||||
public Parameters(List<Parameter> params) {
|
public Parameters(List<Parameter> params, String restName, Location restLocation) {
|
||||||
this.names = new HashSet<>();
|
|
||||||
var len = params.size();
|
var len = params.size();
|
||||||
|
|
||||||
for (var i = params.size() - 1; i >= 0; i--) {
|
for (var i = params.size() - 1; i >= 0; i--) {
|
||||||
@ -18,11 +18,12 @@ public final class Parameters {
|
|||||||
len--;
|
len--;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var param : params) {
|
|
||||||
this.names.add(param.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.params = params;
|
this.params = params;
|
||||||
this.length = len;
|
this.length = len;
|
||||||
|
this.restName = restName;
|
||||||
|
this.restLocation = restLocation;
|
||||||
|
}
|
||||||
|
public Parameters(List<Parameter> params) {
|
||||||
|
this(params, null, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import me.topchetoeu.jscript.common.parsing.Location;
|
|||||||
import me.topchetoeu.jscript.common.parsing.ParseRes;
|
import me.topchetoeu.jscript.common.parsing.ParseRes;
|
||||||
import me.topchetoeu.jscript.common.parsing.Parsing;
|
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.JavaScript.DeclarationType;
|
||||||
import me.topchetoeu.jscript.compilation.values.VariableNode;
|
import me.topchetoeu.jscript.compilation.values.VariableNode;
|
||||||
|
|
||||||
public class VariableDeclareNode extends Node {
|
public class VariableDeclareNode extends Node {
|
||||||
@ -25,51 +26,56 @@ public class VariableDeclareNode extends Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final List<Pair> values;
|
public final List<Pair> values;
|
||||||
|
public final DeclarationType declType;
|
||||||
|
|
||||||
@Override public void resolve(CompileResult target) {
|
@Override public void resolve(CompileResult target) {
|
||||||
for (var entry : values) {
|
if (!declType.strict) {
|
||||||
target.scope.define(entry.name, false, entry.location);
|
for (var entry : values) {
|
||||||
|
target.scope.define(entry.name, false, entry.location);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// let a = 10, b = "test"; var c = () => a + b
|
||||||
@Override public void compile(CompileResult target, boolean pollute) {
|
@Override public void compile(CompileResult target, boolean pollute) {
|
||||||
for (var entry : values) {
|
for (var entry : values) {
|
||||||
if (entry.name == null) continue;
|
if (entry.name == null) continue;
|
||||||
|
if (declType.strict) target.scope.defineStrict(entry.name, declType.readonly, entry.location);
|
||||||
|
|
||||||
if (entry.value != null) {
|
if (entry.value != null) {
|
||||||
FunctionNode.compileWithName(entry.value, target, true, entry.name, BreakpointType.STEP_OVER);
|
FunctionNode.compileWithName(entry.value, target, true, entry.name, BreakpointType.STEP_OVER);
|
||||||
target.add(VariableNode.toSet(target, entry.location, entry.name, false, true));
|
target.add(VariableNode.toSet(target, entry.location, entry.name, false, true));
|
||||||
}
|
}
|
||||||
else {
|
else target.add(_i -> {
|
||||||
target.add(_i -> {
|
var i = target.scope.get(entry.name, true);
|
||||||
var i = target.scope.get(entry.name, true);
|
|
||||||
|
|
||||||
if (i == null) return Instruction.globDef(entry.name);
|
if (i == null) return Instruction.globDef(entry.name);
|
||||||
else return Instruction.nop();
|
else return Instruction.nop();
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pollute) target.add(Instruction.pushUndefined());
|
if (pollute) target.add(Instruction.pushUndefined());
|
||||||
}
|
}
|
||||||
|
|
||||||
public VariableDeclareNode(Location loc, List<Pair> values) {
|
public VariableDeclareNode(Location loc, DeclarationType declType, List<Pair> values) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.values = values;
|
this.values = values;
|
||||||
|
this.declType = declType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ParseRes<VariableDeclareNode> parse(Source src, int i) {
|
public static ParseRes<VariableDeclareNode> parse(Source src, int i) {
|
||||||
var n = Parsing.skipEmpty(src, i);
|
var n = Parsing.skipEmpty(src, i);
|
||||||
var loc = src.loc(i + n);
|
var loc = src.loc(i + n);
|
||||||
|
|
||||||
if (!Parsing.isIdentifier(src, i + n, "var")) return ParseRes.failed();
|
var declType = JavaScript.parseDeclarationType(src, i + n);
|
||||||
n += 3;
|
if (!declType.isSuccess()) return declType.chainError();
|
||||||
|
n += declType.n;
|
||||||
|
|
||||||
var res = new ArrayList<Pair>();
|
var res = new ArrayList<Pair>();
|
||||||
|
|
||||||
var end = JavaScript.parseStatementEnd(src, i + n);
|
var end = JavaScript.parseStatementEnd(src, i + n);
|
||||||
if (end.isSuccess()) {
|
if (end.isSuccess()) {
|
||||||
n += end.n;
|
n += end.n;
|
||||||
return ParseRes.res(new VariableDeclareNode(loc, res), n);
|
return ParseRes.res(new VariableDeclareNode(loc, declType.result, res), n);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
@ -83,6 +89,7 @@ public class VariableDeclareNode extends Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Node val = null;
|
Node val = null;
|
||||||
|
var endN = n;
|
||||||
n += Parsing.skipEmpty(src, i + n);
|
n += Parsing.skipEmpty(src, i + n);
|
||||||
|
|
||||||
if (src.is(i + n, "=")) {
|
if (src.is(i + n, "=")) {
|
||||||
@ -92,6 +99,7 @@ public class VariableDeclareNode extends Node {
|
|||||||
if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after '='");
|
if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after '='");
|
||||||
|
|
||||||
n += valRes.n;
|
n += valRes.n;
|
||||||
|
endN = n;
|
||||||
n += Parsing.skipEmpty(src, i + n);
|
n += Parsing.skipEmpty(src, i + n);
|
||||||
val = valRes.result;
|
val = valRes.result;
|
||||||
}
|
}
|
||||||
@ -103,11 +111,11 @@ public class VariableDeclareNode extends Node {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
end = JavaScript.parseStatementEnd(src, i + n);
|
end = JavaScript.parseStatementEnd(src, i + endN);
|
||||||
|
|
||||||
if (end.isSuccess()) {
|
if (end.isSuccess()) {
|
||||||
n += end.n;
|
n += end.n + endN - n;
|
||||||
return ParseRes.res(new VariableDeclareNode(loc, res), n);
|
return ParseRes.res(new VariableDeclareNode(loc, declType.result, res), n);
|
||||||
}
|
}
|
||||||
else return end.chainError(src.loc(i + n), "Expected a comma or end of statement");
|
else return end.chainError(src.loc(i + n), "Expected a comma or end of statement");
|
||||||
}
|
}
|
||||||
|
@ -12,21 +12,24 @@ 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.JavaScript.DeclarationType;
|
||||||
import me.topchetoeu.jscript.compilation.values.VariableNode;
|
import me.topchetoeu.jscript.compilation.values.VariableNode;
|
||||||
|
|
||||||
public class ForInNode extends Node {
|
public class ForInNode extends Node {
|
||||||
public final String varName;
|
public final String varName;
|
||||||
public final boolean isDeclaration;
|
public final DeclarationType declType;
|
||||||
public final Node object, body;
|
public final Node object, body;
|
||||||
public final String label;
|
public final String label;
|
||||||
public final Location varLocation;
|
public final Location varLocation;
|
||||||
|
|
||||||
@Override public void resolve(CompileResult target) {
|
@Override public void resolve(CompileResult target) {
|
||||||
body.resolve(target);
|
body.resolve(target);
|
||||||
if (isDeclaration) target.scope.define(varName, false, loc());
|
if (declType != null && !declType.strict) target.scope.define(varName, false, loc());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void compile(CompileResult target, boolean pollute) {
|
@Override public void compile(CompileResult target, boolean pollute) {
|
||||||
|
if (declType != null && declType.strict) target.scope.defineStrict(varName, declType.readonly, varLocation);
|
||||||
|
|
||||||
object.compile(target, true, BreakpointType.STEP_OVER);
|
object.compile(target, true, BreakpointType.STEP_OVER);
|
||||||
target.add(Instruction.keys(true));
|
target.add(Instruction.keys(true));
|
||||||
|
|
||||||
@ -36,9 +39,8 @@ public class ForInNode extends Node {
|
|||||||
target.add(Instruction.operation(Operation.EQUALS));
|
target.add(Instruction.operation(Operation.EQUALS));
|
||||||
int mid = target.temp();
|
int mid = target.temp();
|
||||||
|
|
||||||
target.add(Instruction.pushValue("value")).setLocation(varLocation);
|
target.add(Instruction.loadMember("value")).setLocation(varLocation);
|
||||||
target.add(Instruction.loadMember()).setLocation(varLocation);
|
target.add(VariableNode.toSet(target, loc(), varName, pollute, declType != null && declType.strict));
|
||||||
target.add(VariableNode.toSet(target, loc(), varName, pollute, isDeclaration));
|
|
||||||
target.setLocationAndDebug(object.loc(), BreakpointType.STEP_OVER);
|
target.setLocationAndDebug(object.loc(), BreakpointType.STEP_OVER);
|
||||||
|
|
||||||
var end = new DeferredIntSupplier();
|
var end = new DeferredIntSupplier();
|
||||||
@ -49,19 +51,17 @@ public class ForInNode extends Node {
|
|||||||
|
|
||||||
int endI = target.size();
|
int endI = target.size();
|
||||||
|
|
||||||
// WhileNode.replaceBreaks(target, label, mid + 1, end, start, end + 1);
|
|
||||||
|
|
||||||
target.add(Instruction.jmp(start - endI));
|
target.add(Instruction.jmp(start - endI));
|
||||||
target.add(Instruction.discard());
|
target.add(Instruction.discard());
|
||||||
target.set(mid, Instruction.jmpIf(endI - mid + 1));
|
target.set(mid, Instruction.jmpIf(endI - mid + 1));
|
||||||
if (pollute) target.add(Instruction.pushUndefined());
|
if (pollute) target.add(Instruction.pushUndefined());
|
||||||
}
|
}
|
||||||
|
|
||||||
public ForInNode(Location loc, Location varLocation, String label, boolean isDecl, String varName, Node object, Node body) {
|
public ForInNode(Location loc, Location varLocation, String label, DeclarationType declType, String varName, Node object, Node body) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.varLocation = varLocation;
|
this.varLocation = varLocation;
|
||||||
this.label = label;
|
this.label = label;
|
||||||
this.isDeclaration = isDecl;
|
this.declType = declType;
|
||||||
this.varName = varName;
|
this.varName = varName;
|
||||||
this.object = object;
|
this.object = object;
|
||||||
this.body = body;
|
this.body = body;
|
||||||
@ -83,12 +83,8 @@ public class ForInNode extends Node {
|
|||||||
n++;
|
n++;
|
||||||
n += Parsing.skipEmpty(src, i + n);
|
n += Parsing.skipEmpty(src, i + n);
|
||||||
|
|
||||||
var isDecl = false;
|
var declType = JavaScript.parseDeclarationType(src, i + n);
|
||||||
|
n += declType.n;
|
||||||
if (Parsing.isIdentifier(src, i + n, "var")) {
|
|
||||||
isDecl = true;
|
|
||||||
n += 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
var name = Parsing.parseIdentifier(src, i + n);
|
var name = Parsing.parseIdentifier(src, i + n);
|
||||||
if (!name.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a variable name for for-in loop");
|
if (!name.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a variable name for for-in loop");
|
||||||
@ -111,6 +107,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, nameLoc, label.result, isDecl, name.result, obj.result, bodyRes.result), n);
|
return ParseRes.res(new ForInNode(loc, nameLoc, label.result, declType.result, name.result, obj.result, bodyRes.result), n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,28 +23,31 @@ public class ForNode extends Node {
|
|||||||
body.resolve(target);
|
body.resolve(target);
|
||||||
}
|
}
|
||||||
@Override public void compile(CompileResult target, boolean pollute) {
|
@Override public void compile(CompileResult target, boolean pollute) {
|
||||||
declaration.compile(target, false, BreakpointType.STEP_OVER);
|
var subtarget = target.subtarget();
|
||||||
|
subtarget.add(i -> Instruction.stackAlloc(subtarget.scope.allocCount()));
|
||||||
|
|
||||||
int start = target.size();
|
declaration.compile(subtarget, false, BreakpointType.STEP_OVER);
|
||||||
condition.compile(target, true, BreakpointType.STEP_OVER);
|
|
||||||
int mid = target.temp();
|
int start = subtarget.size();
|
||||||
|
condition.compile(subtarget, true, BreakpointType.STEP_OVER);
|
||||||
|
int mid = subtarget.temp();
|
||||||
|
|
||||||
var end = new DeferredIntSupplier();
|
var end = new DeferredIntSupplier();
|
||||||
|
|
||||||
LabelContext.pushLoop(target.env, loc(), label, end, start);
|
LabelContext.pushLoop(subtarget.env, loc(), label, end, start);
|
||||||
body.compile(target, false, BreakpointType.STEP_OVER);
|
body.compile(subtarget, false, BreakpointType.STEP_OVER);
|
||||||
LabelContext.popLoop(target.env, label);
|
LabelContext.popLoop(subtarget.env, label);
|
||||||
|
|
||||||
// int beforeAssign = target.size();
|
assignment.compile(subtarget, false, BreakpointType.STEP_OVER);
|
||||||
assignment.compile(target, false, BreakpointType.STEP_OVER);
|
int endI = subtarget.size();
|
||||||
int endI = target.size();
|
|
||||||
end.set(endI);
|
end.set(endI);
|
||||||
|
|
||||||
// WhileNode.replaceBreaks(target, label, mid + 1, end, beforeAssign, end + 1);
|
subtarget.add(Instruction.jmp(start - endI));
|
||||||
|
subtarget.set(mid, Instruction.jmpIfNot(endI - mid + 1));
|
||||||
|
if (pollute) subtarget.add(Instruction.pushUndefined());
|
||||||
|
|
||||||
target.add(Instruction.jmp(start - endI));
|
subtarget.scope.end();
|
||||||
target.set(mid, Instruction.jmpIfNot(endI - mid + 1));
|
subtarget.add(Instruction.stackFree(subtarget.scope.allocCount()));
|
||||||
if (pollute) target.add(Instruction.pushUndefined());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
|
@ -11,42 +11,41 @@ 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.JavaScript.DeclarationType;
|
||||||
import me.topchetoeu.jscript.compilation.values.VariableNode;
|
import me.topchetoeu.jscript.compilation.values.VariableNode;
|
||||||
|
|
||||||
public class ForOfNode extends Node {
|
public class ForOfNode extends Node {
|
||||||
public final String varName;
|
public final String varName;
|
||||||
public final boolean isDeclaration;
|
public final DeclarationType declType;
|
||||||
public final Node iterable, body;
|
public final Node iterable, body;
|
||||||
public final String label;
|
public final String label;
|
||||||
public final Location varLocation;
|
public final Location varLocation;
|
||||||
|
|
||||||
@Override public void resolve(CompileResult target) {
|
@Override public void resolve(CompileResult target) {
|
||||||
body.resolve(target);
|
body.resolve(target);
|
||||||
if (isDeclaration) target.scope.define(varName, false, varLocation);
|
if (declType != null && !declType.strict) target.scope.define(varName, false, varLocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void compile(CompileResult target, boolean pollute) {
|
@Override public void compile(CompileResult target, boolean pollute) {
|
||||||
|
if (declType != null && declType.strict) target.scope.defineStrict(varName, declType.readonly, varLocation);
|
||||||
|
|
||||||
iterable.compile(target, true, BreakpointType.STEP_OVER);
|
iterable.compile(target, true, BreakpointType.STEP_OVER);
|
||||||
target.add(Instruction.dup());
|
target.add(Instruction.dup());
|
||||||
target.add(Instruction.loadIntrinsics("it_key"));
|
target.add(Instruction.loadIntrinsics("it_key"));
|
||||||
target.add(Instruction.loadMember()).setLocation(iterable.loc());
|
target.add(Instruction.loadMember()).setLocation(iterable.loc());
|
||||||
target.add(Instruction.loadMember()).setLocation(iterable.loc());
|
|
||||||
target.add(Instruction.call(0)).setLocation(iterable.loc());
|
target.add(Instruction.call(0)).setLocation(iterable.loc());
|
||||||
|
|
||||||
int start = target.size();
|
int start = target.size();
|
||||||
target.add(Instruction.dup());
|
target.add(Instruction.dup());
|
||||||
target.add(Instruction.dup());
|
target.add(Instruction.dup());
|
||||||
target.add(Instruction.pushValue("next"));
|
target.add(Instruction.loadMember("next")).setLocation(iterable.loc());
|
||||||
target.add(Instruction.loadMember()).setLocation(iterable.loc());
|
|
||||||
target.add(Instruction.call(0)).setLocation(iterable.loc());
|
target.add(Instruction.call(0)).setLocation(iterable.loc());
|
||||||
target.add(Instruction.dup());
|
target.add(Instruction.dup());
|
||||||
target.add(Instruction.pushValue("done"));
|
target.add(Instruction.loadMember("done")).setLocation(iterable.loc());
|
||||||
target.add(Instruction.loadMember()).setLocation(iterable.loc());
|
|
||||||
int mid = target.temp();
|
int mid = target.temp();
|
||||||
|
|
||||||
target.add(Instruction.pushValue("value"));
|
target.add(Instruction.loadMember("value")).setLocation(varLocation);
|
||||||
target.add(Instruction.loadMember()).setLocation(varLocation);
|
target.add(VariableNode.toSet(target, varLocation, varName, false, declType != null && declType.strict));
|
||||||
target.add(VariableNode.toSet(target, varLocation, varName, false, isDeclaration));
|
|
||||||
|
|
||||||
var end = new DeferredIntSupplier();
|
var end = new DeferredIntSupplier();
|
||||||
|
|
||||||
@ -57,8 +56,6 @@ public class ForOfNode extends Node {
|
|||||||
int endI = target.size();
|
int endI = target.size();
|
||||||
end.set(endI);
|
end.set(endI);
|
||||||
|
|
||||||
// WhileNode.replaceBreaks(target, label, mid + 1, end, start, end + 1);
|
|
||||||
|
|
||||||
target.add(Instruction.jmp(start - endI));
|
target.add(Instruction.jmp(start - endI));
|
||||||
target.add(Instruction.discard());
|
target.add(Instruction.discard());
|
||||||
target.add(Instruction.discard());
|
target.add(Instruction.discard());
|
||||||
@ -66,11 +63,11 @@ public class ForOfNode extends Node {
|
|||||||
if (pollute) target.add(Instruction.pushUndefined());
|
if (pollute) target.add(Instruction.pushUndefined());
|
||||||
}
|
}
|
||||||
|
|
||||||
public ForOfNode(Location loc, Location varLocation, String label, boolean isDecl, String varName, Node object, Node body) {
|
public ForOfNode(Location loc, Location varLocation, String label, DeclarationType declType, String varName, Node object, Node body) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.varLocation = varLocation;
|
this.varLocation = varLocation;
|
||||||
this.label = label;
|
this.label = label;
|
||||||
this.isDeclaration = isDecl;
|
this.declType = declType;
|
||||||
this.varName = varName;
|
this.varName = varName;
|
||||||
this.iterable = object;
|
this.iterable = object;
|
||||||
this.body = body;
|
this.body = body;
|
||||||
@ -92,12 +89,8 @@ public class ForOfNode extends Node {
|
|||||||
n++;
|
n++;
|
||||||
n += Parsing.skipEmpty(src, i + n);
|
n += Parsing.skipEmpty(src, i + n);
|
||||||
|
|
||||||
var isDecl = false;
|
var declType = JavaScript.parseDeclarationType(src, i + n);
|
||||||
|
n += declType.n;
|
||||||
if (Parsing.isIdentifier(src, i + n, "var")) {
|
|
||||||
isDecl = true;
|
|
||||||
n += 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
var name = Parsing.parseIdentifier(src, i + n);
|
var name = Parsing.parseIdentifier(src, i + n);
|
||||||
if (!name.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a variable name for for-of loop");
|
if (!name.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a variable name for for-of loop");
|
||||||
@ -120,6 +113,6 @@ public class ForOfNode extends Node {
|
|||||||
if (!bodyRes.isSuccess()) return bodyRes.chainError(src.loc(i + n), "Expected a for-of body");
|
if (!bodyRes.isSuccess()) return bodyRes.chainError(src.loc(i + n), "Expected a for-of body");
|
||||||
n += bodyRes.n;
|
n += bodyRes.n;
|
||||||
|
|
||||||
return ParseRes.res(new ForOfNode(loc, nameLoc, label.result, isDecl, name.result, obj.result, bodyRes.result), n);
|
return ParseRes.res(new ForOfNode(loc, nameLoc, label.result, declType.result, name.result, obj.result, bodyRes.result), n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ public class VariableNode extends Node implements AssignableNode {
|
|||||||
if (!pollute) target.add(Instruction.discard());
|
if (!pollute) target.add(Instruction.discard());
|
||||||
}
|
}
|
||||||
else if (pollute) {
|
else if (pollute) {
|
||||||
target.add(Instruction.loadVar(i.index()));
|
target.add(_i -> Instruction.loadVar(i.index()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,8 +67,8 @@ public class VariableNode extends Node implements AssignableNode {
|
|||||||
if (target.scope.has(name)) throw new SyntaxException(loc, String.format("Cannot access '%s' before initialization", name));
|
if (target.scope.has(name)) throw new SyntaxException(loc, String.format("Cannot access '%s' before initialization", name));
|
||||||
else return Instruction.globSet(name, keep, define);
|
else return Instruction.globSet(name, keep, define);
|
||||||
};
|
};
|
||||||
|
else if (!define && i.readonly) return _i -> Instruction.throwSyntax(new SyntaxException(loc, "Assignment to constant variable"));
|
||||||
else return _i -> Instruction.storeVar(i.index(), keep);
|
else return _i -> Instruction.storeVar(i.index(), keep);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public VariableNode(Location loc, String name) {
|
public VariableNode(Location loc, String name) {
|
||||||
|
Loading…
Reference in New Issue
Block a user