TopchetoEU/revert-ES5 #31

Merged
TopchetoEU merged 41 commits from TopchetoEU/revert-ES5 into master 2024-12-09 21:39:57 +00:00
12 changed files with 0 additions and 1100 deletions
Showing only changes of commit 5644966dd7 - Show all commits

View File

@ -1,244 +0,0 @@
package me.topchetoeu.jscript.compilation;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Consumer;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.SyntaxException;
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.common.environment.Key;
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.members.FieldMemberNode;
import me.topchetoeu.jscript.compilation.members.Member;
import me.topchetoeu.jscript.compilation.members.MethodMemberNode;
import me.topchetoeu.jscript.compilation.members.PropertyMemberNode;
import me.topchetoeu.jscript.compilation.scope.FunctionScope;
public abstract class ClassNode extends FunctionNode {
public static final class ClassBody {
public final List<Member> staticMembers;
public final List<FieldMemberNode> protoFields;
public final List<Member> protoMembers;
public final Parameters constructorParameters;
public final CompoundNode constructorBody;
public final Node superExpr;
public final boolean hasConstr;
public ClassBody(
List<Member> staticMembers, List<FieldMemberNode> protoFields, List<Member> protoMembers,
Parameters constructorParameters, CompoundNode constructorBody,
Node superExpr, boolean hasConstr
) {
this.staticMembers = staticMembers;
this.protoFields = protoFields;
this.protoMembers = protoMembers;
this.constructorParameters = constructorParameters;
this.constructorBody = constructorBody;
this.superExpr = superExpr;
this.hasConstr = hasConstr;
}
}
public static final Key<Environment> CLASS_ROOT = Key.of();
public static final Key<Consumer<CompileResult>> SUPER = Key.of();
public static final Key<Consumer<CompileResult>> SUPER_PROTO = Key.of();
public static final Key<Consumer<CompileResult>> SUPER_CONSTR = Key.of();
public static final Key<Consumer<CompileResult>> ON_SUPER_CALL = Key.of();
public final ClassBody body;
public final String name;
@Override public String name() { return name; }
public void compileStatic(CompileResult target) {
for (var member : body.staticMembers) {
member.compile(target, true, false);
}
}
public void compilePrototype(CompileResult target) {
if (body.protoMembers.size() > 0) {
target.add(Instruction.dup());
target.add(Instruction.loadMember("prototype"));
for (var i = 0; i < body.protoMembers.size() - 1; i++) {
body.protoMembers.get(i).compile(target, true, false);
}
body.protoMembers.get(body.protoMembers.size() - 1).compile(target, false, false);
}
}
private void compileFieldInits(CompileResult target) {
for (var member : body.protoFields) {
target.add(Instruction.loadThis());
member.compile(target, false, true);
}
}
@Override protected void compilePreBody(CompileResult target) {
if (target.env.hasNotNull(SUPER_PROTO)) {
if (!body.hasConstr) {
throw new SyntaxException(loc(), "Default constructors in derived classes not supported");
// compileFieldInits(target);
}
}
else compileFieldInits(target);
}
@Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
if (body.superExpr == null) {
var id = target.addChild(compileBody(target, name, null));
target.add(_i -> Instruction.loadFunc(id, false, true, false, false, name, captures(id, target)));
compileStatic(target);
compilePrototype(target);
}
else {
var subtarget = target.subtarget().rootEnvironment(JavaScript.COMPILE_ROOT);
subtarget.scope.singleEntry = true;
subtarget.beginScope();
var protoVar = target.scope.defineTemp();
var constrVar = target.scope.defineTemp();
subtarget.env.add(SUPER_PROTO, t -> {
var i = t.scope.get(protoVar, false);
t.add(_i -> i.index().toGet());
});
subtarget.env.add(SUPER_CONSTR, t -> {
var i = t.scope.get(constrVar, false);
t.add(_i -> i.index().toGet());
});
var staticTarget = subtarget.subEnvironment();
staticTarget.env.add(SUPER, subtarget.env.get(SUPER_CONSTR));
staticTarget.env.add(CLASS_ROOT, staticTarget.env);
var protoTarget = subtarget.subEnvironment();
protoTarget.env.add(SUPER, subtarget.env.get(SUPER_PROTO));
protoTarget.env.add(CLASS_ROOT, protoTarget.env);
var constrEnv = subtarget.env.child();
constrEnv.add(SUPER, subtarget.env.get(SUPER_PROTO));
constrEnv.add(ON_SUPER_CALL, this::compileFieldInits);
constrEnv.add(CLASS_ROOT, constrEnv);
var id = target.addChild(compileBody(constrEnv, new FunctionScope(subtarget.scope), false, name, null));
target.add(_i -> Instruction.loadFunc(id, false, true, false, true, name, captures(id, target)));
body.superExpr.compile(target, true);
target.add(Instruction.extend());
target.add(Instruction.dup(1, 0));
target.add(Instruction.loadMember("prototype"));
target.add(_i -> protoVar.index().toInit());
target.add(_i -> constrVar.index().toInit());
compileStatic(staticTarget);
compilePrototype(protoTarget);
subtarget.endScope();
}
}
public ClassNode(Location loc, Location end, String name, ClassBody body) {
super(loc, end, body.constructorParameters, body.constructorBody);
this.name = name;
this.body = body;
}
public static ParseRes<Member> parseMember(Source src, int i) {
return ParseRes.first(src, i,
PropertyMemberNode::parse,
FieldMemberNode::parseClass,
MethodMemberNode::parse
);
}
public static ParseRes<ClassBody> parseBody(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
ParseRes<Node> superExpr = ParseRes.failed();
if (Parsing.isIdentifier(src, i + n, "extends")) {
n += 7;
superExpr = JavaScript.parseExpression(src, i + n, 14);
if (!superExpr.isSuccess()) return superExpr.chainError(src.loc(i + n), "Expected an expression after 'extends'");
n += superExpr.n;
n += Parsing.skipEmpty(src, i + n);
}
if (!src.is(i + n, "{")) return ParseRes.error(src.loc(i + n), "Expected a class body");
n++;
n += Parsing.skipEmpty(src, i + n);
var fields = new LinkedList<FieldMemberNode>();
var members = new LinkedList<Member>();
var statics = new LinkedList<Member>();
var params = new Parameters(new ArrayList<>());
var body = new CompoundNode(loc, false);
var hasConstr = false;
if (src.is(i + n, "}")) {
n++;
return ParseRes.res(new ClassBody(statics, fields, members, params, body, superExpr.result, false), n);
}
while (true) {
ParseRes<Member> prop = parseMember(src, i + n);
if (prop.isSuccess()) {
n += prop.n;
if (prop.result instanceof FieldMemberNode field) fields.add(field);
else if (prop.result instanceof MethodMemberNode method && method.name().equals("constructor")) {
if (hasConstr) return ParseRes.error(loc, "A class may only have one constructor");
params = method.params;
body = method.body;
hasConstr = true;
}
else members.add(prop.result);
}
else if (Parsing.isIdentifier(src, i + n, "static")) {
n += 6;
var staticProp = parseMember(src, i + n);
if (!staticProp.isSuccess()) {
if (prop.isError()) return prop.chainError();
else return staticProp.chainError(src.loc(i + n), "Expected a member after 'static' keyword");
}
n += staticProp.n;
statics.add(staticProp.result);
}
else {
var end = JavaScript.parseStatementEnd(src, i + n);
if (end.isSuccess()) n += end.n;
else return ParseRes.error(src.loc(i + n), "Expected a member, end of statement or a closing colon");
}
n += Parsing.skipEmpty(src, i + n);
if (src.is(i + n, "}")) {
n++;
break;
}
// else return ParseRes.error(src.loc(i + n), "Expected a comma or a closing brace.");
}
return ParseRes.res(new ClassBody(statics, fields, members, params, body, superExpr.result, hasConstr), n);
}
// public FunctionStatementNode(Location loc, Location end, Parameters params, CompoundNode body, String name) {
// super(loc, end, params, body);
// this.name = name;
// }
}

