refactor: clean up parsing

This commit is contained in:
TopchetoEU 2024-08-27 13:53:52 +03:00
parent ef0fc5a61d
commit f09feae08f
Signed by: topchetoeu
GPG Key ID: 6531B8583E5F6ED4
47 changed files with 1760 additions and 1903 deletions

View File

@ -1,101 +1,108 @@
package me.topchetoeu.jscript.common;
import java.util.ArrayList;
import java.util.Objects;
public class Location implements Comparable<Location> {
public static final Location INTERNAL = new Location(-1, -1, new Filename("jscript", "native"));
private int line;
private int start;
private Filename filename;
public abstract class Location implements Comparable<Location> {
public static final Location INTERNAL = Location.of("jscript://native");
public int line() { return line; }
public int start() { return start; }
public Filename filename() { return filename; }
public abstract int line();
public abstract int start();
public abstract Filename filename();
@Override
public String toString() {
public final String toString() {
var res = new ArrayList<String>();
if (filename != null) res.add(filename.toString());
if (line >= 0) res.add(line + "");
if (start >= 0) res.add(start + "");
if (filename() != null) res.add(filename().toString());
if (line() >= 0) res.add(line() + 1 + "");
if (start() >= 0) res.add(start() + 1 + "");
return String.join(":", res);
}
public Location add(int n, boolean clone) {
if (clone) return new Location(line, start + n, filename);
this.start += n;
return this;
public final Location add(int n) {
var self = this;
return new Location() {
@Override public Filename filename() { return self.filename(); }
@Override public int start() { return self.start() + n; }
@Override public int line() { return self.line(); }
};
}
public Location add(int n) {
return add(n, false);
}
public Location nextLine() {
line++;
start = 0;
return this;
}
public Location clone() {
return new Location(line, start, filename);
public final Location nextLine() {
var self = this;
return new Location() {
@Override public Filename filename() { return self.filename(); }
@Override public int start() { return 0; }
@Override public int line() { return self.line() + 1; }
};
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + line;
result = prime * result + start;
result = prime * result + ((filename == null) ? 0 : filename.hashCode());
return result;
@Override public final int hashCode() {
return Objects.hash(line(), start(), filename());
}
@Override
public boolean equals(Object obj) {
@Override public final boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
Location other = (Location) obj;
if (line != other.line) return false;
if (start != other.start) return false;
if (filename == null && other.filename != null) return false;
else if (!filename.equals(other.filename)) return false;
if (!(obj instanceof Location)) return false;
var other = (Location)obj;
if (!Objects.equals(this.start(), other.start())) return false;
if (!Objects.equals(this.line(), other.line())) return false;
if (!Objects.equals(this.filename(), other.filename())) return false;
return true;
}
@Override
public int compareTo(Location other) {
int a = filename.toString().compareTo(other.filename.toString());
int b = Integer.compare(line, other.line);
int c = Integer.compare(start, other.start);
@Override public int compareTo(Location other) {
int a = filename().toString().compareTo(other.filename().toString());
int b = Integer.compare(line(), other.line());
int c = Integer.compare(start(), other.start());
if (a != 0) return a;
if (b != 0) return b;
return c;
}
public Location(int line, int start, Filename filename) {
this.line = line;
this.start = start;
this.filename = filename;
public static Location of(Filename filename, int line, int start) {
return new Location() {
@Override public Filename filename() { return filename; }
@Override public int start() { return start; }
@Override public int line() { return line; }
};
}
public static Location parse(String raw) {
int i0 = -1, i1 = -1;
for (var i = raw.length() - 1; i >= 0; i--) {
if (raw.charAt(i) == ':') {
if (i1 == -1) i1 = i;
else if (i0 == -1) {
i0 = i;
break;
}
public static Location of(String raw) {
var i0 = raw.lastIndexOf(':');
if (i0 < 0) return Location.of(Filename.parse(raw), -1, -1);
var i1 = raw.lastIndexOf(':', i0);
if (i0 < 0) {
try {
return Location.of(Filename.parse(raw.substring(0, i0)), Integer.parseInt(raw.substring(i0 + 1)), -1);
}
catch (NumberFormatException e) {
return Location.of(Filename.parse(raw), -1, -1);
}
}
return new Location(
Integer.parseInt(raw.substring(i0 + 1, i1)),
Integer.parseInt(raw.substring(i1 + 1)),
Filename.parse(raw.substring(0, i0))
);
int start, line;
try {
start = Integer.parseInt(raw.substring(i1 + 1));
}
catch (NumberFormatException e) {
return Location.of(Filename.parse(raw), -1, -1);
}
try {
line = Integer.parseInt(raw.substring(i0 + 1, i1));
}
catch (NumberFormatException e) {
return Location.of(Filename.parse(raw.substring(i1 + 1)), start, -1);
}
return Location.of(Filename.parse(raw.substring(0, i0)), start, line);
}
}

View File

@ -1,85 +1,85 @@
package me.topchetoeu.jscript.common.json;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.ParseRes;
import me.topchetoeu.jscript.compilation.parsing.Operator;
import me.topchetoeu.jscript.compilation.parsing.ParseRes;
import me.topchetoeu.jscript.compilation.parsing.Parsing;
import me.topchetoeu.jscript.compilation.parsing.Token;
import me.topchetoeu.jscript.compilation.values.ConstantStatement;
import me.topchetoeu.jscript.compilation.parsing.Source;
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
public class JSON {
public static ParseRes<String> parseIdentifier(List<Token> tokens, int i) {
return Parsing.parseIdentifier(tokens, i);
public static ParseRes<JSONElement> parseString(Source src, int i) {
var res = Parsing.parseString(src, i);
if (!res.isSuccess()) return res.chainError();
return ParseRes.res(JSONElement.string(res.result), res.n);
}
public static ParseRes<String> parseString(Filename filename, List<Token> tokens, int i) {
var res = ConstantStatement.parseString(filename, tokens, i);
if (res.isSuccess()) return ParseRes.res((String)res.result.value, res.n);
else return res.transform();
}
public static ParseRes<Double> parseNumber(Filename filename, List<Token> tokens, int i) {
var minus = Parsing.isOperator(tokens, i, Operator.SUBTRACT);
if (minus) i++;
public static ParseRes<JSONElement> parseNumber(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var res = ConstantStatement.parseNumber(filename, tokens, i);
if (res.isSuccess()) return ParseRes.res((minus ? -1 : 1) * (Double)res.result.value, res.n + (minus ? 1 : 0));
else return res.transform();
if (src.is(i + n, "-")) {
n++;
var res = Parsing.parseNumber(src, i);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a number after minus");
n += res.n;
return ParseRes.res(JSONElement.number(-res.result), n);
}
else {
var res = Parsing.parseNumber(src, i + n).addN(n);
if (!res.isSuccess()) return res.chainError();
n += res.n;
return ParseRes.res(JSONElement.number(res.result), n);
}
}
public static ParseRes<Boolean> parseBool(Filename filename, List<Token> tokens, int i) {
var id = parseIdentifier(tokens, i);
public static ParseRes<JSONElement> parseLiteral(Source src, int i) {
var id = Parsing.parseIdentifier(src, i);
if (!id.isSuccess()) return ParseRes.failed();
else if (id.result.equals("true")) return ParseRes.res(true, 1);
else if (id.result.equals("false")) return ParseRes.res(false, 1);
else if (id.result.equals("true")) return ParseRes.res(JSONElement.bool(true), id.n);
else if (id.result.equals("false")) return ParseRes.res(JSONElement.bool(false), id.n);
else if (id.result.equals("null")) return ParseRes.res(JSONElement.NULL, id.n);
else return ParseRes.failed();
}
public static ParseRes<?> parseValue(Filename filename, List<Token> tokens, int i) {
return ParseRes.any(
parseString(filename, tokens, i),
parseNumber(filename, tokens, i),
parseBool(filename, tokens, i),
parseMap(filename, tokens, i),
parseList(filename, tokens, i)
public static ParseRes<JSONElement> parseValue(Source src, int i) {
return ParseRes.first(src, i,
JSON::parseString,
JSON::parseNumber,
JSON::parseLiteral,
JSON::parseMap,
JSON::parseList
);
}
public static ParseRes<JSONMap> parseMap(Filename filename, List<Token> tokens, int i) {
int n = 0;
if (!Parsing.isOperator(tokens, i + n++, Operator.BRACE_OPEN)) return ParseRes.failed();
public static ParseRes<JSONMap> parseMap(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
if (!src.is(i + n, "{")) return ParseRes.failed();
n++;
var values = new JSONMap();
if (src.is(i + n, "}")) return ParseRes.res(new JSONMap(Map.of()), n + 1);
while (true) {
if (Parsing.isOperator(tokens, i + n, Operator.BRACE_CLOSE)) {
n++;
break;
}
var name = parseString(src, i + n);
if (!name.isSuccess()) return name.chainError(src.loc(i + n), "Expected an index");
n += name.n;
n += Parsing.skipEmpty(src, i + n);
var name = ParseRes.any(
parseIdentifier(tokens, i + n),
parseString(filename, tokens, i + n),
parseNumber(filename, tokens, i + n)
);
if (!name.isSuccess()) return ParseRes.error(Parsing.getLoc(filename, tokens, i + n), "Expected an index.", name);
else n += name.n;
if (!Parsing.isOperator(tokens, i + n, Operator.COLON)) {
return ParseRes.error(Parsing.getLoc(filename, tokens, i + n), "Expected a colon.", name);
}
if (!src.is(i + n, ":")) return name.chainError(src.loc(i + n), "Expected a colon");
n++;
var res = parseValue(filename, tokens, i + n);
if (!res.isSuccess()) return ParseRes.error(Parsing.getLoc(filename, tokens, i + n), "Expected a list element.", res);
else n += res.n;
var res = parseValue(src, i + n);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a list element");
values.put(name.result.toString(), res.result);
n += res.n;
n += Parsing.skipEmpty(src, i + n);
values.put(name.result.toString(), JSONElement.of(res.result));
if (Parsing.isOperator(tokens, i + n, Operator.COMMA)) n++;
else if (Parsing.isOperator(tokens, i + n, Operator.BRACE_CLOSE)) {
if (src.is(i + n, ",")) n++;
else if (src.is(i + n, "}")) {
n++;
break;
}
@ -87,26 +87,23 @@ public class JSON {
return ParseRes.res(values, n);
}
public static ParseRes<JSONList> parseList(Filename filename, List<Token> tokens, int i) {
int n = 0;
if (!Parsing.isOperator(tokens, i + n++, Operator.BRACKET_OPEN)) return ParseRes.failed();
public static ParseRes<JSONList> parseList(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
if (!src.is(i + n++, "[]")) return ParseRes.failed();
var values = new JSONList();
if (src.is(i + n, "]")) return ParseRes.res(new JSONList(), n + 1);
while (true) {
if (Parsing.isOperator(tokens, i + n, Operator.BRACKET_CLOSE)) {
n++;
break;
}
var res = parseValue(src, i + n);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a list element");
values.add(res.result);
n += res.n;
n += Parsing.skipEmpty(src, i + n);
var res = parseValue(filename, tokens, i + n);
if (!res.isSuccess()) return ParseRes.error(Parsing.getLoc(filename, tokens, i + n), "Expected a list element.", res);
else n += res.n;
values.add(JSONElement.of(res.result));
if (Parsing.isOperator(tokens, i + n, Operator.COMMA)) n++;
else if (Parsing.isOperator(tokens, i + n, Operator.BRACKET_CLOSE)) {
if (src.is(i + n, ",")) n++;
else if (src.is(i + n, "]")) {
n++;
break;
}
@ -116,7 +113,8 @@ public class JSON {
}
public static JSONElement parse(Filename filename, String raw) {
if (filename == null) filename = new Filename("jscript", "json");
var res = parseValue(filename, Parsing.tokenize(filename, raw), 0);
var res = parseValue(new Source(filename, raw), 0);
if (res.isFailed()) throw new SyntaxException(null, "Invalid JSON given.");
else if (res.isError()) throw new SyntaxException(null, res.error);
else return JSONElement.of(res.result);

View File

@ -103,7 +103,7 @@ public class FunctionMap {
var res = new ArrayList<Location>(candidates.size());
for (var candidate : candidates.entrySet()) {
var val = correctBreakpoint(new Location(line, column, candidate.getKey()));
var val = correctBreakpoint(Location.of(candidate.getKey(), line, column));
if (val == null) continue;
res.add(val);
}

View File

@ -1,15 +1,11 @@
package me.topchetoeu.jscript.compilation;
import java.util.List;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Operation;
import me.topchetoeu.jscript.common.ParseRes;
import me.topchetoeu.jscript.common.ParseRes.State;
import me.topchetoeu.jscript.compilation.parsing.Operator;
import me.topchetoeu.jscript.compilation.parsing.ParseRes;
import me.topchetoeu.jscript.compilation.parsing.Parsing;
import me.topchetoeu.jscript.compilation.parsing.Token;
import me.topchetoeu.jscript.compilation.parsing.Source;
public abstract class AssignableStatement extends Statement {
public abstract Statement toAssign(Statement val, Operation operation);
@ -18,41 +14,37 @@ public abstract class AssignableStatement extends Statement {
super(loc);
}
public static ParseRes<? extends Statement> parse(Filename filename, List<Token> tokens, int i, Statement prev, int precedence) {
var loc = Parsing.getLoc(filename, tokens, i);
int n = 0;
public static ParseRes<? extends Statement> parse(Source src, int i, Statement prev, int precedence) {
if (precedence > 2) return ParseRes.failed();
var opRes = Parsing.parseOperator(tokens, i + n++);
if (opRes.state != State.SUCCESS) return ParseRes.failed();
var n = Parsing.skipEmpty(src, i);
var op = opRes.result;
if (!op.isAssign()) return ParseRes.failed();
for (var op : Operator.opsByLength) {
if (!op.assignable || !src.is(i + n, op.readable + "=")) continue;
n += op.readable.length() + 1;
if (!(prev instanceof AssignableStatement)) {
return ParseRes.error(loc, "Invalid expression on left hand side of assign operator.");
if (!(prev instanceof AssignableStatement)) {
return ParseRes.error(src.loc(i + n), "Invalid expression on left hand side of assign operator");
}
var res = Parsing.parseValue(src, i + n, 2);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), String.format("Expected a value after the '%s=' operator", op.readable));
n += res.n;
return ParseRes.res(((AssignableStatement)prev).toAssign(res.result, op.operation), n);
}
var res = Parsing.parseValue(filename, tokens, i + n, 2);
if (!res.isSuccess()) return ParseRes.error(loc, String.format("Expected value after assignment operator '%s'.", op.readable), res);
if (!src.is(i + n, "=")) return ParseRes.failed();
n++;
if (!(prev instanceof AssignableStatement)) {
return ParseRes.error(src.loc(i + n), "Invalid expression on left hand side of assign operator");
}
var res = Parsing.parseValue(src, i + n, 2);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a value after the '=' operator");
n += res.n;
Operation operation = null;
if (op == Operator.ASSIGN_ADD) operation = Operation.ADD;
if (op == Operator.ASSIGN_SUBTRACT) operation = Operation.SUBTRACT;
if (op == Operator.ASSIGN_MULTIPLY) operation = Operation.MULTIPLY;
if (op == Operator.ASSIGN_DIVIDE) operation = Operation.DIVIDE;
if (op == Operator.ASSIGN_MODULO) operation = Operation.MODULO;
if (op == Operator.ASSIGN_OR) operation = Operation.OR;
if (op == Operator.ASSIGN_XOR) operation = Operation.XOR;
if (op == Operator.ASSIGN_AND) operation = Operation.AND;
if (op == Operator.ASSIGN_SHIFT_LEFT) operation = Operation.SHIFT_LEFT;
if (op == Operator.ASSIGN_SHIFT_RIGHT) operation = Operation.SHIFT_RIGHT;
if (op == Operator.ASSIGN_USHIFT_RIGHT) operation = Operation.USHIFT_RIGHT;
return ParseRes.res(((AssignableStatement)prev).toAssign(res.result, operation), n);
return ParseRes.res(((AssignableStatement)prev).toAssign(res.result, null), n);
}
}

View File

@ -4,14 +4,12 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.ParseRes;
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.compilation.parsing.Operator;
import me.topchetoeu.jscript.compilation.parsing.ParseRes;
import me.topchetoeu.jscript.compilation.parsing.Parsing;
import me.topchetoeu.jscript.compilation.parsing.Token;
import me.topchetoeu.jscript.compilation.parsing.Source;
import me.topchetoeu.jscript.compilation.values.FunctionStatement;
public class CompoundStatement extends Statement {
@ -68,45 +66,49 @@ public class CompoundStatement extends Statement {
this.statements = statements;
}
public static ParseRes<CompoundStatement> parseComma(Filename filename, List<Token> tokens, int i, Statement prev, int precedence) {
var loc = Parsing.getLoc(filename, tokens, i);
var n = 0;
public static ParseRes<CompoundStatement> parseComma(Source src, int i, Statement prev, int precedence) {
if (precedence > 1) return ParseRes.failed();
if (!Parsing.isOperator(tokens, i + n++, Operator.COMMA)) return ParseRes.failed();
var res = Parsing.parseValue(filename, tokens, i + n, 2);
if (!res.isSuccess()) return ParseRes.error(loc, "Expected a value after the comma.", res);
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
if (!src.is(i + n, ",")) return ParseRes.failed();
n++;
var res = Parsing.parseValue(src, i + n, 2);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a value after the comma");
n += res.n;
return ParseRes.res(new CompoundStatement(loc, false, prev, res.result), n);
}
public static ParseRes<CompoundStatement> parse(Filename filename, List<Token> tokens, int i) {
var loc = Parsing.getLoc(filename, tokens, i);
int n = 0;
if (!Parsing.isOperator(tokens, i + n++, Operator.BRACE_OPEN)) return ParseRes.failed();
public static ParseRes<CompoundStatement> 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++;
var statements = new ArrayList<Statement>();
while (true) {
if (Parsing.isOperator(tokens, i + n, Operator.BRACE_CLOSE)) {
n += Parsing.skipEmpty(src, i + n);
if (src.is(i + n, "}")) {
n++;
break;
}
if (Parsing.isOperator(tokens, i + n, Operator.SEMICOLON)) {
if (src.is(i + n, ";")) {
n++;
continue;
}
var res = Parsing.parseStatement(filename, tokens, i + n);
if (!res.isSuccess()) {
return ParseRes.error(Parsing.getLoc(filename, tokens, i), "Expected a statement.", res);
}
var res = Parsing.parseStatement(src, i + n);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a statement");
n += res.n;
statements.add(res.result);
}
return ParseRes.res(new CompoundStatement(loc, true, statements.toArray(Statement[]::new)).setEnd(Parsing.getLoc(filename, tokens, i + n - 1)), n);
return ParseRes.res(new CompoundStatement(loc, true, statements.toArray(Statement[]::new)).setEnd(src.loc(i + n - 1)), n);
}
}

View File

@ -3,14 +3,12 @@ package me.topchetoeu.jscript.compilation;
import java.util.ArrayList;
import java.util.List;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.ParseRes;
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.compilation.parsing.Operator;
import me.topchetoeu.jscript.compilation.parsing.ParseRes;
import me.topchetoeu.jscript.compilation.parsing.Parsing;
import me.topchetoeu.jscript.compilation.parsing.Token;
import me.topchetoeu.jscript.compilation.parsing.Source;
import me.topchetoeu.jscript.compilation.values.FunctionStatement;
public class VariableDeclareStatement extends Statement {
@ -56,48 +54,59 @@ public class VariableDeclareStatement extends Statement {
this.values = values;
}
public static ParseRes<VariableDeclareStatement> parse(Filename filename, List<Token> tokens, int i) {
var loc = Parsing.getLoc(filename, tokens, i);
int n = 0;
if (!Parsing.isIdentifier(tokens, i + n++, "var")) return ParseRes.failed();
public static ParseRes<VariableDeclareStatement> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
if (!Parsing.isIdentifier(src, i + n, "var")) return ParseRes.failed();
n += 3;
var res = new ArrayList<Pair>();
if (Parsing.isStatementEnd(tokens, i + n)) {
if (Parsing.isOperator(tokens, i + n, Operator.SEMICOLON)) return ParseRes.res(new VariableDeclareStatement(loc, res), 2);
else return ParseRes.res(new VariableDeclareStatement(loc, res), 1);
var end = Parsing.parseStatementEnd(src, i + n);
if (end.isSuccess()) {
n += end.n;
return ParseRes.res(new VariableDeclareStatement(loc, res), n);
}
while (true) {
var nameLoc = Parsing.getLoc(filename, tokens, i + n);
var nameRes = Parsing.parseIdentifier(tokens, i + n++);
if (!nameRes.isSuccess()) return ParseRes.error(loc, "Expected a variable name.");
var nameLoc = src.loc(i + n);
var name = Parsing.parseIdentifier(src, i + n);
if (!name.isSuccess()) return name.chainError(nameLoc, "Expected a variable name");
n += name.n;
if (!Parsing.checkVarName(nameRes.result)) {
return ParseRes.error(loc, String.format("Unexpected identifier '%s'.", nameRes.result));
if (!Parsing.checkVarName(name.result)) {
return ParseRes.error(src.loc(i + n), String.format("Unexpected identifier '%s'", name.result));
}
Statement val = null;
n += Parsing.skipEmpty(src, i + n);
if (Parsing.isOperator(tokens, i + n, Operator.ASSIGN)) {
if (src.is(i + n, "=")) {
n++;
var valRes = Parsing.parseValue(filename, tokens, i + n, 2);
if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value after '='.", valRes);
var valRes = Parsing.parseValue(src, i + n, 2);
if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after '='");
n += valRes.n;
n += Parsing.skipEmpty(src, i + n);
val = valRes.result;
}
res.add(new Pair(nameRes.result, val, nameLoc));
res.add(new Pair(name.result, val, nameLoc));
if (Parsing.isOperator(tokens, i + n, Operator.COMMA)) {
if (src.is(i + n, ",")) {
n++;
continue;
}
else if (Parsing.isStatementEnd(tokens, i + n)) {
if (Parsing.isOperator(tokens, i + n, Operator.SEMICOLON)) return ParseRes.res(new VariableDeclareStatement(loc, res), n + 1);
else return ParseRes.res(new VariableDeclareStatement(loc, res), n);
end = Parsing.parseStatementEnd(src, i + n);
if (end.isSuccess()) {
n += end.n;
return ParseRes.res(new VariableDeclareStatement(loc, res), n);
}
else return ParseRes.error(loc, "Expected a comma or end of statement.");
else return end.chainError(src.loc(i + n), "Expected a comma or end of statement");
}
}
}

View File

@ -1,16 +1,12 @@
package me.topchetoeu.jscript.compilation.control;
import java.util.List;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.ParseRes;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.parsing.Operator;
import me.topchetoeu.jscript.compilation.parsing.ParseRes;
import me.topchetoeu.jscript.compilation.parsing.Parsing;
import me.topchetoeu.jscript.compilation.parsing.Token;
import me.topchetoeu.jscript.compilation.parsing.Source;
public class BreakStatement extends Statement {
public final String label;
@ -25,22 +21,28 @@ public class BreakStatement extends Statement {
this.label = label;
}
public static ParseRes<BreakStatement> parseBreak(Filename filename, List<Token> tokens, int i) {
if (!Parsing.isIdentifier(tokens, i, "break")) return ParseRes.failed();
public static ParseRes<BreakStatement> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
if (Parsing.isStatementEnd(tokens, i + 1)) {
if (Parsing.isOperator(tokens, i + 1, Operator.SEMICOLON)) return ParseRes.res(new BreakStatement(Parsing.getLoc(filename, tokens, i), null), 2);
else return ParseRes.res(new BreakStatement(Parsing.getLoc(filename, tokens, i), null), 1);
if (!Parsing.isIdentifier(src, i + n, "break")) return ParseRes.failed();
n += 5;
var end = Parsing.parseStatementEnd(src, i + n);
if (end.isSuccess()) {
n += end.n;
return ParseRes.res(new BreakStatement(loc, null), n);
}
var labelRes = Parsing.parseIdentifier(tokens, i + 1);
if (labelRes.isFailed()) return ParseRes.error(Parsing.getLoc(filename, tokens, i), "Expected a label name or an end of statement.");
var label = labelRes.result;
var label = Parsing.parseIdentifier(src, i + n);
if (label.isFailed()) return ParseRes.error(src.loc(i + n), "Expected a label name or an end of statement");
n += label.n;
if (Parsing.isStatementEnd(tokens, i + 2)) {
if (Parsing.isOperator(tokens, i + 2, Operator.SEMICOLON)) return ParseRes.res(new BreakStatement(Parsing.getLoc(filename, tokens, i), label), 3);
else return ParseRes.res(new BreakStatement(Parsing.getLoc(filename, tokens, i), label), 2);
end = Parsing.parseStatementEnd(src, i + n);
if (end.isSuccess()) {
n += end.n;
return ParseRes.res(new BreakStatement(loc, label.result), n);
}
else return ParseRes.error(Parsing.getLoc(filename, tokens, i), "Expected an end of statement.");
else return end.chainError(src.loc(i + n), "Expected end of statement");
}
}

View File

@ -1,16 +1,12 @@
package me.topchetoeu.jscript.compilation.control;
import java.util.List;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.ParseRes;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.parsing.Operator;
import me.topchetoeu.jscript.compilation.parsing.ParseRes;
import me.topchetoeu.jscript.compilation.parsing.Parsing;
import me.topchetoeu.jscript.compilation.parsing.Token;
import me.topchetoeu.jscript.compilation.parsing.Source;
public class ContinueStatement extends Statement {
public final String label;
@ -25,22 +21,28 @@ public class ContinueStatement extends Statement {
this.label = label;
}
public static ParseRes<ContinueStatement> parseContinue(Filename filename, List<Token> tokens, int i) {
if (!Parsing.isIdentifier(tokens, i, "continue")) return ParseRes.failed();
public static ParseRes<ContinueStatement> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
if (Parsing.isStatementEnd(tokens, i + 1)) {
if (Parsing.isOperator(tokens, i + 1, Operator.SEMICOLON)) return ParseRes.res(new ContinueStatement(Parsing.getLoc(filename, tokens, i), null), 2);
else return ParseRes.res(new ContinueStatement(Parsing.getLoc(filename, tokens, i), null), 1);
if (!Parsing.isIdentifier(src, i + n, "continue")) return ParseRes.failed();
n += 8;
var end = Parsing.parseStatementEnd(src, i + n);
if (end.isSuccess()) {
n += end.n;
return ParseRes.res(new ContinueStatement(loc, null), n);
}
var labelRes = Parsing.parseIdentifier(tokens, i + 1);
if (labelRes.isFailed()) return ParseRes.error(Parsing.getLoc(filename, tokens, i), "Expected a label name or an end of statement.");
var label = labelRes.result;
var label = Parsing.parseIdentifier(src, i + n);
if (label.isFailed()) return ParseRes.error(src.loc(i + n), "Expected a label name or an end of statement");
n += label.n;
if (Parsing.isStatementEnd(tokens, i + 2)) {
if (Parsing.isOperator(tokens, i + 2, Operator.SEMICOLON)) return ParseRes.res(new ContinueStatement(Parsing.getLoc(filename, tokens, i), label), 3);
else return ParseRes.res(new ContinueStatement(Parsing.getLoc(filename, tokens, i), label), 2);
end = Parsing.parseStatementEnd(src, i + n);
if (end.isSuccess()) {
n += end.n;
return ParseRes.res(new ContinueStatement(loc, label.result), n);
}
else return ParseRes.error(Parsing.getLoc(filename, tokens, i), "Expected an end of statement.");
else return end.chainError(src.loc(i + n), "Expected end of statement");
}
}

View File

@ -1,16 +1,12 @@
package me.topchetoeu.jscript.compilation.control;
import java.util.List;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.ParseRes;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.parsing.Operator;
import me.topchetoeu.jscript.compilation.parsing.ParseRes;
import me.topchetoeu.jscript.compilation.parsing.Parsing;
import me.topchetoeu.jscript.compilation.parsing.Token;
import me.topchetoeu.jscript.compilation.parsing.Source;
public class DebugStatement extends Statement {
@Override public void compile(CompileResult target, boolean pollute) {
@ -22,14 +18,19 @@ public class DebugStatement extends Statement {
super(loc);
}
public static ParseRes<DebugStatement> parse(Filename filename, List<Token> tokens, int i) {
if (!Parsing.isIdentifier(tokens, i, "debugger")) return ParseRes.failed();
public static ParseRes<DebugStatement> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
if (Parsing.isStatementEnd(tokens, i + 1)) {
if (Parsing.isOperator(tokens, i + 1, Operator.SEMICOLON)) return ParseRes.res(new DebugStatement(Parsing.getLoc(filename, tokens, i)), 2);
else return ParseRes.res(new DebugStatement(Parsing.getLoc(filename, tokens, i)), 1);
if (!Parsing.isIdentifier(src, i + n, "debugger")) return ParseRes.failed();
n += 8;
var end = Parsing.parseStatementEnd(src, i + n);
if (end.isSuccess()) {
n += end.n;
return ParseRes.res(new DebugStatement(loc), n);
}
else return ParseRes.error(Parsing.getLoc(filename, tokens, i), "Expected an end of statement.");
else return end.chainError(src.loc(i + n), "Expected end of statement");
}
}

View File

@ -1,18 +1,15 @@
package me.topchetoeu.jscript.compilation.control;
import java.util.List;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.ParseRes;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.parsing.ParseRes;
import me.topchetoeu.jscript.compilation.parsing.Parsing;
import me.topchetoeu.jscript.compilation.parsing.Token;
import me.topchetoeu.jscript.compilation.values.ConstantStatement;
import me.topchetoeu.jscript.compilation.parsing.Source;
import me.topchetoeu.jscript.compilation.values.IndexStatement;
import me.topchetoeu.jscript.compilation.values.VariableStatement;
import me.topchetoeu.jscript.compilation.values.constants.BoolStatement;
public class DeleteStatement extends Statement {
public final Statement key;
@ -27,13 +24,15 @@ public class DeleteStatement extends Statement {
if (pollute) target.add(Instruction.pushValue(true));
}
public static ParseRes<? extends Statement> parseDelete(Filename filename, List<Token> tokens, int i) {
var loc = Parsing.getLoc(filename, tokens, i);
int n = 0;
if (!Parsing.isIdentifier(tokens, i + n++, "delete")) return ParseRes.failed();
public static ParseRes<? extends Statement> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
var valRes = Parsing.parseValue(filename, tokens, i + n, 15);
if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value after 'delete'.", valRes);
if (!Parsing.isIdentifier(src, i + n, "delete")) return ParseRes.failed();
n += 6;
var valRes = Parsing.parseValue(src, i + n, 15);
if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'delete'");
n += valRes.n;
if (valRes.result instanceof IndexStatement) {
@ -41,9 +40,9 @@ public class DeleteStatement extends Statement {
return ParseRes.res(new DeleteStatement(loc, index.index, index.object), n);
}
else if (valRes.result instanceof VariableStatement) {
return ParseRes.error(loc, "A variable may not be deleted.");
return ParseRes.error(src.loc(i + n), "A variable may not be deleted");
}
else return ParseRes.res(new ConstantStatement(loc, true), n);
else return ParseRes.res(new BoolStatement(loc, true), n);
}
public DeleteStatement(Location loc, Statement key, Statement value) {

View File

@ -1,17 +1,13 @@
package me.topchetoeu.jscript.compilation.control;
import java.util.List;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.ParseRes;
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.parsing.Operator;
import me.topchetoeu.jscript.compilation.parsing.ParseRes;
import me.topchetoeu.jscript.compilation.parsing.Parsing;
import me.topchetoeu.jscript.compilation.parsing.Token;
import me.topchetoeu.jscript.compilation.parsing.Source;
public class DoWhileStatement extends Statement {
public final Statement condition, body;
@ -41,34 +37,41 @@ public class DoWhileStatement extends Statement {
this.body = body;
}
public static ParseRes<DoWhileStatement> parseDoWhile(Filename filename, List<Token> tokens, int i) {
var loc = Parsing.getLoc(filename, tokens, i);
int n = 0;
public static ParseRes<DoWhileStatement> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
var labelRes = WhileStatement.parseLabel(tokens, i + n);
var labelRes = WhileStatement.parseLabel(src, i + n);
n += labelRes.n;
if (!Parsing.isIdentifier(tokens, i + n++, "do")) return ParseRes.failed();
var bodyRes = Parsing.parseStatement(filename, tokens, i + n);
if (!bodyRes.isSuccess()) return ParseRes.error(loc, "Expected a do-while body.", bodyRes);
if (!Parsing.isIdentifier(src, i + n, "do")) return ParseRes.failed();
n += 2;
var bodyRes = Parsing.parseStatement(src, i + n);
if (!bodyRes.isSuccess()) return bodyRes.chainError(src.loc(i + n), "Expected a do-while body.");
n += bodyRes.n;
if (!Parsing.isIdentifier(tokens, i + n++, "while")) return ParseRes.error(loc, "Expected 'while' keyword.");
if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_OPEN)) return ParseRes.error(loc, "Expected a open paren after 'while'.");
if (!Parsing.isIdentifier(src, i + n, "while")) return ParseRes.failed();
n += 5;
n += Parsing.skipEmpty(src, i + n);
var condRes = Parsing.parseValue(filename, tokens, i + n, 0);
if (!condRes.isSuccess()) return ParseRes.error(loc, "Expected a while condition.", condRes);
if (!src.is(i + n, "(")) return ParseRes.error(src.loc(i + n), "Expected a open paren after 'while'.");
n++;
var condRes = Parsing.parseValue(src, i + n, 0);
if (!condRes.isSuccess()) return condRes.chainError(src.loc(i + n), "Expected a do-while condition.");
n += condRes.n;
n += Parsing.skipEmpty(src, i + n);
if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_CLOSE)) return ParseRes.error(loc, "Expected a closing paren after while condition.");
if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a closing paren after do-while condition.");
n++;
var res = ParseRes.res(new DoWhileStatement(loc, labelRes.result, condRes.result, bodyRes.result), n);
if (Parsing.isStatementEnd(tokens, i + n)) {
if (Parsing.isOperator(tokens, i + n, Operator.SEMICOLON)) return res.addN(1);
else return res;
var end = Parsing.parseStatementEnd(src, i + n);
if (end.isSuccess()) {
n += end.n;
return ParseRes.res(new DoWhileStatement(loc, labelRes.result, condRes.result, bodyRes.result), n);
}
else return ParseRes.error(Parsing.getLoc(filename, tokens, i), "Expected a semicolon.");
else return end.chainError(src.loc(i + n), "Expected end of statement");
}
}

View File

@ -1,23 +1,19 @@
package me.topchetoeu.jscript.compilation.control;
import java.util.List;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Operation;
import me.topchetoeu.jscript.common.ParseRes;
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.parsing.Operator;
import me.topchetoeu.jscript.compilation.parsing.ParseRes;
import me.topchetoeu.jscript.compilation.parsing.Parsing;
import me.topchetoeu.jscript.compilation.parsing.Token;
import me.topchetoeu.jscript.compilation.parsing.Source;
public class ForInStatement extends Statement {
public final String varName;
public final boolean isDeclaration;
public final Statement varValue, object, body;
public final Statement object, body;
public final String label;
public final Location varLocation;
@ -31,11 +27,6 @@ public class ForInStatement extends Statement {
if (key instanceof String) target.add(Instruction.makeVar((String)key));
if (varValue != null) {
varValue.compile(target, true);
target.add(Instruction.storeVar(target.scope.getKey(varName)));
}
object.compile(target, true, BreakpointType.STEP_OVER);
target.add(Instruction.keys(true));
@ -61,70 +52,56 @@ public class ForInStatement extends Statement {
if (pollute) target.add(Instruction.pushUndefined());
}
public ForInStatement(Location loc, Location varLocation, String label, boolean isDecl, String varName, Statement varValue, Statement object, Statement body) {
public ForInStatement(Location loc, Location varLocation, String label, boolean isDecl, String varName, Statement object, Statement body) {
super(loc);
this.varLocation = varLocation;
this.label = label;
this.isDeclaration = isDecl;
this.varName = varName;
this.varValue = varValue;
this.object = object;
this.body = body;
}
public static ParseRes<ForInStatement> parse(Filename filename, List<Token> tokens, int i) {
var loc = Parsing.getLoc(filename, tokens, i);
int n = 0;
public static ParseRes<ForInStatement> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
var label = WhileStatement.parseLabel(src, i + n);
n += label.n;
n += Parsing.skipEmpty(src, i + n);
if (!Parsing.isIdentifier(src, i + n, "for")) return ParseRes.failed();
n += 3;
n += Parsing.skipEmpty(src, i + n);
var labelRes = WhileStatement.parseLabel(tokens, i + n);
var isDecl = false;
n += labelRes.n;
if (!Parsing.isIdentifier(tokens, i + n++, "for")) return ParseRes.failed();
if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_OPEN)) return ParseRes.error(loc, "Expected a open paren after 'for'.");
if (Parsing.isIdentifier(tokens, i + n, "var")) {
if (Parsing.isIdentifier(src, i + n, "var")) {
isDecl = true;
n++;
n += 3;
}
var nameRes = Parsing.parseIdentifier(tokens, i + n);
if (!nameRes.isSuccess()) return ParseRes.error(loc, "Expected a variable name for 'for' loop.");
var nameLoc = Parsing.getLoc(filename, tokens, i + n);
n += nameRes.n;
var name = Parsing.parseIdentifier(src, i + n);
if (!name.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a variable name for for-in loop");
var nameLoc = src.loc(i + n);
n += name.n;
n += Parsing.skipEmpty(src, i + n);
Statement varVal = null;
if (!Parsing.isIdentifier(src, i + n, "in")) return ParseRes.error(src.loc(i + n), "Expected 'in' keyword after variable declaration");
n += 2;
if (Parsing.isOperator(tokens, i + n, Operator.ASSIGN)) {
n++;
var obj = Parsing.parseValue(src, i + n, 0);
if (!obj.isSuccess()) return obj.chainError(src.loc(i + n), "Expected a value");
n += obj.n;
n += Parsing.skipEmpty(src, i + n);
var valRes = Parsing.parseValue(filename, tokens, i + n, 2);
if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value after '='.", valRes);
n += nameRes.n;
if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a closing paren");
n++;
varVal = valRes.result;
}
if (!Parsing.isIdentifier(tokens, i + n++, "in")) {
if (varVal == null) {
if (nameRes.result.equals("const")) return ParseRes.error(loc, "'const' declarations are not supported.");
else if (nameRes.result.equals("let")) return ParseRes.error(loc, "'let' declarations are not supported.");
}
return ParseRes.error(loc, "Expected 'in' keyword after variable declaration.");
}
var objRes = Parsing.parseValue(filename, tokens, i + n, 0);
if (!objRes.isSuccess()) return ParseRes.error(loc, "Expected a value.", objRes);
n += objRes.n;
if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_CLOSE)) return ParseRes.error(loc, "Expected a closing paren after for.");
var bodyRes = Parsing.parseStatement(filename, tokens, i + n);
if (!bodyRes.isSuccess()) return ParseRes.error(loc, "Expected a for body.", bodyRes);
var bodyRes = Parsing.parseStatement(src, i + n);
if (!bodyRes.isSuccess()) return bodyRes.chainError(src.loc(i + n), "Expected a for-in body");
n += bodyRes.n;
return ParseRes.res(new ForInStatement(loc, nameLoc, labelRes.result, isDecl, nameRes.result, varVal, objRes.result, bodyRes.result), n);
return ParseRes.res(new ForInStatement(loc, nameLoc, label.result, isDecl, name.result, obj.result, bodyRes.result), n);
}
}

View File

@ -1,17 +1,13 @@
package me.topchetoeu.jscript.compilation.control;
import java.util.List;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.ParseRes;
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.parsing.Operator;
import me.topchetoeu.jscript.compilation.parsing.ParseRes;
import me.topchetoeu.jscript.compilation.parsing.Parsing;
import me.topchetoeu.jscript.compilation.parsing.Token;
import me.topchetoeu.jscript.compilation.parsing.Source;
public class ForOfStatement extends Statement {
public final String varName;
@ -78,44 +74,46 @@ public class ForOfStatement extends Statement {
this.body = body;
}
public static ParseRes<ForOfStatement> parse(Filename filename, List<Token> tokens, int i) {
var loc = Parsing.getLoc(filename, tokens, i);
int n = 0;
public static ParseRes<ForOfStatement> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
var label = WhileStatement.parseLabel(src, i + n);
n += label.n;
n += Parsing.skipEmpty(src, i + n);
if (!Parsing.isIdentifier(src, i + n, "for")) return ParseRes.failed();
n += 3;
n += Parsing.skipEmpty(src, i + n);
var labelRes = WhileStatement.parseLabel(tokens, i + n);
var isDecl = false;
n += labelRes.n;
if (!Parsing.isIdentifier(tokens, i + n++, "for")) return ParseRes.failed();
if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_OPEN)) return ParseRes.error(loc, "Expected a open paren after 'for'.");
if (Parsing.isIdentifier(tokens, i + n, "var")) {
if (Parsing.isIdentifier(src, i + n, "var")) {
isDecl = true;
n++;
n += 3;
}
var nameRes = Parsing.parseIdentifier(tokens, i + n);
if (!nameRes.isSuccess()) return ParseRes.error(loc, "Expected a variable name for 'for' loop.");
var nameLoc = Parsing.getLoc(filename, tokens, i + n);
n += nameRes.n;
var name = Parsing.parseIdentifier(src, i + n);
if (!name.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a variable name for for-of loop");
var nameLoc = src.loc(i + n);
n += name.n;
n += Parsing.skipEmpty(src, i + n);
if (!Parsing.isIdentifier(tokens, i + n++, "of")) {
if (nameRes.result.equals("const")) return ParseRes.error(loc, "'const' declarations are not supported.");
else if (nameRes.result.equals("let")) return ParseRes.error(loc, "'let' declarations are not supported.");
else return ParseRes.error(loc, "Expected 'of' keyword after variable declaration.");
}
if (!Parsing.isIdentifier(src, i + n, "fo")) return ParseRes.error(src.loc(i + n), "Expected 'of' keyword after variable declaration");
n += 2;
var objRes = Parsing.parseValue(filename, tokens, i + n, 0);
if (!objRes.isSuccess()) return ParseRes.error(loc, "Expected a value.", objRes);
n += objRes.n;
var obj = Parsing.parseValue(src, i + n, 0);
if (!obj.isSuccess()) return obj.chainError(src.loc(i + n), "Expected a value");
n += obj.n;
n += Parsing.skipEmpty(src, i + n);
if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_CLOSE)) return ParseRes.error(loc, "Expected a closing paren after for.");
if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a closing paren");
n++;
var bodyRes = Parsing.parseStatement(filename, tokens, i + n);
if (!bodyRes.isSuccess()) return ParseRes.error(loc, "Expected a for body.", bodyRes);
var bodyRes = Parsing.parseStatement(src, i + n);
if (!bodyRes.isSuccess()) return bodyRes.chainError(src.loc(i + n), "Expected a for-of body");
n += bodyRes.n;
return ParseRes.res(new ForOfStatement(loc, nameLoc, labelRes.result, isDecl, nameRes.result, objRes.result, bodyRes.result), n);
return ParseRes.res(new ForOfStatement(loc, nameLoc, label.result, isDecl, name.result, obj.result, bodyRes.result), n);
}
}

View File

@ -1,20 +1,15 @@
package me.topchetoeu.jscript.compilation.control;
import java.util.List;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.ParseRes;
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.CompoundStatement;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.VariableDeclareStatement;
import me.topchetoeu.jscript.compilation.parsing.Operator;
import me.topchetoeu.jscript.compilation.parsing.ParseRes;
import me.topchetoeu.jscript.compilation.parsing.Parsing;
import me.topchetoeu.jscript.compilation.parsing.Token;
import me.topchetoeu.jscript.compilation.values.ConstantStatement;
import me.topchetoeu.jscript.compilation.parsing.Source;
import me.topchetoeu.jscript.compilation.values.DiscardStatement;
public class ForStatement extends Statement {
public final Statement declaration, assignment, condition, body;
@ -53,61 +48,69 @@ public class ForStatement extends Statement {
this.body = body;
}
public static ParseRes<ForStatement> parse(Filename filename, List<Token> tokens, int i) {
var loc = Parsing.getLoc(filename, tokens, i);
int n = 0;
private static ParseRes<Statement> parseSemicolon(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var labelRes = WhileStatement.parseLabel(tokens, i + n);
n += labelRes.n;
if (!src.is(i + n, ";")) return ParseRes.failed();
else return ParseRes.res(new DiscardStatement(src.loc(i), null), n + 1);
}
private static ParseRes<Statement> parseCondition(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
if (!Parsing.isIdentifier(tokens, i + n++, "for")) return ParseRes.failed();
if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_OPEN)) return ParseRes.error(loc, "Expected a open paren after 'for'.");
Statement decl, cond, inc;
if (Parsing.isOperator(tokens, i + n, Operator.SEMICOLON)) {
n++;
decl = new CompoundStatement(loc, false);
}
else {
var declRes = ParseRes.any(
VariableDeclareStatement.parse(filename, tokens, i + n),
Parsing.parseValueStatement(filename, tokens, i + n)
);
if (!declRes.isSuccess()) return ParseRes.error(loc, "Expected a declaration or an expression.", declRes);
n += declRes.n;
decl = declRes.result;
}
if (Parsing.isOperator(tokens, i + n, Operator.SEMICOLON)) {
n++;
cond = new ConstantStatement(loc, 1);
}
else {
var condRes = Parsing.parseValue(filename, tokens, i + n, 0);
if (!condRes.isSuccess()) return ParseRes.error(loc, "Expected a condition.", condRes);
n += condRes.n;
if (!Parsing.isOperator(tokens, i + n++, Operator.SEMICOLON)) return ParseRes.error(loc, "Expected a semicolon.", condRes);
cond = condRes.result;
}
if (Parsing.isOperator(tokens, i + n, Operator.PAREN_CLOSE)) {
n++;
inc = new CompoundStatement(loc, false);
}
else {
var incRes = Parsing.parseValue(filename, tokens, i + n, 0);
if (!incRes.isSuccess()) return ParseRes.error(loc, "Expected a condition.", incRes);
n += incRes.n;
inc = incRes.result;
if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_CLOSE)) return ParseRes.error(loc, "Expected a closing paren after for.");
}
var res = Parsing.parseStatement(filename, tokens, i + n);
if (!res.isSuccess()) return ParseRes.error(loc, "Expected a for body.", res);
var res = Parsing.parseValue(src, i + n, 0);
if (!res.isSuccess()) return res.chainError();
n += res.n;
n += Parsing.skipEmpty(src, i + n);
return ParseRes.res(new ForStatement(loc, labelRes.result, decl, cond, inc, res.result), n);
if (!src.is(i + n, ";")) return ParseRes.error(src.loc(i + n), "Expected a semicolon");
else return ParseRes.res(res.result, n + 1);
}
private static ParseRes<? extends Statement> parseUpdater(Source src, int i) {
return Parsing.parseValue(src, i, 0);
}
public static ParseRes<ForStatement> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
var labelRes = WhileStatement.parseLabel(src, i + n);
n += labelRes.n;
n += Parsing.skipEmpty(src, i + n);
if (!Parsing.isIdentifier(src, i + n, "for")) return ParseRes.failed();
n += 3;
n += Parsing.skipEmpty(src, i + n);
if (!src.is(i + n, "(")) return ParseRes.error(src.loc(i + n), "Expected a open paren after 'for'");
n++;
ParseRes<Statement> decl = ParseRes.first(src, i + n,
ForStatement::parseSemicolon,
VariableDeclareStatement::parse,
ForStatement::parseCondition
);
if (!decl.isSuccess()) return decl.chainError(src.loc(i + n), "Expected a declaration or an expression");
n += decl.n;
ParseRes<Statement> cond = ParseRes.first(src, i + n,
ForStatement::parseSemicolon,
ForStatement::parseCondition
);
if (!cond.isSuccess()) return cond.chainError(src.loc(i + n), "Expected a condition");
n += cond.n;
var update = parseUpdater(src, i + n);
if (update.isError()) return update.chainError();
n += update.n;
n += Parsing.skipEmpty(src, i + n);
if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a close paren after for updater");
n++;
var body = Parsing.parseStatement(src, i + n);
if (!body.isSuccess()) return body.chainError(src.loc(i + n), "Expected a for body.");
n += body.n;
return ParseRes.res(new ForStatement(loc, labelRes.result, decl.result, cond.result, update.result, body.result), n);
}
}

View File

@ -1,17 +1,13 @@
package me.topchetoeu.jscript.compilation.control;
import java.util.List;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.ParseRes;
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.parsing.Operator;
import me.topchetoeu.jscript.compilation.parsing.ParseRes;
import me.topchetoeu.jscript.compilation.parsing.Parsing;
import me.topchetoeu.jscript.compilation.parsing.Token;
import me.topchetoeu.jscript.compilation.parsing.Source;
public class IfStatement extends Statement {
public final Statement condition, body, elseBody;
@ -53,47 +49,58 @@ public class IfStatement extends Statement {
this.elseBody = elseBody;
}
public static ParseRes<IfStatement> parseTernary(Filename filename, List<Token> tokens, int i, Statement prev, int precedence) {
var loc = Parsing.getLoc(filename, tokens, i);
var n = 0;
public static ParseRes<IfStatement> parseTernary(Source src, int i, Statement prev, int precedence) {
if (precedence > 2) return ParseRes.failed();
if (!Parsing.isOperator(tokens, i + n++, Operator.QUESTION)) return ParseRes.failed();
var a = Parsing.parseValue(filename, tokens, i + n, 2);
if (!a.isSuccess()) return ParseRes.error(loc, "Expected a value after the ternary operator.", a);
var n = Parsing.skipEmpty(src, i);
if (!src.is(i + n, "?")) return ParseRes.failed();
var loc = src.loc(i + n);
n++;
var a = Parsing.parseValue(src, i + n, 2);
if (!a.isSuccess()) return a.chainError(src.loc(i + n), "Expected a value after the ternary operator.");
n += a.n;
n += Parsing.skipEmpty(src, i);
if (!Parsing.isOperator(tokens, i + n++, Operator.COLON)) return ParseRes.failed();
if (!src.is(i + n, ":")) return ParseRes.failed();
n++;
var b = Parsing.parseValue(filename, tokens, i + n, 2);
if (!b.isSuccess()) return ParseRes.error(loc, "Expected a second value after the ternary operator.", b);
var b = Parsing.parseValue(src, i + n, 2);
if (!b.isSuccess()) return b.chainError(src.loc(i + n), "Expected a second value after the ternary operator.");
n += b.n;
return ParseRes.res(new IfStatement(loc, prev, a.result, b.result), n);
}
public static ParseRes<IfStatement> parse(Filename filename, List<Token> tokens, int i) {
var loc = Parsing.getLoc(filename, tokens, i);
int n = 0;
public static ParseRes<IfStatement> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
if (!Parsing.isIdentifier(tokens, i + n++, "if")) return ParseRes.failed();
if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_OPEN)) return ParseRes.error(loc, "Expected a open paren after 'if'.");
if (!Parsing.isIdentifier(src, i + n, "if")) return ParseRes.failed();
n += 2;
n += Parsing.skipEmpty(src, i + n);
var condRes = Parsing.parseValue(filename, tokens, i + n, 0);
if (!condRes.isSuccess()) return ParseRes.error(loc, "Expected an if condition.", condRes);
n += condRes.n;
if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_CLOSE)) return ParseRes.error(loc, "Expected a closing paren after if condition.");
var res = Parsing.parseStatement(filename, tokens, i + n);
if (!res.isSuccess()) return ParseRes.error(loc, "Expected an if body.", res);
n += res.n;
if (!Parsing.isIdentifier(tokens, i + n, "else")) return ParseRes.res(new IfStatement(loc, condRes.result, res.result, null), n);
if (!src.is(i + n, "(")) return ParseRes.error(src.loc(i + n), "Expected a open paren after 'if'.");
n++;
var elseRes = Parsing.parseStatement(filename, tokens, i + n);
if (!elseRes.isSuccess()) return ParseRes.error(loc, "Expected an else body.", elseRes);
var condRes = Parsing.parseValue(src, i + n, 0);
if (!condRes.isSuccess()) return condRes.chainError(src.loc(i + n), "Expected an if condition.");
n += condRes.n;
n += Parsing.skipEmpty(src, i + n);
if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a closing paren after if condition.");
n++;
var res = Parsing.parseStatement(src, i + n);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected an if body.");
n += res.n;
var elseKw = Parsing.parseIdentifier(src, i + n, "else");
if (!elseKw.isSuccess()) return ParseRes.res(new IfStatement(loc, condRes.result, res.result, null), n);
n += elseKw.n;
var elseRes = Parsing.parseStatement(src, i + n);
if (!elseRes.isSuccess()) return elseRes.chainError(src.loc(i + n), "Expected an else body.");
n += elseRes.n;
return ParseRes.res(new IfStatement(loc, condRes.result, res.result, elseRes.result), n);

View File

@ -1,16 +1,12 @@
package me.topchetoeu.jscript.compilation.control;
import java.util.List;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.ParseRes;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.parsing.Operator;
import me.topchetoeu.jscript.compilation.parsing.ParseRes;
import me.topchetoeu.jscript.compilation.parsing.Parsing;
import me.topchetoeu.jscript.compilation.parsing.Token;
import me.topchetoeu.jscript.compilation.parsing.Source;
public class ReturnStatement extends Statement {
public final Statement value;
@ -27,26 +23,28 @@ public class ReturnStatement extends Statement {
this.value = value;
}
public static ParseRes<ReturnStatement> parseReturn(Filename filename, List<Token> tokens, int i) {
var loc = Parsing.getLoc(filename, tokens, i);
int n = 0;
if (!Parsing.isIdentifier(tokens, i + n++, "return")) return ParseRes.failed();
public static ParseRes<ReturnStatement> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
if (Parsing.isStatementEnd(tokens, i + n)) {
if (Parsing.isOperator(tokens, i + n, Operator.SEMICOLON)) return ParseRes.res(new ReturnStatement(loc, null), 2);
else return ParseRes.res(new ReturnStatement(loc, null), 1);
if (!Parsing.isIdentifier(src, i + n, "return")) return ParseRes.failed();
n += 6;
var end = Parsing.parseStatementEnd(src, i + n);
if (end.isSuccess()) {
n += end.n;
return ParseRes.res(new ReturnStatement(loc, null), n);
}
var valRes = Parsing.parseValue(filename, tokens, i + n, 0);
n += valRes.n;
if (valRes.isError()) return ParseRes.error(loc, "Expected a return value.", valRes);
var val = Parsing.parseValue(src, i + n, 0);
if (val.isFailed()) return ParseRes.error(src.loc(i + n), "Expected a value");
n += val.n;
var res = ParseRes.res(new ReturnStatement(loc, valRes.result), n);
if (Parsing.isStatementEnd(tokens, i + n)) {
if (Parsing.isOperator(tokens, i + n, Operator.SEMICOLON)) return res.addN(1);
else return res;
end = Parsing.parseStatementEnd(src, i + n);
if (end.isSuccess()) {
n += end.n;
return ParseRes.res(new ReturnStatement(loc, val.result), n);
}
else return ParseRes.error(Parsing.getLoc(filename, tokens, i), "Expected an end of statement.", valRes);
else return end.chainError(src.loc(i + n), "Expected end of statement");
}
}

View File

@ -2,21 +2,17 @@ package me.topchetoeu.jscript.compilation.control;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Operation;
import me.topchetoeu.jscript.common.ParseRes;
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.common.Instruction.Type;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.CompoundStatement;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.parsing.Operator;
import me.topchetoeu.jscript.compilation.parsing.ParseRes;
import me.topchetoeu.jscript.compilation.parsing.Parsing;
import me.topchetoeu.jscript.compilation.parsing.Token;
import me.topchetoeu.jscript.compilation.parsing.Source;
public class SwitchStatement extends Statement {
public static class SwitchCase {
@ -87,78 +83,96 @@ public class SwitchStatement extends Statement {
this.body = body;
}
private static ParseRes<Statement> parseSwitchCase(Filename filename, List<Token> tokens, int i) {
var loc = Parsing.getLoc(filename, tokens, i);
int n = 0;
private static ParseRes<Statement> parseSwitchCase(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
if (!Parsing.isIdentifier(tokens, i + n++, "case")) return ParseRes.failed();
if (!Parsing.isIdentifier(src, i + n, "case")) return ParseRes.failed();
n += 4;
var valRes = Parsing.parseValue(filename, tokens, i + n, 0);
if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value after 'case'.", valRes);
var valRes = Parsing.parseValue(src, i + n, 0);
if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'case'");
n += valRes.n;
if (!Parsing.isOperator(tokens, i + n++, Operator.COLON)) return ParseRes.error(loc, "Expected colons after 'case' value.");
if (!src.is(i + n, ":")) return ParseRes.error(src.loc(i + n), "Expected colons after 'case' value");
n++;
return ParseRes.res(valRes.result, n);
}
private static ParseRes<Statement> parseDefaultCase(List<Token> tokens, int i) {
if (!Parsing.isIdentifier(tokens, i, "default")) return ParseRes.failed();
if (!Parsing.isOperator(tokens, i + 1, Operator.COLON)) return ParseRes.error(Parsing.getLoc(null, tokens, i), "Expected colons after 'default'.");
private static ParseRes<Void> parseDefaultCase(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
return ParseRes.res(null, 2);
if (!Parsing.isIdentifier(src, i + n, "default")) return ParseRes.failed();
n += 7;
n += Parsing.skipEmpty(src, i + n);
if (!src.is(i + n, ":")) return ParseRes.error(src.loc(i + n), "Expected colons after 'default'");
n++;
return ParseRes.res(null, n);
}
public static ParseRes<SwitchStatement> parse(Filename filename, List<Token> tokens, int i) {
var loc = Parsing.getLoc(filename, tokens, i);
int n = 0;
@SuppressWarnings("unused")
public static ParseRes<SwitchStatement> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
if (!Parsing.isIdentifier(tokens, i + n++, "switch")) return ParseRes.failed();
if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_OPEN)) return ParseRes.error(loc, "Expected a open paren after 'switch'.");
if (!Parsing.isIdentifier(src, i + n, "switch")) return ParseRes.failed();
n += 6;
n += Parsing.skipEmpty(src, i + n);
if (!src.is(i + n, "(")) return ParseRes.error(src.loc(i + n), "Expected a open paren after 'switch'");
n++;
var valRes = Parsing.parseValue(filename, tokens, i + n, 0);
if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a switch value.", valRes);
var valRes = Parsing.parseValue(src, i + n, 0);
if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a switch value");
n += valRes.n;
n += Parsing.skipEmpty(src, i + n);
if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_CLOSE)) return ParseRes.error(loc, "Expected a closing paren after switch value.");
if (!Parsing.isOperator(tokens, i + n++, Operator.BRACE_OPEN)) return ParseRes.error(loc, "Expected an opening brace after switch value.");
if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a closing paren after switch value");
n++;
n += Parsing.skipEmpty(src, i + n);
if (!src.is(i + n, "{")) return ParseRes.error(src.loc(i + n), "Expected an opening brace after switch value");
n++;
n += Parsing.skipEmpty(src, i + n);
var statements = new ArrayList<Statement>();
var cases = new ArrayList<SwitchCase>();
var defaultI = -1;
while (true) {
if (Parsing.isOperator(tokens, i + n, Operator.BRACE_CLOSE)) {
n += Parsing.skipEmpty(src, i + n);
if (src.is(i + n, "}")) {
n++;
break;
}
if (Parsing.isOperator(tokens, i + n, Operator.SEMICOLON)) {
if (src.is(i + n, ";")) {
n++;
continue;
}
var defaultRes = SwitchStatement.parseDefaultCase(tokens, i + n);
var caseRes = SwitchStatement.parseSwitchCase(filename, tokens, i + n);
ParseRes<Statement> caseRes = ParseRes.first(src, i + n,
SwitchStatement::parseDefaultCase,
SwitchStatement::parseSwitchCase
);
if (defaultRes.isSuccess()) {
defaultI = statements.size();
n += defaultRes.n;
}
else if (caseRes.isSuccess()) {
cases.add(new SwitchCase(caseRes.result, statements.size()));
// Parsing::parseStatement
if (caseRes.isSuccess()) {
n += caseRes.n;
if (caseRes.result == null) defaultI = statements.size();
else cases.add(new SwitchCase(caseRes.result, statements.size()));
continue;
}
else if (defaultRes.isError()) return defaultRes.transform();
else if (caseRes.isError()) return defaultRes.transform();
else {
var res = ParseRes.any(
Parsing.parseStatement(filename, tokens, i + n),
CompoundStatement.parse(filename, tokens, i + n)
);
if (!res.isSuccess()) {
return ParseRes.error(Parsing.getLoc(filename, tokens, i), "Expected a statement.", res);
}
n += res.n;
statements.add(res.result);
if (caseRes.isError()) return caseRes.chainError();
var stm = Parsing.parseStatement(src, i + n);
if (stm.isSuccess()) {
n += stm.n;
statements.add(stm.result);
continue;
}
else stm.chainError(src.loc(i + n), "Expected a statement, 'case' or 'default'");
}
return ParseRes.res(new SwitchStatement(

View File

@ -1,16 +1,12 @@
package me.topchetoeu.jscript.compilation.control;
import java.util.List;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.ParseRes;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.parsing.Operator;
import me.topchetoeu.jscript.compilation.parsing.ParseRes;
import me.topchetoeu.jscript.compilation.parsing.Parsing;
import me.topchetoeu.jscript.compilation.parsing.Token;
import me.topchetoeu.jscript.compilation.parsing.Source;
public class ThrowStatement extends Statement {
public final Statement value;
@ -26,21 +22,28 @@ public class ThrowStatement extends Statement {
this.value = value;
}
public static ParseRes<ThrowStatement> parseThrow(Filename filename, List<Token> tokens, int i) {
var loc = Parsing.getLoc(filename, tokens, i);
int n = 0;
if (!Parsing.isIdentifier(tokens, i + n++, "throw")) return ParseRes.failed();
public static ParseRes<ThrowStatement> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
var valRes = Parsing.parseValue(filename, tokens, i + n, 0);
n += valRes.n;
if (valRes.isError()) return ParseRes.error(loc, "Expected a throw value.", valRes);
if (!Parsing.isIdentifier(src, i + n, "throw")) return ParseRes.failed();
n += 5;
var res = ParseRes.res(new ThrowStatement(loc, valRes.result), n);
if (Parsing.isStatementEnd(tokens, i + n)) {
if (Parsing.isOperator(tokens, i + n, Operator.SEMICOLON)) return res.addN(1);
else return res;
var end = Parsing.parseStatementEnd(src, i + n);
if (end.isSuccess()) {
n += end.n;
return ParseRes.res(new ThrowStatement(loc, null), n);
}
else return ParseRes.error(Parsing.getLoc(filename, tokens, i), "Expected an end of statement.", valRes);
var val = Parsing.parseValue(src, i + n, 0);
if (val.isFailed()) return ParseRes.error(src.loc(i + n), "Expected a value");
n += val.n;
end = Parsing.parseStatementEnd(src, i + n);
if (end.isSuccess()) {
n += end.n;
return ParseRes.res(new ThrowStatement(loc, val.result), n);
}
else return end.chainError(src.loc(i + n), "Expected end of statement");
}
}

View File

@ -1,17 +1,14 @@
package me.topchetoeu.jscript.compilation.control;
import java.util.List;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.ParseRes;
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.CompoundStatement;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.parsing.Operator;
import me.topchetoeu.jscript.compilation.parsing.ParseRes;
import me.topchetoeu.jscript.compilation.parsing.Parsing;
import me.topchetoeu.jscript.compilation.parsing.Token;
import me.topchetoeu.jscript.compilation.parsing.Source;
public class TryStatement extends Statement {
public final Statement tryBody;
@ -61,44 +58,56 @@ public class TryStatement extends Statement {
this.name = name;
}
public static ParseRes<TryStatement> parse(Filename filename, List<Token> tokens, int i) {
var loc = Parsing.getLoc(filename, tokens, i);
int n = 0;
public static ParseRes<TryStatement> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
if (!Parsing.isIdentifier(tokens, i + n++, "try")) return ParseRes.failed();
if (!Parsing.isIdentifier(src, i + n, "try")) return ParseRes.failed();
n += 3;
var res = Parsing.parseStatement(filename, tokens, i + n);
if (!res.isSuccess()) return ParseRes.error(loc, "Expected an if body.", res);
n += res.n;
var tryBody = CompoundStatement.parse(src, i + n);
if (!tryBody.isSuccess()) return tryBody.chainError(src.loc(i + n), "Expected a try body");
n += tryBody.n;
n += Parsing.skipEmpty(src, i + n);
String name = null;
Statement catchBody = null, finallyBody = null;
if (Parsing.isIdentifier(tokens, i + n, "catch")) {
n++;
if (Parsing.isOperator(tokens, i + n, Operator.PAREN_OPEN)) {
if (Parsing.isIdentifier(src, i + n, "catch")) {
n += 5;
n += Parsing.skipEmpty(src, i + n);
if (src.is(i + n, "(")) {
n++;
var nameRes = Parsing.parseIdentifier(tokens, i + n++);
if (!nameRes.isSuccess()) return ParseRes.error(loc, "Expected a catch variable name.");
var nameRes = Parsing.parseIdentifier(src, i + n);
if (!nameRes.isSuccess()) return nameRes.chainError(src.loc(i + n), "xpected a catch variable name");
name = nameRes.result;
if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_CLOSE)) return ParseRes.error(loc, "Expected a closing paren after catch variable name.");
n += nameRes.n;
n += Parsing.skipEmpty(src, i + n);
if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a closing paren after catch variable name");
n++;
}
var catchRes = Parsing.parseStatement(filename, tokens, i + n);
if (!catchRes.isSuccess()) return ParseRes.error(loc, "Expected a catch body.", catchRes);
n += catchRes.n;
catchBody = catchRes.result;
var bodyRes = CompoundStatement.parse(src, i + n);
if (!bodyRes.isSuccess()) return tryBody.chainError(src.loc(i + n), "Expected a catch body");
n += bodyRes.n;
n += Parsing.skipEmpty(src, i + n);
catchBody = bodyRes.result;
}
if (Parsing.isIdentifier(tokens, i + n, "finally")) {
n++;
var finallyRes = Parsing.parseStatement(filename, tokens, i + n);
if (!finallyRes.isSuccess()) return ParseRes.error(loc, "Expected a finally body.", finallyRes);
n += finallyRes.n;
finallyBody = finallyRes.result;
if (Parsing.isIdentifier(src, i + n, "finally")) {
n += 7;
var bodyRes = CompoundStatement.parse(src, i + n);
if (!bodyRes.isSuccess()) return tryBody.chainError(src.loc(i + n), "Expected a finally body");
n += bodyRes.n;
n += Parsing.skipEmpty(src, i + n);
finallyBody = bodyRes.result;
}
return ParseRes.res(new TryStatement(loc, res.result, catchBody, finallyBody, name), n);
if (finallyBody == null && catchBody == null) ParseRes.error(src.loc(i + n), "Expected catch or finally");
return ParseRes.res(new TryStatement(loc, tryBody.result, catchBody, finallyBody, name), n);
}
}

View File

@ -1,18 +1,14 @@
package me.topchetoeu.jscript.compilation.control;
import java.util.List;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.ParseRes;
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.common.Instruction.Type;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.parsing.Operator;
import me.topchetoeu.jscript.compilation.parsing.ParseRes;
import me.topchetoeu.jscript.compilation.parsing.Parsing;
import me.topchetoeu.jscript.compilation.parsing.Token;
import me.topchetoeu.jscript.compilation.parsing.Source;
public class WhileStatement extends Statement {
public final Statement condition, body;
@ -38,14 +34,20 @@ public class WhileStatement extends Statement {
if (pollute) target.add(Instruction.pushUndefined());
}
public static ParseRes<String> parseLabel(List<Token> tokens, int i) {
int n = 0;
public static ParseRes<String> parseLabel(Source src, int i) {
int n = Parsing.skipEmpty(src, i);
var nameRes = Parsing.parseIdentifier(tokens, i + n++);
if (!Parsing.isOperator(tokens, i + n++, Operator.COLON)) return ParseRes.failed();
var nameRes = Parsing.parseIdentifier(src, i + n);
if (!nameRes.isSuccess()) return nameRes.chainError();
n += nameRes.n;
n += Parsing.skipEmpty(src, i + n);
if (!src.is(i + n, ":")) return ParseRes.failed();
n++;
return ParseRes.res(nameRes.result, n);
}
public WhileStatement(Location loc, String label, Statement condition, Statement body) {
super(loc);
this.label = label;
@ -65,24 +67,31 @@ public class WhileStatement extends Statement {
}
}
public static ParseRes<WhileStatement> parseWhile(Filename filename, List<Token> tokens, int i) {
var loc = Parsing.getLoc(filename, tokens, i);
int n = 0;
public static ParseRes<WhileStatement> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
var labelRes = WhileStatement.parseLabel(tokens, i + n);
var labelRes = WhileStatement.parseLabel(src, i + n);
n += labelRes.n;
n += Parsing.skipEmpty(src, i + n);
if (!Parsing.isIdentifier(tokens, i + n++, "while")) return ParseRes.failed();
if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_OPEN)) return ParseRes.error(loc, "Expected a open paren after 'while'.");
if (!Parsing.isIdentifier(src, i + n, "while")) return ParseRes.failed();
n += 5;
n += Parsing.skipEmpty(src, i + n);
var condRes = Parsing.parseValue(filename, tokens, i + n, 0);
if (!condRes.isSuccess()) return ParseRes.error(loc, "Expected a while condition.", condRes);
if (!src.is(i + n, "(")) return ParseRes.error(src.loc(i + n), "Expected a open paren after 'while'.");
n++;
var condRes = Parsing.parseValue(src, i + n, 0);
if (!condRes.isSuccess()) return condRes.chainError(src.loc(i + n), "Expected a while condition.");
n += condRes.n;
n += Parsing.skipEmpty(src, i + n);
if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_CLOSE)) return ParseRes.error(loc, "Expected a closing paren after while condition.");
if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a closing paren after while condition.");
n++;
var res = Parsing.parseStatement(filename, tokens, i + n);
if (!res.isSuccess()) return ParseRes.error(loc, "Expected a while body.", res);
var res = Parsing.parseStatement(src, i + n);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a while body.");
n += res.n;
return ParseRes.res(new WhileStatement(loc, labelRes.result, condRes.result, res.result), n);

View File

@ -1,113 +1,48 @@
package me.topchetoeu.jscript.compilation.parsing;
import java.util.HashMap;
import java.util.Map;
import java.util.Arrays;
import java.util.LinkedHashSet;
import me.topchetoeu.jscript.common.Operation;
public enum Operator {
MULTIPLY("*", Operation.MULTIPLY, 13),
DIVIDE("/", Operation.DIVIDE, 12),
MODULO("%", Operation.MODULO, 12),
SUBTRACT("-", Operation.SUBTRACT, 11),
ADD("+", Operation.ADD, 11),
SHIFT_RIGHT(">>", Operation.SHIFT_RIGHT, 10),
SHIFT_LEFT("<<", Operation.SHIFT_LEFT, 10),
USHIFT_RIGHT(">>>", Operation.USHIFT_RIGHT, 10),
GREATER(">", Operation.GREATER, 9),
LESS("<", Operation.LESS, 9),
GREATER_EQUALS(">=", Operation.GREATER_EQUALS, 9),
LESS_EQUALS("<=", Operation.LESS_EQUALS, 9),
NOT_EQUALS("!=", Operation.LOOSE_NOT_EQUALS, 8),
LOOSE_NOT_EQUALS("!==", Operation.NOT_EQUALS, 8),
EQUALS("==", Operation.LOOSE_EQUALS, 8),
LOOSE_EQUALS("===", Operation.EQUALS, 8),
AND("&", Operation.AND, 7),
XOR("^", Operation.XOR, 6),
OR("|", Operation.OR, 5),
LAZY_AND("&&", 4),
LAZY_OR("||", 3),
ASSIGN_SHIFT_LEFT("<<=", 2, true),
ASSIGN_SHIFT_RIGHT(">>=", 2, true),
ASSIGN_USHIFT_RIGHT(">>>=", 2, true),
ASSIGN_AND("&=", 2, true),
ASSIGN_OR("|=", 2, true),
ASSIGN_XOR("^=", 2, true),
ASSIGN_MODULO("%=", 2, true),
ASSIGN_DIVIDE("/=", 2, true),
ASSIGN_MULTIPLY("*=", 2, true),
ASSIGN_SUBTRACT("-=", 2, true),
ASSIGN_ADD("+=", 2, true),
ASSIGN("=", 2, true),
SEMICOLON(";"),
COLON(":"),
PAREN_OPEN("("),
PAREN_CLOSE(")"),
BRACKET_OPEN("["),
BRACKET_CLOSE("]"),
BRACE_OPEN("{"),
BRACE_CLOSE("}"),
DOT("."),
COMMA(","),
NOT("!"),
QUESTION("?"),
INVERSE("~"),
INCREASE("++"),
DECREASE("--");
MULTIPLY("*", Operation.MULTIPLY, 13, true),
DIVIDE("/", Operation.DIVIDE, 12, true),
MODULO("%", Operation.MODULO, 12, true),
SUBTRACT("-", Operation.SUBTRACT, 11, true),
ADD("+", Operation.ADD, 11, true),
SHIFT_RIGHT(">>", Operation.SHIFT_RIGHT, 10, true),
SHIFT_LEFT("<<", Operation.SHIFT_LEFT, 10, true),
USHIFT_RIGHT(">>>", Operation.USHIFT_RIGHT, 10, true),
GREATER(">", Operation.GREATER, 9, false),
LESS("<", Operation.LESS, 9, false),
GREATER_EQUALS(">=", Operation.GREATER_EQUALS, 9, false),
LESS_EQUALS("<=", Operation.LESS_EQUALS, 9, false),
NOT_EQUALS("!=", Operation.LOOSE_NOT_EQUALS, 8, false),
LOOSE_NOT_EQUALS("!==", Operation.NOT_EQUALS, 8, false),
EQUALS("==", Operation.LOOSE_EQUALS, 8, false),
LOOSE_EQUALS("===", Operation.EQUALS, 8, false),
AND("&", Operation.AND, 7, true),
XOR("^", Operation.XOR, 6, true),
OR("|", Operation.OR, 5, true);
public final String readable;
public final Operation operation;
public final int precedence;
public final boolean reverse;
private static final Map<String, Operator> ops = new HashMap<>();
public final boolean assignable;
public static final LinkedHashSet<Operator> opsByLength = new LinkedHashSet<Operator>();
static {
for (var el : Operator.values()) {
ops.put(el.readable, el);
}
var vals = Operator.values();
Arrays.sort(vals, (a, b) -> b.readable.length() - a.readable.length());
for (var el : vals) opsByLength.add(el);
}
public boolean isAssign() { return precedence == 2; }
public static Operator parse(String val) {
return ops.get(val);
}
private Operator() {
this.readable = null;
this.operation = null;
this.precedence = -1;
this.reverse = false;
}
private Operator(String value) {
this.readable = value;
this.operation = null;
this.precedence = -1;
this.reverse = false;
}
private Operator(String value, int precedence) {
this.readable = value;
this.operation = null;
this.precedence = precedence;
this.reverse = false;
}
private Operator(String value, int precedence, boolean reverse) {
this.readable = value;
this.operation = null;
this.precedence = precedence;
this.reverse = reverse;
}
private Operator(String value, Operation funcName, int precedence) {
private Operator(String value, Operation funcName, int precedence, boolean assignable) {
this.readable = value;
this.operation = funcName;
this.precedence = precedence;
this.reverse = false;
}
private Operator(String value, Operation funcName, int precedence, boolean reverse) {
this.readable = value;
this.operation = funcName;
this.precedence = precedence;
this.reverse = reverse;
this.assignable = assignable;
}
}

View File

@ -1,13 +1,12 @@
package me.topchetoeu.jscript.common;
package me.topchetoeu.jscript.compilation.parsing;
import java.util.List;
import java.util.Map;
import me.topchetoeu.jscript.compilation.parsing.TestRes;
import me.topchetoeu.jscript.compilation.parsing.Token;
import me.topchetoeu.jscript.compilation.parsing.Parsing.Parser;
import me.topchetoeu.jscript.common.Location;
public class ParseRes<T> {
public static interface Parser<T> {
public ParseRes<T> parse(Source src, int i);
}
public static enum State {
SUCCESS,
FAILED,
@ -34,11 +33,11 @@ public class ParseRes<T> {
if (!state.isSuccess()) return this;
return new ParseRes<>(state, null, result, i);
}
public ParseRes<T> addN(int i) {
public ParseRes<T> addN(int n) {
if (!state.isSuccess()) return this;
return new ParseRes<>(state, null, result, this.n + i);
return new ParseRes<>(state, null, result, this.n + n);
}
public <T2> ParseRes<T2> transform() {
public <T2> ParseRes<T2> chainError() {
if (isSuccess()) throw new RuntimeException("Can't transform a ParseRes that hasn't failed.");
return new ParseRes<>(state, error, null, 0);
}
@ -59,41 +58,26 @@ public class ParseRes<T> {
if (loc != null) error = loc + ": " + error;
return new ParseRes<>(State.ERROR, error, null, 0);
}
public static <T> ParseRes<T> error(Location loc, String error, ParseRes<?> other) {
public <T2> ParseRes<T2> chainError(Location loc, String error) {
if (loc != null) error = loc + ": " + error;
if (!other.isError()) return new ParseRes<>(State.ERROR, error, null, 0);
return new ParseRes<>(State.ERROR, other.error, null, 0);
if (!this.isError()) return new ParseRes<>(State.ERROR, error, null, 0);
return new ParseRes<>(State.ERROR, this.error, null, 0);
}
public static <T> ParseRes<T> res(T val, int i) {
return new ParseRes<>(State.SUCCESS, null, val, i);
}
@SafeVarargs
public static <T> ParseRes<? extends T> any(ParseRes<? extends T> ...parsers) {
return any(List.of(parsers));
}
public static <T> ParseRes<? extends T> any(List<ParseRes<? extends T>> parsers) {
ParseRes<? extends T> best = null;
ParseRes<? extends T> error = ParseRes.failed();
@SuppressWarnings("all")
// to hell with all of java's bullshit generics that do jack shit nothing
public static <T> ParseRes<T> first(Source src, int i, Parser ...parsers) {
int n = Parsing.skipEmpty(src, i);
ParseRes<T> error = ParseRes.failed();
for (var parser : parsers) {
if (parser.isSuccess()) {
if (best == null || best.n < parser.n) best = parser;
}
else if (parser.isError() && error.isFailed()) error = parser.transform();
}
if (best != null) return best;
else return error;
}
@SafeVarargs
public static <T> ParseRes<? extends T> first(String filename, List<Token> tokens, Map<String, Parser<T>> named, Parser<? extends T> ...parsers) {
ParseRes<? extends T> error = ParseRes.failed();
for (var parser : parsers) {
var res = parser.parse(null, tokens, 0);
if (res.isSuccess()) return res;
else if (res.isError() && error.isFailed()) error = res.transform();
var res = parser.parse(src, i + n);
if (res.isSuccess()) return res.addN(n);
if (res.isError() && error.isFailed()) error = res.chainError();
}
return error;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,69 @@
package me.topchetoeu.jscript.compilation.parsing;
import java.util.function.Predicate;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.Location;
public class Source {
public final Filename filename;
public final String src;
private int[] lineStarts;
public Location loc(int offset) {
return new SourceLocation(filename, lineStarts, offset);
}
public boolean is(int i, char c) {
return i >= 0 && i < src.length() && src.charAt(i) == c;
}
public boolean is(int i, String src) {
if (i < 0 || i + src.length() > size()) return false;
for (int j = 0; j < src.length(); j++) {
if (at(i + j) != src.charAt(j)) return false;
}
return true;
}
public boolean is(int i, Predicate<Character> predicate) {
if (i < 0 || i >= src.length()) return false;
return predicate.test(at(i));
}
public char at(int i) {
return src.charAt(i);
}
public char at(int i, char defaultVal) {
if (i < 0 || i >= src.length()) return defaultVal;
else return src.charAt(i);
}
public int size() {
return src.length();
}
public String slice(int start, int end) {
return src.substring(start, end);
}
public Source(Filename filename, String src) {
this.filename = filename;
this.src = src;
int n = 1;
lineStarts = new int[16];
lineStarts[0] = 0;
for (int i = src.indexOf("\n"); i > 0; i = src.indexOf("\n", i + 1)) {
if (n >= lineStarts.length) {
var newArr = new int[lineStarts.length * 2];
System.arraycopy(lineStarts, 0, newArr, 0, n);
lineStarts = newArr;
}
lineStarts[n++] = i + 1;
}
var newArr = new int[n];
System.arraycopy(lineStarts, 0, newArr, 0, n);
lineStarts = newArr;
}
}

View File

@ -0,0 +1,55 @@
package me.topchetoeu.jscript.compilation.parsing;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.Location;
public class SourceLocation extends Location {
private int[] lineStarts;
private int line;
private int start;
private final Filename filename;
private final int offset;
private void update() {
if (lineStarts == null) return;
int start = 0;
int end = lineStarts.length - 1;
while (true) {
if (start + 1 >= end) break;
var mid = -((-start - end) >> 1);
var el = lineStarts[mid];
if (el < offset) start = mid;
else if (el > offset) end = mid;
else {
this.line = mid;
this.start = 0;
this.lineStarts = null;
return;
}
}
this.line = start;
this.start = offset - lineStarts[start];
this.lineStarts = null;
return;
}
@Override public Filename filename() { return filename; }
@Override public int line() {
update();
return line;
}
@Override public int start() {
update();
return start;
}
public SourceLocation(Filename filename, int[] lineStarts, int offset) {
this.filename = filename;
this.lineStarts = lineStarts;
this.offset = offset;
}
}

View File

@ -1,8 +1,7 @@
package me.topchetoeu.jscript.compilation.parsing;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.ParseRes;
import me.topchetoeu.jscript.common.ParseRes.State;
import me.topchetoeu.jscript.compilation.parsing.ParseRes.State;
public class TestRes {
public final State state;

View File

@ -1,17 +1,14 @@
package me.topchetoeu.jscript.compilation.values;
import java.util.ArrayList;
import java.util.List;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.ParseRes;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.parsing.Operator;
import me.topchetoeu.jscript.compilation.parsing.ParseRes;
import me.topchetoeu.jscript.compilation.parsing.Parsing;
import me.topchetoeu.jscript.compilation.parsing.Token;
import me.topchetoeu.jscript.compilation.parsing.Source;
public class ArrayStatement extends Statement {
public final Statement[] statements;
@ -46,36 +43,42 @@ public class ArrayStatement extends Statement {
this.statements = statements;
}
public static ParseRes<ArrayStatement> parse(Filename filename, List<Token> tokens, int i) {
var loc = Parsing.getLoc(filename, tokens, i);
int n = 0;
if (!Parsing.isOperator(tokens, i + n++, Operator.BRACKET_OPEN)) return ParseRes.failed();
public static ParseRes<ArrayStatement> 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++;
var values = new ArrayList<Statement>();
loop: while (true) {
if (Parsing.isOperator(tokens, i + n, Operator.BRACKET_CLOSE)) {
n += Parsing.skipEmpty(src, i + n);
if (src.is(i + n, "]")) {
n++;
break;
}
while (Parsing.isOperator(tokens, i + n, Operator.COMMA)) {
while (src.is(i + n, ",")) {
n++;
n += Parsing.skipEmpty(src, i + n);
values.add(null);
if (Parsing.isOperator(tokens, i + n, Operator.BRACKET_CLOSE)) {
if (src.is(i + n, "]")) {
n++;
break loop;
}
}
var res = Parsing.parseValue(filename, tokens, i + n, 2);
if (!res.isSuccess()) return ParseRes.error(loc, "Expected an array element.", res);
else n += res.n;
var res = Parsing.parseValue(src, i + n, 2);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected an array element.");
n += res.n;
n += Parsing.skipEmpty(src, i + n);
values.add(res.result);
if (Parsing.isOperator(tokens, i + n, Operator.COMMA)) n++;
else if (Parsing.isOperator(tokens, i + n, Operator.BRACKET_CLOSE)) {
if (src.is(i + n, ",")) n++;
else if (src.is(i + n, "]")) {
n++;
break;
}

View File

@ -1,18 +1,15 @@
package me.topchetoeu.jscript.compilation.values;
import java.util.ArrayList;
import java.util.List;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.ParseRes;
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.parsing.Operator;
import me.topchetoeu.jscript.compilation.parsing.ParseRes;
import me.topchetoeu.jscript.compilation.parsing.Parsing;
import me.topchetoeu.jscript.compilation.parsing.Token;
import me.topchetoeu.jscript.compilation.parsing.Source;
public class CallStatement extends Statement {
public final Statement func;
@ -47,51 +44,58 @@ public class CallStatement extends Statement {
this.args = args;
}
public static ParseRes<CallStatement> parseCall(Filename filename, List<Token> tokens, int i, Statement prev, int precedence) {
var loc = Parsing.getLoc(filename, tokens, i);
var n = 0;
public static ParseRes<CallStatement> parseCall(Source src, int i, Statement prev, int precedence) {
if (precedence > 17) return ParseRes.failed();
if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_OPEN)) return ParseRes.failed();
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
if (!src.is(i + n, "(")) return ParseRes.failed();
n++;
var args = new ArrayList<Statement>();
boolean prevArg = false;
while (true) {
var argRes = Parsing.parseValue(filename, tokens, i + n, 2);
var argRes = Parsing.parseValue(src, i + n, 2);
n += argRes.n;
n += Parsing.skipEmpty(src, i + n);
if (argRes.isSuccess()) {
args.add(argRes.result);
n += argRes.n;
prevArg = true;
}
else if (argRes.isError()) return argRes.transform();
else if (prevArg && Parsing.isOperator(tokens, i + n, Operator.COMMA)) {
else if (argRes.isError()) return argRes.chainError();
else if (prevArg && src.is(i + n, ",")) {
prevArg = false;
n++;
}
else if (Parsing.isOperator(tokens, i + n, Operator.PAREN_CLOSE)) {
else if (src.is(i + n, ")")) {
n++;
break;
}
else return ParseRes.error(Parsing.getLoc(filename, tokens, i + n), prevArg ? "Expected a comma or a closing paren." : "Expected an expression or a closing paren.");
else if (prevArg) return ParseRes.error(src.loc(i + n), "Expected a comma or a closing paren");
else return ParseRes.error(src.loc(i + n), "Expected an expression or a closing paren");
}
return ParseRes.res(new CallStatement(loc, false, prev, args.toArray(Statement[]::new)), n);
}
public static ParseRes<CallStatement> parseNew(Filename filename, List<Token> tokens, int i) {
var loc = Parsing.getLoc(filename, tokens, i);
var n = 0;
if (!Parsing.isIdentifier(tokens, i + n++, "new")) return ParseRes.failed();
public static ParseRes<CallStatement> parseNew(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
var valRes = Parsing.parseValue(filename, tokens, i + n, 18);
if (!Parsing.isIdentifier(src, i + n, "new")) return ParseRes.failed();
n += 3;
var valRes = Parsing.parseValue(src, i + n, 18);
if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'new' keyword.");
n += valRes.n;
if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value after 'new' keyword.", valRes);
var callRes = CallStatement.parseCall(filename, tokens, i + n, valRes.result, 0);
n += callRes.n;
if (callRes.isError()) return callRes.transform();
else if (callRes.isFailed()) return ParseRes.res(new CallStatement(loc, true, valRes.result), n);
var call = (CallStatement)callRes.result;
return ParseRes.res(new CallStatement(loc, true, call.func, call.args), n);
var callRes = CallStatement.parseCall(src, i + n, valRes.result, 0);
if (callRes.isFailed()) return ParseRes.res(new CallStatement(loc, true, valRes.result), n);
if (callRes.isError()) return callRes.chainError();
n += callRes.n;
return ParseRes.res(new CallStatement(loc, true, callRes.result.func, callRes.result.args), n);
}
}

View File

@ -1,18 +1,15 @@
package me.topchetoeu.jscript.compilation.values;
import java.util.List;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Operation;
import me.topchetoeu.jscript.common.ParseRes;
import me.topchetoeu.jscript.compilation.AssignableStatement;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.parsing.Operator;
import me.topchetoeu.jscript.compilation.parsing.ParseRes;
import me.topchetoeu.jscript.compilation.parsing.Parsing;
import me.topchetoeu.jscript.compilation.parsing.Token;
import me.topchetoeu.jscript.compilation.parsing.Source;
import me.topchetoeu.jscript.compilation.values.constants.NumberStatement;
public class ChangeStatement extends Statement {
public final AssignableStatement value;
@ -20,7 +17,7 @@ public class ChangeStatement extends Statement {
public final boolean postfix;
@Override public void compile(CompileResult target, boolean pollute) {
value.toAssign(new ConstantStatement(loc(), -addAmount), Operation.SUBTRACT).compile(target, true);
value.toAssign(new NumberStatement(loc(), -addAmount), Operation.SUBTRACT).compile(target, true);
if (!pollute) target.add(Instruction.discard());
else if (postfix) {
target.add(Instruction.pushValue(addAmount));
@ -35,39 +32,55 @@ public class ChangeStatement extends Statement {
this.postfix = postfix;
}
public static ParseRes<ChangeStatement> parsePrefix(Filename filename, List<Token> tokens, int i) {
var loc = Parsing.getLoc(filename, tokens, i);
int n = 0;
public static ParseRes<ChangeStatement> parsePrefixIncrease(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
var opState = Parsing.parseOperator(tokens, i + n++);
if (!opState.isSuccess()) return ParseRes.failed();
if (!src.is(i + n, "++")) return ParseRes.failed();
n += 2;
int change = 0;
var res = Parsing.parseValue(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 AssignableStatement)) return ParseRes.error(src.loc(i + n), "Expected assignable value after prefix operator.");
if (opState.result == Operator.INCREASE) change = 1;
else if (opState.result == Operator.DECREASE) change = -1;
else return ParseRes.failed();
var res = Parsing.parseValue(filename, tokens, i + n, 15);
if (!(res.result instanceof AssignableStatement)) return ParseRes.error(loc, "Expected assignable value after prefix operator.");
return ParseRes.res(new ChangeStatement(loc, (AssignableStatement)res.result, change, false), n + res.n);
return ParseRes.res(new ChangeStatement(loc, (AssignableStatement)res.result, 1, false), n + res.n);
}
public static ParseRes<ChangeStatement> parsePostfix(Filename filename, List<Token> tokens, int i, Statement prev, int precedence) {
var loc = Parsing.getLoc(filename, tokens, i);
int n = 0;
public static ParseRes<ChangeStatement> parsePrefixDecrease(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 += 2;
var res = Parsing.parseValue(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 AssignableStatement)) return ParseRes.error(src.loc(i + n), "Expected assignable value after prefix operator.");
return ParseRes.res(new ChangeStatement(loc, (AssignableStatement)res.result, -1, false), n + res.n);
}
public static ParseRes<ChangeStatement> parsePostfixIncrease(Source src, int i, Statement prev, int precedence) {
if (precedence > 15) return ParseRes.failed();
var opState = Parsing.parseOperator(tokens, i + n++);
if (!opState.isSuccess()) return ParseRes.failed();
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
int change = 0;
if (!src.is(i + n, "++")) return ParseRes.failed();
if (!(prev instanceof AssignableStatement)) return ParseRes.error(src.loc(i + n), "Expected assignable value before suffix operator.");
n += 2;
if (opState.result == Operator.INCREASE) change = 1;
else if (opState.result == Operator.DECREASE) change = -1;
else return ParseRes.failed();
return ParseRes.res(new ChangeStatement(loc, (AssignableStatement)prev, 1, true), n);
}
public static ParseRes<ChangeStatement> parsePostfixDecrease(Source src, int i, Statement prev, int precedence) {
if (precedence > 15) return ParseRes.failed();
if (!(prev instanceof AssignableStatement)) return ParseRes.error(loc, "Expected assignable value before suffix operator.");
return ParseRes.res(new ChangeStatement(loc, (AssignableStatement)prev, change, true), n);
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
if (!src.is(i + n, "--")) return ParseRes.failed();
if (!(prev instanceof AssignableStatement)) return ParseRes.error(src.loc(i + n), "Expected assignable value before suffix operator.");
n += 2;
return ParseRes.res(new ChangeStatement(loc, (AssignableStatement)prev, -1, true), n);
}
}

View File

@ -1,74 +0,0 @@
package me.topchetoeu.jscript.compilation.values;
import java.util.List;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.ParseRes;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.parsing.Parsing;
import me.topchetoeu.jscript.compilation.parsing.Token;
public class ConstantStatement extends Statement {
public final Object value;
public final boolean isNull;
@Override public boolean pure() { return true; }
@Override
public void compile(CompileResult target, boolean pollute) {
if (pollute) {
if (isNull) target.add(Instruction.pushNull());
else if (value instanceof Double) target.add(Instruction.pushValue((Double)value));
else if (value instanceof String) target.add(Instruction.pushValue((String)value));
else if (value instanceof Boolean) target.add(Instruction.pushValue((Boolean)value));
else target.add(Instruction.pushUndefined());
}
}
private ConstantStatement(Location loc, Object val, boolean isNull) {
super(loc);
this.value = val;
this.isNull = isNull;
}
public ConstantStatement(Location loc, boolean val) {
this(loc, val, false);
}
public ConstantStatement(Location loc, String val) {
this(loc, val, false);
}
public ConstantStatement(Location loc, double val) {
this(loc, val, false);
}
public static ConstantStatement ofUndefined(Location loc) {
return new ConstantStatement(loc, null, false);
}
public static ConstantStatement ofNull(Location loc) {
return new ConstantStatement(loc, null, true);
}
public static ParseRes<ConstantStatement> parseNumber(Filename filename, List<Token> tokens, int i) {
var loc = Parsing.getLoc(filename, tokens, i);
if (Parsing.inBounds(tokens, i)) {
if (tokens.get(i).isNumber()) {
return ParseRes.res(new ConstantStatement(loc, tokens.get(i).number()), 1);
}
else return ParseRes.failed();
}
else return ParseRes.failed();
}
public static ParseRes<ConstantStatement> parseString(Filename filename, List<Token> tokens, int i) {
var loc = Parsing.getLoc(filename, tokens, i);
if (Parsing.inBounds(tokens, i)) {
if (tokens.get(i).isString()) {
return ParseRes.res(new ConstantStatement(loc, tokens.get(i).string()), 1);
}
else return ParseRes.failed();
}
else return ParseRes.failed();
}
}

View File

@ -1,24 +1,20 @@
package me.topchetoeu.jscript.compilation.values;
import java.util.List;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.ParseRes;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.parsing.ParseRes;
import me.topchetoeu.jscript.compilation.parsing.Parsing;
import me.topchetoeu.jscript.compilation.parsing.Token;
import me.topchetoeu.jscript.compilation.parsing.Source;
public class DiscardStatement extends Statement {
public final Statement value;
@Override public boolean pure() { return value.pure(); }
@Override
public void compile(CompileResult target, boolean pollute) {
value.compile(target, false);
@Override public void compile(CompileResult target, boolean pollute) {
if (value != null) value.compile(target, false);
if (pollute) target.add(Instruction.pushUndefined());
}
@ -27,13 +23,15 @@ public class DiscardStatement extends Statement {
this.value = val;
}
public static ParseRes<DiscardStatement> parse(Filename filename, List<Token> tokens, int i) {
var loc = Parsing.getLoc(filename, tokens, i);
var n = 0;
if (!Parsing.isIdentifier(tokens, i + n++, "void")) return ParseRes.failed();
public static ParseRes<DiscardStatement> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
var valRes = Parsing.parseValue(filename, tokens, i + n, 14);
if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value after 'void' keyword.", valRes);
if (!Parsing.isIdentifier(src, i + n, "void")) return ParseRes.failed();
n += 4;
var valRes = Parsing.parseValue(src, i + n, 14);
if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'void' keyword.");
n += valRes.n;
return ParseRes.res(new DiscardStatement(loc, valRes.result), n);

View File

@ -1,20 +1,15 @@
package me.topchetoeu.jscript.compilation.values;
import java.util.ArrayList;
import java.util.List;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.ParseRes;
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.common.Instruction.Type;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.CompoundStatement;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.parsing.Operator;
import me.topchetoeu.jscript.compilation.parsing.ParseRes;
import me.topchetoeu.jscript.compilation.parsing.Parsing;
import me.topchetoeu.jscript.compilation.parsing.Token;
import me.topchetoeu.jscript.compilation.parsing.Source;
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
public class FunctionStatement extends Statement {
@ -133,47 +128,30 @@ public class FunctionStatement extends Statement {
else stm.compile(target, pollute, bp);
}
public static ParseRes<FunctionStatement> parseFunction(Filename filename, List<Token> tokens, int i, boolean statement) {
var loc = Parsing.getLoc(filename, tokens, i);
int n = 0;
public static ParseRes<FunctionStatement> parseFunction(Source src, int i, boolean statement) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
if (!Parsing.isIdentifier(tokens, i + n++, "function")) return ParseRes.failed();
if (!Parsing.isIdentifier(src, i + n, "function")) return ParseRes.failed();
n += 8;
var nameRes = Parsing.parseIdentifier(tokens, i + n);
if (!nameRes.isSuccess() && statement) return ParseRes.error(loc, "A statement function requires a name, one is not present.");
var name = nameRes.result;
var nameRes = Parsing.parseIdentifier(src, i + n);
if (!nameRes.isSuccess() && statement) return ParseRes.error(src.loc(i + n), "A statement function requires a name");
n += nameRes.n;
n += Parsing.skipEmpty(src, i + n);
if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_OPEN)) return ParseRes.error(loc, "Expected a parameter list.");
var args = Parsing.parseParamList(src, i + n);
if (!args.isSuccess()) return args.chainError(src.loc(i + n), "Expected a parameter list");
n += args.n;
var args = new ArrayList<String>();
if (Parsing.isOperator(tokens, i + n, Operator.PAREN_CLOSE)) {
n++;
}
else {
while (true) {
var argRes = Parsing.parseIdentifier(tokens, i + n);
if (argRes.isSuccess()) {
args.add(argRes.result);
n++;
if (Parsing.isOperator(tokens, i + n, Operator.COMMA)) {
n++;
}
if (Parsing.isOperator(tokens, i + n, Operator.PAREN_CLOSE)) {
n++;
break;
}
}
else return ParseRes.error(loc, "Expected an argument, comma or a closing brace.");
}
}
var res = CompoundStatement.parse(filename, tokens, i + n);
var res = CompoundStatement.parse(src, i + n);
if (!res.isSuccess()) res.chainError(src.loc(i + n), "Expected a compound statement for function.");
n += res.n;
var end = Parsing.getLoc(filename, tokens, i + n - 1);
if (res.isSuccess()) return ParseRes.res(new FunctionStatement(loc, end, name, args.toArray(String[]::new), statement, res.result), n);
else return ParseRes.error(loc, "Expected a compound statement for function.", res);
return ParseRes.res(new FunctionStatement(
loc, src.loc(i + n - 1),
nameRes.result, args.result.toArray(String[]::new),
statement, res.result
), n);
}
}

View File

@ -1,19 +1,16 @@
package me.topchetoeu.jscript.compilation.values;
import java.util.List;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Operation;
import me.topchetoeu.jscript.common.ParseRes;
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.compilation.AssignableStatement;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.parsing.Operator;
import me.topchetoeu.jscript.compilation.parsing.ParseRes;
import me.topchetoeu.jscript.compilation.parsing.Parsing;
import me.topchetoeu.jscript.compilation.parsing.Token;
import me.topchetoeu.jscript.compilation.parsing.Source;
import me.topchetoeu.jscript.compilation.values.constants.StringStatement;
public class IndexStatement extends AssignableStatement {
public final Statement object;
@ -42,33 +39,38 @@ public class IndexStatement extends AssignableStatement {
this.index = index;
}
public static ParseRes<IndexStatement> parseIndex(Filename filename, List<Token> tokens, int i, Statement prev, int precedence) {
var loc = Parsing.getLoc(filename, tokens, i);
var n = 0;
public static ParseRes<IndexStatement> parseIndex(Source src, int i, Statement prev, int precedence) {
if (precedence > 18) return ParseRes.failed();
if (!Parsing.isOperator(tokens, i + n++, Operator.BRACKET_OPEN)) return ParseRes.failed();
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
var valRes = Parsing.parseValue(filename, tokens, i + n, 0);
if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value in index expression.", valRes);
if (!src.is(i + n, "[")) return ParseRes.failed();
n++;
var valRes = Parsing.parseValue(src, i + n, 0);
if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value in index expression");
n += valRes.n;
n += Parsing.skipEmpty(src, i + n);
if (!Parsing.isOperator(tokens, i + n++, Operator.BRACKET_CLOSE)) return ParseRes.error(loc, "Expected a closing bracket.");
if (!src.is(i + n, "]")) return ParseRes.error(src.loc(i + n), "Expected a closing bracket");
n++;
return ParseRes.res(new IndexStatement(loc, prev, valRes.result), n);
}
public static ParseRes<IndexStatement> parseMember(Filename filename, List<Token> tokens, int i, Statement prev, int precedence) {
var loc = Parsing.getLoc(filename, tokens, i);
var n = 0;
public static ParseRes<IndexStatement> parseMember(Source src, int i, Statement prev, int precedence) {
if (precedence > 18) return ParseRes.failed();
if (!Parsing.isOperator(tokens, i + n++, Operator.DOT)) return ParseRes.failed();
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
var literal = Parsing.parseIdentifier(tokens, i + n++);
if (!literal.isSuccess()) return ParseRes.error(loc, "Expected an identifier after member access.");
if (!src.is(i + n, ".")) return ParseRes.failed();
n++;
return ParseRes.res(new IndexStatement(loc, prev, new ConstantStatement(loc, literal.result)), n);
var literal = Parsing.parseIdentifier(src, i + n);
if (!literal.isSuccess()) return literal.chainError(src.loc(i + n), "Expected an identifier after member access.");
n += literal.n;
return ParseRes.res(new IndexStatement(loc, prev, new StringStatement(loc, literal.result)), n);
}
}

View File

@ -4,6 +4,9 @@ import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.parsing.ParseRes;
import me.topchetoeu.jscript.compilation.parsing.Parsing;
import me.topchetoeu.jscript.compilation.parsing.Source;
public class LazyAndStatement extends Statement {
public final Statement first, second;
@ -25,4 +28,20 @@ public class LazyAndStatement extends Statement {
this.first = first;
this.second = second;
}
public static ParseRes<LazyAndStatement> parse(Source src, int i, Statement prev, int precedence) {
if (precedence < 4) return ParseRes.failed();
var n = Parsing.skipEmpty(src, i);
if (!src.is(i + n, "&&")) return ParseRes.failed();
var loc = src.loc(i + n);
n += 2;
var res = Parsing.parseValue(src, i + n, 4);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a value after the '&&' operator.");
n += res.n;
return ParseRes.res(new LazyAndStatement(loc, prev, res.result), n);
}
}

View File

@ -4,6 +4,9 @@ import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.parsing.ParseRes;
import me.topchetoeu.jscript.compilation.parsing.Parsing;
import me.topchetoeu.jscript.compilation.parsing.Source;
public class LazyOrStatement extends Statement {
public final Statement first, second;
@ -25,4 +28,20 @@ public class LazyOrStatement extends Statement {
this.first = first;
this.second = second;
}
public static ParseRes<LazyOrStatement> parse(Source src, int i, Statement prev, int precedence) {
if (precedence < 3) return ParseRes.failed();
var n = Parsing.skipEmpty(src, i);
if (!src.is(i + n, "||")) return ParseRes.failed();
var loc = src.loc(i + n);
n += 2;
var res = Parsing.parseValue(src, i + n, 4);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a value after the '||' operator.");
n += res.n;
return ParseRes.res(new LazyOrStatement(loc, prev, res.result), n);
}
}

View File

@ -2,22 +2,30 @@ package me.topchetoeu.jscript.compilation.values;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.ParseRes;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.CompoundStatement;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.parsing.Operator;
import me.topchetoeu.jscript.compilation.parsing.ParseRes;
import me.topchetoeu.jscript.compilation.parsing.Parsing;
import me.topchetoeu.jscript.compilation.parsing.Parsing.ObjProp;
import me.topchetoeu.jscript.compilation.parsing.Token;
import me.topchetoeu.jscript.compilation.parsing.Source;
public class ObjectStatement extends Statement {
public static class ObjProp {
public final String name;
public final String access;
public final FunctionStatement func;
public ObjProp(String name, String access, FunctionStatement func) {
this.name = name;
this.access = access;
this.func = func;
}
}
public final Map<String, Statement> map;
public final Map<String, FunctionStatement> getters;
public final Map<String, FunctionStatement> setters;
@ -68,100 +76,107 @@ public class ObjectStatement extends Statement {
this.setters = setters;
}
private static ParseRes<String> parsePropName(Filename filename, List<Token> tokens, int i) {
var loc = Parsing.getLoc(filename, tokens, i);
private static ParseRes<String> parsePropName(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
if (Parsing.inBounds(tokens, i)) {
var token = tokens.get(i);
if (token.isNumber() || token.isIdentifier() || token.isString()) return ParseRes.res(token.rawValue, 1);
else return ParseRes.error(loc, "Expected identifier, string or number literal.");
}
else return ParseRes.failed();
}
private static ParseRes<ObjProp> parseObjectProp(Filename filename, List<Token> tokens, int i) {
var loc =Parsing. getLoc(filename, tokens, i);
int n = 0;
var accessRes = Parsing.parseIdentifier(tokens, i + n++);
if (!accessRes.isSuccess()) return ParseRes.failed();
var access = accessRes.result;
if (!access.equals("get") && !access.equals("set")) return ParseRes.failed();
var nameRes = parsePropName(filename, tokens, i + n);
if (!nameRes.isSuccess()) return ParseRes.error(loc, "Expected a property name after '" + access + "'.");
var name = nameRes.result;
n += nameRes.n;
var argsRes = Parsing.parseParamList(filename, tokens, i + n);
if (!argsRes.isSuccess()) return ParseRes.error(loc, "Expected an argument list.", argsRes);
n += argsRes.n;
var res = CompoundStatement.parse(filename, tokens, i + n);
if (!res.isSuccess()) return ParseRes.error(loc, "Expected a compound statement for property accessor.", res);
var res = ParseRes.first(src, i + n,
Parsing::parseIdentifier,
Parsing::parseString,
Parsing::parseNumber
);
n += res.n;
var end = Parsing.getLoc(filename, tokens, i + n - 1);
if (!res.isSuccess()) return res.chainError();
return ParseRes.res(res.result.toString(), n);
}
private static ParseRes<ObjectStatement.ObjProp> parseObjectProp(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 = Parsing.parseParamList(src, i + n);
if (!params.isSuccess()) return params.chainError(src.loc(i + n), "Expected an argument list");
n += params.n;
var body = CompoundStatement.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 ObjProp(
name, access,
new FunctionStatement(loc, end, access + " " + name.toString(), argsRes.result.toArray(String[]::new), false, res.result)
name.result, access.result,
new FunctionStatement(loc, end, access + " " + name.result.toString(), params.result.toArray(String[]::new), false, body.result)
), n);
}
public static ParseRes<ObjectStatement> parse(Filename filename, List<Token> tokens, int i) {
var loc = Parsing.getLoc(filename, tokens, i);
int n = 0;
if (!Parsing.isOperator(tokens, i + n++, Operator.BRACE_OPEN)) return ParseRes.failed();
public static ParseRes<ObjectStatement> 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 values = new LinkedHashMap<String, Statement>();
var getters = new LinkedHashMap<String, FunctionStatement>();
var setters = new LinkedHashMap<String, FunctionStatement>();
if (Parsing.isOperator(tokens, i + n, Operator.BRACE_CLOSE)) {
if (src.is(i + n, "}")) {
n++;
return ParseRes.res(new ObjectStatement(loc, values, getters, setters), n);
}
while (true) {
var propRes = parseObjectProp(filename, tokens, i + n);
var prop = parseObjectProp(src, i + n);
if (propRes.isSuccess()) {
n += propRes.n;
if (propRes.result.access.equals("set")) {
setters.put(propRes.result.name, propRes.result.func);
}
else {
getters.put(propRes.result.name, propRes.result.func);
}
if (prop.isSuccess()) {
n += prop.n;
if (prop.result.access.equals("set")) setters.put(prop.result.name, prop.result.func);
else getters.put(prop.result.name, prop.result.func);
}
else {
var nameRes = parsePropName(filename, tokens, i + n);
if (!nameRes.isSuccess()) return ParseRes.error(loc, "Expected a field name.", propRes);
n += nameRes.n;
var name = parsePropName(src, i + n);
if (!name.isSuccess()) return prop.chainError(src.loc(i + n), "Expected a field name");
n += name.n;
n += Parsing.skipEmpty(src, i + n);
if (!Parsing.isOperator(tokens, i + n++, Operator.COLON)) return ParseRes.error(loc, "Expected a colon.");
if (!src.is(i + n, ":")) return ParseRes.error(src.loc(i + n), "Expected a colon");
n++;
var valRes = Parsing.parseValue(filename, tokens, i + n, 2);
if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value in array list.", valRes);
var valRes = Parsing.parseValue(src, i + n, 2);
if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value in array list");
n += valRes.n;
values.put(nameRes.result, valRes.result);
values.put(name.result, valRes.result);
}
if (Parsing.isOperator(tokens, i + n, Operator.COMMA)) {
n += Parsing.skipEmpty(src, i + n);
if (src.is(i + n, ",")) {
n++;
if (Parsing.isOperator(tokens, i + n, Operator.BRACE_CLOSE)) {
n += Parsing.skipEmpty(src, i + n);
if (src.is(i + n, "}")) {
n++;
break;
}
continue;
}
else if (Parsing.isOperator(tokens, i + n, Operator.BRACE_CLOSE)) {
else if (src.is(i + n, "}")) {
n++;
break;
}
else ParseRes.error(loc, "Expected a comma or a closing brace.");
else ParseRes.error(src.loc(i + n), "Expected a comma or a closing brace.");
}
return ParseRes.res(new ObjectStatement(loc, values, getters, setters), n);

View File

@ -1,18 +1,14 @@
package me.topchetoeu.jscript.compilation.values;
import java.util.List;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Operation;
import me.topchetoeu.jscript.common.ParseRes;
import me.topchetoeu.jscript.compilation.AssignableStatement;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.parsing.Operator;
import me.topchetoeu.jscript.compilation.parsing.ParseRes;
import me.topchetoeu.jscript.compilation.parsing.Parsing;
import me.topchetoeu.jscript.compilation.parsing.Token;
import me.topchetoeu.jscript.compilation.parsing.Source;
public class OperationStatement extends Statement {
public final Statement[] args;
@ -41,75 +37,74 @@ public class OperationStatement extends Statement {
this.args = args;
}
public static ParseRes<OperationStatement> parseUnary(Filename filename, List<Token> tokens, int i) {
var loc = Parsing.getLoc(filename, tokens, i);
int n = 0;
var opState = Parsing.parseOperator(tokens, i + n++);
if (!opState.isSuccess()) return ParseRes.failed();
var op = opState.result;
public static ParseRes<OperationStatement> parsePrefix(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
Operation operation = null;
String op;
if (op == Operator.ADD) operation = Operation.POS;
else if (op == Operator.SUBTRACT) operation = Operation.NEG;
else if (op == Operator.INVERSE) operation = Operation.INVERSE;
else if (op == Operator.NOT) operation = Operation.NOT;
if (src.is(i + n, op = "+")) operation = Operation.POS;
else if (src.is(i + n, op = "-")) operation = Operation.NEG;
else if (src.is(i + n, op = "~")) operation = Operation.INVERSE;
else if (src.is(i + n, op = "!")) operation = Operation.NOT;
else return ParseRes.failed();
var res = Parsing.parseValue(filename, tokens, n + i, 14);
n++;
var res = Parsing.parseValue(src, i + n, 14);
if (res.isSuccess()) return ParseRes.res(new OperationStatement(loc, operation, res.result), n + res.n);
else return ParseRes.error(loc, String.format("Expected a value after the unary operator '%s'.", op.readable), res);
else return res.chainError(src.loc(i + n), String.format("Expected a value after the unary operator '%s'.", op));
}
public static ParseRes<OperationStatement> parseInstanceof(Filename filename, List<Token> tokens, int i, Statement prev, int precedence) {
var loc = Parsing.getLoc(filename, tokens, i);
int n = 0;
public static ParseRes<OperationStatement> parseInstanceof(Source src, int i, Statement prev, int precedence) {
if (precedence > 9) return ParseRes.failed();
if (!Parsing.isIdentifier(tokens, i + n++, "instanceof")) return ParseRes.failed();
var valRes = Parsing.parseValue(filename, tokens, i + n, 10);
if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value after 'instanceof'.", valRes);
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
var kw = Parsing.parseIdentifier(src, i + n, "instanceof");
if (!kw.isSuccess()) return kw.chainError();
n += kw.n;
var valRes = Parsing.parseValue(src, i + n, 10);
if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'instanceof'.");
n += valRes.n;
return ParseRes.res(new OperationStatement(loc, Operation.INSTANCEOF, prev, valRes.result), n);
}
public static ParseRes<OperationStatement> parseIn(Filename filename, List<Token> tokens, int i, Statement prev, int precedence) {
var loc = Parsing.getLoc(filename, tokens, i);
int n = 0;
public static ParseRes<OperationStatement> parseIn(Source src, int i, Statement prev, int precedence) {
if (precedence > 9) return ParseRes.failed();
if (!Parsing.isIdentifier(tokens, i + n++, "in")) return ParseRes.failed();
var valRes = Parsing.parseValue(filename, tokens, i + n, 10);
if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value after 'in'.", valRes);
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
var kw = Parsing.parseIdentifier(src, i + n, "in");
if (!kw.isSuccess()) return kw.chainError();
n += kw.n;
var valRes = Parsing.parseValue(src, i + n, 10);
if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'in'.");
n += valRes.n;
return ParseRes.res(new OperationStatement(loc, Operation.IN, prev, valRes.result), n);
return ParseRes.res(new OperationStatement(loc, Operation.IN, valRes.result, prev), n);
}
public static ParseRes<? extends Statement> parseOperator(Filename filename, List<Token> tokens, int i, Statement prev, int precedence) {
var loc = Parsing.getLoc(filename, tokens, i);
var n = 0;
public static ParseRes<? extends Statement> parseOperator(Source src, int i, Statement prev, int precedence) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
var opRes = Parsing.parseOperator(tokens, i + n++);
if (!opRes.isSuccess()) return ParseRes.failed();
var op = opRes.result;
for (var op : Operator.opsByLength) {
if (!src.is(i + n, op.readable)) continue;
if (op.precedence < precedence) return ParseRes.failed();
n += op.readable.length();
if (op.precedence < precedence) return ParseRes.failed();
if (op.isAssign()) return AssignableStatement.parse(filename, tokens, i + n - 1, prev, precedence);
var res = Parsing.parseValue(src, i + n, op.precedence + 1);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), String.format("Expected a value after the '%s' operator.", op.readable));
n += res.n;
var res = Parsing.parseValue(filename, tokens, i + n, op.precedence + (op.reverse ? 0 : 1));
if (!res.isSuccess()) return ParseRes.error(loc, String.format("Expected a value after the '%s' operator.", op.readable), res);
n += res.n;
if (op == Operator.LAZY_AND) {
return ParseRes.res(new LazyAndStatement(loc, prev, res.result), n);
}
if (op == Operator.LAZY_OR) {
return ParseRes.res(new LazyOrStatement(loc, prev, res.result), n);
return ParseRes.res(new OperationStatement(loc, op.operation, prev, res.result), n);
}
return ParseRes.res(new OperationStatement(loc, op.operation, prev, res.result), n);
return ParseRes.failed();
}
}

View File

@ -1,15 +1,12 @@
package me.topchetoeu.jscript.compilation.values;
import java.util.List;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.ParseRes;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.parsing.ParseRes;
import me.topchetoeu.jscript.compilation.parsing.Parsing;
import me.topchetoeu.jscript.compilation.parsing.Token;
import me.topchetoeu.jscript.compilation.parsing.Source;
public class RegexStatement extends Statement {
public final String pattern, flags;
@ -23,19 +20,56 @@ public class RegexStatement extends Statement {
if (!pollute) target.add(Instruction.discard());
}
public static ParseRes<RegexStatement> parse(Filename filename, List<Token> tokens, int i) {
var loc = Parsing.getLoc(filename, tokens, i);
if (Parsing.inBounds(tokens, i)) {
if (tokens.get(i).isRegex()) {
var val = tokens.get(i).regex();
var index = val.lastIndexOf('/');
var first = val.substring(1, index);
var second = val.substring(index + 1);
return ParseRes.res(new RegexStatement(loc, first, second), 1);
public static ParseRes<RegexStatement> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
if (!src.is(i + n, '/')) return ParseRes.failed();
var loc = src.loc(i + n);
n++;
var source = new StringBuilder();
var flags = new StringBuilder();
var inBrackets = false;
while (true) {
if (src.is(i + n, '[')) {
n++;
inBrackets = true;
source.append(src.at(i + n));
continue;
}
else return ParseRes.failed();
else if (src.is(i + n, ']')) {
n++;
inBrackets = false;
source.append(src.at(i + n));
continue;
}
else if (src.is(i + n, '/') && !inBrackets) {
n++;
break;
}
var charRes = Parsing.parseChar(src, i + n);
if (charRes.result == null) return ParseRes.error(src.loc(i + n), "Multiline regular expressions are not allowed");
source.append(charRes.result);
n++;
}
return ParseRes.failed();
while (true) {
char c = src.at(i + n, '\0');
if (src.is(i + n, v -> Parsing.isAny(c, "dgimsuy"))) {
if (flags.indexOf(c + "") >= 0) return ParseRes.error(src.loc(i + n), "The flags of a regular expression may not be repeated");
flags.append(c);
}
else break;
n++;
}
return ParseRes.res(new RegexStatement(loc, source.toString(), flags.toString()), n);
}
public RegexStatement(Location loc, String pattern, String flags) {

View File

@ -1,15 +1,12 @@
package me.topchetoeu.jscript.compilation.values;
import java.util.List;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.ParseRes;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.parsing.ParseRes;
import me.topchetoeu.jscript.compilation.parsing.Parsing;
import me.topchetoeu.jscript.compilation.parsing.Token;
import me.topchetoeu.jscript.compilation.parsing.Source;
public class TypeofStatement extends Statement {
public final Statement value;
@ -36,13 +33,15 @@ public class TypeofStatement extends Statement {
this.value = value;
}
public static ParseRes<TypeofStatement> parse(Filename filename, List<Token> tokens, int i) {
var loc = Parsing.getLoc(filename, tokens, i);
var n = 0;
if (!Parsing.isIdentifier(tokens, i + n++, "typeof")) return ParseRes.failed();
public static ParseRes<TypeofStatement> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
var valRes = Parsing.parseValue(filename, tokens, i + n, 15);
if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value after 'typeof' keyword.", valRes);
if (!Parsing.isIdentifier(src, i + n, "typeof")) return ParseRes.failed();
n += 6;
var valRes = Parsing.parseValue(src, i + n, 15);
if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'typeof' keyword.");
n += valRes.n;
return ParseRes.res(new TypeofStatement(loc, valRes.result), n);

View File

@ -1,17 +1,14 @@
package me.topchetoeu.jscript.compilation.values;
import java.util.List;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Operation;
import me.topchetoeu.jscript.common.ParseRes;
import me.topchetoeu.jscript.compilation.AssignableStatement;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.parsing.ParseRes;
import me.topchetoeu.jscript.compilation.parsing.Parsing;
import me.topchetoeu.jscript.compilation.parsing.Token;
import me.topchetoeu.jscript.compilation.parsing.Source;
public class VariableStatement extends AssignableStatement {
public final String name;
@ -35,19 +32,21 @@ public class VariableStatement extends AssignableStatement {
this.name = name;
}
public static ParseRes<VariableStatement> parseVariable(Filename filename, List<Token> tokens, int i) {
var loc = Parsing.getLoc(filename, tokens, i);
var literal = Parsing.parseIdentifier(tokens, i);
public static ParseRes<VariableStatement> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
if (!literal.isSuccess()) return ParseRes.failed();
var literal = Parsing.parseIdentifier(src, i);
if (!literal.isSuccess()) return literal.chainError();
n += literal.n;
if (!Parsing.checkVarName(literal.result)) {
if (literal.result.equals("await")) return ParseRes.error(loc, "'await' expressions are not supported.");
if (literal.result.equals("const")) return ParseRes.error(loc, "'const' declarations are not supported.");
if (literal.result.equals("let")) return ParseRes.error(loc, "'let' declarations are not supported.");
return ParseRes.error(loc, String.format("Unexpected identifier '%s'.", 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));
}
return ParseRes.res(new VariableStatement(loc, literal.result), 1);
return ParseRes.res(new VariableStatement(loc, literal.result), n);
}
}

View File

@ -0,0 +1,21 @@
package me.topchetoeu.jscript.compilation.values.constants;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
public class BoolStatement extends Statement {
public final boolean value;
@Override public boolean pure() { return true; }
@Override public void compile(CompileResult target, boolean pollute) {
if (pollute) target.add(Instruction.pushValue(value));
}
public BoolStatement(Location loc, boolean value) {
super(loc);
this.value = value;
}
}

View File

@ -0,0 +1,4 @@
package me.topchetoeu.jscript.compilation.values.constants;
public class ConstantStatements {
}

View File

@ -0,0 +1,16 @@
package me.topchetoeu.jscript.compilation.values.constants;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
public class NullStatement extends Statement {
@Override public boolean pure() { return true; }
@Override public void compile(CompileResult target, boolean pollute) {
target.add(Instruction.pushNull());
}
public NullStatement(Location loc) { super(loc); }
}

View File

@ -0,0 +1,42 @@
package me.topchetoeu.jscript.compilation.values.constants;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.parsing.ParseRes;
import me.topchetoeu.jscript.compilation.parsing.Parsing;
import me.topchetoeu.jscript.compilation.parsing.Source;
public class NumberStatement extends Statement {
public final double value;
@Override public boolean pure() { return true; }
@Override public void compile(CompileResult target, boolean pollute) {
if (pollute) target.add(Instruction.pushValue(value));
}
public NumberStatement(Location loc, double value) {
super(loc);
this.value = value;
}
public static double power(double a, long b) {
if (b == 0) return 1;
if (b == 1) return a;
if (b < 0) return 1 / power(a, -b);
if ((b & 1) == 0) return power(a * a, b / 2);
else return a * power(a * a, b / 2);
}
public static ParseRes<NumberStatement> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
var res = Parsing.parseNumber(src, i + n);
if (res.isSuccess()) return ParseRes.res(new NumberStatement(loc, res.result), n + res.n);
else return res.chainError();
}
}

View File

@ -0,0 +1,33 @@
package me.topchetoeu.jscript.compilation.values.constants;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.parsing.ParseRes;
import me.topchetoeu.jscript.compilation.parsing.Parsing;
import me.topchetoeu.jscript.compilation.parsing.Source;
public class StringStatement extends Statement {
public final String value;
@Override public boolean pure() { return true; }
@Override public void compile(CompileResult target, boolean pollute) {
if (pollute) target.add(Instruction.pushValue(value));
}
public StringStatement(Location loc, String value) {
super(loc);
this.value = value;
}
public static ParseRes<StringStatement> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
var res = Parsing.parseString(src, i + n);
if (res.isSuccess()) return ParseRes.res(new StringStatement(loc, res.result), n + res.n);
else return res.chainError();
}
}

View File

@ -19,7 +19,7 @@ import me.topchetoeu.jscript.runtime.values.functions.NativeFunction;
import me.topchetoeu.jscript.runtime.values.primitives.VoidValue;
public class SimpleRepl {
static Thread engineTask, debugTask;
static Thread engineTask;
static Engine engine = new Engine();
static Environment environment = Environment.empty();
@ -69,11 +69,6 @@ public class SimpleRepl {
}
private static void initEnv() {
var glob = GlobalScope.get(environment);
glob.define(null, false, new NativeFunction("exit", args -> {
throw new InterruptException();
}));
// glob.define(null, false, new NativeFunction("go", args -> {
// try {
// var f = Path.of("do.js");
@ -84,13 +79,6 @@ public class SimpleRepl {
// throw new EngineException("Couldn't open do.js");
// }
// }));
glob.define(null, false, new NativeFunction("log", args -> {
for (var el : args.args) {
System.out.print(el.toReadable(args.env));
}
return null;
}));
// var fs = new RootFilesystem(PermissionsProvider.get(environment));
// fs.protocols.put("temp", new MemoryFilesystem(Mode.READ_WRITE));
@ -107,6 +95,18 @@ public class SimpleRepl {
environment.add(Compiler.KEY, (filename, source) -> {
return Parsing.compile(filename, source).body();
});
var glob = GlobalScope.get(environment);
glob.define(null, false, new NativeFunction("exit", args -> {
throw new InterruptException();
}));
glob.define(null, false, new NativeFunction("log", args -> {
for (var el : args.args) {
System.out.print(el.toReadable(args.env));
}
return null;
}));
}
private static void initEngine() {
// var ctx = new DebugContext();
@ -131,7 +131,7 @@ public class SimpleRepl {
reader.start();
engine.thread().join();
debugTask.interrupt();
// debugTask.interrupt();
engineTask.interrupt();
}
}

View File

@ -53,7 +53,7 @@ public abstract class FunctionValue extends ObjectValue {
return super.getOwnMember(env, key);
}
@Override public boolean deleteOwnMember(Environment env, Value key) {
if (!super.deleteMember(env, key)) return false;
if (!super.deleteOwnMember(env, key)) return false;
var el = key.toString(env).value;