From 0b3dca8b13f62bf668cf11b9024f35f1ecbfbc7e Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Thu, 19 Sep 2024 18:08:11 +0300 Subject: [PATCH] refactor: extract members into own classes --- .../members/AssignShorthandNode.java | 54 ++++ .../compilation/members/FieldMemberNode.java | 89 +++++++ .../compilation/members/MethodMemberNode.java | 62 +++++ .../members/PropertyMemberNode.java | 81 ++++++ .../compilation/values/ObjectNode.java | 237 +----------------- 5 files changed, 291 insertions(+), 232 deletions(-) create mode 100644 src/main/java/me/topchetoeu/jscript/compilation/members/AssignShorthandNode.java create mode 100644 src/main/java/me/topchetoeu/jscript/compilation/members/FieldMemberNode.java create mode 100644 src/main/java/me/topchetoeu/jscript/compilation/members/MethodMemberNode.java create mode 100644 src/main/java/me/topchetoeu/jscript/compilation/members/PropertyMemberNode.java diff --git a/src/main/java/me/topchetoeu/jscript/compilation/members/AssignShorthandNode.java b/src/main/java/me/topchetoeu/jscript/compilation/members/AssignShorthandNode.java new file mode 100644 index 0000000..5fc491c --- /dev/null +++ b/src/main/java/me/topchetoeu/jscript/compilation/members/AssignShorthandNode.java @@ -0,0 +1,54 @@ +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 extends Node { + public final Node key; + public final AssignTarget target; + public final Node value; + + @Override public void compile(CompileResult target, boolean pollute) { + throw new SyntaxException(loc(), "Unexpected assign shorthand in non-destructor context"); + } + + public AssignShorthandNode(Location loc, Node key, AssignTarget target, Node value) { + super(loc); + this.key = key; + this.target = target; + this.value = value; + } + + public AssignTarget target() { + return new AssignNode(loc(), target, value); + } + + public static ParseRes 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); + } +} \ No newline at end of file diff --git a/src/main/java/me/topchetoeu/jscript/compilation/members/FieldMemberNode.java b/src/main/java/me/topchetoeu/jscript/compilation/members/FieldMemberNode.java new file mode 100644 index 0000000..9706722 --- /dev/null +++ b/src/main/java/me/topchetoeu/jscript/compilation/members/FieldMemberNode.java @@ -0,0 +1,89 @@ +package me.topchetoeu.jscript.compilation.members; + +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.JavaScript; +import me.topchetoeu.jscript.compilation.Node; +import me.topchetoeu.jscript.compilation.values.ObjectNode; +import me.topchetoeu.jscript.compilation.values.VariableNode; +import me.topchetoeu.jscript.compilation.values.constants.StringNode; + +public class FieldMemberNode extends Node { + public final Node key; + public final Node value; + + @Override public void compile(CompileResult target, boolean pollute) { + if (pollute) target.add(Instruction.dup()); + key.compile(target, true); + + if (value == null) target.add(Instruction.pushUndefined()); + else value.compile(target, true); + + target.add(Instruction.defField()); + } + + public FieldMemberNode(Location loc, Node key, Node value) { + super(loc); + this.key = key; + this.value = value; + } + + public static ParseRes parseObject(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; + 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 value"); + n += value.n; + + return ParseRes.res(new FieldMemberNode(loc, name.result, value.result), n); + } + + public static ParseRes parseShorthand(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; + + return ParseRes.res(new FieldMemberNode(loc, new StringNode(loc, var.result.name), var.result), n); + } + + public static ParseRes parseClass(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; + n += Parsing.skipEmpty(src, i + n); + + if (!src.is(i + n, "=")) { + var end = JavaScript.parseStatement(src, i + n); + if (!end.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected an end of statement or a field initializer"); + n += end.n; + + return ParseRes.res(new FieldMemberNode(loc, name.result, null), n); + } + n++; + + var value = JavaScript.parseExpression(src, i + n, 2); + if (!value.isSuccess()) return value.chainError(src.loc(i + n), "Expected a value"); + n += value.n; + + return ParseRes.res(new FieldMemberNode(loc, name.result, value.result), n); + } +} \ No newline at end of file diff --git a/src/main/java/me/topchetoeu/jscript/compilation/members/MethodMemberNode.java b/src/main/java/me/topchetoeu/jscript/compilation/members/MethodMemberNode.java new file mode 100644 index 0000000..8636c3d --- /dev/null +++ b/src/main/java/me/topchetoeu/jscript/compilation/members/MethodMemberNode.java @@ -0,0 +1,62 @@ +package me.topchetoeu.jscript.compilation.members; + +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.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 { + public final Node key; + + @Override public String name() { + if (key instanceof StringNode str) return str.value; + else return null; + } + + @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, name, captures(id, target))); + + target.add(Instruction.defField()); + } + + public MethodMemberNode(Location loc, Location end, Node key, Parameters params, CompoundNode body) { + super(loc, end, params, body); + this.key = key; + } + + public static ParseRes 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); + } +} \ No newline at end of file diff --git a/src/main/java/me/topchetoeu/jscript/compilation/members/PropertyMemberNode.java b/src/main/java/me/topchetoeu/jscript/compilation/members/PropertyMemberNode.java new file mode 100644 index 0000000..6e49e62 --- /dev/null +++ b/src/main/java/me/topchetoeu/jscript/compilation/members/PropertyMemberNode.java @@ -0,0 +1,81 @@ +package me.topchetoeu.jscript.compilation.members; + +import java.util.Arrays; + +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.FunctionNode; +import me.topchetoeu.jscript.compilation.Node; +import me.topchetoeu.jscript.compilation.Parameters; +import me.topchetoeu.jscript.compilation.patterns.Pattern; +import me.topchetoeu.jscript.compilation.values.ObjectNode; +import me.topchetoeu.jscript.compilation.values.constants.StringNode; + +public class PropertyMemberNode extends FunctionNode { + public final Node key; + public final Pattern argument; + + @Override public String name() { + if (key instanceof StringNode str) { + if (isGetter()) return "get " + str.value; + else return "set " + str.value; + } + else return null; + } + + public boolean isGetter() { return argument == null; } + public boolean isSetter() { return argument != null; } + + @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, name, captures(id, target))); + + target.add(Instruction.defProp(isSetter())); + } + + public PropertyMemberNode(Location loc, Location end, Node key, Pattern argument, CompoundNode body) { + super(loc, end, argument == null ? new Parameters(Arrays.asList()) : new Parameters(Arrays.asList(argument)), body); + this.key = key; + this.argument = argument; + } + + public static ParseRes parse(Source src, int i) { + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); + + var access = Parsing.parseIdentifier(src, i + n); + if (!access.isSuccess()) return ParseRes.failed(); + if (!access.result.equals("get") && !access.result.equals("set")) return ParseRes.failed(); + n += access.n; + + var name = ObjectNode.parsePropName(src, i + n); + if (!name.isSuccess()) return name.chainError(src.loc(i + n), "Expected a property name after '" + access + "'"); + n += name.n; + + var params = Parameters.parseParameters(src, i + n); + if (!params.isSuccess()) return params.chainError(src.loc(i + n), "Expected an argument list"); + if (access.result.equals("get") && params.result.params.size() != 0) return ParseRes.error(src.loc(i + n), "Getter must not have any parameters"); + if (access.result.equals("set") && params.result.params.size() != 1) return ParseRes.error(src.loc(i + n), "Setter must have exactly one parameter"); + if (params.result.rest != null) return ParseRes.error(params.result.rest.loc(), "Property members may not have rest arguments"); + 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 PropertyMemberNode( + loc, end, name.result, access.result.equals("get") ? null : params.result.params.get(0), body.result + ), n); + } +} \ No newline at end of file diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/ObjectNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/ObjectNode.java index 670a680..eee8f17 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/ObjectNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/ObjectNode.java @@ -1,251 +1,28 @@ package me.topchetoeu.jscript.compilation.values; -import java.util.Arrays; import java.util.LinkedList; import java.util.List; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.SyntaxException; -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.FunctionNode; import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Node; -import me.topchetoeu.jscript.compilation.Parameters; +import me.topchetoeu.jscript.compilation.members.AssignShorthandNode; +import me.topchetoeu.jscript.compilation.members.FieldMemberNode; +import me.topchetoeu.jscript.compilation.members.MethodMemberNode; +import me.topchetoeu.jscript.compilation.members.PropertyMemberNode; import me.topchetoeu.jscript.compilation.patterns.AssignTarget; import me.topchetoeu.jscript.compilation.patterns.AssignTargetLike; import me.topchetoeu.jscript.compilation.patterns.ObjectPattern; -import me.topchetoeu.jscript.compilation.patterns.Pattern; import me.topchetoeu.jscript.compilation.values.constants.NumberNode; import me.topchetoeu.jscript.compilation.values.constants.StringNode; -import me.topchetoeu.jscript.compilation.values.operations.AssignNode; public class ObjectNode extends Node implements AssignTargetLike { - public static class PropertyMemberNode extends FunctionNode { - public final Node key; - public final Pattern argument; - - @Override public String name() { - if (key instanceof StringNode str) { - if (isGetter()) return "get " + str.value; - else return "set " + str.value; - } - else return null; - } - - public boolean isGetter() { return argument == null; } - public boolean isSetter() { return argument != null; } - - @Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) { - key.compile(target, true); - - var id = target.addChild(compileBody(target, name, null)); - target.add(_i -> Instruction.loadFunc(id, true, false, false, name, captures(id, target))); - - target.add(Instruction.defProp(isSetter())); - } - - public PropertyMemberNode(Location loc, Location end, Node key, Pattern argument, CompoundNode body) { - super(loc, end, argument == null ? new Parameters(Arrays.asList()) : new Parameters(Arrays.asList(argument)), body); - this.key = key; - this.argument = argument; - } - - public static ParseRes parse(Source src, int i) { - var n = Parsing.skipEmpty(src, i); - var loc = src.loc(i + n); - - var access = Parsing.parseIdentifier(src, i + n); - if (!access.isSuccess()) return ParseRes.failed(); - if (!access.result.equals("get") && !access.result.equals("set")) return ParseRes.failed(); - n += access.n; - - var name = parsePropName(src, i + n); - if (!name.isSuccess()) return name.chainError(src.loc(i + n), "Expected a property name after '" + access + "'"); - n += name.n; - - var params = Parameters.parseParameters(src, i + n); - if (!params.isSuccess()) return params.chainError(src.loc(i + n), "Expected an argument list"); - if (access.result.equals("get") && params.result.params.size() != 0) return ParseRes.error(src.loc(i + n), "Getter must not have any parameters"); - if (access.result.equals("set") && params.result.params.size() != 1) return ParseRes.error(src.loc(i + n), "Setter must have exactly one parameter"); - if (params.result.rest != null) return ParseRes.error(params.result.rest.loc(), "Property members may not have rest arguments"); - 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 PropertyMemberNode( - loc, end, name.result, access.result.equals("get") ? null : params.result.params.get(0), body.result - ), n); - } - } - public static class MethodMemberNode extends FunctionNode { - public final Node key; - - @Override public String name() { - if (key instanceof StringNode str) return str.value; - else return null; - } - - @Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) { - key.compile(target, true); - - var id = target.addChild(compileBody(target, name, null)); - target.add(_i -> Instruction.loadFunc(id, true, false, false, name, captures(id, target))); - - target.add(Instruction.defField()); - } - - public MethodMemberNode(Location loc, Location end, Node key, Parameters params, CompoundNode body) { - super(loc, end, params, body); - this.key = key; - } - - public static ParseRes parse(Source src, int i) { - var n = Parsing.skipEmpty(src, i); - var loc = src.loc(i + n); - - var name = 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); - } - } - public static class FieldMemberNode extends Node { - public final Node key; - public final Node value; - - @Override public void compile(CompileResult target, boolean pollute) { - key.compile(target, true); - - if (value == null) target.add(Instruction.pushUndefined()); - else value.compile(target, true); - - target.add(Instruction.defField()); - } - - public FieldMemberNode(Location loc, Node key, Node value) { - super(loc); - this.key = key; - this.value = value; - } - - public static ParseRes parseObject(Source src, int i) { - var n = Parsing.skipEmpty(src, i); - var loc = src.loc(i + n); - - var name = parsePropName(src, i + n); - if (!name.isSuccess()) return name.chainError(); - n += name.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 value"); - n += value.n; - - return ParseRes.res(new FieldMemberNode(loc, name.result, value.result), n); - } - - public static ParseRes parseShorthand(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; - - return ParseRes.res(new FieldMemberNode(loc, new StringNode(loc, var.result.name), var.result), n); - } - - public static ParseRes parseClass(Source src, int i) { - var n = Parsing.skipEmpty(src, i); - var loc = src.loc(i + n); - - var name = parsePropName(src, i + n); - if (!name.isSuccess()) return name.chainError(); - n += name.n; - n += Parsing.skipEmpty(src, i + n); - - if (!src.is(i + n, "=")) { - var end = JavaScript.parseStatement(src, i + n); - if (!end.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected an end of statement or a field initializer"); - n += end.n; - - return ParseRes.res(new FieldMemberNode(loc, name.result, null), n); - } - n++; - - var value = JavaScript.parseExpression(src, i + n, 2); - if (!value.isSuccess()) return value.chainError(src.loc(i + n), "Expected a value"); - n += value.n; - - return ParseRes.res(new FieldMemberNode(loc, name.result, value.result), n); - } - } - public static class AssignShorthandNode extends Node { - public final Node key; - public final AssignTarget target; - public final Node value; - - @Override public void compile(CompileResult target, boolean pollute) { - throw new SyntaxException(loc(), "Unexpected assign shorthand in non-destructor context"); - } - - public AssignShorthandNode(Location loc, Node key, AssignTarget target, Node value) { - super(loc); - this.key = key; - this.target = target; - this.value = value; - } - - public AssignTarget target() { - return new AssignNode(loc(), target, value); - } - - public static ParseRes 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); - } - } - public final List members; // TODO: Implement spreading into object @@ -287,11 +64,7 @@ public class ObjectNode extends Node implements AssignTargetLike { @Override public void compile(CompileResult target, boolean pollute) { target.add(Instruction.loadObj()); - - for (var el : members) { - target.add(Instruction.dup()); - el.compile(target, false); - } + for (var el : members) el.compile(target, true); } @Override public AssignTarget toAssignTarget() {