View File

@ -1,40 +0,0 @@
package me.topchetoeu.jscript.compilation;
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.JavaScript.DeclarationType;
public class ClassStatementNode extends ClassNode {
@Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
super.compile(target, pollute, name, bp);
var i = target.scope.define(DeclarationType.LET, name(), loc());
target.add(_i -> i.index().toInit());
if (pollute) target.add(Instruction.pushUndefined());
}
public ClassStatementNode(Location loc, Location end, String name, ClassBody body) {
super(loc, end, name, body);
}
public static ParseRes<ClassStatementNode> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
if (!Parsing.isIdentifier(src, i + n, "class")) return ParseRes.failed();
n += 5;
var name = Parsing.parseIdentifier(src, i + n);
if (!name.isSuccess()) return name.chainError(src.loc(i + n), "Expected a class name");
n += name.n;
var body = parseBody(src, i + n);
if (!body.isSuccess()) return body.chainError(src.loc(i + n), "Expected a class body");
n += body.n;
return ParseRes.res(new ClassStatementNode(loc, src.loc(i + n), name.result, body.result), n);
}
}

View File

@ -1,78 +0,0 @@
package me.topchetoeu.jscript.compilation;
import java.util.Arrays;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.common.environment.Environment;
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;
import me.topchetoeu.jscript.compilation.patterns.Pattern;
public class FunctionArrowNode extends FunctionNode {
@Override public String name() { return null; }
@Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
var id = target.addChild(compileBody(target, name, null));
target.add(_i -> Instruction.loadFunc(id, true, false, true, false, null, captures(id, target)));
}
@Override protected Environment rootEnv(Environment env) {
return env.getWith(ClassNode.CLASS_ROOT, () -> super.rootEnv(env));
}
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(), false, 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 = Parameters.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 = Pattern.parse(src, i + n, true);
if (!singleParam.isSuccess()) return ParseRes.failed();
n += singleParam.n;
n += Parsing.skipEmpty(src, i + n);
params = new Parameters(Arrays.asList(singleParam.result));
}
if (!src.is(i + n, "=>")) return ParseRes.failed();
n += 2;
n += Parsing.skipEmpty(src, i + n);
if (src.is(i + n, "{")) {
var body = CompoundNode.parse(src, i + n);
if (!body.isSuccess()) return body.chainError(src.loc(i + n), "Expected a compount statement after '=>'");
n += body.n;
return ParseRes.res(new FunctionArrowNode(loc, src.loc(i + n - 1), params, body.result), n);
}
else {
var body = JavaScript.parseExpression(src, i + n, 2);
if (!body.isSuccess()) return body.chainError(src.loc(i + n), "Expected a compount statement after '=>'");
n += body.n;
return ParseRes.res(new FunctionArrowNode(loc, src.loc(i + n - 1), params, body.result), n);
}
}
}

