Classes #29

Merged
TopchetoEU merged 7 commits from TopchetoEU/classes into master 2024-09-19 15:21:01 +00:00
6 changed files with 238 additions and 3 deletions
Showing only changes of commit 0258cc0a90 - Show all commits

View File

@ -0,0 +1,158 @@
package me.topchetoeu.jscript.compilation;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.common.parsing.ParseRes;
import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.compilation.members.FieldMemberNode;
import me.topchetoeu.jscript.compilation.members.MethodMemberNode;
import me.topchetoeu.jscript.compilation.members.PropertyMemberNode;
public abstract class ClassNode extends FunctionNode {
public static final class ClassBody {
public final List<Node> staticMembers;
public final List<FieldMemberNode> protoFields;
public final List<Node> protoMembers;
public final Parameters constructorParameters;
public final CompoundNode constructorBody;
public ClassBody(
List<Node> staticMembers, List<FieldMemberNode> protoFields, List<Node> protoMembers,
Parameters constructorParameters, CompoundNode constructorBody
) {
this.staticMembers = staticMembers;
this.protoFields = protoFields;
this.protoMembers = protoMembers;
this.constructorParameters = constructorParameters;
this.constructorBody = constructorBody;
}
}
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);
}
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);
}
body.protoMembers.get(body.protoMembers.size() - 1).compile(target, false);
}
}
@Override protected void compilePreBody(CompileResult target) {
for (var member : body.protoFields) {
target.add(Instruction.loadThis());
member.compile(target, false);
}
}
@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, false, true, false, name, captures(id, target)));
compileStatic(target);
compilePrototype(target);
}
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<Node> 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);
if (!src.is(i + n, "{")) return ParseRes.failed();
n++;
n += Parsing.skipEmpty(src, i + n);
var fields = new LinkedList<FieldMemberNode>();
var members = new LinkedList<Node>();
var statics = new LinkedList<Node>();
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), n);
}
while (true) {
ParseRes<Node> 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 ParseRes.error(src.loc(i + n), "Expected a comma or a closing brace.");
}
return ParseRes.res(new ClassBody(statics, fields, members, params, body), n);
}
// public FunctionStatementNode(Location loc, Location end, Parameters params, CompoundNode body, String name) {
// super(loc, end, params, body);
// this.name = name;
// }
}

View File

@ -0,0 +1,40 @@
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

@ -131,7 +131,7 @@ public final class CompileResult {
for (var suppl : instructions) {
instrRes[i] = suppl.apply(i);
System.out.println(instrRes[i]);
// System.out.println(instrRes[i]);
i++;
}

View File

@ -22,6 +22,8 @@ public abstract class FunctionNode extends Node {
return ((FunctionScope)target.children.get(id).scope).getCaptureIndices();
}
protected void compilePreBody(CompileResult target) { }
public final CompileResult compileBody(Environment env, FunctionScope scope, boolean lastReturn, String _name, String selfName) {
var name = this.name() != null ? this.name() : _name;
@ -30,6 +32,8 @@ public abstract class FunctionNode extends Node {
.remove(LabelContext.CONTINUE_CTX);
return new CompileResult(env, scope, params.params.size(), target -> {
compilePreBody(target);
if (params.params.size() > 0) {
target.add(Instruction.loadArgs(true));
if (params.params.size() > 1) target.add(Instruction.dup(params.params.size() - 1, 0));

View File

@ -28,6 +28,7 @@ import me.topchetoeu.jscript.compilation.control.WhileNode;
import me.topchetoeu.jscript.compilation.scope.FunctionScope;
import me.topchetoeu.jscript.compilation.values.ArgumentsNode;
import me.topchetoeu.jscript.compilation.values.ArrayNode;
import me.topchetoeu.jscript.compilation.values.ClassValueNode;
import me.topchetoeu.jscript.compilation.values.ObjectNode;
import me.topchetoeu.jscript.compilation.values.RegexNode;
import me.topchetoeu.jscript.compilation.values.ThisNode;
@ -63,7 +64,7 @@ public final class JavaScript {
"finally", "for", "do", "while", "switch", "case", "default", "new",
"function", "var", "return", "throw", "typeof", "delete", "break",
"continue", "debugger", "implements", "interface", "package", "private",
"protected", "public", "static", "arguments"
"protected", "public", "static", "arguments", "class"
));
public static ParseRes<? extends Node> parseParens(Source src, int i) {
@ -88,6 +89,7 @@ public final class JavaScript {
return ParseRes.first(src, i,
(s, j) -> statement ? ParseRes.failed() : ObjectNode.parse(s, j),
(s, j) -> statement ? ParseRes.failed() : FunctionNode.parseFunction(s, j, false),
(s, j) -> statement ? ParseRes.failed() : ClassValueNode.parse(s, j),
JavaScript::parseLiteral,
StringNode::parse,
RegexNode::parse,
@ -96,7 +98,7 @@ public final class JavaScript {
ChangeNode::parsePrefixIncrease,
OperationNode::parsePrefix,
ArrayNode::parse,
FunctionArrowNode::parse,
(s, j) -> statement ? ParseRes.failed() : FunctionArrowNode.parse(s, j),
JavaScript::parseParens,
CallNode::parseNew,
TypeofNode::parse,
@ -188,6 +190,7 @@ public final class JavaScript {
if (Parsing.isIdentifier(src, i + n, "with")) return ParseRes.error(src.loc(i + n), "'with' statements are not allowed.");
ParseRes<? extends Node> res = ParseRes.first(src, i + n,
ClassStatementNode::parse,
VariableDeclareNode::parse,
ReturnNode::parse,
ThrowNode::parse,

View File

@ -0,0 +1,30 @@
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;
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);
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 ClassValueNode(loc, src.loc(i + n), name.result, body.result), n);
}
}