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 final FunctionBody[] children;
|
||||
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.length = length;
|
||||
this.argsN = argsN;
|
||||
this.localsN = localsN;
|
||||
this.capturesN = capturesN;
|
||||
this.instructions = instructions;
|
||||
|
@ -1,16 +1,15 @@
|
||||
package me.topchetoeu.jscript.common;
|
||||
|
||||
// import java.io.DataInputStream;
|
||||
// import java.io.DataOutputStream;
|
||||
// import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.function.IntFunction;
|
||||
import java.util.function.IntSupplier;
|
||||
|
||||
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
|
||||
|
||||
public class Instruction {
|
||||
public static enum Type {
|
||||
NOP(0x00),
|
||||
RETURN(0x01),
|
||||
RETURN(0x00),
|
||||
NOP(0x01),
|
||||
THROW(0x02),
|
||||
THROW_SYNTAX(0x03),
|
||||
DELETE(0x04),
|
||||
@ -41,10 +40,18 @@ public class Instruction {
|
||||
|
||||
LOAD_VAR(0x40),
|
||||
LOAD_MEMBER(0x41),
|
||||
LOAD_ARGS(0x42),
|
||||
LOAD_THIS(0x43),
|
||||
LOAD_MEMBER_INT(0x42),
|
||||
LOAD_MEMBER_STR(0x43),
|
||||
|
||||
LOAD_ARGS(0x44),
|
||||
LOAD_REST_ARGS(0x45),
|
||||
LOAD_CALLEE(0x46),
|
||||
LOAD_THIS(0x47),
|
||||
|
||||
STORE_VAR(0x48),
|
||||
STORE_MEMBER(0x49),
|
||||
STORE_MEMBER_INT(0x4A),
|
||||
STORE_MEMBER_STR(0x4B),
|
||||
|
||||
DEF_PROP(0x50),
|
||||
KEYS(0x51),
|
||||
@ -286,6 +293,7 @@ public class Instruction {
|
||||
public static Instruction callNew(int argn) {
|
||||
return new Instruction(Type.CALL_NEW, argn, "");
|
||||
}
|
||||
|
||||
public static Instruction jmp(int offset) {
|
||||
return new Instruction(Type.JMP, offset);
|
||||
}
|
||||
@ -296,6 +304,17 @@ public class Instruction {
|
||||
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() {
|
||||
return new Instruction(Type.PUSH_UNDEFINED);
|
||||
}
|
||||
@ -313,7 +332,7 @@ public class Instruction {
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -332,6 +351,12 @@ public class Instruction {
|
||||
public static Instruction loadArgs() {
|
||||
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() {
|
||||
return new Instruction(Type.LOAD_GLOB);
|
||||
}
|
||||
@ -341,17 +366,26 @@ public class Instruction {
|
||||
public static Instruction loadMember() {
|
||||
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) {
|
||||
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 = "";
|
||||
|
||||
var args = new Object[2 + captures.length];
|
||||
var args = new Object[5 + captures.length];
|
||||
args[0] = id;
|
||||
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);
|
||||
}
|
||||
public static Instruction loadObj() {
|
||||
@ -373,12 +407,28 @@ public class Instruction {
|
||||
public static Instruction storeVar(int i, boolean keep) {
|
||||
return new Instruction(Type.STORE_VAR, i, keep);
|
||||
}
|
||||
|
||||
public static Instruction storeMember() {
|
||||
return new Instruction(Type.STORE_MEMBER, false);
|
||||
}
|
||||
public static Instruction storeMember(boolean 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() {
|
||||
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) {
|
||||
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()));
|
||||
|
||||
for (var stm : this.statements) {
|
||||
@ -41,8 +41,10 @@ public class CompoundNode extends Node {
|
||||
else stm.compile(subtarget, polluted = pollute, BreakpointType.STEP_OVER);
|
||||
}
|
||||
|
||||
subtarget.scope.end();
|
||||
if (alloc) subtarget.add(Instruction.stackFree(subtarget.scope.allocCount()));
|
||||
if (alloc) {
|
||||
subtarget.scope.end();
|
||||
subtarget.add(Instruction.stackFree(subtarget.scope.allocCount()));
|
||||
}
|
||||
|
||||
if (!polluted && pollute) {
|
||||
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) {
|
||||
target.add(Instruction.loadFunc(target.children.size(), name, captures));
|
||||
protected void compileLoadFunc(CompileResult target, int id, int[] captures, String name) {
|
||||
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()
|
||||
.remove(LabelContext.BREAK_CTX)
|
||||
.remove(LabelContext.CONTINUE_CTX);
|
||||
@ -47,43 +47,74 @@ public abstract class FunctionNode extends Node {
|
||||
var funcScope = new FunctionScope(target.scope);
|
||||
var subtarget = new CompileResult(env, new LocalScope(funcScope));
|
||||
|
||||
for (var param : params.params) {
|
||||
// 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);
|
||||
subtarget.length = params.params.size();
|
||||
|
||||
if (param.node != null) {
|
||||
var end = new DeferredIntSupplier();
|
||||
if (hasArgs || params.params.size() > 0) subtarget.add(Instruction.loadArgs());
|
||||
|
||||
subtarget.add(_i -> Instruction.loadVar(i.index()));
|
||||
subtarget.add(Instruction.pushUndefined());
|
||||
subtarget.add(Instruction.operation(Operation.EQUALS));
|
||||
subtarget.add(_i -> Instruction.jmpIfNot(end.getAsInt() - _i));
|
||||
param.node.compile(subtarget, pollute);
|
||||
subtarget.add(_i -> Instruction.storeVar(i.index()));
|
||||
if (hasArgs) {
|
||||
var argsVar = funcScope.defineParam("arguments", true, loc());
|
||||
subtarget.add(_i -> Instruction.storeVar(argsVar.index(), params.params.size() > 0));
|
||||
}
|
||||
|
||||
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.compile(subtarget, false, false, BreakpointType.NONE);
|
||||
|
||||
subtarget.length = params.length;
|
||||
subtarget.assignN = params.params.size();
|
||||
subtarget.scope.end();
|
||||
funcScope.end();
|
||||
|
||||
if (pollute) compileLoadFunc(target, funcScope.getCaptureIndices(), name);
|
||||
if (pollute) compileLoadFunc(target, target.children.size(), funcScope.getCaptureIndices(), name);
|
||||
|
||||
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();
|
||||
|
||||
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 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) {
|
||||
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));
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,7 @@ public class FunctionValueNode extends FunctionNode {
|
||||
@Override public String name() { return name; }
|
||||
|
||||
@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) {
|
||||
|
@ -26,9 +26,7 @@ import me.topchetoeu.jscript.compilation.control.TryNode;
|
||||
import me.topchetoeu.jscript.compilation.control.WhileNode;
|
||||
import me.topchetoeu.jscript.compilation.scope.GlobalScope;
|
||||
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.GlobalThisNode;
|
||||
import me.topchetoeu.jscript.compilation.values.ObjectNode;
|
||||
import me.topchetoeu.jscript.compilation.values.RegexNode;
|
||||
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.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(
|
||||
"true", "false", "void", "null", "this", "if", "else", "try", "catch",
|
||||
"finally", "for", "do", "while", "switch", "case", "default", "new",
|
||||
@ -84,6 +95,7 @@ public class JavaScript {
|
||||
ChangeNode::parsePrefixIncrease,
|
||||
OperationNode::parsePrefix,
|
||||
ArrayNode::parse,
|
||||
FunctionArrowNode::parse,
|
||||
JavaScript::parseParens,
|
||||
CallNode::parseNew,
|
||||
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("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("this")) return ParseRes.res(new ThisNode(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("arguments")) return ParseRes.res(new ArgumentsNode(loc), n);
|
||||
// if (id.result.equals("globalThis")) return ParseRes.res(new GlobalThisNode(loc), n);
|
||||
|
||||
return ParseRes.failed();
|
||||
}
|
||||
@ -228,10 +240,25 @@ public class JavaScript {
|
||||
while (true) {
|
||||
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 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 += Parsing.skipEmpty(src, i + n);
|
||||
|
||||
@ -239,7 +266,7 @@ public class JavaScript {
|
||||
n++;
|
||||
|
||||
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 += Parsing.skipEmpty(src, i + n);
|
||||
|
||||
@ -251,6 +278,7 @@ public class JavaScript {
|
||||
n++;
|
||||
n += Parsing.skipEmpty(src, i + n);
|
||||
}
|
||||
|
||||
if (src.is(i + n, ")")) {
|
||||
n++;
|
||||
break;
|
||||
@ -261,6 +289,17 @@ public class JavaScript {
|
||||
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) {
|
||||
var src = new Source(env, filename, raw);
|
||||
var list = new ArrayList<Node>();
|
||||
@ -289,17 +328,20 @@ public class JavaScript {
|
||||
public static CompileResult compile(Environment env, Node ...statements) {
|
||||
var target = new CompileResult(env, new LocalScope(new GlobalScope()));
|
||||
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.compile(target, true, false, BreakpointType.NONE);
|
||||
// FunctionNode.checkBreakAndCont(target, 0);
|
||||
}
|
||||
catch (SyntaxException e) {
|
||||
target = new CompileResult(env, new LocalScope(new GlobalScope()));
|
||||
// }
|
||||
// catch (SyntaxException e) {
|
||||
// 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;
|
||||
}
|
||||
|
@ -1,16 +1,16 @@
|
||||
package me.topchetoeu.jscript.compilation;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import me.topchetoeu.jscript.common.parsing.Location;
|
||||
|
||||
public final class Parameters {
|
||||
public final int length;
|
||||
public final List<Parameter> params;
|
||||
public final Set<String> names;
|
||||
public final String restName;
|
||||
public final Location restLocation;
|
||||
|
||||
public Parameters(List<Parameter> params) {
|
||||
this.names = new HashSet<>();
|
||||
public Parameters(List<Parameter> params, String restName, Location restLocation) {
|
||||
var len = params.size();
|
||||
|
||||
for (var i = params.size() - 1; i >= 0; i--) {
|
||||
@ -18,11 +18,12 @@ public final class Parameters {
|
||||
len--;
|
||||
}
|
||||
|
||||
for (var param : params) {
|
||||
this.names.add(param.name);
|
||||
}
|
||||
|
||||
this.params = params;
|
||||
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.Parsing;
|
||||
import me.topchetoeu.jscript.common.parsing.Source;
|
||||
import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType;
|
||||
import me.topchetoeu.jscript.compilation.values.VariableNode;
|
||||
|
||||
public class VariableDeclareNode extends Node {
|
||||
@ -25,51 +26,56 @@ public class VariableDeclareNode extends Node {
|
||||
}
|
||||
|
||||
public final List<Pair> values;
|
||||
public final DeclarationType declType;
|
||||
|
||||
@Override public void resolve(CompileResult target) {
|
||||
for (var entry : values) {
|
||||
target.scope.define(entry.name, false, entry.location);
|
||||
if (!declType.strict) {
|
||||
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) {
|
||||
for (var entry : values) {
|
||||
if (entry.name == null) continue;
|
||||
if (declType.strict) target.scope.defineStrict(entry.name, declType.readonly, entry.location);
|
||||
|
||||
if (entry.value != null) {
|
||||
FunctionNode.compileWithName(entry.value, target, true, entry.name, BreakpointType.STEP_OVER);
|
||||
target.add(VariableNode.toSet(target, entry.location, entry.name, false, true));
|
||||
}
|
||||
else {
|
||||
target.add(_i -> {
|
||||
var i = target.scope.get(entry.name, true);
|
||||
else target.add(_i -> {
|
||||
var i = target.scope.get(entry.name, true);
|
||||
|
||||
if (i == null) return Instruction.globDef(entry.name);
|
||||
else return Instruction.nop();
|
||||
});
|
||||
}
|
||||
if (i == null) return Instruction.globDef(entry.name);
|
||||
else return Instruction.nop();
|
||||
});
|
||||
}
|
||||
|
||||
if (pollute) target.add(Instruction.pushUndefined());
|
||||
}
|
||||
|
||||
public VariableDeclareNode(Location loc, List<Pair> values) {
|
||||
public VariableDeclareNode(Location loc, DeclarationType declType, List<Pair> values) {
|
||||
super(loc);
|
||||
this.values = values;
|
||||
this.declType = declType;
|
||||
}
|
||||
|
||||
public static ParseRes<VariableDeclareNode> parse(Source src, int i) {
|
||||
var n = Parsing.skipEmpty(src, i);
|
||||
var loc = src.loc(i + n);
|
||||
|
||||
if (!Parsing.isIdentifier(src, i + n, "var")) return ParseRes.failed();
|
||||
n += 3;
|
||||
var declType = JavaScript.parseDeclarationType(src, i + n);
|
||||
if (!declType.isSuccess()) return declType.chainError();
|
||||
n += declType.n;
|
||||
|
||||
var res = new ArrayList<Pair>();
|
||||
|
||||
var end = JavaScript.parseStatementEnd(src, i + n);
|
||||
if (end.isSuccess()) {
|
||||
n += end.n;
|
||||
return ParseRes.res(new VariableDeclareNode(loc, res), n);
|
||||
return ParseRes.res(new VariableDeclareNode(loc, declType.result, res), n);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
@ -83,6 +89,7 @@ public class VariableDeclareNode extends Node {
|
||||
}
|
||||
|
||||
Node val = null;
|
||||
var endN = n;
|
||||
n += Parsing.skipEmpty(src, 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 '='");
|
||||
|
||||
n += valRes.n;
|
||||
endN = n;
|
||||
n += Parsing.skipEmpty(src, i + n);
|
||||
val = valRes.result;
|
||||
}
|
||||
@ -103,11 +111,11 @@ public class VariableDeclareNode extends Node {
|
||||
continue;
|
||||
}
|
||||
|
||||
end = JavaScript.parseStatementEnd(src, i + n);
|
||||
end = JavaScript.parseStatementEnd(src, i + endN);
|
||||
|
||||
if (end.isSuccess()) {
|
||||
n += end.n;
|
||||
return ParseRes.res(new VariableDeclareNode(loc, res), n);
|
||||
n += end.n + endN - 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");
|
||||
}
|
||||
|
@ -12,21 +12,24 @@ import me.topchetoeu.jscript.compilation.DeferredIntSupplier;
|
||||
import me.topchetoeu.jscript.compilation.JavaScript;
|
||||
import me.topchetoeu.jscript.compilation.LabelContext;
|
||||
import me.topchetoeu.jscript.compilation.Node;
|
||||
import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType;
|
||||
import me.topchetoeu.jscript.compilation.values.VariableNode;
|
||||
|
||||
public class ForInNode extends Node {
|
||||
public final String varName;
|
||||
public final boolean isDeclaration;
|
||||
public final DeclarationType declType;
|
||||
public final Node object, body;
|
||||
public final String label;
|
||||
public final Location varLocation;
|
||||
|
||||
@Override public void resolve(CompileResult 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) {
|
||||
if (declType != null && declType.strict) target.scope.defineStrict(varName, declType.readonly, varLocation);
|
||||
|
||||
object.compile(target, true, BreakpointType.STEP_OVER);
|
||||
target.add(Instruction.keys(true));
|
||||
|
||||
@ -36,9 +39,8 @@ public class ForInNode extends Node {
|
||||
target.add(Instruction.operation(Operation.EQUALS));
|
||||
int mid = target.temp();
|
||||
|
||||
target.add(Instruction.pushValue("value")).setLocation(varLocation);
|
||||
target.add(Instruction.loadMember()).setLocation(varLocation);
|
||||
target.add(VariableNode.toSet(target, loc(), varName, pollute, isDeclaration));
|
||||
target.add(Instruction.loadMember("value")).setLocation(varLocation);
|
||||
target.add(VariableNode.toSet(target, loc(), varName, pollute, declType != null && declType.strict));
|
||||
target.setLocationAndDebug(object.loc(), BreakpointType.STEP_OVER);
|
||||
|
||||
var end = new DeferredIntSupplier();
|
||||
@ -49,19 +51,17 @@ public class ForInNode extends Node {
|
||||
|
||||
int endI = target.size();
|
||||
|
||||
// WhileNode.replaceBreaks(target, label, mid + 1, end, start, end + 1);
|
||||
|
||||
target.add(Instruction.jmp(start - endI));
|
||||
target.add(Instruction.discard());
|
||||
target.set(mid, Instruction.jmpIf(endI - mid + 1));
|
||||
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);
|
||||
this.varLocation = varLocation;
|
||||
this.label = label;
|
||||
this.isDeclaration = isDecl;
|
||||
this.declType = declType;
|
||||
this.varName = varName;
|
||||
this.object = object;
|
||||
this.body = body;
|
||||
@ -83,12 +83,8 @@ public class ForInNode extends Node {
|
||||
n++;
|
||||
n += Parsing.skipEmpty(src, i + n);
|
||||
|
||||
var isDecl = false;
|
||||
|
||||
if (Parsing.isIdentifier(src, i + n, "var")) {
|
||||
isDecl = true;
|
||||
n += 3;
|
||||
}
|
||||
var declType = JavaScript.parseDeclarationType(src, i + n);
|
||||
n += declType.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");
|
||||
@ -111,6 +107,6 @@ public class ForInNode extends Node {
|
||||
if (!bodyRes.isSuccess()) return bodyRes.chainError(src.loc(i + n), "Expected a for-in body");
|
||||
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);
|
||||
}
|
||||
@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();
|
||||
condition.compile(target, true, BreakpointType.STEP_OVER);
|
||||
int mid = target.temp();
|
||||
declaration.compile(subtarget, false, BreakpointType.STEP_OVER);
|
||||
|
||||
int start = subtarget.size();
|
||||
condition.compile(subtarget, true, BreakpointType.STEP_OVER);
|
||||
int mid = subtarget.temp();
|
||||
|
||||
var end = new DeferredIntSupplier();
|
||||
|
||||
LabelContext.pushLoop(target.env, loc(), label, end, start);
|
||||
body.compile(target, false, BreakpointType.STEP_OVER);
|
||||
LabelContext.popLoop(target.env, label);
|
||||
LabelContext.pushLoop(subtarget.env, loc(), label, end, start);
|
||||
body.compile(subtarget, false, BreakpointType.STEP_OVER);
|
||||
LabelContext.popLoop(subtarget.env, label);
|
||||
|
||||
// int beforeAssign = target.size();
|
||||
assignment.compile(target, false, BreakpointType.STEP_OVER);
|
||||
int endI = target.size();
|
||||
assignment.compile(subtarget, false, BreakpointType.STEP_OVER);
|
||||
int endI = subtarget.size();
|
||||
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));
|
||||
target.set(mid, Instruction.jmpIfNot(endI - mid + 1));
|
||||
if (pollute) target.add(Instruction.pushUndefined());
|
||||
subtarget.scope.end();
|
||||
subtarget.add(Instruction.stackFree(subtarget.scope.allocCount()));
|
||||
}
|
||||
|
||||
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.LabelContext;
|
||||
import me.topchetoeu.jscript.compilation.Node;
|
||||
import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType;
|
||||
import me.topchetoeu.jscript.compilation.values.VariableNode;
|
||||
|
||||
public class ForOfNode extends Node {
|
||||
public final String varName;
|
||||
public final boolean isDeclaration;
|
||||
public final DeclarationType declType;
|
||||
public final Node iterable, body;
|
||||
public final String label;
|
||||
public final Location varLocation;
|
||||
|
||||
@Override public void resolve(CompileResult 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) {
|
||||
if (declType != null && declType.strict) target.scope.defineStrict(varName, declType.readonly, varLocation);
|
||||
|
||||
iterable.compile(target, true, BreakpointType.STEP_OVER);
|
||||
target.add(Instruction.dup());
|
||||
target.add(Instruction.loadIntrinsics("it_key"));
|
||||
target.add(Instruction.loadMember()).setLocation(iterable.loc());
|
||||
target.add(Instruction.loadMember()).setLocation(iterable.loc());
|
||||
target.add(Instruction.call(0)).setLocation(iterable.loc());
|
||||
|
||||
int start = target.size();
|
||||
target.add(Instruction.dup());
|
||||
target.add(Instruction.dup());
|
||||
target.add(Instruction.pushValue("next"));
|
||||
target.add(Instruction.loadMember()).setLocation(iterable.loc());
|
||||
target.add(Instruction.loadMember("next")).setLocation(iterable.loc());
|
||||
target.add(Instruction.call(0)).setLocation(iterable.loc());
|
||||
target.add(Instruction.dup());
|
||||
target.add(Instruction.pushValue("done"));
|
||||
target.add(Instruction.loadMember()).setLocation(iterable.loc());
|
||||
target.add(Instruction.loadMember("done")).setLocation(iterable.loc());
|
||||
int mid = target.temp();
|
||||
|
||||
target.add(Instruction.pushValue("value"));
|
||||
target.add(Instruction.loadMember()).setLocation(varLocation);
|
||||
target.add(VariableNode.toSet(target, varLocation, varName, false, isDeclaration));
|
||||
target.add(Instruction.loadMember("value")).setLocation(varLocation);
|
||||
target.add(VariableNode.toSet(target, varLocation, varName, false, declType != null && declType.strict));
|
||||
|
||||
var end = new DeferredIntSupplier();
|
||||
|
||||
@ -57,8 +56,6 @@ public class ForOfNode extends Node {
|
||||
int endI = target.size();
|
||||
end.set(endI);
|
||||
|
||||
// WhileNode.replaceBreaks(target, label, mid + 1, end, start, end + 1);
|
||||
|
||||
target.add(Instruction.jmp(start - endI));
|
||||
target.add(Instruction.discard());
|
||||
target.add(Instruction.discard());
|
||||
@ -66,11 +63,11 @@ public class ForOfNode extends Node {
|
||||
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);
|
||||
this.varLocation = varLocation;
|
||||
this.label = label;
|
||||
this.isDeclaration = isDecl;
|
||||
this.declType = declType;
|
||||
this.varName = varName;
|
||||
this.iterable = object;
|
||||
this.body = body;
|
||||
@ -92,12 +89,8 @@ public class ForOfNode extends Node {
|
||||
n++;
|
||||
n += Parsing.skipEmpty(src, i + n);
|
||||
|
||||
var isDecl = false;
|
||||
|
||||
if (Parsing.isIdentifier(src, i + n, "var")) {
|
||||
isDecl = true;
|
||||
n += 3;
|
||||
}
|
||||
var declType = JavaScript.parseDeclarationType(src, i + n);
|
||||
n += declType.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");
|
||||
@ -120,6 +113,6 @@ public class ForOfNode extends Node {
|
||||
if (!bodyRes.isSuccess()) return bodyRes.chainError(src.loc(i + n), "Expected a for-of body");
|
||||
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());
|
||||
}
|
||||
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));
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
public VariableNode(Location loc, String name) {
|
||||
|
Loading…
Reference in New Issue
Block a user