View File

@ -1,84 +0,0 @@
package me.topchetoeu.jscript.compilation;
import java.util.ArrayList;
import java.util.List;
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.patterns.Pattern;
import me.topchetoeu.jscript.compilation.values.operations.AssignNode;
public final class Parameters {
public final int length;
public final List<Pattern> params;
public final Pattern rest;
public Parameters(List<Pattern> params, Pattern rest) {
var len = params.size();
for (var i = params.size() - 1; i >= 0; i--) {
if (!(params.get(i) instanceof AssignNode)) break;
len--;
}
this.params = params;
this.length = len;
this.rest = rest;
}
public Parameters(List<Pattern> params) {
this(params, null);
}
public static ParseRes<Parameters> parseParameters(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var openParen = Parsing.parseOperator(src, i + n, "(");
if (!openParen.isSuccess()) return openParen.chainError(src.loc(i + n), "Expected a parameter list");
n += openParen.n;
var params = new ArrayList<Pattern>();
var closeParen = Parsing.parseOperator(src, i + n, ")");
n += closeParen.n;
if (!closeParen.isSuccess()) {
while (true) {
n += Parsing.skipEmpty(src, i + n);
if (src.is(i + n, "...")) {
n += 3;
var rest = Pattern.parse(src, i + n, true);
if (!rest.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a rest parameter");
n += rest.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, rest.result), n);
}
var param = Pattern.parse(src, i + n, true);
if (!param.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a parameter or a closing brace");
n += param.n;
n += Parsing.skipEmpty(src, i + n);
params.add(param.result);
if (src.is(i + n, ",")) {
n++;
n += Parsing.skipEmpty(src, i + n);
}
if (src.is(i + n, ")")) {
n++;
break;
}
}
}
return ParseRes.res(new Parameters(params), n);
}
}

