ES6 Object and assignment destructors + object stuff #28

Merged
TopchetoEU merged 15 commits from TopchetoEU/destructing into master 2024-09-14 12:38:02 +00:00
17 changed files with 494 additions and 64 deletions
Showing only changes of commit 2a01b3d86e - Show all commits

View File

@ -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() {

View File

@ -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;
}
}

View File

@ -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());
}

View File

@ -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);
}
}

View File

@ -0,0 +1,4 @@
package me.topchetoeu.jscript.compilation.destructing;
public interface AssignTarget extends Destructor {
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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
*

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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);
// }

View File

@ -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) {

View File

@ -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);
}
}