ES6 Object and assignment destructors + object stuff #28
@ -449,8 +449,8 @@ public class Instruction {
|
||||
return new Instruction(Type.TYPEOF, varName);
|
||||
}
|
||||
|
||||
public static Instruction keys(boolean forInFormat) {
|
||||
return new Instruction(Type.KEYS, forInFormat);
|
||||
public static Instruction keys(boolean own, boolean onlyEnumerable) {
|
||||
return new Instruction(Type.KEYS, own, onlyEnumerable);
|
||||
}
|
||||
|
||||
public static Instruction defProp() {
|
||||
|
@ -1,9 +0,0 @@
|
||||
package me.topchetoeu.jscript.compilation;
|
||||
|
||||
public interface AssignableNode {
|
||||
public void compileBeforeAssign(CompileResult target, boolean operator);
|
||||
public void compileAfterAssign(CompileResult target, boolean operator, boolean pollute);
|
||||
public default String assignName() {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
package me.topchetoeu.jscript.compilation.control;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.Operation;
|
||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
||||
import me.topchetoeu.jscript.common.parsing.Location;
|
||||
import me.topchetoeu.jscript.common.parsing.ParseRes;
|
||||
@ -33,12 +32,10 @@ public class ForInNode extends Node {
|
||||
if (declType != null && declType.strict) target.scope.defineStrict(new Variable(varName, declType.readonly), varLocation);
|
||||
|
||||
object.compile(target, true, BreakpointType.STEP_OVER);
|
||||
target.add(Instruction.keys(true));
|
||||
target.add(Instruction.keys(false, true));
|
||||
|
||||
int start = target.size();
|
||||
target.add(Instruction.dup());
|
||||
target.add(Instruction.pushUndefined());
|
||||
target.add(Instruction.operation(Operation.EQUALS));
|
||||
int mid = target.temp();
|
||||
|
||||
target.add(Instruction.loadMember("value")).setLocation(varLocation);
|
||||
@ -55,7 +52,7 @@ public class ForInNode extends Node {
|
||||
|
||||
target.add(Instruction.jmp(start - endI));
|
||||
target.add(Instruction.discard());
|
||||
target.set(mid, Instruction.jmpIf(endI - mid + 1));
|
||||
target.set(mid, Instruction.jmpIfNot(endI - mid + 1));
|
||||
if (pollute) target.add(Instruction.pushUndefined());
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,69 @@
|
||||
package me.topchetoeu.jscript.compilation.destructing;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.Operation;
|
||||
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.DeferredIntSupplier;
|
||||
import me.topchetoeu.jscript.compilation.JavaScript;
|
||||
import me.topchetoeu.jscript.compilation.Node;
|
||||
import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType;
|
||||
import me.topchetoeu.jscript.compilation.scope.Variable;
|
||||
import me.topchetoeu.jscript.compilation.values.VariableNode;
|
||||
|
||||
public class AssignDestructorNode extends Node implements Destructor {
|
||||
public final String name;
|
||||
public final Node value;
|
||||
|
||||
@Override public void destructDeclResolve(CompileResult target) {
|
||||
target.scope.define(new Variable(name, false), loc());
|
||||
}
|
||||
@Override public void destructArg(CompileResult target) {
|
||||
var v = target.scope.define(new Variable(name, false), loc());
|
||||
destructAssign(target);
|
||||
target.add(_i -> v.index().toSet(false));
|
||||
}
|
||||
@Override public void afterAssign(CompileResult target, DeclarationType decl) {
|
||||
if (decl != null && decl.strict) target.scope.define(new Variable(name, decl.readonly), loc());
|
||||
var end = new DeferredIntSupplier();
|
||||
|
||||
target.add(Instruction.dup());
|
||||
target.add(Instruction.pushUndefined());
|
||||
target.add(Instruction.operation(Operation.EQUALS));
|
||||
target.add(Instruction.jmpIfNot(end));
|
||||
target.add(Instruction.discard());
|
||||
value.compile(target, true);
|
||||
|
||||
end.set(target.size());
|
||||
|
||||
target.add(VariableNode.toSet(target, loc(), name, false, decl != null));
|
||||
}
|
||||
|
||||
public AssignDestructorNode(Location loc, String name, Node value) {
|
||||
super(loc);
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static ParseRes<AssignDestructorNode> parse(Source src, int i) {
|
||||
var n = Parsing.skipEmpty(src, i);
|
||||
var loc = src.loc(i + n);
|
||||
|
||||
var name = Parsing.parseIdentifier(src, i);
|
||||
if (!JavaScript.checkVarName(null)) return ParseRes.error(src.loc(i + n), String.format("Unexpected keyword '%s'", name.result));
|
||||
n += name.n;
|
||||
n += Parsing.skipEmpty(src, i + n);
|
||||
|
||||
if (!src.is(i, "=")) return ParseRes.failed();
|
||||
n++;
|
||||
|
||||
var value = JavaScript.parseExpression(src, i, 2);
|
||||
if (value.isError()) return ParseRes.error(src.loc(i + n), "Expected a value after '='");
|
||||
n += value.n;
|
||||
|
||||
return ParseRes.res(new AssignDestructorNode(loc, name.result, value.result), n);
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
package me.topchetoeu.jscript.compilation.destructing;
|
||||
|
||||
public interface AssignTarget extends Destructor {
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package me.topchetoeu.jscript.compilation.destructing;
|
||||
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
|
||||
public interface ChangeTarget extends AssignTarget {
|
||||
void beforeChange(CompileResult target);
|
||||
void afterChange(CompileResult target, boolean pollute);
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package me.topchetoeu.jscript.compilation.destructing;
|
||||
|
||||
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;
|
||||
import me.topchetoeu.jscript.compilation.values.ObjectDestructorNode;
|
||||
import me.topchetoeu.jscript.compilation.values.VariableNode;
|
||||
|
||||
public interface Destructor {
|
||||
void destructDeclResolve(CompileResult target);
|
||||
|
||||
default void destructArg(CompileResult target) {
|
||||
beforeAssign(target, null);
|
||||
afterAssign(target, null);
|
||||
}
|
||||
default void beforeAssign(CompileResult target, DeclarationType decl) {}
|
||||
void afterAssign(CompileResult target, DeclarationType decl, boolean pollute);
|
||||
|
||||
public static ParseRes<Destructor> parse(Source src, int i) {
|
||||
var n = Parsing.skipEmpty(src, i);
|
||||
|
||||
ParseRes<Destructor> first = ParseRes.first(src, i + n,
|
||||
ObjectDestructorNode::parse,
|
||||
AssignDestructorNode::parse,
|
||||
VariableNode::parse
|
||||
);
|
||||
if (first.isSuccess()) return first.addN(n);
|
||||
|
||||
var exp = JavaScript.parseExpression(src, i, 2);
|
||||
if (!(exp.result instanceof Destructor destructor)) return ParseRes.error(src.loc(i + n), "Expected a destructor expression");
|
||||
n += exp.n;
|
||||
|
||||
return ParseRes.res(destructor, n);
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package me.topchetoeu.jscript.compilation.destructing;
|
||||
|
||||
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.values.VariableNode;
|
||||
|
||||
public interface NamedDestructor extends Destructor {
|
||||
String name();
|
||||
|
||||
public static ParseRes<Destructor> parse(Source src, int i) {
|
||||
var n = Parsing.skipEmpty(src, i);
|
||||
|
||||
ParseRes<Destructor> first = ParseRes.first(src, i + n,
|
||||
AssignDestructorNode::parse,
|
||||
VariableNode::parse
|
||||
);
|
||||
return first.addN(n);
|
||||
}
|
||||
}
|
@ -61,6 +61,16 @@ public class Scope {
|
||||
if (ended) throw new IllegalStateException("Cannot define in an ended scope");
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a nameless variable for holding intermediate temporary values
|
||||
*
|
||||
* @throws RuntimeException If the scope is finalized or has an active child
|
||||
*/
|
||||
public Variable defineTemp() {
|
||||
checkNotEnded();
|
||||
return this.variables.add(new Variable("<temp>", false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines an ES5-style variable
|
||||
*
|
||||
|
@ -0,0 +1,199 @@
|
||||
package me.topchetoeu.jscript.compilation.values;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
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.destructing.Destructor;
|
||||
|
||||
|
||||
public class ObjectDestructorNode extends Node implements Destructor {
|
||||
public final LinkedHashMap<String, Destructor> destructors;
|
||||
public final VariableNode restDestructor;
|
||||
|
||||
private void compileRestObjBuilder(CompileResult target, int srcDupN) {
|
||||
var subtarget = target.subtarget();
|
||||
var src = subtarget.scope.defineTemp();
|
||||
var dst = subtarget.scope.defineTemp();
|
||||
|
||||
target.add(Instruction.loadObj());
|
||||
target.add(_i -> src.index().toSet(true));
|
||||
target.add(_i -> dst.index().toSet(destructors.size() > 0));
|
||||
|
||||
target.add(Instruction.keys(true, true));
|
||||
var start = target.size();
|
||||
|
||||
target.add(Instruction.dup());
|
||||
var mid = target.temp();
|
||||
|
||||
target.add(_i -> src.index().toGet());
|
||||
target.add(Instruction.dup(1, 1));
|
||||
target.add(Instruction.loadMember());
|
||||
|
||||
target.add(_i -> dst.index().toGet());
|
||||
target.add(Instruction.dup(1, 1));
|
||||
target.add(Instruction.storeMember());
|
||||
|
||||
target.add(Instruction.discard());
|
||||
var end = target.size();
|
||||
target.add(Instruction.jmp(start - end));
|
||||
target.set(mid, Instruction.jmpIfNot(end - mid + 1));
|
||||
|
||||
target.add(Instruction.discard());
|
||||
|
||||
target.add(Instruction.dup(srcDupN, 1));
|
||||
|
||||
target.scope.end();
|
||||
}
|
||||
|
||||
@Override public void destructDeclResolve(CompileResult target) {
|
||||
for (var el : destructors.values()) {
|
||||
el.destructDeclResolve(target);
|
||||
}
|
||||
|
||||
if (restDestructor != null) restDestructor.destructDeclResolve(target);
|
||||
}
|
||||
@Override public void destructArg(CompileResult target) {
|
||||
if (restDestructor != null) compileRestObjBuilder(target, destructors.size() * 2);
|
||||
else if (destructors.size() > 0) target.add(Instruction.dup(destructors.size(), 0));
|
||||
|
||||
for (var el : destructors.entrySet()) {
|
||||
if (restDestructor != null) {
|
||||
target.add(Instruction.pushValue(el.getKey()));
|
||||
target.add(Instruction.delete());
|
||||
}
|
||||
|
||||
target.add(Instruction.loadMember(el.getKey()));
|
||||
el.getValue().destructArg(target);
|
||||
}
|
||||
|
||||
if (restDestructor != null) restDestructor.destructArg(target);
|
||||
|
||||
target.add(Instruction.discard());
|
||||
}
|
||||
@Override public void afterAssign(CompileResult target, DeclarationType decl) {
|
||||
if (restDestructor != null) compileRestObjBuilder(target, destructors.size() * 2);
|
||||
else if (destructors.size() > 0) target.add(Instruction.dup(destructors.size(), 0));
|
||||
|
||||
for (var el : destructors.entrySet()) {
|
||||
if (restDestructor != null) {
|
||||
target.add(Instruction.pushValue(el.getKey()));
|
||||
target.add(Instruction.delete());
|
||||
}
|
||||
|
||||
target.add(Instruction.loadMember(el.getKey()));
|
||||
el.getValue().afterAssign(target, decl);
|
||||
}
|
||||
|
||||
if (restDestructor != null) restDestructor.afterAssign(target, decl);
|
||||
|
||||
target.add(Instruction.discard());
|
||||
}
|
||||
@Override public void destructAssign(CompileResult target, boolean pollute) {
|
||||
if (restDestructor != null) compileRestObjBuilder(target, destructors.size() * 2);
|
||||
else if (destructors.size() > 0) target.add(Instruction.dup(destructors.size(), 0));
|
||||
|
||||
for (var el : destructors.entrySet()) {
|
||||
if (restDestructor != null) {
|
||||
target.add(Instruction.pushValue(el.getKey()));
|
||||
target.add(Instruction.delete());
|
||||
}
|
||||
|
||||
target.add(Instruction.loadMember(el.getKey()));
|
||||
el.getValue().destructAssign(target, pollute);
|
||||
}
|
||||
|
||||
if (restDestructor != null) restDestructor.destructAssign(target, pollute);
|
||||
|
||||
if (!pollute) target.add(Instruction.discard());
|
||||
}
|
||||
|
||||
public ObjectDestructorNode(Location loc, Map<String, Destructor> map, VariableNode rest) {
|
||||
super(loc);
|
||||
this.destructors = new LinkedHashMap<>(map);
|
||||
this.restDestructor = rest;
|
||||
}
|
||||
public ObjectDestructorNode(Location loc, Map<String, Destructor> map) {
|
||||
super(loc);
|
||||
this.destructors = new LinkedHashMap<>(map);
|
||||
this.restDestructor = null;
|
||||
}
|
||||
|
||||
private static ParseRes<String> parsePropName(Source src, int i) {
|
||||
var n = Parsing.skipEmpty(src, i);
|
||||
|
||||
var res = ParseRes.first(src, i + n,
|
||||
Parsing::parseIdentifier,
|
||||
Parsing::parseString,
|
||||
(s, j) -> Parsing.parseNumber(s, j, false)
|
||||
);
|
||||
if (!res.isSuccess()) return res.chainError();
|
||||
n += res.n;
|
||||
|
||||
if (!src.is(i + n, ":")) return ParseRes.error(src.loc(i + n), "Expected a colon");
|
||||
n++;
|
||||
|
||||
return ParseRes.res(res.result.toString(), n);
|
||||
}
|
||||
|
||||
public static ParseRes<ObjectDestructorNode> 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 destructors = new LinkedHashMap<String, Destructor>();
|
||||
|
||||
if (src.is(i + n, "}")) {
|
||||
n++;
|
||||
return ParseRes.res(new ObjectDestructorNode(loc, destructors), n);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
n += Parsing.skipEmpty(src, i + n);
|
||||
|
||||
// if (src.is(i, null))
|
||||
|
||||
var name = parsePropName(src, i + n);
|
||||
if (!name.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a field name");
|
||||
n += name.n;
|
||||
n += Parsing.skipEmpty(src, i + n);
|
||||
|
||||
var destructor = Destructor.parse(src, i + n);
|
||||
if (!destructor.isSuccess()) return destructor.chainError(src.loc(i + n), "Expected a value in array list");
|
||||
n += destructor.n;
|
||||
|
||||
destructors.put(name.result, destructor.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 ObjectDestructorNode(loc, destructors), n);
|
||||
}
|
||||
|
||||
}
|
@ -15,9 +15,10 @@ import me.topchetoeu.jscript.compilation.FunctionNode;
|
||||
import me.topchetoeu.jscript.compilation.FunctionValueNode;
|
||||
import me.topchetoeu.jscript.compilation.JavaScript;
|
||||
import me.topchetoeu.jscript.compilation.Node;
|
||||
import me.topchetoeu.jscript.compilation.destructing.Destructor;
|
||||
|
||||
|
||||
public class ObjectNode extends Node {
|
||||
public class ObjectNode extends Node implements Destructor {
|
||||
public static class ObjProp {
|
||||
public final String name;
|
||||
public final String access;
|
||||
@ -148,7 +149,7 @@ public class ObjectNode extends Node {
|
||||
n++;
|
||||
|
||||
var valRes = JavaScript.parseExpression(src, i + n, 2);
|
||||
if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value in array list");
|
||||
if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after property key");
|
||||
n += valRes.n;
|
||||
|
||||
values.put(name.result, valRes.result);
|
||||
|
@ -12,9 +12,11 @@ import me.topchetoeu.jscript.compilation.AssignableNode;
|
||||
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;
|
||||
import me.topchetoeu.jscript.compilation.scope.Variable;
|
||||
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
|
||||
|
||||
public class VariableNode extends Node implements AssignableNode {
|
||||
public class VariableNode extends Node implements AssignTarget {
|
||||
public final String name;
|
||||
|
||||
@Override public String assignName() { return name; }
|
||||
@ -28,6 +30,25 @@ public class VariableNode extends Node implements AssignableNode {
|
||||
target.add(VariableNode.toSet(target, loc(), name, pollute, false));
|
||||
}
|
||||
|
||||
@Override public void destructArg(CompileResult target) {
|
||||
target.add(_i -> target.scope.define(new Variable(name, false), loc()).index().toSet(false));
|
||||
}
|
||||
@Override public void destructDeclResolve(CompileResult target) {
|
||||
target.scope.define(new Variable(name, false), loc());
|
||||
}
|
||||
@Override public void afterAssign(CompileResult target, DeclarationType decl) {
|
||||
if (decl.strict) {
|
||||
var v = target.scope.defineStrict(new Variable(name, decl.readonly), loc());
|
||||
target.add(_i -> v.index().toSet(false));
|
||||
}
|
||||
else {
|
||||
target.add(VariableNode.toSet(target, loc(), name, false, true));
|
||||
}
|
||||
}
|
||||
@Override public void destructAssign(CompileResult target, boolean pollute) {
|
||||
target.add(VariableNode.toSet(target, loc(), name, pollute, false));
|
||||
}
|
||||
|
||||
@Override public void compile(CompileResult target, boolean pollute) {
|
||||
var i = target.scope.get(name, false);
|
||||
|
||||
@ -81,10 +102,8 @@ public class VariableNode extends Node implements AssignableNode {
|
||||
n += literal.n;
|
||||
|
||||
if (!JavaScript.checkVarName(literal.result)) {
|
||||
if (literal.result.equals("await")) return ParseRes.error(src.loc(i + n), "'await' expressions are not supported.");
|
||||
if (literal.result.equals("const")) return ParseRes.error(src.loc(i + n), "'const' declarations are not supported.");
|
||||
if (literal.result.equals("let")) return ParseRes.error(src.loc(i + n), "'let' declarations are not supported.");
|
||||
return ParseRes.error(src.loc(i + n), String.format("Unexpected keyword '%s'.", literal.result));
|
||||
if (literal.result.equals("await")) return ParseRes.error(src.loc(i + n), "'await' expressions are not supported");
|
||||
return ParseRes.error(src.loc(i + n), String.format("Unexpected keyword '%s'", literal.result));
|
||||
}
|
||||
|
||||
return ParseRes.res(new VariableNode(loc, literal.result), n);
|
||||
|
@ -1,21 +1,68 @@
|
||||
package me.topchetoeu.jscript.compilation.values.operations;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.Operation;
|
||||
import me.topchetoeu.jscript.common.parsing.Location;
|
||||
import me.topchetoeu.jscript.compilation.AssignableNode;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.Node;
|
||||
import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType;
|
||||
import me.topchetoeu.jscript.compilation.destructing.AssignTarget;
|
||||
import me.topchetoeu.jscript.compilation.destructing.Destructor;
|
||||
import me.topchetoeu.jscript.compilation.scope.Variable;
|
||||
import me.topchetoeu.jscript.compilation.values.VariableNode;
|
||||
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
|
||||
|
||||
public class AssignNode extends Node {
|
||||
public final AssignableNode assignable;
|
||||
public class AssignNode extends Node implements Destructor {
|
||||
public final AssignTarget assignable;
|
||||
public final Node value;
|
||||
|
||||
@Override public void compile(CompileResult target, boolean pollute) {
|
||||
assignable.compileBeforeAssign(target, false);
|
||||
value.compile(target, true);
|
||||
assignable.compileAfterAssign(target, false, pollute);
|
||||
@Override public void destructDeclResolve(CompileResult target) {
|
||||
if (!(assignable instanceof VariableNode var)) {
|
||||
throw new SyntaxException(loc(), "Assign target in declaration destructor must be a variable");
|
||||
}
|
||||
|
||||
public AssignNode(Location loc, AssignableNode assignable, Node value) {
|
||||
target.scope.define(new Variable(var.name, false), var.loc());
|
||||
}
|
||||
|
||||
@Override public void destructArg(CompileResult target) {
|
||||
if (!(assignable instanceof VariableNode var)) {
|
||||
throw new SyntaxException(loc(), "Assign target in declaration destructor must be a variable");
|
||||
}
|
||||
|
||||
var v = target.scope.define(new Variable(var.name, false), var.loc());
|
||||
afterAssign(target, null, false);
|
||||
target.add(_i -> v.index().toSet(false));
|
||||
}
|
||||
@Override public void afterAssign(CompileResult target, DeclarationType decl, boolean pollute) {
|
||||
if (decl != null && decl.strict) {
|
||||
if (!(assignable instanceof VariableNode var)) {
|
||||
throw new SyntaxException(loc(), "Assign target in declaration destructor must be a variable");
|
||||
}
|
||||
target.scope.define(new Variable(var.name, decl.strict), var.loc());
|
||||
}
|
||||
|
||||
assignable.beforeAssign(target, decl);
|
||||
|
||||
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.jmp(target.size() - start));
|
||||
|
||||
assignable.afterAssign(target, decl, pollute);
|
||||
}
|
||||
|
||||
@Override public void compile(CompileResult target, boolean pollute) {
|
||||
assignable.beforeAssign(target, null);
|
||||
value.compile(target, true);
|
||||
assignable.afterAssign(target, null, pollute);
|
||||
}
|
||||
|
||||
public AssignNode(Location loc, AssignTarget assignable, Node value) {
|
||||
super(loc);
|
||||
this.assignable = assignable;
|
||||
this.value = value;
|
||||
|
@ -6,25 +6,25 @@ 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.AssignableNode;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.JavaScript;
|
||||
import me.topchetoeu.jscript.compilation.Node;
|
||||
import me.topchetoeu.jscript.compilation.destructing.ChangeTarget;
|
||||
import me.topchetoeu.jscript.compilation.values.constants.NumberNode;
|
||||
|
||||
public class ChangeNode extends Node {
|
||||
public final AssignableNode assignable;
|
||||
public final ChangeTarget assignable;
|
||||
public final Node value;
|
||||
public final Operation op;
|
||||
|
||||
@Override public void compile(CompileResult target, boolean pollute) {
|
||||
assignable.compileBeforeAssign(target, true);
|
||||
assignable.beforeChange(target);
|
||||
value.compile(target, true);
|
||||
target.add(Instruction.operation(op));
|
||||
assignable.compileAfterAssign(target, true, pollute);
|
||||
assignable.afterChange(target, pollute);
|
||||
}
|
||||
|
||||
public ChangeNode(Location loc, AssignableNode assignable, Node value, Operation op) {
|
||||
public ChangeNode(Location loc, ChangeTarget assignable, Node value, Operation op) {
|
||||
super(loc);
|
||||
this.assignable = assignable;
|
||||
this.value = value;
|
||||
@ -40,9 +40,9 @@ public class ChangeNode extends Node {
|
||||
|
||||
var res = JavaScript.parseExpression(src, i + n, 15);
|
||||
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected assignable value after prefix operator.");
|
||||
else if (!(res.result instanceof AssignableNode)) return ParseRes.error(src.loc(i + n), "Expected assignable value after prefix operator.");
|
||||
else if (!(res.result instanceof ChangeTarget)) return ParseRes.error(src.loc(i + n), "Expected assignable value after prefix operator.");
|
||||
|
||||
return ParseRes.res(new ChangeNode(loc, (AssignableNode)res.result, new NumberNode(loc, -1), Operation.SUBTRACT), n + res.n);
|
||||
return ParseRes.res(new ChangeNode(loc, (ChangeTarget)res.result, new NumberNode(loc, -1), Operation.SUBTRACT), n + res.n);
|
||||
}
|
||||
public static ParseRes<ChangeNode> parsePrefixDecrease(Source src, int i) {
|
||||
var n = Parsing.skipEmpty(src, i);
|
||||
@ -53,8 +53,8 @@ public class ChangeNode extends Node {
|
||||
|
||||
var res = JavaScript.parseExpression(src, i + n, 15);
|
||||
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected assignable value after prefix operator.");
|
||||
else if (!(res.result instanceof AssignableNode)) return ParseRes.error(src.loc(i + n), "Expected assignable value after prefix operator.");
|
||||
else if (!(res.result instanceof ChangeTarget)) return ParseRes.error(src.loc(i + n), "Expected assignable value after prefix operator.");
|
||||
|
||||
return ParseRes.res(new ChangeNode(loc, (AssignableNode)res.result, new NumberNode(loc, 1), Operation.SUBTRACT), n + res.n);
|
||||
return ParseRes.res(new ChangeNode(loc, (ChangeTarget)res.result, new NumberNode(loc, 1), Operation.SUBTRACT), n + res.n);
|
||||
}
|
||||
}
|
||||
|
@ -10,39 +10,56 @@ import me.topchetoeu.jscript.compilation.AssignableNode;
|
||||
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;
|
||||
import me.topchetoeu.jscript.compilation.destructing.AssignTarget;
|
||||
import me.topchetoeu.jscript.compilation.destructing.ChangeTarget;
|
||||
import me.topchetoeu.jscript.compilation.values.constants.NumberNode;
|
||||
import me.topchetoeu.jscript.compilation.values.constants.StringNode;
|
||||
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
|
||||
|
||||
public class IndexNode extends Node implements AssignableNode {
|
||||
public class IndexNode extends Node implements ChangeTarget {
|
||||
public final Node object;
|
||||
public final Node index;
|
||||
|
||||
@Override public void compileBeforeAssign(CompileResult target, boolean op) {
|
||||
@Override public void destructDeclResolve(CompileResult target) {
|
||||
throw new SyntaxException(loc(), "Unexpected index in destructor");
|
||||
}
|
||||
@Override public void destructArg(CompileResult target) {
|
||||
throw new SyntaxException(loc(), "Unexpected index in destructor");
|
||||
}
|
||||
|
||||
@Override public void beforeAssign(CompileResult target, DeclarationType decl) {
|
||||
if (decl != null) throw new SyntaxException(loc(), "Unexpected index in destructor");
|
||||
object.compile(target, true);
|
||||
|
||||
if (index instanceof NumberNode num && (int)num.value == num.value) return;
|
||||
if (index instanceof StringNode) return;
|
||||
index.compile(target, true);
|
||||
|
||||
target.add(Instruction.dup(1, 1));
|
||||
target.add(Instruction.dup(1, 1));
|
||||
target.add(Instruction.loadMember());
|
||||
}
|
||||
@Override public void beforeChange(CompileResult target) {
|
||||
object.compile(target, true);
|
||||
|
||||
if (index instanceof NumberNode num && (int)num.value == num.value) {
|
||||
if (op) {
|
||||
target.add(Instruction.dup());
|
||||
target.add(Instruction.loadMember((int)num.value));
|
||||
}
|
||||
}
|
||||
else if (index instanceof StringNode str) {
|
||||
if (op) {
|
||||
target.add(Instruction.dup());
|
||||
target.add(Instruction.loadMember(str.value));
|
||||
}
|
||||
}
|
||||
else {
|
||||
index.compile(target, true);
|
||||
|
||||
if (op) {
|
||||
target.add(Instruction.dup(1, 1));
|
||||
target.add(Instruction.dup(1, 1));
|
||||
target.add(Instruction.loadMember());
|
||||
}
|
||||
}
|
||||
}
|
||||
@Override public void compileAfterAssign(CompileResult target, boolean op, boolean pollute) {
|
||||
@Override public void afterAssign(CompileResult target, boolean op, boolean pollute) {
|
||||
if (index instanceof NumberNode num && (int)num.value == num.value) {
|
||||
target.add(Instruction.storeMember((int)num.value, pollute));
|
||||
}
|
||||
@ -54,6 +71,16 @@ public class IndexNode extends Node implements AssignableNode {
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void afterAssign(CompileResult target, DeclarationType decl) {
|
||||
throw new SyntaxException(loc(), "Illegal index in declaration destruction context");
|
||||
}
|
||||
@Override public void destructAssign(CompileResult target, boolean pollute) {
|
||||
object.compile(target, true);
|
||||
target.add(Instruction.dup(1, 1));
|
||||
compileAfterAssign(target, false, false);
|
||||
if (!pollute) target.add(Instruction.discard());
|
||||
}
|
||||
|
||||
// @Override public Node toAssign(Node val, Operation operation) {
|
||||
// return new IndexAssignNode(loc(), object, index, val, operation);
|
||||
// }
|
||||
|
@ -54,13 +54,13 @@ public class OperationNode extends Node {
|
||||
@Override public ParseRes<Node> construct(Source src, int i, Node prev) {
|
||||
var loc = src.loc(i);
|
||||
|
||||
if (!(prev instanceof AssignableNode)) return ParseRes.error(loc, String.format("Expected an assignable expression before '%s'", token));
|
||||
if (!(prev instanceof AssignTarget)) return ParseRes.error(loc, String.format("Expected an assignable expression before '%s'", token));
|
||||
|
||||
var other = JavaScript.parseExpression(src, i, precedence);
|
||||
if (!other.isSuccess()) return other.chainError(src.loc(i + other.n), String.format("Expected a value after '%s'", token));
|
||||
|
||||
if (operation == null) return ParseRes.res(new AssignNode(loc, ((AssignableNode)prev), other.result), other.n);
|
||||
else return ParseRes.res(new ChangeNode(loc, ((AssignableNode)prev), other.result, operation), other.n);
|
||||
if (operation == null) return ParseRes.res(new AssignNode(loc, ((AssignTarget)prev), other.result), other.n);
|
||||
else return ParseRes.res(new ChangeNode(loc, ((AssignTarget)prev), other.result, operation), other.n);
|
||||
}
|
||||
|
||||
public AssignmentOperatorFactory(String token, int precedence, Operation operation) {
|
||||
|
@ -21,7 +21,7 @@ public class PostfixNode extends ChangeNode {
|
||||
}
|
||||
}
|
||||
|
||||
public PostfixNode(Location loc, AssignableNode value, double addAmount) {
|
||||
public PostfixNode(Location loc, AssignTarget value, double addAmount) {
|
||||
super(loc, value, new NumberNode(loc, -addAmount), Operation.SUBTRACT);
|
||||
}
|
||||
|
||||
@ -32,10 +32,10 @@ public class PostfixNode extends ChangeNode {
|
||||
var loc = src.loc(i + n);
|
||||
|
||||
if (!src.is(i + n, "++")) return ParseRes.failed();
|
||||
if (!(prev instanceof AssignableNode)) return ParseRes.error(src.loc(i + n), "Expected assignable value before suffix operator.");
|
||||
if (!(prev instanceof AssignTarget)) return ParseRes.error(src.loc(i + n), "Expected assignable value before suffix operator.");
|
||||
n += 2;
|
||||
|
||||
return ParseRes.res(new PostfixNode(loc, (AssignableNode)prev, 1), n);
|
||||
return ParseRes.res(new PostfixNode(loc, (AssignTarget)prev, 1), n);
|
||||
}
|
||||
public static ParseRes<ChangeNode> parsePostfixDecrease(Source src, int i, Node prev, int precedence) {
|
||||
if (precedence > 15) return ParseRes.failed();
|
||||
@ -44,9 +44,9 @@ public class PostfixNode extends ChangeNode {
|
||||
var loc = src.loc(i + n);
|
||||
|
||||
if (!src.is(i + n, "--")) return ParseRes.failed();
|
||||
if (!(prev instanceof AssignableNode)) return ParseRes.error(src.loc(i + n), "Expected assignable value before suffix operator.");
|
||||
if (!(prev instanceof AssignTarget)) return ParseRes.error(src.loc(i + n), "Expected assignable value before suffix operator.");
|
||||
n += 2;
|
||||
|
||||
return ParseRes.res(new PostfixNode(loc, (AssignableNode)prev, -1), n);
|
||||
return ParseRes.res(new PostfixNode(loc, (AssignTarget)prev, -1), n);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user