View File

@ -1,109 +0,0 @@
package me.topchetoeu.jscript.compilation.control;
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.CompileResult;
import me.topchetoeu.jscript.compilation.CompoundNode;
import me.topchetoeu.jscript.compilation.DeferredIntSupplier;
import me.topchetoeu.jscript.compilation.JavaScript;
import me.topchetoeu.jscript.compilation.LabelContext;
import me.topchetoeu.jscript.compilation.Node;
import me.topchetoeu.jscript.compilation.patterns.Binding;
public class ForOfNode extends Node {
public final Binding binding;
public final Node iterable, body;
public final String label;
@Override public void resolve(CompileResult target) {
body.resolve(target);
binding.resolve(target);
}
@Override public void compile(CompileResult target, boolean pollute) {
binding.declareLateInit(target);
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.call(0, true)).setLocation(iterable.loc());
int start = target.size();
target.add(Instruction.dup(2, 0));
target.add(Instruction.loadMember("next")).setLocation(iterable.loc());
target.add(Instruction.call(0, true)).setLocation(iterable.loc());
target.add(Instruction.dup());
target.add(Instruction.loadMember("done")).setLocation(iterable.loc());
int mid = target.temp();
target.add(Instruction.loadMember("value")).setLocation(binding.loc);
binding.assign(target, false);
var end = new DeferredIntSupplier();
LabelContext.pushLoop(target.env, loc(), label, end, start);
CompoundNode.compileMultiEntry(body, target, false, BreakpointType.STEP_OVER);
LabelContext.popLoop(target.env, label);
int endI = target.size();
end.set(endI);
target.add(Instruction.jmp(start - endI));
target.add(Instruction.discard());
target.add(Instruction.discard());
target.set(mid, Instruction.jmpIf(endI - mid + 1));
if (pollute) target.add(Instruction.pushUndefined());
}
public ForOfNode(Location loc, String label, Binding binding, Node object, Node body) {
super(loc);
this.label = label;
this.binding = binding;
this.iterable = object;
this.body = body;
}
public static ParseRes<ForOfNode> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
var label = JavaScript.parseLabel(src, i + n);
n += label.n;
n += Parsing.skipEmpty(src, i + n);
if (!Parsing.isIdentifier(src, i + n, "for")) return ParseRes.failed();
n += 3;
n += Parsing.skipEmpty(src, i + n);
if (!src.is(i + n, "(")) return ParseRes.error(src.loc(i + n), "Expected an opening paren");
n++;
n += Parsing.skipEmpty(src, i + n);
var binding = Binding.parse(src, i + n);
if (!binding.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a binding in for-of loop");
n += binding.n;
n += Parsing.skipEmpty(src, i + n);
if (!Parsing.isIdentifier(src, i + n, "of")) return ParseRes.error(src.loc(i + n), "Expected 'of' keyword after variable declaration");
n += 2;
var obj = JavaScript.parseExpression(src, i + n, 0);
if (!obj.isSuccess()) return obj.chainError(src.loc(i + n), "Expected a value");
n += obj.n;
n += Parsing.skipEmpty(src, i + n);
if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a closing paren");
n++;
var bodyRes = JavaScript.parseStatement(src, i + n);
if (!bodyRes.isSuccess()) return bodyRes.chainError(src.loc(i + n), "Expected a for-of body");
n += bodyRes.n;
return ParseRes.res(new ForOfNode(loc, label.result, binding.result, obj.result, bodyRes.result), n);
}
}

