refactor: clean up parsing
This commit is contained in:
parent
ef0fc5a61d
commit
f09feae08f
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
var nameRes = Parsing.parseIdentifier(tokens, i + n++);
|
||||
if (!Parsing.isOperator(tokens, i + n++, Operator.COLON)) return ParseRes.failed();
|
||||
|
||||
public static ParseRes<String> parseLabel(Source src, int i) {
|
||||
int n = Parsing.skipEmpty(src, i);
|
||||
|
||||
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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
var opState = Parsing.parseOperator(tokens, i + n++);
|
||||
if (!opState.isSuccess()) return ParseRes.failed();
|
||||
|
||||
int change = 0;
|
||||
|
||||
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);
|
||||
public static ParseRes<ChangeStatement> parsePrefixIncrease(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> 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();
|
||||
|
||||
int change = 0;
|
||||
|
||||
if (opState.result == Operator.INCREASE) change = 1;
|
||||
else if (opState.result == Operator.DECREASE) change = -1;
|
||||
else 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);
|
||||
}
|
||||
public static ParseRes<ChangeStatement> parsePostfixDecrease(Source src, int i, Statement prev, int precedence) {
|
||||
if (precedence > 15) return ParseRes.failed();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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 valRes = Parsing.parseValue(filename, tokens, i + n, 0);
|
||||
if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value in index expression.", valRes);
|
||||
|
||||
var n = Parsing.skipEmpty(src, i);
|
||||
var loc = src.loc(i + n);
|
||||
|
||||
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;
|
||||
|
||||
if (!Parsing.isOperator(tokens, i + n++, Operator.BRACKET_CLOSE)) return ParseRes.error(loc, "Expected a closing bracket.");
|
||||
|
||||
n += Parsing.skipEmpty(src, i + n);
|
||||
|
||||
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 literal = Parsing.parseIdentifier(tokens, i + n++);
|
||||
if (!literal.isSuccess()) return ParseRes.error(loc, "Expected an identifier after member access.");
|
||||
|
||||
return ParseRes.res(new IndexStatement(loc, prev, new ConstantStatement(loc, literal.result)), n);
|
||||
|
||||
var n = Parsing.skipEmpty(src, i);
|
||||
var loc = src.loc(i + n);
|
||||
|
||||
if (!src.is(i + n, ".")) return ParseRes.failed();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
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);
|
||||
}
|
||||
var prop = parseObjectProp(src, i + n);
|
||||
|
||||
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;
|
||||
|
||||
if (!Parsing.isOperator(tokens, i + n++, Operator.COLON)) return ParseRes.error(loc, "Expected a colon.");
|
||||
|
||||
var valRes = Parsing.parseValue(filename, tokens, i + n, 2);
|
||||
if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value in array list.", valRes);
|
||||
n += valRes.n;
|
||||
|
||||
values.put(nameRes.result, valRes.result);
|
||||
}
|
||||
|
||||
if (Parsing.isOperator(tokens, i + n, Operator.COMMA)) {
|
||||
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 (!src.is(i + n, ":")) return ParseRes.error(src.loc(i + n), "Expected a colon");
|
||||
n++;
|
||||
if (Parsing.isOperator(tokens, i + n, Operator.BRACE_CLOSE)) {
|
||||
|
||||
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(name.result, valRes.result);
|
||||
}
|
||||
|
||||
n += Parsing.skipEmpty(src, i + n);
|
||||
if (src.is(i + n, ",")) {
|
||||
n++;
|
||||
n += Parsing.skipEmpty(src, i + n);
|
||||
|
||||
if (src.is(i + n, "}")) {
|
||||
n++;
|
||||
break;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
else if (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);
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
|
||||
var valRes = Parsing.parseValue(filename, tokens, i + n, 15);
|
||||
if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value after 'typeof' keyword.", valRes);
|
||||
public static ParseRes<TypeofStatement> parse(Source src, int i) {
|
||||
var n = Parsing.skipEmpty(src, i);
|
||||
var loc = src.loc(i + n);
|
||||
|
||||
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);
|
||||
|
@ -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);
|
||||
|
||||
if (!literal.isSuccess()) return ParseRes.failed();
|
||||
|
||||
public static ParseRes<VariableStatement> parse(Source src, int i) {
|
||||
var n = Parsing.skipEmpty(src, i);
|
||||
var loc = src.loc(i + n);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
package me.topchetoeu.jscript.compilation.values.constants;
|
||||
|
||||
public class ConstantStatements {
|
||||
}
|
@ -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); }
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user