View File

@ -1,57 +0,0 @@
package me.topchetoeu.jscript.compilation.members;
import me.topchetoeu.jscript.common.SyntaxException;
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.CompileResult;
import me.topchetoeu.jscript.compilation.JavaScript;
import me.topchetoeu.jscript.compilation.Node;
import me.topchetoeu.jscript.compilation.patterns.AssignTarget;
import me.topchetoeu.jscript.compilation.values.VariableNode;
import me.topchetoeu.jscript.compilation.values.constants.StringNode;
import me.topchetoeu.jscript.compilation.values.operations.AssignNode;
public class AssignShorthandNode implements Member {
public final Location loc;
public final Node key;
public final AssignTarget target;
public final Node value;
@Override public Location loc() { return loc; }
@Override public void compile(CompileResult target, boolean pollute, boolean enumerable) {
throw new SyntaxException(loc(), "Unexpected assign shorthand in non-destructor context");
}
public AssignShorthandNode(Location loc, Node key, AssignTarget target, Node value) {
this.loc = loc;
this.key = key;
this.target = target;
this.value = value;
}
public AssignTarget target() {
return new AssignNode(loc(), target, value);
}
public static ParseRes<AssignShorthandNode> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
var var = VariableNode.parse(src, i + n);
if (!var.isSuccess()) return var.chainError();
n += var.n;
n += Parsing.skipEmpty(src, i + n);
if (!src.is(i + n, "=")) return ParseRes.failed();
n++;
var value = JavaScript.parseExpression(src, i + n, 2);
if (!value.isSuccess()) return value.chainError(src.loc(i + n), "Expected a shorthand initializer");
n += value.n;
return ParseRes.res(new AssignShorthandNode(loc, new StringNode(loc, var.result.name), var.result, value.result), n);
}
}

View File

@ -1,70 +0,0 @@
package me.topchetoeu.jscript.compilation.members;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.common.environment.Environment;
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.CompileResult;
import me.topchetoeu.jscript.compilation.CompoundNode;
import me.topchetoeu.jscript.compilation.FunctionNode;
import me.topchetoeu.jscript.compilation.Node;
import me.topchetoeu.jscript.compilation.Parameters;
import me.topchetoeu.jscript.compilation.values.ObjectNode;
import me.topchetoeu.jscript.compilation.values.constants.StringNode;
public class MethodMemberNode extends FunctionNode implements Member {
public final Node key;
@Override public String name() {
if (key instanceof StringNode str) return str.value;
else return null;
}
@Override protected Environment rootEnv(Environment env) {
return env;
}
@Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
if (pollute) target.add(Instruction.dup());
key.compile(target, true);
var id = target.addChild(compileBody(target, name, null));
target.add(_i -> Instruction.loadFunc(id, true, false, false, false, null, captures(id, target)));
}
@Override public void compile(CompileResult target, boolean pollute, boolean enumerable) {
compile(target, pollute);
target.add(Instruction.defField(enumerable));
}
public MethodMemberNode(Location loc, Location end, Node key, Parameters params, CompoundNode body) {
super(loc, end, params, body);
this.key = key;
}
public static ParseRes<MethodMemberNode> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
var name = ObjectNode.parsePropName(src, i + n);
if (!name.isSuccess()) return name.chainError();
n += name.n;
var params = Parameters.parseParameters(src, i + n);
if (!params.isSuccess()) return params.chainError(src.loc(i + n), "Expected an argument list");
n += params.n;
var body = CompoundNode.parse(src, i + n);
if (!body.isSuccess()) return body.chainError(src.loc(i + n), "Expected a compound statement for property accessor.");
n += body.n;
var end = src.loc(i + n - 1);
return ParseRes.res(new MethodMemberNode(
loc, end, name.result, params.result, body.result
), n);
}
}

View File

@ -1,84 +0,0 @@
package me.topchetoeu.jscript.compilation.patterns;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Operation;
import me.topchetoeu.jscript.common.SyntaxException;
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.CompileResult;
import me.topchetoeu.jscript.compilation.JavaScript;
import me.topchetoeu.jscript.compilation.Node;
import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType;
public class AssignPattern implements Pattern {
public final Location loc;
public final AssignTarget assignable;
public final Node value;
@Override public Location loc() { return loc; }
@Override public void destructDeclResolve(CompileResult target) {
if (!(assignable instanceof Pattern p)) throw new SyntaxException(assignable.loc(), "Unexpected non-pattern in destruct context");
p.destructDeclResolve(target);
}
private void common(CompileResult target) {
target.add(Instruction.dup());
target.add(Instruction.pushUndefined());
target.add(Instruction.operation(Operation.EQUALS));
var start = target.temp();
target.add(Instruction.discard());
value.compile(target, true);
target.set(start, Instruction.jmpIfNot(target.size() - start));
}
@Override public void declare(CompileResult target, DeclarationType decl, boolean lateInitializer) {
if (lateInitializer) {
if (assignable instanceof Pattern p) p.declare(target, decl, lateInitializer);
else throw new SyntaxException(assignable.loc(), "Unexpected non-pattern in destruct context");
}
else throw new SyntaxException(loc(), "Expected an assignment value for destructor declaration");
}
@Override public void destruct(CompileResult target, DeclarationType decl, boolean shouldDeclare) {
if (!(assignable instanceof Pattern p)) throw new SyntaxException(assignable.loc(), "Unexpected non-pattern in destruct context");
common(target);
p.destruct(target, decl, shouldDeclare);
}
@Override public void beforeAssign(CompileResult target) {
assignable.beforeAssign(target);
}
@Override public void afterAssign(CompileResult target, boolean pollute) {
common(target);
assignable.afterAssign(target, false);
}
public AssignPattern(Location loc, Pattern assignable, Node value) {
this.loc = loc;
this.assignable = assignable;
this.value = value;
}
public static ParseRes<AssignPattern> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
var pattern = Pattern.parse(src, i + n, false);
if (!pattern.isSuccess()) return pattern.chainError();
n += pattern.n;
n += Parsing.skipEmpty(src, i + n);
if (!src.is(i + n, "=")) return ParseRes.failed();
n++;
var value = JavaScript.parseExpression(src, i + n, 2);
if (!value.isSuccess()) return value.chainError(src.loc(i + n), "Expected a default value");
n += value.n;
return ParseRes.res(new AssignPattern(loc, pattern.result, value.result), n);
}
}

View File

@ -1,80 +0,0 @@
package me.topchetoeu.jscript.compilation.patterns;
import me.topchetoeu.jscript.common.SyntaxException;
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.CompileResult;
import me.topchetoeu.jscript.compilation.JavaScript;
import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType;
public class Binding implements Pattern {
public final Location loc;
public final DeclarationType type;
public final AssignTarget assignable;
@Override public Location loc() { return loc; }
@Override public void destructDeclResolve(CompileResult target) {
if (type != null && !type.strict) {
if (!(assignable instanceof Pattern p)) throw new SyntaxException(assignable.loc(), "Unexpected non-pattern in destruct context");
p.destructDeclResolve(target);
}
}
@Override public void destruct(CompileResult target, DeclarationType decl, boolean shouldDeclare) {
if (!(assignable instanceof Pattern p)) throw new SyntaxException(assignable.loc(), "Unexpected non-pattern in destruct context");
p.destruct(target, decl, shouldDeclare);
}
@Override public void declare(CompileResult target, DeclarationType decl, boolean lateInitializer) {
if (!(assignable instanceof Pattern p)) throw new SyntaxException(assignable.loc(), "Unexpected non-pattern in destruct context");
p.declare(target, decl, lateInitializer);
}
public void resolve(CompileResult target) {
if (type != null) destructDeclResolve(target);
}
public void declare(CompileResult target, boolean hasInit) {
if (type != null) destructVar(target, type, hasInit);
}
public void declareLateInit(CompileResult target) {
if (type != null) declare(target, type, true);
}
@Override public void afterAssign(CompileResult target, boolean pollute) {
assignable.assign(target, pollute);
}
public Binding(Location loc, DeclarationType type, AssignTarget assignable) {
this.loc = loc;
this.type = type;
this.assignable = assignable;
}
public static ParseRes<Binding> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
var declType = JavaScript.parseDeclarationType(src, i + n);
if (!declType.isSuccess()) {
var res = JavaScript.parseExpression(src, i + n, 13);
if (res.isSuccess() && res.result instanceof AssignTargetLike target) {
n += res.n;
return ParseRes.res(new Binding(loc, null, target.toAssignTarget()), n);
}
else return ParseRes.failed();
}
else {
n += declType.n;
n += Parsing.skipEmpty(src, i + n);
var res = Pattern.parse(src, i + n, false);
if (!res.isSuccess()) return ParseRes.failed();
n += res.n;
return ParseRes.res(new Binding(loc, declType.result, res.result), n);
}
}
}

View File

@ -1,161 +0,0 @@
package me.topchetoeu.jscript.compilation.patterns;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Consumer;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.SyntaxException;
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.CompileResult;
import me.topchetoeu.jscript.compilation.Node;
import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType;
import me.topchetoeu.jscript.compilation.values.ObjectNode;
import me.topchetoeu.jscript.compilation.values.VariableNode;
import me.topchetoeu.jscript.compilation.values.constants.StringNode;
import me.topchetoeu.jscript.compilation.values.operations.IndexNode;
public class ObjectPattern extends Node implements Pattern {
public static final class Member {
public final Node key;
public final AssignTarget consumable;
public Member(Node key, AssignTarget consumer) {
this.key = key;
this.consumable = consumer;
}
}
public final List<Member> members;
public void compile(CompileResult target, Consumer<AssignTarget> consumer, boolean pollute) {
for (var el : members) {
target.add(Instruction.dup());
IndexNode.indexLoad(target, el.key, true);
consumer.accept(el.consumable);
}
if (!pollute) target.add(Instruction.discard());
}
@Override public void destructDeclResolve(CompileResult target) {
for (var t : members) {
if (t.consumable instanceof Pattern p) p.destructDeclResolve(target);
else throw new SyntaxException(t.consumable.loc(), "Unexpected non-pattern in destruct context");
}
}
@Override public void destruct(CompileResult target, DeclarationType decl, boolean shouldDeclare) {
compile(target, t -> {
if (t instanceof Pattern p) p.destruct(target, decl, shouldDeclare);
else throw new SyntaxException(t.loc(), "Unexpected non-pattern in destruct context");
}, false);
}
@Override public void afterAssign(CompileResult target, boolean pollute) {
compile(target, t -> t.assign(target, false), pollute);
}
@Override public void declare(CompileResult target, DeclarationType decl, boolean lateInitializer) {
if (lateInitializer) {
for (var t : members) {
if (t.consumable instanceof Pattern p) p.declare(target, decl, lateInitializer);
else throw new SyntaxException(t.consumable.loc(), "Unexpected non-pattern in destruct context");
}
}
else throw new SyntaxException(loc(), "Object pattern must be initialized");
}
public ObjectPattern(Location loc, List<Member> members) {
super(loc);
this.members = members;
}
private static ParseRes<Member> parseShorthand(Source src, int i) {
ParseRes<Pattern> res = ParseRes.first(src, i,
AssignPattern::parse,
VariableNode::parse
);
if (res.isSuccess()) {
if (res.result instanceof AssignPattern assign) {
if (assign.assignable instanceof VariableNode var) {
return ParseRes.res(new Member(new StringNode(var.loc(), var.name), res.result), res.n);
}
}
else if (res.result instanceof VariableNode var) {
return ParseRes.res(new Member(new StringNode(var.loc(), var.name), res.result), res.n);
}
}
return res.chainError();
}
private static ParseRes<Member> parseKeyed(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var key = ObjectNode.parsePropName(src, i + n);
if (!key.isSuccess()) return key.chainError();
n += key.n;
n += Parsing.skipEmpty(src, i + n);
if (!src.is(i + n , ":")) return ParseRes.failed();
n++;
ParseRes<Pattern> res = Pattern.parse(src, i + n, true);
if (!res.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a pattern after colon");
n += res.n;
return ParseRes.res(new Member(key.result, res.result), n);
}
public static ParseRes<ObjectPattern> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
if (!src.is(i + n, "{")) return ParseRes.failed();
n++;
n += Parsing.skipEmpty(src, i + n);
var members = new LinkedList<Member>();
if (src.is(i + n, "}")) {
n++;
return ParseRes.res(new ObjectPattern(loc, members), n);
}
while (true) {
ParseRes<Member> prop = ParseRes.first(src, i + n,
ObjectPattern::parseKeyed,
ObjectPattern::parseShorthand
);
if (!prop.isSuccess()) return prop.chainError(src.loc(i + n), "Expected a member in object pattern");
n += prop.n;
members.add(prop.result);
n += Parsing.skipEmpty(src, i + n);
if (src.is(i + n, ",")) {
n++;
n += Parsing.skipEmpty(src, i + n);
if (src.is(i + n, "}")) {
n++;
break;
}
continue;
}
else if (src.is(i + n, "}")) {
n++;
break;
}
else ParseRes.error(src.loc(i + n), "Expected a comma or a closing brace.");
}
return ParseRes.res(new ObjectPattern(loc, members), n);
}
}

View File

@ -1,59 +0,0 @@
package me.topchetoeu.jscript.compilation.patterns;
import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.common.parsing.ParseRes;
import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType;
import me.topchetoeu.jscript.compilation.values.VariableNode;
/**
* Represents all nodes that can be a destructors (note that all destructors are assign targets, too)
*/
public interface Pattern extends AssignTarget {
Location loc();
/**
* Called when the destructor has to declare
* @param target
*/
void destructDeclResolve(CompileResult target);
/**
* Called when a declaration-like is being destructed
* @param decl The variable type the destructor must declare, if it is a named pne
*/
void destruct(CompileResult target, DeclarationType decl, boolean shouldDeclare);
/**
* Run when destructing a declaration without an initializer
*/
void declare(CompileResult target, DeclarationType decl, boolean lateInitializer);
public default void destructArg(CompileResult target, DeclarationType decl) {
destruct(target, decl, false);
}
public default void destructVar(CompileResult target, DeclarationType decl, boolean hasInitializer) {
if (hasInitializer) {
if (decl == null || !decl.strict) destruct(target, null, true);
else destruct(target, decl, true);
}
else {
if (decl == null || !decl.strict) declare(target, null, false);
else declare(target, decl, false);
}
}
public static ParseRes<Pattern> parse(Source src, int i, boolean withDefault) {
return withDefault ?
ParseRes.first(src, i,
AssignPattern::parse,
ObjectPattern::parse,
VariableNode::parse
) :
ParseRes.first(src, i,
ObjectPattern::parse,
VariableNode::parse
);
}
}

View File

@ -1,34 +0,0 @@
package me.topchetoeu.jscript.compilation.values;
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.ClassNode;
import me.topchetoeu.jscript.compilation.JavaScript;
public class ClassValueNode extends ClassNode {
public ClassValueNode(Location loc, Location end, String name, ClassBody body) {
super(loc, end, name, body);
}
public static ParseRes<ClassValueNode> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
if (!Parsing.isIdentifier(src, i + n, "class")) return ParseRes.failed();
n += 5;
var name = Parsing.parseIdentifier(src, i + n);
if (name.isSuccess() && !JavaScript.checkVarName(name.result)) {
name = ParseRes.error(src.loc(i + n), "Unexpected keyword '" + name.result + "'");
}
n += name.n;
var body = parseBody(src, i + n);
if (!body.isSuccess()) return body.chainError(name).chainError(src.loc(i + n), "Expected a class body");
n += body.n;
return ParseRes.res(new ClassValueNode(loc, src.loc(i + n), name.result, body.result), n);
}
}