ES6 Support Groundwork + Fixes (OLD ONE DON'T LOOK AT ME!!!) #22

Merged
TopchetoEU merged 49 commits from ES6 into master 2024-09-05 14:17:52 +00:00
95 changed files with 1976 additions and 2377 deletions
Showing only changes of commit d0ccf00f14 - Show all commits

View File

@ -1,5 +1,8 @@
package me.topchetoeu.jscript.common; package me.topchetoeu.jscript.common;
import me.topchetoeu.jscript.common.parsing.Filename;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.ES5;
import me.topchetoeu.jscript.runtime.debug.DebugContext; import me.topchetoeu.jscript.runtime.debug.DebugContext;
import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.environment.Key; import me.topchetoeu.jscript.runtime.environment.Key;
@ -8,18 +11,36 @@ import me.topchetoeu.jscript.runtime.scope.ValueVariable;
import me.topchetoeu.jscript.runtime.values.functions.CodeFunction; import me.topchetoeu.jscript.runtime.values.functions.CodeFunction;
public interface Compiler { public interface Compiler {
public static final Compiler DEFAULT = (env, filename, raw) -> {
var res = ES5.compile(filename, raw);
var body = res.body();
DebugContext.get(env).onSource(filename, raw);
registerFunc(env, body, res);
return body;
};
public Key<Compiler> KEY = new Key<>(); public Key<Compiler> KEY = new Key<>();
public FunctionBody compile(Filename filename, String source); public FunctionBody compile(Environment env, Filename filename, String source);
public static Compiler get(Environment ext) { public static Compiler get(Environment ext) {
return ext.get(KEY, (filename, src) -> { return ext.get(KEY, (env, filename, src) -> {
throw EngineException.ofError("No compiler attached to engine."); throw EngineException.ofError("No compiler attached to engine.");
}); });
} }
public static CodeFunction compile(Environment env, Filename filename, String raw) { private static void registerFunc(Environment env, FunctionBody body, CompileResult res) {
DebugContext.get(env).onSource(filename, raw); var map = res.map();
return new CodeFunction(env, filename.toString(), Compiler.get(env).compile(filename, raw), new ValueVariable[0]);
DebugContext.get(env).onFunctionLoad(body, map);
for (var i = 0; i < body.children.length; i++) {
registerFunc(env, body.children[i], res.children.get(i));
}
}
public static CodeFunction compileFunc(Environment env, Filename filename, String raw) {
return new CodeFunction(env, filename.toString(), get(env).compile(env, filename, raw), new ValueVariable[0]);
} }
} }

View File

@ -9,48 +9,46 @@ import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
public class Instruction { public class Instruction {
public static enum Type { public static enum Type {
NOP(0), NOP(0x00),
RETURN(1), RETURN(0x01),
THROW(2), THROW(0x02),
THROW_SYNTAX(3), THROW_SYNTAX(0x03),
DELETE(4), DELETE(0x04),
TRY_START(5), TRY_START(0x05),
TRY_END(6), TRY_END(0x06),
CALL(7), CALL(0x10),
CALL_NEW(8), CALL_MEMBER(0x11),
JMP_IF(9), CALL_NEW(0x12),
JMP_IFN(10), JMP_IF(0x13),
JMP(11), JMP_IFN(0x14),
JMP(0x15),
PUSH_UNDEFINED(12), PUSH_UNDEFINED(0x20),
PUSH_NULL(13), PUSH_NULL(0x21),
PUSH_BOOL(14), PUSH_BOOL(0x22),
PUSH_NUMBER(15), PUSH_NUMBER(0x23),
PUSH_STRING(16), PUSH_STRING(0x24),
DUP(0x25),
DISCARD(0x26),
LOAD_VAR(17), LOAD_FUNC(0x30),
LOAD_MEMBER(18), LOAD_ARR(0x31),
LOAD_GLOB(20), LOAD_OBJ(0x32),
STORE_SELF_FUNC(0x33),
LOAD_REGEX(0x34),
LOAD_FUNC(21), LOAD_VAR(0x40),
LOAD_ARR(22), LOAD_MEMBER(0x41),
LOAD_OBJ(23), LOAD_GLOB(0x42),
STORE_SELF_FUNC(24), STORE_VAR(0x43),
LOAD_REGEX(25), STORE_MEMBER(0x44),
DUP(26), MAKE_VAR(0x50),
DEF_PROP(0x51),
STORE_VAR(27), KEYS(0x52),
STORE_MEMBER(28), TYPEOF(0x53),
DISCARD(29), OPERATION(0x54);
MAKE_VAR(30),
DEF_PROP(31),
KEYS(32),
TYPEOF(33),
OPERATION(34);
private static final HashMap<Integer, Type> types = new HashMap<>(); private static final HashMap<Integer, Type> types = new HashMap<>();
public final int numeric; public final int numeric;
@ -125,8 +123,12 @@ public class Instruction {
writer.writeByte(rawType); writer.writeByte(rawType);
switch (type) { switch (type) {
case CALL: writer.writeInt(get(0)); break; case CALL:
case CALL_NEW: writer.writeInt(get(0)); break; case CALL_NEW:
case CALL_MEMBER:
writer.writeInt(get(0));
writer.writeUTF(get(1));
break;
case DUP: writer.writeInt(get(0)); break; case DUP: writer.writeInt(get(0)); break;
case JMP: writer.writeInt(get(0)); break; case JMP: writer.writeInt(get(0)); break;
case JMP_IF: writer.writeInt(get(0)); break; case JMP_IF: writer.writeInt(get(0)); break;
@ -140,6 +142,7 @@ public class Instruction {
} }
writer.writeInt(get(0)); writer.writeInt(get(0));
writer.writeUTF(get(0));
break; break;
} }
case LOAD_REGEX: writer.writeUTF(get(0)); break; case LOAD_REGEX: writer.writeUTF(get(0)); break;
@ -174,8 +177,9 @@ public class Instruction {
var flag = (rawType & 128) != 0; var flag = (rawType & 128) != 0;
switch (type) { switch (type) {
case CALL: return call(stream.readInt()); case CALL: return call(stream.readInt(), stream.readUTF());
case CALL_NEW: return callNew(stream.readInt()); case CALL_NEW: return callNew(stream.readInt(), stream.readUTF());
case CALL_MEMBER: return callNew(stream.readInt(), stream.readUTF());
case DEF_PROP: return defProp(); case DEF_PROP: return defProp();
case DELETE: return delete(); case DELETE: return delete();
case DISCARD: return discard(); case DISCARD: return discard();
@ -192,7 +196,7 @@ public class Instruction {
captures[i] = stream.readInt(); captures[i] = stream.readInt();
} }
return loadFunc(stream.readInt(), captures); return loadFunc(stream.readInt(), stream.readUTF(), captures);
} }
case LOAD_GLOB: return loadGlob(); case LOAD_GLOB: return loadGlob();
case LOAD_MEMBER: return loadMember(); case LOAD_MEMBER: return loadMember();
@ -251,11 +255,23 @@ public class Instruction {
return new Instruction(Type.NOP, params); return new Instruction(Type.NOP, params);
} }
public static Instruction call(int argn, String name) {
return new Instruction(Type.CALL, argn, name);
}
public static Instruction call(int argn) { public static Instruction call(int argn) {
return new Instruction(Type.CALL, argn); return call(argn, "");
}
public static Instruction callMember(int argn, String name) {
return new Instruction(Type.CALL_MEMBER, argn, name);
}
public static Instruction callMember(int argn) {
return new Instruction(Type.CALL_MEMBER, argn, "");
}
public static Instruction callNew(int argn, String name) {
return new Instruction(Type.CALL_NEW, argn, name);
} }
public static Instruction callNew(int argn) { public static Instruction callNew(int argn) {
return new Instruction(Type.CALL_NEW, argn); return new Instruction(Type.CALL_NEW, argn, "");
} }
public static Instruction jmp(int offset) { public static Instruction jmp(int offset) {
return new Instruction(Type.JMP, offset); return new Instruction(Type.JMP, offset);
@ -299,10 +315,13 @@ public class Instruction {
public static Instruction loadRegex(String pattern, String flags) { public static Instruction loadRegex(String pattern, String flags) {
return new Instruction(Type.LOAD_REGEX, pattern, flags); return new Instruction(Type.LOAD_REGEX, pattern, flags);
} }
public static Instruction loadFunc(int id, int[] captures) { public static Instruction loadFunc(int id, String name, int[] captures) {
var args = new Object[1 + captures.length]; if (name == null) name = "";
var args = new Object[2 + captures.length];
args[0] = id; args[0] = id;
for (var i = 0; i < captures.length; i++) args[i + 1] = captures[i]; args[1] = name;
for (var i = 0; i < captures.length; i++) args[i + 2] = captures[i];
return new Instruction(Type.LOAD_FUNC, args); return new Instruction(Type.LOAD_FUNC, args);
} }
public static Instruction loadObj() { public static Instruction loadObj() {

View File

@ -1,23 +0,0 @@
package me.topchetoeu.jscript.common;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
public class RefTracker {
public static void onDestroy(Object obj, Runnable runnable) {
var queue = new ReferenceQueue<>();
var ref = new WeakReference<>(obj, queue);
obj = null;
var th = new Thread(() -> {
try {
queue.remove();
ref.get();
runnable.run();
}
catch (InterruptedException e) { return; }
});
th.setDaemon(true);
th.start();
}
}

View File

@ -1,5 +0,0 @@
package me.topchetoeu.jscript.common;
public interface ResultRunnable<T> {
T run();
}

View File

@ -1,31 +0,0 @@
package me.topchetoeu.jscript.common.events;
public class DataNotifier<T> {
private Notifier notifier = new Notifier();
private boolean isErr;
private T val;
private RuntimeException err;
public void error(RuntimeException t) {
err = t;
isErr = true;
notifier.next();
}
public void next(T val) {
this.val = val;
isErr = false;
notifier.next();
}
public T await() {
notifier.await();
try {
if (isErr) throw err;
else return val;
}
finally {
this.err = null;
this.val = null;
}
}
}

View File

@ -1,19 +0,0 @@
package me.topchetoeu.jscript.common.events;
import me.topchetoeu.jscript.runtime.exceptions.InterruptException;
public class Notifier {
private boolean ok = false;
public synchronized void next() {
ok = true;
notifyAll();
}
public synchronized void await() {
try {
while (!ok) wait();
ok = false;
}
catch (InterruptedException e) { throw new InterruptException(e); }
}
}

View File

@ -1,12 +1,13 @@
package me.topchetoeu.jscript.common.json; package me.topchetoeu.jscript.common.json;
import java.math.BigDecimal;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.parsing.Filename;
import me.topchetoeu.jscript.compilation.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.ParseRes;
import me.topchetoeu.jscript.compilation.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.compilation.parsing.Source; import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
public class JSON { public class JSON {
@ -16,23 +17,9 @@ public class JSON {
return ParseRes.res(JSONElement.string(res.result), res.n); return ParseRes.res(JSONElement.string(res.result), res.n);
} }
public static ParseRes<JSONElement> parseNumber(Source src, int i) { public static ParseRes<JSONElement> parseNumber(Source src, int i) {
var n = Parsing.skipEmpty(src, i); var res = Parsing.parseNumber(src, i, true);
if (!res.isSuccess()) return res.chainError();
if (src.is(i + n, "-")) { else return ParseRes.res(JSONElement.number(res.result), res.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<JSONElement> parseLiteral(Source src, int i) { public static ParseRes<JSONElement> parseLiteral(Source src, int i) {
var id = Parsing.parseIdentifier(src, i); var id = Parsing.parseIdentifier(src, i);
@ -121,7 +108,13 @@ public class JSON {
} }
public static String stringify(JSONElement el) { public static String stringify(JSONElement el) {
if (el.isNumber()) return Double.toString(el.number()); if (el.isNumber()) {
var d = el.number();
if (d == Double.NEGATIVE_INFINITY) return "-Infinity";
if (d == Double.POSITIVE_INFINITY) return "Infinity";
if (Double.isNaN(d)) return "NaN";
return BigDecimal.valueOf(d).stripTrailingZeros().toPlainString();
}
if (el.isBoolean()) return el.bool() ? "true" : "false"; if (el.isBoolean()) return el.bool() ? "true" : "false";
if (el.isNull()) return "null"; if (el.isNull()) return "null";
if (el.isString()) { if (el.isString()) {

View File

@ -11,9 +11,9 @@ import java.util.TreeSet;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.common.parsing.Filename;
import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.compilation.scope.LocalScopeRecord; import me.topchetoeu.jscript.compilation.scope.LocalScopeRecord;
public class FunctionMap { public class FunctionMap {

View File

@ -1,4 +1,4 @@
package me.topchetoeu.jscript.common; package me.topchetoeu.jscript.common.parsing;
import java.io.File; import java.io.File;
import java.nio.file.Path; import java.nio.file.Path;

View File

@ -1,4 +1,4 @@
package me.topchetoeu.jscript.common; package me.topchetoeu.jscript.common.parsing;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Objects; import java.util.Objects;

View File

@ -1,6 +1,4 @@
package me.topchetoeu.jscript.compilation.parsing; package me.topchetoeu.jscript.common.parsing;
import me.topchetoeu.jscript.common.Location;
public class ParseRes<T> { public class ParseRes<T> {
public static interface Parser<T> { public static interface Parser<T> {
@ -41,11 +39,6 @@ public class ParseRes<T> {
if (isSuccess()) throw new RuntimeException("Can't transform a ParseRes that hasn't failed."); if (isSuccess()) throw new RuntimeException("Can't transform a ParseRes that hasn't failed.");
return new ParseRes<>(state, error, null, 0); return new ParseRes<>(state, error, null, 0);
} }
public TestRes toTest() {
if (isSuccess()) return TestRes.res(n);
else if (isError()) return TestRes.error(null, error);
else return TestRes.failed();
}
public boolean isSuccess() { return state.isSuccess(); } public boolean isSuccess() { return state.isSuccess(); }
public boolean isFailed() { return state.isFailed(); } public boolean isFailed() { return state.isFailed(); }

View File

@ -0,0 +1,383 @@
package me.topchetoeu.jscript.common.parsing;
import me.topchetoeu.jscript.compilation.values.constants.NumberStatement;
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
// TODO: this has to be rewritten
// @SourceFile
public class Parsing {
public static boolean isDigit(Character c) {
return c != null && c >= '0' && c <= '9';
}
public static boolean isAny(char c, String alphabet) {
return alphabet.contains(Character.toString(c));
}
public static int fromHex(char c) {
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
if (c >= '0' && c <= '9') return c - '0';
return -1;
}
public static int skipEmpty(Source src, int i) {
int n = 0;
while (n < src.size() && src.is(i + n, Character::isWhitespace)) n++;
return n;
}
public static ParseRes<Character> parseChar(Source src, int i) {
int n = 0;
if (src.is(i + n, '\\')) {
n++;
char c = src.at(i + n++);
if (c == 'b') return ParseRes.res('\b', n);
else if (c == 't') return ParseRes.res('\t', n);
else if (c == 'n') return ParseRes.res('\n', n);
else if (c == 'f') return ParseRes.res('\f', n);
else if (c == 'r') return ParseRes.res('\r', n);
else if (c == '0') {
if (src.is(i + n, Parsing::isDigit)) return ParseRes.error(src.loc(i), "Octal escape sequences are not allowed");
else return ParseRes.res('\0', n);
}
else if (c >= '1' && c <= '9') return ParseRes.error(src.loc(i), "Octal escape sequences are not allowed");
else if (c == 'x') {
var newC = 0;
for (var j = 0; j < 2; j++) {
if (i + n >= src.size()) return ParseRes.error(src.loc(i), "Invalid hexadecimal escape sequence.");
int val = fromHex(src.at(i + n));
if (val == -1) throw new SyntaxException(src.loc(i + n), "Invalid hexadecimal escape sequence.");
n++;
newC = (newC << 4) | val;
}
return ParseRes.res((char)newC, n);
}
else if (c == 'u') {
var newC = 0;
for (var j = 0; j < 4; j++) {
if (i + n >= src.size()) return ParseRes.error(src.loc(i), "Invalid Unicode escape sequence");
int val = fromHex(src.at(i + n));
if (val == -1) throw new SyntaxException(src.loc(i + n), "Invalid Unicode escape sequence");
n++;
newC = (newC << 4) | val;
}
return ParseRes.res((char)newC, n);
}
else if (c == '\n') return ParseRes.res(null, n);
}
return ParseRes.res(src.at(i + n), n + 1);
}
public static ParseRes<String> parseIdentifier(Source src, int i) {
var n = skipEmpty(src, i);
var res = new StringBuilder();
var first = true;
while (true) {
if (i + n > src.size()) break;
char c = src.at(i + n, '\0');
if (first && Parsing.isDigit(c)) break;
if (!Character.isLetterOrDigit(c) && c != '_' && c != '$') break;
res.append(c);
n++;
first = false;
}
if (res.length() <= 0) return ParseRes.failed();
else return ParseRes.res(res.toString(), n);
}
public static ParseRes<String> parseIdentifier(Source src, int i, String test) {
var n = skipEmpty(src, i);
var res = new StringBuilder();
var first = true;
while (true) {
if (i + n > src.size()) break;
char c = src.at(i + n, '\0');
if (first && Parsing.isDigit(c)) break;
if (!Character.isLetterOrDigit(c) && c != '_' && c != '$') break;
res.append(c);
n++;
first = false;
}
if (res.length() <= 0) return ParseRes.failed();
else if (test == null || res.toString().equals(test)) return ParseRes.res(res.toString(), n);
else return ParseRes.failed();
}
public static boolean isIdentifier(Source src, int i, String test) {
return parseIdentifier(src, i, test).isSuccess();
}
public static ParseRes<String> parseOperator(Source src, int i, String op) {
var n = skipEmpty(src, i);
if (src.is(i + n, op)) return ParseRes.res(op, n + op.length());
else return ParseRes.failed();
}
private static ParseRes<Double> parseHex(Source src, int i) {
int n = 0;
double res = 0;
while (true) {
int digit = Parsing.fromHex(src.at(i + n, '\0'));
if (digit < 0) {
if (n <= 0) return ParseRes.failed();
else return ParseRes.res(res, n);
}
n++;
res *= 16;
res += digit;
}
}
private static ParseRes<Double> parseOct(Source src, int i) {
int n = 0;
double res = 0;
while (true) {
int digit = src.at(i + n, '\0') - '0';
if (digit < 0 || digit > 9) break;
if (digit > 7) return ParseRes.error(src.loc(i + n), "Digits in octal literals must be from 0 to 7, encountered " + digit);
if (digit < 0) {
if (n <= 0) return ParseRes.failed();
else return ParseRes.res(res, n);
}
n++;
res *= 8;
res += digit;
}
return ParseRes.res(res, n);
}
public static ParseRes<String> parseString(Source src, int i) {
var n = skipEmpty(src, i);
char quote;
if (src.is(i + n, '\'')) quote = '\'';
else if (src.is(i + n, '"')) quote = '"';
else return ParseRes.failed();
n++;
var res = new StringBuilder();
while (true) {
if (i + n >= src.size()) return ParseRes.error(src.loc(i + n), "Unterminated string literal");
if (src.is(i + n, quote)) {
n++;
return ParseRes.res(res.toString(), n);
}
var charRes = parseChar(src, i + n);
if (!charRes.isSuccess()) return charRes.chainError(src.loc(i + n), "Invalid character");
n += charRes.n;
if (charRes.result != null) res.append(charRes.result);
}
}
public static ParseRes<Double> parseNumber(Source src, int i, boolean withMinus) {
var n = skipEmpty(src, i);
double whole = 0;
double fract = 0;
long exponent = 0;
boolean parsedAny = false;
boolean negative = false;
if (withMinus && src.is(i + n, "-")) {
negative = true;
n++;
}
if (src.is(i + n, "0x") || src.is(i + n, "0X")) {
n += 2;
var res = parseHex(src, i + n);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Incomplete hexadecimal literal");
n += res.n;
if (negative) return ParseRes.res(-res.result, n);
else return ParseRes.res(res.result, n);
}
else if (src.is(i + n, "0o") || src.is(i + n, "0O")) {
n += 2;
var res = parseOct(src, i + n);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Incomplete octal literal");
n += res.n;
if (negative) return ParseRes.res(-res.result, n);
else return ParseRes.res(res.result, n);
}
else if (src.is(i + n, '0')) {
n++;
parsedAny = true;
if (src.is(i + n, Parsing::isDigit)) return ParseRes.error(src.loc(i + n), "Decimals with leading zeroes are not allowed");
}
while (src.is(i + n, Parsing::isDigit)) {
parsedAny = true;
whole *= 10;
whole += src.at(i + n++) - '0';
}
if (src.is(i + n, '.')) {
parsedAny = true;
n++;
while (src.is(i + n, Parsing::isDigit)) {
fract += src.at(i + n++) - '0';
fract /= 10;
}
}
if (src.is(i + n, 'e') || src.is(i + n, 'E')) {
n++;
parsedAny = true;
boolean expNegative = false;
boolean parsedE = false;
if (src.is(i + n, '-')) {
expNegative = true;
n++;
}
else if (src.is(i + n, '+')) n++;
while (src.is(i + n, Parsing::isDigit)) {
parsedE = true;
exponent *= 10;
if (expNegative) exponent -= src.at(i + n++) - '0';
else exponent += src.at(i + n++) - '0';
}
if (!parsedE) return ParseRes.error(src.loc(i + n), "Incomplete number exponent");
}
if (!parsedAny) {
if (negative) return ParseRes.error(src.loc(i + n), "Expected number immediatly after minus");
return ParseRes.failed();
}
else if (negative) return ParseRes.res(-(whole + fract) * NumberStatement.power(10, exponent), n);
else return ParseRes.res((whole + fract) * NumberStatement.power(10, exponent), n);
}
public static ParseRes<Double> parseFloat(Source src, int i, boolean withMinus) {
var n = skipEmpty(src, i);
double whole = 0;
double fract = 0;
long exponent = 0;
boolean parsedAny = false;
boolean negative = false;
if (withMinus && src.is(i + n, "-")) {
negative = true;
n++;
}
while (src.is(i + n, Parsing::isDigit)) {
parsedAny = true;
whole *= 10;
whole += src.at(i + n++) - '0';
}
if (src.is(i + n, '.')) {
parsedAny = true;
n++;
while (src.is(i + n, Parsing::isDigit)) {
fract += src.at(i + n++) - '0';
fract /= 10;
}
}
if (src.is(i + n, 'e') || src.is(i + n, 'E')) {
n++;
parsedAny = true;
boolean expNegative = false;
boolean parsedE = false;
if (src.is(i + n, '-')) {
expNegative = true;
n++;
}
else if (src.is(i + n, '+')) n++;
while (src.is(i + n, Parsing::isDigit)) {
parsedE = true;
exponent *= 10;
if (expNegative) exponent -= src.at(i + n++) - '0';
else exponent += src.at(i + n++) - '0';
}
if (!parsedE) return ParseRes.error(src.loc(i + n), "Incomplete number exponent");
}
if (!parsedAny) {
if (negative) return ParseRes.error(src.loc(i + n), "Expected number immediatly after minus");
return ParseRes.failed();
}
else if (negative) return ParseRes.res(-(whole + fract) * NumberStatement.power(10, exponent), n);
else return ParseRes.res((whole + fract) * NumberStatement.power(10, exponent), n);
}
public static ParseRes<Double> parseInt(Source src, int i, String alphabet, boolean withMinus) {
var n = skipEmpty(src, i);
double result = 0;
boolean parsedAny = false;
boolean negative = false;
if (withMinus && src.is(i + n, "-")) {
negative = true;
n++;
}
if (alphabet == null && src.is(i + n, "0x") || src.is(i + n, "0X")) {
n += 2;
var res = parseHex(src, i);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Incomplete hexadecimal literal");
n += res.n;
if (negative) return ParseRes.res(-res.result, n);
else return ParseRes.res(res.result, n);
}
while (true) {
var digit = alphabet.indexOf(Character.toLowerCase(src.at(i + n)));
if (digit < 0) break;
parsedAny = true;
result += digit;
result *= alphabet.length();
}
if (!parsedAny) {
if (negative) return ParseRes.error(src.loc(i + n), "Expected number immediatly after minus");
return ParseRes.failed();
}
else if (negative) return ParseRes.res(-result, n);
else return ParseRes.res(-result, n);
}
}

View File

@ -1,10 +1,7 @@
package me.topchetoeu.jscript.compilation.parsing; package me.topchetoeu.jscript.common.parsing;
import java.util.function.Predicate; import java.util.function.Predicate;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.Location;
public class Source { public class Source {
public final Filename filename; public final Filename filename;
public final String src; public final String src;

View File

@ -1,7 +1,4 @@
package me.topchetoeu.jscript.compilation.parsing; package me.topchetoeu.jscript.common.parsing;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.Location;
public class SourceLocation extends Location { public class SourceLocation extends Location {
private int[] lineStarts; private int[] lineStarts;

View File

@ -1,11 +1,7 @@
package me.topchetoeu.jscript.compilation; package me.topchetoeu.jscript.compilation;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Operation; import me.topchetoeu.jscript.common.Operation;
import me.topchetoeu.jscript.compilation.parsing.Operator; import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.compilation.parsing.ParseRes;
import me.topchetoeu.jscript.compilation.parsing.Parsing;
import me.topchetoeu.jscript.compilation.parsing.Source;
public abstract class AssignableStatement extends Statement { public abstract class AssignableStatement extends Statement {
public abstract Statement toAssign(Statement val, Operation operation); public abstract Statement toAssign(Statement val, Operation operation);
@ -13,38 +9,53 @@ public abstract class AssignableStatement extends Statement {
protected AssignableStatement(Location loc) { protected AssignableStatement(Location loc) {
super(loc); super(loc);
} }
// private static final Map<String, Operation> operations = Map.ofEntries(
// Map.entry("*=", Operation.MULTIPLY),
// Map.entry("/=", Operation.DIVIDE),
// Map.entry("%=", Operation.MODULO),
// Map.entry("-=", Operation.SUBTRACT),
// Map.entry("+=", Operation.ADD),
// Map.entry(">>=", Operation.SHIFT_RIGHT),
// Map.entry("<<=", Operation.SHIFT_LEFT),
// Map.entry(">>>=", Operation.USHIFT_RIGHT),
// Map.entry("&=", Operation.AND),
// Map.entry("^=", Operation.XOR),
// Map.entry("|=", Operation.OR)
// );
// private static final List<String> operatorsByLength = operations.keySet().stream().sorted().collect(Collectors.toList());
public static ParseRes<? extends Statement> parse(Source src, int i, Statement prev, int precedence) {
if (precedence > 2) return ParseRes.failed();
var n = Parsing.skipEmpty(src, i); // public static ParseRes<? extends Statement> parse(Source src, int i, Statement prev, int precedence) {
// if (precedence > 2) return ParseRes.failed();
for (var op : Operator.opsByLength) { // var n = Parsing.skipEmpty(src, i);
if (!op.assignable || !src.is(i + n, op.readable + "=")) continue;
n += op.readable.length() + 1;
if (!(prev instanceof AssignableStatement)) { // for (var op : operatorsByLength) {
return ParseRes.error(src.loc(i + n), "Invalid expression on left hand side of assign operator"); // if (!src.is(i + n, op)) continue;
} // n += op.length() + 1;
var res = Parsing.parseValue(src, i + n, 2); // if (!(prev instanceof AssignableStatement)) {
if (!res.isSuccess()) return res.chainError(src.loc(i + n), String.format("Expected a value after the '%s=' operator", op.readable)); // return ParseRes.error(src.loc(i + n), "Invalid expression on left hand side of assign operator");
n += res.n; // }
return ParseRes.res(((AssignableStatement)prev).toAssign(res.result, op.operation), n); // 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));
// n += res.n;
if (!src.is(i + n, "=")) return ParseRes.failed(); // return ParseRes.res(((AssignableStatement)prev).toAssign(res.result, operations.get(op)), n);
n++; // }
if (!(prev instanceof AssignableStatement)) { // if (!src.is(i + n, "=")) return ParseRes.failed();
return ParseRes.error(src.loc(i + n), "Invalid expression on left hand side of assign operator"); // n++;
}
var res = Parsing.parseValue(src, i + n, 2); // if (!(prev instanceof AssignableStatement)) {
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a value after the '=' operator"); // return ParseRes.error(src.loc(i + n), "Invalid expression on left hand side of assign operator");
n += res.n; // }
return ParseRes.res(((AssignableStatement)prev).toAssign(res.result, null), n); // 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;
// return ParseRes.res(((AssignableStatement)prev).toAssign(res.result, null), n);
// }
} }

View File

@ -6,10 +6,10 @@ import java.util.Vector;
import me.topchetoeu.jscript.common.FunctionBody; import me.topchetoeu.jscript.common.FunctionBody;
import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.common.mapping.FunctionMap; import me.topchetoeu.jscript.common.mapping.FunctionMap;
import me.topchetoeu.jscript.common.mapping.FunctionMap.FunctionMapBuilder; import me.topchetoeu.jscript.common.mapping.FunctionMap.FunctionMapBuilder;
import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.compilation.scope.LocalScopeRecord; import me.topchetoeu.jscript.compilation.scope.LocalScopeRecord;
public class CompileResult { public class CompileResult {

View File

@ -5,11 +5,11 @@ import java.util.List;
import java.util.Vector; import java.util.Vector;
import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.compilation.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.compilation.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.ParseRes;
import me.topchetoeu.jscript.compilation.parsing.Source; import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.compilation.values.FunctionStatement; import me.topchetoeu.jscript.compilation.values.FunctionStatement;
public class CompoundStatement extends Statement { public class CompoundStatement extends Statement {
@ -75,7 +75,7 @@ public class CompoundStatement extends Statement {
if (!src.is(i + n, ",")) return ParseRes.failed(); if (!src.is(i + n, ",")) return ParseRes.failed();
n++; n++;
var res = Parsing.parseValue(src, i + n, 2); var res = ES5.parseExpression(src, i + n, 2);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a value after the comma"); if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a value after the comma");
n += res.n; n += res.n;
@ -102,7 +102,7 @@ public class CompoundStatement extends Statement {
continue; continue;
} }
var res = Parsing.parseStatement(src, i + n); var res = ES5.parseStatement(src, i + n);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a statement"); if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a statement");
n += res.n; n += res.n;

View File

@ -0,0 +1,302 @@
package me.topchetoeu.jscript.compilation;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.parsing.Filename;
import me.topchetoeu.jscript.common.parsing.ParseRes;
import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.compilation.control.BreakStatement;
import me.topchetoeu.jscript.compilation.control.ContinueStatement;
import me.topchetoeu.jscript.compilation.control.DebugStatement;
import me.topchetoeu.jscript.compilation.control.DeleteStatement;
import me.topchetoeu.jscript.compilation.control.DoWhileStatement;
import me.topchetoeu.jscript.compilation.control.ForInStatement;
import me.topchetoeu.jscript.compilation.control.ForOfStatement;
import me.topchetoeu.jscript.compilation.control.ForStatement;
import me.topchetoeu.jscript.compilation.control.IfStatement;
import me.topchetoeu.jscript.compilation.control.ReturnStatement;
import me.topchetoeu.jscript.compilation.control.SwitchStatement;
import me.topchetoeu.jscript.compilation.control.ThrowStatement;
import me.topchetoeu.jscript.compilation.control.TryStatement;
import me.topchetoeu.jscript.compilation.control.WhileStatement;
import me.topchetoeu.jscript.compilation.scope.LocalScopeRecord;
import me.topchetoeu.jscript.compilation.values.ArrayStatement;
import me.topchetoeu.jscript.compilation.values.FunctionStatement;
import me.topchetoeu.jscript.compilation.values.GlobalThisStatement;
import me.topchetoeu.jscript.compilation.values.ObjectStatement;
import me.topchetoeu.jscript.compilation.values.RegexStatement;
import me.topchetoeu.jscript.compilation.values.VariableStatement;
import me.topchetoeu.jscript.compilation.values.constants.BoolStatement;
import me.topchetoeu.jscript.compilation.values.constants.NullStatement;
import me.topchetoeu.jscript.compilation.values.constants.NumberStatement;
import me.topchetoeu.jscript.compilation.values.constants.StringStatement;
import me.topchetoeu.jscript.compilation.values.operations.CallStatement;
import me.topchetoeu.jscript.compilation.values.operations.ChangeStatement;
import me.topchetoeu.jscript.compilation.values.operations.DiscardStatement;
import me.topchetoeu.jscript.compilation.values.operations.IndexStatement;
import me.topchetoeu.jscript.compilation.values.operations.OperationStatement;
import me.topchetoeu.jscript.compilation.values.operations.TypeofStatement;
import me.topchetoeu.jscript.compilation.values.operations.VariableIndexStatement;
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
public class ES5 {
static final Set<String> reserved = Set.of(
"true", "false", "void", "null", "this", "if", "else", "try", "catch",
"finally", "for", "do", "while", "switch", "case", "default", "new",
"function", "var", "return", "throw", "typeof", "delete", "break",
"continue", "debugger", "implements", "interface", "package", "private",
"protected", "public", "static"
);
public static ParseRes<? extends Statement> parseParens(Source src, int i) {
int n = 0;
var openParen = Parsing.parseOperator(src, i + n, "(");
if (!openParen.isSuccess()) return openParen.chainError();
n += openParen.n;
var res = ES5.parseExpression(src, i + n, 0);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected an expression in parens");
n += res.n;
var closeParen = Parsing.parseOperator(src, i + n, ")");
if (!closeParen.isSuccess()) return closeParen.chainError(src.loc(i + n), "Expected a closing paren");
n += closeParen.n;
return ParseRes.res(res.result, n);
}
public static ParseRes<? extends Statement> parseSimple(Source src, int i, boolean statement) {
return ParseRes.first(src, i,
(a, b) -> statement ? ParseRes.failed() : ObjectStatement.parse(a, b),
(a, b) -> statement ? ParseRes.failed() : FunctionStatement.parseFunction(a, b, false),
ES5::parseLiteral,
StringStatement::parse,
RegexStatement::parse,
NumberStatement::parse,
ChangeStatement::parsePrefixDecrease,
ChangeStatement::parsePrefixIncrease,
OperationStatement::parsePrefix,
ArrayStatement::parse,
ES5::parseParens,
CallStatement::parseNew,
TypeofStatement::parse,
DiscardStatement::parse,
DeleteStatement::parse,
VariableStatement::parse
);
}
public static ParseRes<? extends Statement> parseLiteral(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
var id = Parsing.parseIdentifier(src, i);
if (!id.isSuccess()) return id.chainError();
n += id.n;
if (id.result.equals("true")) return ParseRes.res(new BoolStatement(loc, true), n);
if (id.result.equals("false")) return ParseRes.res(new BoolStatement(loc, false), n);
if (id.result.equals("undefined")) return ParseRes.res(new DiscardStatement(loc, null), n);
if (id.result.equals("null")) return ParseRes.res(new NullStatement(loc), n);
if (id.result.equals("this")) return ParseRes.res(new VariableIndexStatement(loc, 0), n);
if (id.result.equals("arguments")) return ParseRes.res(new VariableIndexStatement(loc, 1), n);
if (id.result.equals("globalThis")) return ParseRes.res(new GlobalThisStatement(loc), n);
return ParseRes.failed();
}
public static ParseRes<? extends Statement> parseExpression(Source src, int i, int precedence, boolean statement) {
var n = Parsing.skipEmpty(src, i);
Statement prev = null;
while (true) {
if (prev == null) {
var res = parseSimple(src, i + n, statement);
if (res.isSuccess()) {
n += res.n;
prev = res.result;
}
else if (res.isError()) return res.chainError();
else break;
}
else {
var _prev = prev;
ParseRes<Statement> res = ParseRes.first(src, i + n,
(s, j) -> OperationStatement.parseInstanceof(s, j, _prev, precedence),
(s, j) -> OperationStatement.parseIn(s, j, _prev, precedence),
(s, j) -> ChangeStatement.parsePostfixIncrease(s, j, _prev, precedence),
(s, j) -> ChangeStatement.parsePostfixDecrease(s, j, _prev, precedence),
(s, j) -> OperationStatement.parseOperator(s, j, _prev, precedence),
(s, j) -> IfStatement.parseTernary(s, j, _prev, precedence),
(s, j) -> IndexStatement.parseMember(s, j, _prev, precedence),
(s, j) -> IndexStatement.parseIndex(s, j, _prev, precedence),
(s, j) -> CallStatement.parseCall(s, j, _prev, precedence),
(s, j) -> CompoundStatement.parseComma(s, j, _prev, precedence)
);
if (res.isSuccess()) {
n += res.n;
prev = res.result;
continue;
}
else if (res.isError()) return res.chainError();
break;
}
}
if (prev == null) return ParseRes.failed();
else return ParseRes.res(prev, n);
}
public static ParseRes<? extends Statement> parseExpression(Source src, int i, int precedence) {
return parseExpression(src, i, precedence, false);
}
public static ParseRes<? extends Statement> parseExpressionStatement(Source src, int i) {
var res = parseExpression(src, i, 0, true);
if (!res.isSuccess()) return res.chainError();
var end = ES5.parseStatementEnd(src, i + res.n);
if (!end.isSuccess()) return ParseRes.error(src.loc(i + res.n), "Expected an end of statement");
return res.addN(end.n);
}
public static ParseRes<? extends Statement> parseStatement(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
if (src.is(i + n, ";")) return ParseRes.res(new DiscardStatement(src.loc(i+ n), null), n + 1);
if (Parsing.isIdentifier(src, i + n, "with")) return ParseRes.error(src.loc(i + n), "'with' statements are not allowed.");
ParseRes<? extends Statement> res = ParseRes.first(src, i + n,
VariableDeclareStatement::parse,
ReturnStatement::parse,
ThrowStatement::parse,
ContinueStatement::parse,
BreakStatement::parse,
DebugStatement::parse,
IfStatement::parse,
WhileStatement::parse,
SwitchStatement::parse,
ForStatement::parse,
ForInStatement::parse,
ForOfStatement::parse,
DoWhileStatement::parse,
TryStatement::parse,
CompoundStatement::parse,
(s, j) -> FunctionStatement.parseFunction(s, j, true),
ES5::parseExpressionStatement
);
return res.addN(n);
}
public static Statement[] parse(Filename filename, String raw) {
var src = new Source(filename, raw);
var list = new ArrayList<Statement>();
int i = 0;
while (true) {
if (i >= src.size()) break;
var res = parseStatement(src, i);
if (res.isError()) throw new SyntaxException(src.loc(i), res.error);
else if (res.isFailed()) throw new SyntaxException(src.loc(i), "Unexpected syntax");
i += res.n;
list.add(res.result);
}
return list.toArray(Statement[]::new);
}
public static ParseRes<Boolean> parseStatementEnd(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
if (i >= src.size()) return ParseRes.res(true, n + 1);
for (var j = i; j < i + n; j++) {
if (src.is(j, '\n')) return ParseRes.res(true, n);
}
if (src.is(i + n, ';')) return ParseRes.res(true, n + 1);
if (src.is(i + n, '}')) return ParseRes.res(true, n);
return ParseRes.failed();
}
public static boolean checkVarName(String name) {
return !ES5.reserved.contains(name);
}
public static ParseRes<List<String>> parseParamList(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var openParen = Parsing.parseOperator(src, i + n, "(");
if (!openParen.isSuccess()) return openParen.chainError(src.loc(i + n), "Expected a parameter list.");
n += openParen.n;
var args = new ArrayList<String>();
var closeParen = Parsing.parseOperator(src, i + n, ")");
n += closeParen.n;
if (!closeParen.isSuccess()) {
while (true) {
var argRes = Parsing.parseIdentifier(src, i + n);
if (argRes.isSuccess()) {
args.add(argRes.result);
n += argRes.n;
n += Parsing.skipEmpty(src, i);
if (src.is(i + n, ",")) {
n++;
n += Parsing.skipEmpty(src, i + n);
}
if (src.is(i + n, ")")) {
n++;
break;
}
}
else return ParseRes.error(src.loc(i + n), "Expected an argument, or a closing brace.");
}
}
return ParseRes.res(args, n);
}
public static CompileResult compile(Statement ...statements) {
var target = new CompileResult(new LocalScopeRecord());
var stm = new CompoundStatement(null, true, statements);
target.scope.define("this");
target.scope.define("arguments");
try {
stm.compile(target, true);
FunctionStatement.checkBreakAndCont(target, 0);
}
catch (SyntaxException e) {
target = new CompileResult(new LocalScopeRecord());
target.scope.define("this");
target.scope.define("arguments");
target.add(Instruction.throwSyntax(e)).setLocation(stm.loc());
}
target.add(Instruction.ret()).setLocation(stm.loc());
return target;
}
public static CompileResult compile(Filename filename, String raw) {
return ES5.compile(ES5.parse(filename, raw));
}
}

View File

@ -1,7 +1,7 @@
package me.topchetoeu.jscript.compilation; package me.topchetoeu.jscript.compilation;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.common.parsing.Location;
public abstract class Statement { public abstract class Statement {
private Location _loc; private Location _loc;

View File

@ -4,11 +4,11 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.compilation.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.compilation.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.ParseRes;
import me.topchetoeu.jscript.compilation.parsing.Source; import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.compilation.values.FunctionStatement; import me.topchetoeu.jscript.compilation.values.FunctionStatement;
public class VariableDeclareStatement extends Statement { public class VariableDeclareStatement extends Statement {
@ -63,7 +63,7 @@ public class VariableDeclareStatement extends Statement {
var res = new ArrayList<Pair>(); var res = new ArrayList<Pair>();
var end = Parsing.parseStatementEnd(src, i + n); var end = ES5.parseStatementEnd(src, i + n);
if (end.isSuccess()) { if (end.isSuccess()) {
n += end.n; n += end.n;
return ParseRes.res(new VariableDeclareStatement(loc, res), n); return ParseRes.res(new VariableDeclareStatement(loc, res), n);
@ -75,7 +75,7 @@ public class VariableDeclareStatement extends Statement {
if (!name.isSuccess()) return name.chainError(nameLoc, "Expected a variable name"); if (!name.isSuccess()) return name.chainError(nameLoc, "Expected a variable name");
n += name.n; n += name.n;
if (!Parsing.checkVarName(name.result)) { if (!ES5.checkVarName(name.result)) {
return ParseRes.error(src.loc(i + n), String.format("Unexpected identifier '%s'", name.result)); return ParseRes.error(src.loc(i + n), String.format("Unexpected identifier '%s'", name.result));
} }
@ -85,7 +85,7 @@ public class VariableDeclareStatement extends Statement {
if (src.is(i + n, "=")) { if (src.is(i + n, "=")) {
n++; n++;
var valRes = Parsing.parseValue(src, i + n, 2); var valRes = ES5.parseExpression(src, i + n, 2);
if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after '='"); if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after '='");
n += valRes.n; n += valRes.n;
@ -100,7 +100,7 @@ public class VariableDeclareStatement extends Statement {
continue; continue;
} }
end = Parsing.parseStatementEnd(src, i + n); end = ES5.parseStatementEnd(src, i + n);
if (end.isSuccess()) { if (end.isSuccess()) {
n += end.n; n += end.n;

View File

@ -1,12 +1,13 @@
package me.topchetoeu.jscript.compilation.control; package me.topchetoeu.jscript.compilation.control;
import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.common.parsing.ParseRes;
import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.ES5;
import me.topchetoeu.jscript.compilation.Statement; 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 BreakStatement extends Statement { public class BreakStatement extends Statement {
public final String label; public final String label;
@ -28,7 +29,7 @@ public class BreakStatement extends Statement {
if (!Parsing.isIdentifier(src, i + n, "break")) return ParseRes.failed(); if (!Parsing.isIdentifier(src, i + n, "break")) return ParseRes.failed();
n += 5; n += 5;
var end = Parsing.parseStatementEnd(src, i + n); var end = ES5.parseStatementEnd(src, i + n);
if (end.isSuccess()) { if (end.isSuccess()) {
n += end.n; n += end.n;
return ParseRes.res(new BreakStatement(loc, null), n); return ParseRes.res(new BreakStatement(loc, null), n);
@ -38,7 +39,7 @@ public class BreakStatement extends Statement {
if (label.isFailed()) return ParseRes.error(src.loc(i + n), "Expected a label name or an end of statement"); if (label.isFailed()) return ParseRes.error(src.loc(i + n), "Expected a label name or an end of statement");
n += label.n; n += label.n;
end = Parsing.parseStatementEnd(src, i + n); end = ES5.parseStatementEnd(src, i + n);
if (end.isSuccess()) { if (end.isSuccess()) {
n += end.n; n += end.n;
return ParseRes.res(new BreakStatement(loc, label.result), n); return ParseRes.res(new BreakStatement(loc, label.result), n);

View File

@ -1,12 +1,13 @@
package me.topchetoeu.jscript.compilation.control; package me.topchetoeu.jscript.compilation.control;
import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.common.parsing.ParseRes;
import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.ES5;
import me.topchetoeu.jscript.compilation.Statement; 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 ContinueStatement extends Statement { public class ContinueStatement extends Statement {
public final String label; public final String label;
@ -28,7 +29,7 @@ public class ContinueStatement extends Statement {
if (!Parsing.isIdentifier(src, i + n, "continue")) return ParseRes.failed(); if (!Parsing.isIdentifier(src, i + n, "continue")) return ParseRes.failed();
n += 8; n += 8;
var end = Parsing.parseStatementEnd(src, i + n); var end = ES5.parseStatementEnd(src, i + n);
if (end.isSuccess()) { if (end.isSuccess()) {
n += end.n; n += end.n;
return ParseRes.res(new ContinueStatement(loc, null), n); return ParseRes.res(new ContinueStatement(loc, null), n);
@ -38,7 +39,7 @@ public class ContinueStatement extends Statement {
if (label.isFailed()) return ParseRes.error(src.loc(i + n), "Expected a label name or an end of statement"); if (label.isFailed()) return ParseRes.error(src.loc(i + n), "Expected a label name or an end of statement");
n += label.n; n += label.n;
end = Parsing.parseStatementEnd(src, i + n); end = ES5.parseStatementEnd(src, i + n);
if (end.isSuccess()) { if (end.isSuccess()) {
n += end.n; n += end.n;
return ParseRes.res(new ContinueStatement(loc, label.result), n); return ParseRes.res(new ContinueStatement(loc, label.result), n);

View File

@ -1,12 +1,13 @@
package me.topchetoeu.jscript.compilation.control; package me.topchetoeu.jscript.compilation.control;
import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.common.parsing.ParseRes;
import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.ES5;
import me.topchetoeu.jscript.compilation.Statement; 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 DebugStatement extends Statement { public class DebugStatement extends Statement {
@Override public void compile(CompileResult target, boolean pollute) { @Override public void compile(CompileResult target, boolean pollute) {
@ -25,7 +26,7 @@ public class DebugStatement extends Statement {
if (!Parsing.isIdentifier(src, i + n, "debugger")) return ParseRes.failed(); if (!Parsing.isIdentifier(src, i + n, "debugger")) return ParseRes.failed();
n += 8; n += 8;
var end = Parsing.parseStatementEnd(src, i + n); var end = ES5.parseStatementEnd(src, i + n);
if (end.isSuccess()) { if (end.isSuccess()) {
n += end.n; n += end.n;
return ParseRes.res(new DebugStatement(loc), n); return ParseRes.res(new DebugStatement(loc), n);

View File

@ -1,15 +1,16 @@
package me.topchetoeu.jscript.compilation.control; package me.topchetoeu.jscript.compilation.control;
import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.common.parsing.ParseRes;
import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.ES5;
import me.topchetoeu.jscript.compilation.Statement; 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;
import me.topchetoeu.jscript.compilation.values.IndexStatement;
import me.topchetoeu.jscript.compilation.values.VariableStatement; import me.topchetoeu.jscript.compilation.values.VariableStatement;
import me.topchetoeu.jscript.compilation.values.constants.BoolStatement; import me.topchetoeu.jscript.compilation.values.constants.BoolStatement;
import me.topchetoeu.jscript.compilation.values.operations.IndexStatement;
public class DeleteStatement extends Statement { public class DeleteStatement extends Statement {
public final Statement key; public final Statement key;
@ -31,7 +32,7 @@ public class DeleteStatement extends Statement {
if (!Parsing.isIdentifier(src, i + n, "delete")) return ParseRes.failed(); if (!Parsing.isIdentifier(src, i + n, "delete")) return ParseRes.failed();
n += 6; n += 6;
var valRes = Parsing.parseValue(src, i + n, 15); var valRes = ES5.parseExpression(src, i + n, 15);
if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'delete'"); if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'delete'");
n += valRes.n; n += valRes.n;

View File

@ -1,13 +1,14 @@
package me.topchetoeu.jscript.compilation.control; package me.topchetoeu.jscript.compilation.control;
import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.common.parsing.ParseRes;
import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.ES5;
import me.topchetoeu.jscript.compilation.Statement; 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 DoWhileStatement extends Statement { public class DoWhileStatement extends Statement {
public final Statement condition, body; public final Statement condition, body;
@ -47,7 +48,7 @@ public class DoWhileStatement extends Statement {
if (!Parsing.isIdentifier(src, i + n, "do")) return ParseRes.failed(); if (!Parsing.isIdentifier(src, i + n, "do")) return ParseRes.failed();
n += 2; n += 2;
var bodyRes = Parsing.parseStatement(src, i + n); var bodyRes = ES5.parseStatement(src, i + n);
if (!bodyRes.isSuccess()) return bodyRes.chainError(src.loc(i + n), "Expected a do-while body."); if (!bodyRes.isSuccess()) return bodyRes.chainError(src.loc(i + n), "Expected a do-while body.");
n += bodyRes.n; n += bodyRes.n;
@ -58,7 +59,7 @@ public class DoWhileStatement extends Statement {
if (!src.is(i + n, "(")) return ParseRes.error(src.loc(i + n), "Expected a open paren after 'while'."); if (!src.is(i + n, "(")) return ParseRes.error(src.loc(i + n), "Expected a open paren after 'while'.");
n++; n++;
var condRes = Parsing.parseValue(src, i + n, 0); var condRes = ES5.parseExpression(src, i + n, 0);
if (!condRes.isSuccess()) return condRes.chainError(src.loc(i + n), "Expected a do-while condition."); if (!condRes.isSuccess()) return condRes.chainError(src.loc(i + n), "Expected a do-while condition.");
n += condRes.n; n += condRes.n;
n += Parsing.skipEmpty(src, i + n); n += Parsing.skipEmpty(src, i + n);
@ -66,7 +67,7 @@ public class DoWhileStatement extends Statement {
if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a closing paren after do-while condition."); if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a closing paren after do-while condition.");
n++; n++;
var end = Parsing.parseStatementEnd(src, i + n); var end = ES5.parseStatementEnd(src, i + n);
if (end.isSuccess()) { if (end.isSuccess()) {
n += end.n; n += end.n;
return ParseRes.res(new DoWhileStatement(loc, labelRes.result, condRes.result, bodyRes.result), n); return ParseRes.res(new DoWhileStatement(loc, labelRes.result, condRes.result, bodyRes.result), n);

View File

@ -1,14 +1,15 @@
package me.topchetoeu.jscript.compilation.control; package me.topchetoeu.jscript.compilation.control;
import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Operation; import me.topchetoeu.jscript.common.Operation;
import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.common.parsing.ParseRes;
import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.ES5;
import me.topchetoeu.jscript.compilation.Statement; 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 ForInStatement extends Statement { public class ForInStatement extends Statement {
public final String varName; public final String varName;
@ -90,7 +91,7 @@ public class ForInStatement extends Statement {
if (!Parsing.isIdentifier(src, i + n, "in")) return ParseRes.error(src.loc(i + n), "Expected 'in' keyword after variable declaration"); if (!Parsing.isIdentifier(src, i + n, "in")) return ParseRes.error(src.loc(i + n), "Expected 'in' keyword after variable declaration");
n += 2; n += 2;
var obj = Parsing.parseValue(src, i + n, 0); var obj = ES5.parseExpression(src, i + n, 0);
if (!obj.isSuccess()) return obj.chainError(src.loc(i + n), "Expected a value"); if (!obj.isSuccess()) return obj.chainError(src.loc(i + n), "Expected a value");
n += obj.n; n += obj.n;
n += Parsing.skipEmpty(src, i + n); n += Parsing.skipEmpty(src, i + n);
@ -98,7 +99,7 @@ public class ForInStatement extends Statement {
if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a closing paren"); if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a closing paren");
n++; n++;
var bodyRes = Parsing.parseStatement(src, i + n); var bodyRes = ES5.parseStatement(src, i + n);
if (!bodyRes.isSuccess()) return bodyRes.chainError(src.loc(i + n), "Expected a for-in body"); if (!bodyRes.isSuccess()) return bodyRes.chainError(src.loc(i + n), "Expected a for-in body");
n += bodyRes.n; n += bodyRes.n;

View File

@ -1,13 +1,14 @@
package me.topchetoeu.jscript.compilation.control; package me.topchetoeu.jscript.compilation.control;
import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.common.parsing.ParseRes;
import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.ES5;
import me.topchetoeu.jscript.compilation.Statement; 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 ForOfStatement extends Statement { public class ForOfStatement extends Statement {
public final String varName; public final String varName;
@ -102,7 +103,7 @@ public class ForOfStatement extends Statement {
if (!Parsing.isIdentifier(src, i + n, "fo")) return ParseRes.error(src.loc(i + n), "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; n += 2;
var obj = Parsing.parseValue(src, i + n, 0); var obj = ES5.parseExpression(src, i + n, 0);
if (!obj.isSuccess()) return obj.chainError(src.loc(i + n), "Expected a value"); if (!obj.isSuccess()) return obj.chainError(src.loc(i + n), "Expected a value");
n += obj.n; n += obj.n;
n += Parsing.skipEmpty(src, i + n); n += Parsing.skipEmpty(src, i + n);
@ -110,7 +111,7 @@ public class ForOfStatement extends Statement {
if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a closing paren"); if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a closing paren");
n++; n++;
var bodyRes = Parsing.parseStatement(src, i + n); var bodyRes = ES5.parseStatement(src, i + n);
if (!bodyRes.isSuccess()) return bodyRes.chainError(src.loc(i + n), "Expected a for-of body"); if (!bodyRes.isSuccess()) return bodyRes.chainError(src.loc(i + n), "Expected a for-of body");
n += bodyRes.n; n += bodyRes.n;

View File

@ -1,15 +1,16 @@
package me.topchetoeu.jscript.compilation.control; package me.topchetoeu.jscript.compilation.control;
import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.common.parsing.ParseRes;
import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.ES5;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.VariableDeclareStatement; import me.topchetoeu.jscript.compilation.VariableDeclareStatement;
import me.topchetoeu.jscript.compilation.parsing.ParseRes; import me.topchetoeu.jscript.compilation.values.operations.DiscardStatement;
import me.topchetoeu.jscript.compilation.parsing.Parsing;
import me.topchetoeu.jscript.compilation.parsing.Source;
import me.topchetoeu.jscript.compilation.values.DiscardStatement;
public class ForStatement extends Statement { public class ForStatement extends Statement {
public final Statement declaration, assignment, condition, body; public final Statement declaration, assignment, condition, body;
@ -57,7 +58,7 @@ public class ForStatement extends Statement {
private static ParseRes<Statement> parseCondition(Source src, int i) { private static ParseRes<Statement> parseCondition(Source src, int i) {
var n = Parsing.skipEmpty(src, i); var n = Parsing.skipEmpty(src, i);
var res = Parsing.parseValue(src, i + n, 0); var res = ES5.parseExpression(src, i + n, 0);
if (!res.isSuccess()) return res.chainError(); if (!res.isSuccess()) return res.chainError();
n += res.n; n += res.n;
n += Parsing.skipEmpty(src, i + n); n += Parsing.skipEmpty(src, i + n);
@ -66,7 +67,7 @@ public class ForStatement extends Statement {
else return ParseRes.res(res.result, n + 1); else return ParseRes.res(res.result, n + 1);
} }
private static ParseRes<? extends Statement> parseUpdater(Source src, int i) { private static ParseRes<? extends Statement> parseUpdater(Source src, int i) {
return Parsing.parseValue(src, i, 0); return ES5.parseExpression(src, i, 0);
} }
public static ParseRes<ForStatement> parse(Source src, int i) { public static ParseRes<ForStatement> parse(Source src, int i) {
@ -107,7 +108,7 @@ public class ForStatement extends Statement {
if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a close paren after for updater"); if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a close paren after for updater");
n++; n++;
var body = Parsing.parseStatement(src, i + n); var body = ES5.parseStatement(src, i + n);
if (!body.isSuccess()) return body.chainError(src.loc(i + n), "Expected a for body."); if (!body.isSuccess()) return body.chainError(src.loc(i + n), "Expected a for body.");
n += body.n; n += body.n;

View File

@ -1,13 +1,14 @@
package me.topchetoeu.jscript.compilation.control; package me.topchetoeu.jscript.compilation.control;
import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.common.parsing.ParseRes;
import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.ES5;
import me.topchetoeu.jscript.compilation.Statement; 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 IfStatement extends Statement { public class IfStatement extends Statement {
public final Statement condition, body, elseBody; public final Statement condition, body, elseBody;
@ -58,7 +59,7 @@ public class IfStatement extends Statement {
var loc = src.loc(i + n); var loc = src.loc(i + n);
n++; n++;
var a = Parsing.parseValue(src, i + n, 2); var a = ES5.parseExpression(src, i + n, 2);
if (!a.isSuccess()) return a.chainError(src.loc(i + n), "Expected a value after the ternary operator."); if (!a.isSuccess()) return a.chainError(src.loc(i + n), "Expected a value after the ternary operator.");
n += a.n; n += a.n;
n += Parsing.skipEmpty(src, i); n += Parsing.skipEmpty(src, i);
@ -66,7 +67,7 @@ public class IfStatement extends Statement {
if (!src.is(i + n, ":")) return ParseRes.failed(); if (!src.is(i + n, ":")) return ParseRes.failed();
n++; n++;
var b = Parsing.parseValue(src, i + n, 2); var b = ES5.parseExpression(src, i + n, 2);
if (!b.isSuccess()) return b.chainError(src.loc(i + n), "Expected a second value after the ternary operator."); if (!b.isSuccess()) return b.chainError(src.loc(i + n), "Expected a second value after the ternary operator.");
n += b.n; n += b.n;
@ -83,7 +84,7 @@ public class IfStatement extends Statement {
if (!src.is(i + n, "(")) return ParseRes.error(src.loc(i + n), "Expected a open paren after 'if'."); if (!src.is(i + n, "(")) return ParseRes.error(src.loc(i + n), "Expected a open paren after 'if'.");
n++; n++;
var condRes = Parsing.parseValue(src, i + n, 0); var condRes = ES5.parseExpression(src, i + n, 0);
if (!condRes.isSuccess()) return condRes.chainError(src.loc(i + n), "Expected an if condition."); if (!condRes.isSuccess()) return condRes.chainError(src.loc(i + n), "Expected an if condition.");
n += condRes.n; n += condRes.n;
n += Parsing.skipEmpty(src, i + n); n += Parsing.skipEmpty(src, i + n);
@ -91,7 +92,7 @@ public class IfStatement extends Statement {
if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a closing paren after if condition."); if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a closing paren after if condition.");
n++; n++;
var res = Parsing.parseStatement(src, i + n); var res = ES5.parseStatement(src, i + n);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected an if body."); if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected an if body.");
n += res.n; n += res.n;
@ -99,7 +100,7 @@ public class IfStatement extends Statement {
if (!elseKw.isSuccess()) return ParseRes.res(new IfStatement(loc, condRes.result, res.result, null), n); if (!elseKw.isSuccess()) return ParseRes.res(new IfStatement(loc, condRes.result, res.result, null), n);
n += elseKw.n; n += elseKw.n;
var elseRes = Parsing.parseStatement(src, i + n); var elseRes = ES5.parseStatement(src, i + n);
if (!elseRes.isSuccess()) return elseRes.chainError(src.loc(i + n), "Expected an else body."); if (!elseRes.isSuccess()) return elseRes.chainError(src.loc(i + n), "Expected an else body.");
n += elseRes.n; n += elseRes.n;

View File

@ -1,12 +1,13 @@
package me.topchetoeu.jscript.compilation.control; package me.topchetoeu.jscript.compilation.control;
import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.common.parsing.ParseRes;
import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.ES5;
import me.topchetoeu.jscript.compilation.Statement; 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 ReturnStatement extends Statement { public class ReturnStatement extends Statement {
public final Statement value; public final Statement value;
@ -30,21 +31,21 @@ public class ReturnStatement extends Statement {
if (!Parsing.isIdentifier(src, i + n, "return")) return ParseRes.failed(); if (!Parsing.isIdentifier(src, i + n, "return")) return ParseRes.failed();
n += 6; n += 6;
var end = Parsing.parseStatementEnd(src, i + n); var end = ES5.parseStatementEnd(src, i + n);
if (end.isSuccess()) { if (end.isSuccess()) {
n += end.n; n += end.n;
return ParseRes.res(new ReturnStatement(loc, null), n); return ParseRes.res(new ReturnStatement(loc, null), n);
} }
var val = Parsing.parseValue(src, i + n, 0); var val = ES5.parseExpression(src, i + n, 0);
if (val.isFailed()) return ParseRes.error(src.loc(i + n), "Expected a value"); if (val.isError()) return val.chainError();
n += val.n; n += val.n;
end = Parsing.parseStatementEnd(src, i + n); end = ES5.parseStatementEnd(src, i + n);
if (end.isSuccess()) { if (end.isSuccess()) {
n += end.n; n += end.n;
return ParseRes.res(new ReturnStatement(loc, val.result), n); return ParseRes.res(new ReturnStatement(loc, val.result), n);
} }
else return end.chainError(src.loc(i + n), "Expected end of statement"); else return end.chainError(src.loc(i + n), "Expected end of statement or a return value");
} }
} }

View File

@ -4,15 +4,16 @@ import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Operation; import me.topchetoeu.jscript.common.Operation;
import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.common.Instruction.Type; import me.topchetoeu.jscript.common.Instruction.Type;
import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.common.parsing.ParseRes;
import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.ES5;
import me.topchetoeu.jscript.compilation.Statement; 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 SwitchStatement extends Statement { public class SwitchStatement extends Statement {
public static class SwitchCase { public static class SwitchCase {
@ -89,7 +90,7 @@ public class SwitchStatement extends Statement {
if (!Parsing.isIdentifier(src, i + n, "case")) return ParseRes.failed(); if (!Parsing.isIdentifier(src, i + n, "case")) return ParseRes.failed();
n += 4; n += 4;
var valRes = Parsing.parseValue(src, i + n, 0); var valRes = ES5.parseExpression(src, i + n, 0);
if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'case'"); if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'case'");
n += valRes.n; n += valRes.n;
@ -121,7 +122,7 @@ public class SwitchStatement extends Statement {
if (!src.is(i + n, "(")) return ParseRes.error(src.loc(i + n), "Expected a open paren after 'switch'"); if (!src.is(i + n, "(")) return ParseRes.error(src.loc(i + n), "Expected a open paren after 'switch'");
n++; n++;
var valRes = Parsing.parseValue(src, i + n, 0); var valRes = ES5.parseExpression(src, i + n, 0);
if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a switch value"); if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a switch value");
n += valRes.n; n += valRes.n;
n += Parsing.skipEmpty(src, i + n); n += Parsing.skipEmpty(src, i + n);
@ -166,7 +167,7 @@ public class SwitchStatement extends Statement {
} }
if (caseRes.isError()) return caseRes.chainError(); if (caseRes.isError()) return caseRes.chainError();
var stm = Parsing.parseStatement(src, i + n); var stm = ES5.parseStatement(src, i + n);
if (stm.isSuccess()) { if (stm.isSuccess()) {
n += stm.n; n += stm.n;
statements.add(stm.result); statements.add(stm.result);

View File

@ -1,12 +1,13 @@
package me.topchetoeu.jscript.compilation.control; package me.topchetoeu.jscript.compilation.control;
import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.common.parsing.ParseRes;
import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.ES5;
import me.topchetoeu.jscript.compilation.Statement; 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 ThrowStatement extends Statement { public class ThrowStatement extends Statement {
public final Statement value; public final Statement value;
@ -29,17 +30,17 @@ public class ThrowStatement extends Statement {
if (!Parsing.isIdentifier(src, i + n, "throw")) return ParseRes.failed(); if (!Parsing.isIdentifier(src, i + n, "throw")) return ParseRes.failed();
n += 5; n += 5;
var end = Parsing.parseStatementEnd(src, i + n); var end = ES5.parseStatementEnd(src, i + n);
if (end.isSuccess()) { if (end.isSuccess()) {
n += end.n; n += end.n;
return ParseRes.res(new ThrowStatement(loc, null), n); return ParseRes.res(new ThrowStatement(loc, null), n);
} }
var val = Parsing.parseValue(src, i + n, 0); var val = ES5.parseExpression(src, i + n, 0);
if (val.isFailed()) return ParseRes.error(src.loc(i + n), "Expected a value"); if (val.isFailed()) return ParseRes.error(src.loc(i + n), "Expected a value");
n += val.n; n += val.n;
end = Parsing.parseStatementEnd(src, i + n); end = ES5.parseStatementEnd(src, i + n);
if (end.isSuccess()) { if (end.isSuccess()) {
n += end.n; n += end.n;
return ParseRes.res(new ThrowStatement(loc, val.result), n); return ParseRes.res(new ThrowStatement(loc, val.result), n);

View File

@ -1,14 +1,14 @@
package me.topchetoeu.jscript.compilation.control; package me.topchetoeu.jscript.compilation.control;
import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.common.parsing.ParseRes;
import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.CompoundStatement; import me.topchetoeu.jscript.compilation.CompoundStatement;
import me.topchetoeu.jscript.compilation.Statement; 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 TryStatement extends Statement { public class TryStatement extends Statement {
public final Statement tryBody; public final Statement tryBody;

View File

@ -1,14 +1,15 @@
package me.topchetoeu.jscript.compilation.control; package me.topchetoeu.jscript.compilation.control;
import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.common.Instruction.Type; import me.topchetoeu.jscript.common.Instruction.Type;
import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.common.parsing.ParseRes;
import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.ES5;
import me.topchetoeu.jscript.compilation.Statement; 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 WhileStatement extends Statement { public class WhileStatement extends Statement {
public final Statement condition, body; public final Statement condition, body;
@ -82,7 +83,7 @@ public class WhileStatement extends Statement {
if (!src.is(i + n, "(")) return ParseRes.error(src.loc(i + n), "Expected a open paren after 'while'."); if (!src.is(i + n, "(")) return ParseRes.error(src.loc(i + n), "Expected a open paren after 'while'.");
n++; n++;
var condRes = Parsing.parseValue(src, i + n, 0); var condRes = ES5.parseExpression(src, i + n, 0);
if (!condRes.isSuccess()) return condRes.chainError(src.loc(i + n), "Expected a while condition."); if (!condRes.isSuccess()) return condRes.chainError(src.loc(i + n), "Expected a while condition.");
n += condRes.n; n += condRes.n;
n += Parsing.skipEmpty(src, i + n); n += Parsing.skipEmpty(src, i + n);
@ -90,7 +91,7 @@ public class WhileStatement extends Statement {
if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "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++; n++;
var res = Parsing.parseStatement(src, i + n); var res = ES5.parseStatement(src, i + n);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a while body."); if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a while body.");
n += res.n; n += res.n;

View File

@ -1,48 +0,0 @@
package me.topchetoeu.jscript.compilation.parsing;
import java.util.Arrays;
import java.util.LinkedHashSet;
import me.topchetoeu.jscript.common.Operation;
public enum Operator {
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 assignable;
public static final LinkedHashSet<Operator> opsByLength = new LinkedHashSet<Operator>();
static {
var vals = Operator.values();
Arrays.sort(vals, (a, b) -> b.readable.length() - a.readable.length());
for (var el : vals) opsByLength.add(el);
}
private Operator(String value, Operation funcName, int precedence, boolean assignable) {
this.readable = value;
this.operation = funcName;
this.precedence = precedence;
this.assignable = assignable;
}
}

View File

@ -1,539 +0,0 @@
package me.topchetoeu.jscript.compilation.parsing;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.compilation.*;
import me.topchetoeu.jscript.compilation.control.*;
import me.topchetoeu.jscript.compilation.scope.LocalScopeRecord;
import me.topchetoeu.jscript.compilation.values.*;
import me.topchetoeu.jscript.compilation.values.constants.BoolStatement;
import me.topchetoeu.jscript.compilation.values.constants.NullStatement;
import me.topchetoeu.jscript.compilation.values.constants.NumberStatement;
import me.topchetoeu.jscript.compilation.values.constants.StringStatement;
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
// TODO: this has to be rewritten
// @SourceFile
public class Parsing {
public static final HashMap<Long, ArrayList<Instruction>> functions = new HashMap<>();
private static final Set<String> reserved = Set.of(
"true", "false", "void", "null", "this", "if", "else", "try", "catch",
"finally", "for", "do", "while", "switch", "case", "default", "new",
"function", "var", "return", "throw", "typeof", "delete", "break",
"continue", "debugger", "implements", "interface", "package", "private",
"protected", "public", "static",
// Although ES5 allow these, we will comply to ES6 here
"const", "let",
// These are allowed too, however our parser considers them keywords
// We allow yield and await, because they're part of the custom async and generator functions
"undefined", "arguments", "globalThis", "window", "self"
);
public static boolean isDigit(Character c) {
return c != null && c >= '0' && c <= '9';
}
public static boolean isAny(char c, String alphabet) {
return alphabet.contains(Character.toString(c));
}
public static int fromHex(char c) {
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
if (c >= '0' && c <= '9') return c - '0';
return -1;
}
public static int skipEmpty(Source src, int i) {
int n = 0;
while (n < src.size() && src.is(i + n, Character::isWhitespace)) n++;
return n;
}
public static ParseRes<Character> parseChar(Source src, int i) {
int n = 0;
if (src.is(i + n, '\\')) {
n++;
char c = src.at(i + n++);
if (c == 'b') return ParseRes.res('\b', n);
else if (c == 't') return ParseRes.res('\t', n);
else if (c == 'n') return ParseRes.res('\n', n);
else if (c == 'f') return ParseRes.res('\f', n);
else if (c == 'r') return ParseRes.res('\r', n);
else if (c == '0') {
if (src.is(i + n, Parsing::isDigit)) return ParseRes.error(src.loc(i), "Octal escape sequences are not allowed");
else return ParseRes.res('\0', n);
}
else if (c >= '1' && c <= '9') return ParseRes.error(src.loc(i), "Octal escape sequences are not allowed");
else if (c == 'x') {
var newC = 0;
for (var j = 0; j < 2; j++) {
if (i + n >= src.size()) return ParseRes.error(src.loc(i), "Invalid hexadecimal escape sequence.");
int val = fromHex(src.at(i + n));
if (val == -1) throw new SyntaxException(src.loc(i + n), "Invalid hexadecimal escape sequence.");
n++;
newC = (newC << 4) | val;
}
return ParseRes.res((char)newC, n);
}
else if (c == 'u') {
var newC = 0;
for (var j = 0; j < 4; j++) {
if (i + n >= src.size()) return ParseRes.error(src.loc(i), "Invalid Unicode escape sequence");
int val = fromHex(src.at(i + n));
if (val == -1) throw new SyntaxException(src.loc(i + n), "Invalid Unicode escape sequence");
n++;
newC = (newC << 4) | val;
}
return ParseRes.res((char)newC, n);
}
else if (c == '\n') return ParseRes.res(null, n);
}
return ParseRes.res(src.at(i + n), n + 1);
}
public static ParseRes<String> parseIdentifier(Source src, int i) {
var n = skipEmpty(src, i);
var res = new StringBuilder();
var first = false;
while (true) {
if (i + n > src.size()) break;
char c = src.at(i + n, '\0');
if (first && !Character.isLetterOrDigit(c) && c != '_' && c != '$') break;
if (!first && !Character.isLetter(c) && c != '_' && c != '$') break;
res.append(c);
n++;
}
if (res.length() <= 0) return ParseRes.failed();
else return ParseRes.res(res.toString(), n);
}
public static ParseRes<String> parseIdentifier(Source src, int i, String test) {
var n = skipEmpty(src, i);
var res = new StringBuilder();
var first = true;
while (true) {
if (i + n > src.size()) break;
char c = src.at(i + n, '\0');
if (first && !Character.isLetterOrDigit(c) && c != '_' && c != '$') break;
if (!first && !Character.isLetter(c) && c != '_' && c != '$') break;
first = false;
res.append(c);
n++;
}
if (res.length() <= 0) return ParseRes.failed();
else if (test == null || res.toString().equals(test)) return ParseRes.res(res.toString(), n);
else return ParseRes.failed();
}
public static boolean isIdentifier(Source src, int i, String test) {
return parseIdentifier(src, i, test).isSuccess();
}
public static ParseRes<String> parseOperator(Source src, int i, String op) {
var n = skipEmpty(src, i);
if (src.is(i + n, op)) return ParseRes.res(op, n + op.length());
else return ParseRes.failed();
}
public static ParseRes<Boolean> parseStatementEnd(Source src, int i) {
var n = skipEmpty(src, i);
if (i >= src.size()) return ParseRes.res(true, n + 1);
for (var j = i; j < i + n; j++) {
if (src.is(j, '\n')) return ParseRes.res(true, n);
}
if (src.is(i + n, ';')) return ParseRes.res(true, n + 1);
if (src.is(i + n, '}')) return ParseRes.res(true, n);
return ParseRes.failed();
}
public static boolean checkVarName(String name) {
return !reserved.contains(name);
}
public static ParseRes<List<String>> parseParamList(Source src, int i) {
var n = skipEmpty(src, i);
var openParen = parseOperator(src, i + n, "(");
if (!openParen.isSuccess()) return openParen.chainError(src.loc(i + n), "Expected a parameter list.");
n += openParen.n;
var args = new ArrayList<String>();
var closeParen = parseOperator(src, i + n, ")");
n += closeParen.n;
if (!closeParen.isSuccess()) {
while (true) {
var argRes = parseIdentifier(src, i + n);
if (argRes.isSuccess()) {
args.add(argRes.result);
n++;
n += skipEmpty(src, i);
if (src.is(i + n, ",")) {
n++;
n += skipEmpty(src, i + n);
}
if (src.is(i + n, ")")) {
n++;
break;
}
}
else return ParseRes.error(src.loc(i + n), "Expected an argument, or a closing brace.");
}
}
return ParseRes.res(args, n);
}
public static ParseRes<? extends Statement> parseParens(Source src, int i) {
int n = 0;
var openParen = parseOperator(src, i + n, "(");
if (!openParen.isSuccess()) return openParen.chainError();
n += openParen.n;
var res = parseValue(src, i + n, 0);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected an expression in parens");
n += res.n;
var closeParen = parseOperator(src, i + n, ")");
if (!closeParen.isSuccess()) return closeParen.chainError(src.loc(i + n), "Expected a closing paren");
n += closeParen.n;
return ParseRes.res(res.result, n);
}
public static ParseRes<? extends Statement> parseSimple(Source src, int i, boolean statement) {
return ParseRes.first(src, i,
(a, b) -> statement ? ParseRes.failed() : ObjectStatement.parse(a, b),
(a, b) -> statement ? ParseRes.failed() : FunctionStatement.parseFunction(a, b, false),
VariableStatement::parse,
Parsing::parseLiteral,
StringStatement::parse,
RegexStatement::parse,
NumberStatement::parse,
ChangeStatement::parsePrefixDecrease,
ChangeStatement::parsePrefixIncrease,
OperationStatement::parsePrefix,
ArrayStatement::parse,
Parsing::parseParens,
CallStatement::parseNew,
TypeofStatement::parse,
DiscardStatement::parse,
DeleteStatement::parse
);
}
public static ParseRes<? extends Statement> parseLiteral(Source src, int i) {
var n = skipEmpty(src, i);
var loc = src.loc(i + n);
var id = parseIdentifier(src, i);
if (!id.isSuccess()) return id.chainError();
n += id.n;
if (id.result.equals("true")) return ParseRes.res(new BoolStatement(loc, true), n);
if (id.result.equals("false")) return ParseRes.res(new BoolStatement(loc, false), n);
if (id.result.equals("undefined")) return ParseRes.res(new DiscardStatement(loc, null), n);
if (id.result.equals("null")) return ParseRes.res(new NullStatement(loc), n);
if (id.result.equals("this")) return ParseRes.res(new VariableIndexStatement(loc, 0), n);
if (id.result.equals("arguments")) return ParseRes.res(new VariableIndexStatement(loc, 1), n);
if (id.result.equals("globalThis")) return ParseRes.res(new GlobalThisStatement(loc), n);
return ParseRes.failed();
}
public static ParseRes<? extends Statement> parseValue(Source src, int i, int precedence, boolean statement) {
var n = skipEmpty(src, i);
Statement prev = null;
while (true) {
if (prev == null) {
var res = parseSimple(src, i + n, statement);
if (res.isSuccess()) {
n += res.n;
prev = res.result;
}
else if (res.isError()) return res.chainError();
else break;
}
else {
var _prev = prev;
ParseRes<Statement> res = ParseRes.first(src, i + n,
(s, j) -> OperationStatement.parseInstanceof(s, j, _prev, precedence),
(s, j) -> OperationStatement.parseIn(s, j, _prev, precedence),
(s, j) -> LazyOrStatement.parse(s, j, _prev, precedence),
(s, j) -> LazyAndStatement.parse(s, j, _prev, precedence),
(s, j) -> ChangeStatement.parsePostfixIncrease(s, j, _prev, precedence),
(s, j) -> ChangeStatement.parsePostfixDecrease(s, j, _prev, precedence),
(s, j) -> AssignableStatement.parse(s, j, _prev, precedence),
(s, j) -> OperationStatement.parseOperator(s, j, _prev, precedence),
(s, j) -> IfStatement.parseTernary(s, j, _prev, precedence),
(s, j) -> IndexStatement.parseMember(s, j, _prev, precedence),
(s, j) -> IndexStatement.parseIndex(s, j, _prev, precedence),
(s, j) -> CallStatement.parseCall(s, j, _prev, precedence),
(s, j) -> CompoundStatement.parseComma(s, j, _prev, precedence)
);
if (res.isSuccess()) {
n += res.n;
prev = res.result;
continue;
}
else if (res.isError()) return res.chainError();
break;
}
}
if (prev == null) return ParseRes.failed();
else return ParseRes.res(prev, n);
}
public static ParseRes<? extends Statement> parseValue(Source src, int i, int precedence) {
return parseValue(src, i, precedence, false);
}
public static ParseRes<? extends Statement> parseValueStatement(Source src, int i) {
var res = parseValue(src, i, 0, true);
if (!res.isSuccess()) return res.chainError();
var end = parseStatementEnd(src, i + res.n);
if (!end.isSuccess()) return ParseRes.error(src.loc(i + res.n), "Expected an end of statement");
return res.addN(end.n);
}
public static ParseRes<? extends Statement> parseStatement(Source src, int i) {
var n = skipEmpty(src, i);
if (src.is(i + n, ";")) return ParseRes.res(new DiscardStatement(src.loc(i+ n), null), n + 1);
if (isIdentifier(src, i + n, "with")) return ParseRes.error(src.loc(i + n), "'with' statements are not allowed.");
ParseRes<? extends Statement> res = ParseRes.first(src, i + n,
VariableDeclareStatement::parse,
ReturnStatement::parse,
ThrowStatement::parse,
ContinueStatement::parse,
BreakStatement::parse,
DebugStatement::parse,
IfStatement::parse,
WhileStatement::parse,
SwitchStatement::parse,
ForStatement::parse,
ForInStatement::parse,
ForOfStatement::parse,
DoWhileStatement::parse,
TryStatement::parse,
CompoundStatement::parse,
(s, j) -> FunctionStatement.parseFunction(s, j, true),
Parsing::parseValueStatement
);
return res.addN(n);
}
public static Statement[] parse(Filename filename, String raw) {
var src = new Source(filename, raw);
var list = new ArrayList<Statement>();
int i = 0;
while (true) {
if (i >= src.size()) break;
var res = Parsing.parseStatement(src, i);
if (res.isError()) throw new SyntaxException(src.loc(i), res.error);
else if (res.isFailed()) throw new SyntaxException(src.loc(i), "Unexpected syntax");
i += res.n;
list.add(res.result);
}
return list.toArray(Statement[]::new);
}
public static CompileResult compile(Statement ...statements) {
var target = new CompileResult(new LocalScopeRecord());
var stm = new CompoundStatement(null, true, statements);
target.scope.define("this");
target.scope.define("arguments");
try {
stm.compile(target, true);
FunctionStatement.checkBreakAndCont(target, 0);
}
catch (SyntaxException e) {
target = new CompileResult(new LocalScopeRecord());
target.scope.define("this");
target.scope.define("arguments");
target.add(Instruction.throwSyntax(e)).setLocation(stm.loc());
}
target.add(Instruction.ret()).setLocation(stm.loc());
return target;
}
public static CompileResult compile(Filename filename, String raw) {
return compile(parse(filename, raw));
}
private static ParseRes<Double> parseHex(Source src, int i) {
int n = 0;
double res = 0;
while (true) {
int digit = Parsing.fromHex(src.at(i + n, '\0'));
if (digit < 0) {
if (n <= 0) return ParseRes.failed();
else return ParseRes.res(res, n);
}
n++;
res *= 16;
res += digit;
}
}
private static ParseRes<Double> parseOct(Source src, int i) {
int n = 0;
double res = 0;
while (true) {
int digit = src.at(i + n, '\0') - '0';
if (digit < 0 || digit > 9) break;
if (digit > 7) return ParseRes.error(src.loc(i + n), "Digits in octal literals must be from 0 to 7, encountered " + digit);
if (digit < 0) {
if (n <= 0) return ParseRes.failed();
else return ParseRes.res(res, n);
}
n++;
res *= 8;
res += digit;
}
return ParseRes.res(res, n);
}
public static ParseRes<String> parseString(Source src, int i) {
var n = skipEmpty(src, i);
char quote;
if (src.is(i + n, '\'')) quote = '\'';
else if (src.is(i + n, '"')) quote = '"';
else return ParseRes.failed();
n++;
var res = new StringBuilder();
while (true) {
if (i + n >= src.size()) return ParseRes.error(src.loc(i + n), "Unterminated string literal");
if (src.is(i + n, quote)) {
n++;
return ParseRes.res(res.toString(), n);
}
var charRes = parseChar(src, i + n);
if (!charRes.isSuccess()) return charRes.chainError(src.loc(i + n), "Invalid character");
n += charRes.n;
if (charRes.result != null) res.append(charRes.result);
}
}
public static ParseRes<Double> parseNumber(Source src, int i) {
var n = skipEmpty(src, i);
double whole = 0;
double fract = 0;
long exponent = 0;
boolean parsedAny = false;
if (src.is(i + n, "0x") || src.is(i + n, "0X")) {
n += 2;
var res = parseHex(src, i);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Incomplete hexadecimal literal");
else return res.addN(2);
}
else if (src.is(i + n, "0o") || src.is(i + n, "0O")) {
n += 2;
var res = parseOct(src, i);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Incomplete octal literal");
else return res.addN(2);
}
else if (src.is(i + n, '0')) {
n++;
parsedAny = true;
if (src.is(i + n, Parsing::isDigit)) return ParseRes.error(src.loc(i + n), "Decimals with leading zeroes are not allowed");
}
while (src.is(i + n, Parsing::isDigit)) {
parsedAny = true;
whole *= 10;
whole += src.at(i + n++) - '0';
}
if (src.is(i + n, '.')) {
parsedAny = true;
n++;
while (src.is(i + n, Parsing::isDigit)) {
fract += src.at(i + n++) - '0';
fract /= 10;
}
}
if (src.is(i + n, 'e') || src.is(i + n, 'E')) {
n++;
parsedAny = true;
boolean negative = src.is(i + n, '-');
boolean parsedE = false;
if (negative) n++;
while (src.is(i + n, Parsing::isDigit)) {
parsedE = true;
exponent *= 10;
if (negative) exponent -= src.at(i + n++) - '0';
else exponent += src.at(i + n++) - '0';
}
if (!parsedE) return ParseRes.error(src.loc(i + n), "Incomplete number exponent");
}
if (!parsedAny) return ParseRes.failed();
else return ParseRes.res((whole + fract) * NumberStatement.power(10, exponent), n);
}
}

View File

@ -1,15 +0,0 @@
package me.topchetoeu.jscript.compilation.parsing;
public class RawToken {
public final String value;
public final TokenType type;
public final int line;
public final int start;
public RawToken(String value, TokenType type, int line, int start) {
this.value = value;
this.type = type;
this.line = line;
this.start = start;
}
}

View File

@ -1,45 +0,0 @@
package me.topchetoeu.jscript.compilation.parsing;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.compilation.parsing.ParseRes.State;
public class TestRes {
public final State state;
public final String error;
public final int i;
private TestRes(ParseRes.State state, String error, int i) {
this.i = i;
this.state = state;
this.error = error;
}
public TestRes add(int n) {
return new TestRes(state, null, this.i + n);
}
public <T> ParseRes<T> transform() {
if (isSuccess()) throw new RuntimeException("Can't transform a TestRes that hasn't failed.");
else if (isError()) return ParseRes.error(null, error);
else return ParseRes.failed();
}
public boolean isSuccess() { return state.isSuccess(); }
public boolean isFailed() { return state.isFailed(); }
public boolean isError() { return state.isError(); }
public static TestRes failed() {
return new TestRes(State.FAILED, null, 0);
}
public static TestRes error(Location loc, String error) {
if (loc != null) error = loc + ": " + error;
return new TestRes(State.ERROR, error, 0);
}
public static TestRes error(Location loc, String error, TestRes other) {
if (loc != null) error = loc + ": " + error;
if (!other.isError()) return new TestRes(State.ERROR, error, 0);
return new TestRes(State.ERROR, other.error, 0);
}
public static TestRes res(int i) {
return new TestRes(State.SUCCESS, null, i);
}
}

View File

@ -1,58 +0,0 @@
package me.topchetoeu.jscript.compilation.parsing;
public class Token {
public final Object value;
public final String rawValue;
public final boolean isString;
public final boolean isRegex;
public final int line;
public final int start;
private Token(int line, int start, Object value, String rawValue, boolean isString, boolean isRegex) {
this.value = value;
this.rawValue = rawValue;
this.line = line;
this.start = start;
this.isString = isString;
this.isRegex = isRegex;
}
private Token(int line, int start, Object value, String rawValue) {
this.value = value;
this.rawValue = rawValue;
this.line = line;
this.start = start;
this.isString = false;
this.isRegex = false;
}
public boolean isString() { return isString; }
public boolean isRegex() { return isRegex; }
public boolean isNumber() { return value instanceof Number; }
public boolean isIdentifier() { return !isString && !isRegex && value instanceof String; }
public boolean isOperator() { return value instanceof Operator; }
public boolean isIdentifier(String lit) { return !isString && !isRegex && value.equals(lit); }
public boolean isOperator(Operator op) { return value.equals(op); }
public String string() { return (String)value; }
public String regex() { return (String)value; }
public double number() { return (double)value; }
public String identifier() { return (String)value; }
public Operator operator() { return (Operator)value; }
public static Token regex(int line, int start, String val, String rawValue) {
return new Token(line, start, val, rawValue, false, true);
}
public static Token string(int line, int start, String val, String rawValue) {
return new Token(line, start, val, rawValue, true, false);
}
public static Token number(int line, int start, double val, String rawValue) {
return new Token(line, start, val, rawValue);
}
public static Token identifier(int line, int start, String val) {
return new Token(line, start, val, val);
}
public static Token operator(int line, int start, Operator val) {
return new Token(line, start, val, val.readable);
}
}

View File

@ -1,9 +0,0 @@
package me.topchetoeu.jscript.compilation.parsing;
enum TokenType {
REGEX,
STRING,
NUMBER,
LITERAL,
OPERATOR,
}

View File

@ -3,12 +3,13 @@ package me.topchetoeu.jscript.compilation.values;
import java.util.ArrayList; import java.util.ArrayList;
import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.common.parsing.ParseRes;
import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.ES5;
import me.topchetoeu.jscript.compilation.Statement; 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 ArrayStatement extends Statement { public class ArrayStatement extends Statement {
public final Statement[] statements; public final Statement[] statements;
@ -21,8 +22,7 @@ public class ArrayStatement extends Statement {
return true; return true;
} }
@Override @Override public void compile(CompileResult target, boolean pollute) {
public void compile(CompileResult target, boolean pollute) {
target.add(Instruction.loadArr(statements.length)); target.add(Instruction.loadArr(statements.length));
for (var i = 0; i < statements.length; i++) { for (var i = 0; i < statements.length; i++) {
@ -70,7 +70,7 @@ public class ArrayStatement extends Statement {
} }
} }
var res = Parsing.parseValue(src, i + n, 2); var res = ES5.parseExpression(src, i + n, 2);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected an array element."); if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected an array element.");
n += res.n; n += res.n;
n += Parsing.skipEmpty(src, i + n); n += Parsing.skipEmpty(src, i + n);

View File

@ -1,101 +0,0 @@
package me.topchetoeu.jscript.compilation.values;
import java.util.ArrayList;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
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.ParseRes;
import me.topchetoeu.jscript.compilation.parsing.Parsing;
import me.topchetoeu.jscript.compilation.parsing.Source;
public class CallStatement extends Statement {
public final Statement func;
public final Statement[] args;
public final boolean isNew;
@Override public void compile(CompileResult target, boolean pollute, BreakpointType type) {
if (isNew) func.compile(target, true);
else if (func instanceof IndexStatement) {
((IndexStatement)func).compile(target, true, true);
}
else {
target.add(Instruction.pushUndefined());
func.compile(target, true);
}
for (var arg : args) arg.compile(target, true);
if (isNew) target.add(Instruction.callNew(args.length)).setLocationAndDebug(loc(), type);
else target.add(Instruction.call(args.length)).setLocationAndDebug(loc(), type);
if (!pollute) target.add(Instruction.discard());
}
@Override public void compile(CompileResult target, boolean pollute) {
compile(target, pollute, BreakpointType.STEP_IN);
}
public CallStatement(Location loc, boolean isNew, Statement func, Statement ...args) {
super(loc);
this.isNew = isNew;
this.func = func;
this.args = args;
}
public static ParseRes<CallStatement> parseCall(Source src, int i, Statement prev, int precedence) {
if (precedence > 17) 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(src, i + n, 2);
n += argRes.n;
n += Parsing.skipEmpty(src, i + n);
if (argRes.isSuccess()) {
args.add(argRes.result);
prevArg = true;
}
else if (argRes.isError()) return argRes.chainError();
else if (prevArg && src.is(i + n, ",")) {
prevArg = false;
n++;
}
else if (src.is(i + n, ")")) {
n++;
break;
}
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(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
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;
var callRes = CallStatement.parseCall(src, i + n, valRes.result, 0);
if (callRes.isFailed()) return ParseRes.res(new CallStatement(loc, true, valRes.result), n);
if (callRes.isError()) return callRes.chainError();
n += callRes.n;
return ParseRes.res(new CallStatement(loc, true, callRes.result.func, callRes.result.args), n);
}
}

View File

@ -1,15 +1,16 @@
package me.topchetoeu.jscript.compilation.values; package me.topchetoeu.jscript.compilation.values;
import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.common.Instruction.Type; import me.topchetoeu.jscript.common.Instruction.Type;
import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.common.parsing.ParseRes;
import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.CompoundStatement; import me.topchetoeu.jscript.compilation.CompoundStatement;
import me.topchetoeu.jscript.compilation.ES5;
import me.topchetoeu.jscript.compilation.Statement; 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;
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
public class FunctionStatement extends Statement { public class FunctionStatement extends Statement {
@ -39,7 +40,7 @@ public class FunctionStatement extends Statement {
} }
} }
private CompileResult compileBody(CompileResult target, boolean pollute, BreakpointType bp) { private CompileResult compileBody(CompileResult target, String name, boolean pollute, BreakpointType bp) {
for (var i = 0; i < args.length; i++) { for (var i = 0; i < args.length; i++) {
for (var j = 0; j < i; j++) { for (var j = 0; j < i; j++) {
if (args[i].equals(args[j])) { if (args[i].equals(args[j])) {
@ -72,7 +73,7 @@ public class FunctionStatement extends Statement {
subtarget.add(Instruction.ret()).setLocation(end); subtarget.add(Instruction.ret()).setLocation(end);
checkBreakAndCont(subtarget, 0); checkBreakAndCont(subtarget, 0);
if (pollute) target.add(Instruction.loadFunc(target.children.size(), subtarget.scope.getCaptures())); if (pollute) target.add(Instruction.loadFunc(target.children.size(), name, subtarget.scope.getCaptures()));
return target.addChild(subtarget); return target.addChild(subtarget);
} }
@ -80,16 +81,8 @@ public class FunctionStatement extends Statement {
if (this.varName != null) name = this.varName; if (this.varName != null) name = this.varName;
var hasVar = this.varName != null && statement; var hasVar = this.varName != null && statement;
var hasName = name != null;
compileBody(target, pollute || hasVar || hasName, bp); compileBody(target, name, pollute || hasVar, bp);
if (hasName) {
if (pollute || hasVar) target.add(Instruction.dup());
target.add(Instruction.pushValue("name"));
target.add(Instruction.pushValue(name));
target.add(Instruction.storeMember());
}
if (hasVar) { if (hasVar) {
var key = target.scope.getKey(this.varName); var key = target.scope.getKey(this.varName);
@ -140,12 +133,12 @@ public class FunctionStatement extends Statement {
n += nameRes.n; n += nameRes.n;
n += Parsing.skipEmpty(src, i + n); n += Parsing.skipEmpty(src, i + n);
var args = Parsing.parseParamList(src, i + n); var args = ES5.parseParamList(src, i + n);
if (!args.isSuccess()) return args.chainError(src.loc(i + n), "Expected a parameter list"); if (!args.isSuccess()) return args.chainError(src.loc(i + n), "Expected a parameter list");
n += args.n; n += args.n;
var res = CompoundStatement.parse(src, i + n); var res = CompoundStatement.parse(src, i + n);
if (!res.isSuccess()) res.chainError(src.loc(i + n), "Expected a compound statement for function."); if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a compound statement for function.");
n += res.n; n += res.n;
return ParseRes.res(new FunctionStatement( return ParseRes.res(new FunctionStatement(

View File

@ -1,7 +1,7 @@
package me.topchetoeu.jscript.compilation.values; package me.topchetoeu.jscript.compilation.values;
import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;

View File

@ -5,13 +5,14 @@ import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.common.parsing.ParseRes;
import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.CompoundStatement; import me.topchetoeu.jscript.compilation.CompoundStatement;
import me.topchetoeu.jscript.compilation.ES5;
import me.topchetoeu.jscript.compilation.Statement; 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 ObjectStatement extends Statement { public class ObjectStatement extends Statement {
public static class ObjProp { public static class ObjProp {
@ -82,7 +83,7 @@ public class ObjectStatement extends Statement {
var res = ParseRes.first(src, i + n, var res = ParseRes.first(src, i + n,
Parsing::parseIdentifier, Parsing::parseIdentifier,
Parsing::parseString, Parsing::parseString,
Parsing::parseNumber (s, j) -> Parsing.parseNumber(s, j, false)
); );
n += res.n; n += res.n;
@ -102,7 +103,7 @@ public class ObjectStatement extends Statement {
if (!name.isSuccess()) return name.chainError(src.loc(i + n), "Expected a property name after '" + access + "'"); if (!name.isSuccess()) return name.chainError(src.loc(i + n), "Expected a property name after '" + access + "'");
n += name.n; n += name.n;
var params = Parsing.parseParamList(src, i + n); var params = ES5.parseParamList(src, i + n);
if (!params.isSuccess()) return params.chainError(src.loc(i + n), "Expected an argument list"); if (!params.isSuccess()) return params.chainError(src.loc(i + n), "Expected an argument list");
n += params.n; n += params.n;
@ -153,7 +154,7 @@ public class ObjectStatement extends Statement {
if (!src.is(i + n, ":")) return ParseRes.error(src.loc(i + n), "Expected a colon"); if (!src.is(i + n, ":")) return ParseRes.error(src.loc(i + n), "Expected a colon");
n++; n++;
var valRes = Parsing.parseValue(src, i + n, 2); var valRes = ES5.parseExpression(src, i + n, 2);
if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value in array list"); if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value in array list");
n += valRes.n; n += valRes.n;

View File

@ -1,110 +0,0 @@
package me.topchetoeu.jscript.compilation.values;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Operation;
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.Source;
public class OperationStatement extends Statement {
public final Statement[] args;
public final Operation operation;
@Override public boolean pure() {
for (var el : args) {
if (!el.pure()) return false;
}
return true;
}
@Override public void compile(CompileResult target, boolean pollute) {
for (var arg : args) {
arg.compile(target, true);
}
if (pollute) target.add(Instruction.operation(operation));
else target.add(Instruction.discard());
}
public OperationStatement(Location loc, Operation operation, Statement ...args) {
super(loc);
this.operation = operation;
this.args = args;
}
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 (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();
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 res.chainError(src.loc(i + n), String.format("Expected a value after the unary operator '%s'.", op));
}
public static ParseRes<OperationStatement> parseInstanceof(Source src, int i, Statement prev, int precedence) {
if (precedence > 9) return ParseRes.failed();
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(Source src, int i, Statement prev, int precedence) {
if (precedence > 9) return ParseRes.failed();
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, valRes.result, prev), n);
}
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);
for (var op : Operator.opsByLength) {
if (!src.is(i + n, op.readable)) continue;
if (op.precedence < precedence) return ParseRes.failed();
n += op.readable.length();
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;
return ParseRes.res(new OperationStatement(loc, op.operation, prev, res.result), n);
}
return ParseRes.failed();
}
}

View File

@ -1,12 +1,12 @@
package me.topchetoeu.jscript.compilation.values; package me.topchetoeu.jscript.compilation.values;
import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.common.parsing.ParseRes;
import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement; 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 RegexStatement extends Statement { public class RegexStatement extends Statement {
public final String pattern, flags; public final String pattern, flags;

View File

@ -1,14 +1,16 @@
package me.topchetoeu.jscript.compilation.values; package me.topchetoeu.jscript.compilation.values;
import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Operation; import me.topchetoeu.jscript.common.Operation;
import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.common.parsing.ParseRes;
import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.compilation.AssignableStatement; import me.topchetoeu.jscript.compilation.AssignableStatement;
import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.ES5;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.parsing.ParseRes; import me.topchetoeu.jscript.compilation.values.operations.VariableAssignStatement;
import me.topchetoeu.jscript.compilation.parsing.Parsing;
import me.topchetoeu.jscript.compilation.parsing.Source;
public class VariableStatement extends AssignableStatement { public class VariableStatement extends AssignableStatement {
public final String name; public final String name;
@ -40,7 +42,7 @@ public class VariableStatement extends AssignableStatement {
if (!literal.isSuccess()) return literal.chainError(); if (!literal.isSuccess()) return literal.chainError();
n += literal.n; n += literal.n;
if (!Parsing.checkVarName(literal.result)) { if (!ES5.checkVarName(literal.result)) {
if (literal.result.equals("await")) return ParseRes.error(src.loc(i + n), "'await' expressions are not supported."); 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("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."); if (literal.result.equals("let")) return ParseRes.error(src.loc(i + n), "'let' declarations are not supported.");

View File

@ -1,7 +1,7 @@
package me.topchetoeu.jscript.compilation.values.constants; package me.topchetoeu.jscript.compilation.values.constants;
import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;

View File

@ -1,7 +1,7 @@
package me.topchetoeu.jscript.compilation.values.constants; package me.topchetoeu.jscript.compilation.values.constants;
import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;

View File

@ -1,12 +1,12 @@
package me.topchetoeu.jscript.compilation.values.constants; package me.topchetoeu.jscript.compilation.values.constants;
import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.common.parsing.ParseRes;
import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement; 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 class NumberStatement extends Statement {
public final double value; public final double value;
@ -35,7 +35,7 @@ public class NumberStatement extends Statement {
var n = Parsing.skipEmpty(src, i); var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n); var loc = src.loc(i + n);
var res = Parsing.parseNumber(src, i + n); var res = Parsing.parseNumber(src, i + n, false);
if (res.isSuccess()) return ParseRes.res(new NumberStatement(loc, res.result), n + res.n); if (res.isSuccess()) return ParseRes.res(new NumberStatement(loc, res.result), n + res.n);
else return res.chainError(); else return res.chainError();
} }

View File

@ -1,12 +1,12 @@
package me.topchetoeu.jscript.compilation.values.constants; package me.topchetoeu.jscript.compilation.values.constants;
import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.common.parsing.ParseRes;
import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement; 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 class StringStatement extends Statement {
public final String value; public final String value;

View File

@ -0,0 +1,180 @@
package me.topchetoeu.jscript.compilation.values.operations;
import java.util.ArrayList;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.common.json.JSON;
import me.topchetoeu.jscript.common.json.JSONElement;
import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.common.parsing.ParseRes;
import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.ES5;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.values.ArrayStatement;
import me.topchetoeu.jscript.compilation.values.ObjectStatement;
import me.topchetoeu.jscript.compilation.values.VariableStatement;
import me.topchetoeu.jscript.compilation.values.constants.BoolStatement;
import me.topchetoeu.jscript.compilation.values.constants.NumberStatement;
import me.topchetoeu.jscript.compilation.values.constants.StringStatement;
public class CallStatement extends Statement {
public static boolean ATTACH_NAME = true;
public final Statement func;
public final Statement[] args;
public final boolean isNew;
private String generateName(Statement func, Statement index) {
String res = "(intermediate value)";
boolean shouldParen = false;
if (func instanceof ObjectStatement) {
var obj = (ObjectStatement)func;
shouldParen = true;
if (obj.getters.size() > 0 || obj.setters.size() > 0 || obj.map.size() > 0) res = "{}";
else res = "{(intermediate value)}";
}
else if (func instanceof StringStatement) {
res = JSON.stringify(JSONElement.string(((StringStatement)func).value));
}
else if (func instanceof NumberStatement) {
res = JSON.stringify(JSONElement.number(((NumberStatement)func).value));
}
else if (func instanceof BoolStatement) {
res = ((BoolStatement)func).value ? "true" : "false";
}
else if (func instanceof VariableStatement) {
res = ((VariableStatement)func).name;
}
else if (func instanceof VariableIndexStatement) {
var i = ((VariableIndexStatement)func).index;
if (i == 0) res = "this";
else if (i == 1) res = "arguments";
}
else if (func instanceof ArrayStatement) {
var els = new ArrayList<String>();
for (var el : ((ArrayStatement)func).statements) {
if (el != null) els.add(generateName(el, null));
else els.add("(intermediate value)");
}
res = "[" + String.join(",", els) + "]";
}
if (index == null) return res;
if (shouldParen) res = "(" + res + ")";
if (index instanceof StringStatement) {
var val = ((StringStatement)index).value;
var bracket = JSON.stringify(JSONElement.string(val));
if (!bracket.substring(1, bracket.length() - 1).equals(val)) return res + "[" + bracket + "]";
if (Parsing.parseIdentifier(new Source(null, val), 0).n != val.length()) return res + "[" + bracket + "]";
return res + "." + val;
}
return res + "[" + generateName(index, null) + "]";
}
@Override public void compile(CompileResult target, boolean pollute, BreakpointType type) {
if (!isNew && func instanceof IndexStatement) {
var obj = ((IndexStatement)func).object;
var index = ((IndexStatement)func).index;
String name = "";
obj.compile(target, true);
index.compile(target, true);
for (var arg : args) arg.compile(target, true);
if (ATTACH_NAME) name = generateName(obj, index);
target.add(Instruction.callMember(args.length, name)).setLocationAndDebug(loc(), type);
}
else {
String name = "";
func.compile(target, true);
for (var arg : args) arg.compile(target, true);
if (ATTACH_NAME) name = generateName(func, null);
if (isNew) target.add(Instruction.callNew(args.length, name)).setLocationAndDebug(loc(), type);
else target.add(Instruction.call(args.length, name)).setLocationAndDebug(loc(), type);
}
if (!pollute) target.add(Instruction.discard());
}
@Override public void compile(CompileResult target, boolean pollute) {
compile(target, pollute, BreakpointType.STEP_IN);
}
public CallStatement(Location loc, boolean isNew, Statement func, Statement ...args) {
super(loc);
this.isNew = isNew;
this.func = func;
this.args = args;
}
public static ParseRes<CallStatement> parseCall(Source src, int i, Statement prev, int precedence) {
if (precedence > 17) 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 = ES5.parseExpression(src, i + n, 2);
n += argRes.n;
n += Parsing.skipEmpty(src, i + n);
if (argRes.isSuccess()) {
args.add(argRes.result);
prevArg = true;
}
else if (argRes.isError()) return argRes.chainError();
else if (prevArg && src.is(i + n, ",")) {
prevArg = false;
n++;
}
else if (src.is(i + n, ")")) {
n++;
break;
}
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(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
if (!Parsing.isIdentifier(src, i + n, "new")) return ParseRes.failed();
n += 3;
var valRes = ES5.parseExpression(src, i + n, 18);
if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'new' keyword.");
n += valRes.n;
var callRes = CallStatement.parseCall(src, i + n, valRes.result, 0);
if (callRes.isFailed()) return ParseRes.res(new CallStatement(loc, true, valRes.result), n);
if (callRes.isError()) return callRes.chainError();
n += callRes.n;
return ParseRes.res(new CallStatement(loc, true, callRes.result.func, callRes.result.args), n);
}
}

View File

@ -1,14 +1,15 @@
package me.topchetoeu.jscript.compilation.values; package me.topchetoeu.jscript.compilation.values.operations;
import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Operation; import me.topchetoeu.jscript.common.Operation;
import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.common.parsing.ParseRes;
import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.compilation.AssignableStatement; import me.topchetoeu.jscript.compilation.AssignableStatement;
import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.ES5;
import me.topchetoeu.jscript.compilation.Statement; 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;
import me.topchetoeu.jscript.compilation.values.constants.NumberStatement; import me.topchetoeu.jscript.compilation.values.constants.NumberStatement;
public class ChangeStatement extends Statement { public class ChangeStatement extends Statement {
@ -39,7 +40,7 @@ public class ChangeStatement extends Statement {
if (!src.is(i + n, "++")) return ParseRes.failed(); if (!src.is(i + n, "++")) return ParseRes.failed();
n += 2; n += 2;
var res = Parsing.parseValue(src, i + n, 15); var res = ES5.parseExpression(src, i + n, 15);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected assignable value after prefix operator."); 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."); else if (!(res.result instanceof AssignableStatement)) return ParseRes.error(src.loc(i + n), "Expected assignable value after prefix operator.");
@ -52,7 +53,7 @@ public class ChangeStatement extends Statement {
if (!src.is(i + n, "--")) return ParseRes.failed(); if (!src.is(i + n, "--")) return ParseRes.failed();
n += 2; n += 2;
var res = Parsing.parseValue(src, i + n, 15); var res = ES5.parseExpression(src, i + n, 15);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected assignable value after prefix operator."); 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."); else if (!(res.result instanceof AssignableStatement)) return ParseRes.error(src.loc(i + n), "Expected assignable value after prefix operator.");

View File

@ -1,12 +1,13 @@
package me.topchetoeu.jscript.compilation.values; package me.topchetoeu.jscript.compilation.values.operations;
import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.common.parsing.ParseRes;
import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.ES5;
import me.topchetoeu.jscript.compilation.Statement; 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 DiscardStatement extends Statement { public class DiscardStatement extends Statement {
public final Statement value; public final Statement value;
@ -30,7 +31,7 @@ public class DiscardStatement extends Statement {
if (!Parsing.isIdentifier(src, i + n, "void")) return ParseRes.failed(); if (!Parsing.isIdentifier(src, i + n, "void")) return ParseRes.failed();
n += 4; n += 4;
var valRes = Parsing.parseValue(src, i + n, 14); var valRes = ES5.parseExpression(src, i + n, 14);
if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'void' keyword."); if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'void' keyword.");
n += valRes.n; n += valRes.n;

View File

@ -1,9 +1,9 @@
package me.topchetoeu.jscript.compilation.values; package me.topchetoeu.jscript.compilation.values.operations;
import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Operation; import me.topchetoeu.jscript.common.Operation;
import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;

View File

@ -1,15 +1,16 @@
package me.topchetoeu.jscript.compilation.values; package me.topchetoeu.jscript.compilation.values.operations;
import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Operation; import me.topchetoeu.jscript.common.Operation;
import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.common.parsing.ParseRes;
import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.compilation.AssignableStatement; import me.topchetoeu.jscript.compilation.AssignableStatement;
import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.ES5;
import me.topchetoeu.jscript.compilation.Statement; 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;
import me.topchetoeu.jscript.compilation.values.constants.StringStatement; import me.topchetoeu.jscript.compilation.values.constants.StringStatement;
public class IndexStatement extends AssignableStatement { public class IndexStatement extends AssignableStatement {
@ -48,7 +49,7 @@ public class IndexStatement extends AssignableStatement {
if (!src.is(i + n, "[")) return ParseRes.failed(); if (!src.is(i + n, "[")) return ParseRes.failed();
n++; n++;
var valRes = Parsing.parseValue(src, i + n, 0); var valRes = ES5.parseExpression(src, i + n, 0);
if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value in index expression"); if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value in index expression");
n += valRes.n; n += valRes.n;
n += Parsing.skipEmpty(src, i + n); n += Parsing.skipEmpty(src, i + n);

View File

@ -1,12 +1,13 @@
package me.topchetoeu.jscript.compilation.values; package me.topchetoeu.jscript.compilation.values.operations;
import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.common.parsing.ParseRes;
import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.ES5;
import me.topchetoeu.jscript.compilation.Statement; 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 class LazyAndStatement extends Statement {
public final Statement first, second; public final Statement first, second;
@ -38,7 +39,7 @@ public class LazyAndStatement extends Statement {
var loc = src.loc(i + n); var loc = src.loc(i + n);
n += 2; n += 2;
var res = Parsing.parseValue(src, i + n, 4); var res = ES5.parseExpression(src, i + n, 4);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a value after the '&&' operator."); if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a value after the '&&' operator.");
n += res.n; n += res.n;

View File

@ -1,12 +1,13 @@
package me.topchetoeu.jscript.compilation.values; package me.topchetoeu.jscript.compilation.values.operations;
import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.common.parsing.ParseRes;
import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.ES5;
import me.topchetoeu.jscript.compilation.Statement; 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 class LazyOrStatement extends Statement {
public final Statement first, second; public final Statement first, second;
@ -38,7 +39,7 @@ public class LazyOrStatement extends Statement {
var loc = src.loc(i + n); var loc = src.loc(i + n);
n += 2; n += 2;
var res = Parsing.parseValue(src, i + n, 4); var res = ES5.parseExpression(src, i + n, 4);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a value after the '||' operator."); if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a value after the '||' operator.");
n += res.n; n += res.n;

View File

@ -0,0 +1,234 @@
package me.topchetoeu.jscript.compilation.values.operations;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Operation;
import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.common.parsing.ParseRes;
import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.compilation.AssignableStatement;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.ES5;
import me.topchetoeu.jscript.compilation.Statement;
public class OperationStatement extends Statement {
private static interface OperatorFactory {
String token();
int precedence();
ParseRes<Statement> construct(Source src, int i, Statement prev);
}
private static class NormalOperatorFactory implements OperatorFactory {
public final String token;
public final int precedence;
public final Operation operation;
@Override public int precedence() { return precedence; }
@Override public String token() { return token; }
@Override public ParseRes<Statement> construct(Source src, int i, Statement prev) {
var loc = src.loc(i);
var other = ES5.parseExpression(src, i, precedence + 1);
if (!other.isSuccess()) return other.chainError(src.loc(i + other.n), String.format("Expected a value after '%s'", token));
return ParseRes.res(new OperationStatement(loc, operation, prev, (Statement)other.result), other.n);
}
public NormalOperatorFactory(String token, int precedence, Operation operation) {
this.token = token;
this.precedence = precedence;
this.operation = operation;
}
}
private static class AssignmentOperatorFactory implements OperatorFactory {
public final String token;
public final int precedence;
public final Operation operation;
@Override public int precedence() { return precedence; }
@Override public String token() { return token; }
@Override public ParseRes<Statement> construct(Source src, int i, Statement prev) {
var loc = src.loc(i);
if (!(prev instanceof AssignableStatement)) return ParseRes.error(loc, String.format("Expected an assignable expression before '%s'", token));
var other = ES5.parseExpression(src, i, precedence);
if (!other.isSuccess()) return other.chainError(src.loc(i + other.n), String.format("Expected a value after '%s'", token));
return ParseRes.res(((AssignableStatement)prev).toAssign(other.result, operation), other.n);
}
public AssignmentOperatorFactory(String token, int precedence, Operation operation) {
this.token = token;
this.precedence = precedence;
this.operation = operation;
}
}
private static class LazyAndFactory implements OperatorFactory {
@Override public int precedence() { return 4; }
@Override public String token() { return "&&"; }
@Override public ParseRes<Statement> construct(Source src, int i, Statement prev) {
var loc = src.loc(i);
var other = ES5.parseExpression(src, i, 5);
if (!other.isSuccess()) return other.chainError(src.loc(i + other.n), "Expected a value after '&&'");
return ParseRes.res(new LazyAndStatement(loc, prev, (Statement)other.result), other.n);
}
}
private static class LazyOrFactory implements OperatorFactory {
@Override public int precedence() { return 5; }
@Override public String token() { return "||"; }
@Override public ParseRes<Statement> construct(Source src, int i, Statement prev) {
var loc = src.loc(i);
var other = ES5.parseExpression(src, i, 6);
if (!other.isSuccess()) return other.chainError(src.loc(i + other.n), "Expected a value after '||'");
return ParseRes.res(new LazyOrStatement(loc, prev, (Statement)other.result), other.n);
}
}
public final Statement[] args;
public final Operation operation;
@Override public boolean pure() {
for (var el : args) {
if (!el.pure()) return false;
}
return true;
}
@Override public void compile(CompileResult target, boolean pollute) {
for (var arg : args) {
arg.compile(target, true);
}
if (pollute) target.add(Instruction.operation(operation));
else target.add(Instruction.discard());
}
public OperationStatement(Location loc, Operation operation, Statement ...args) {
super(loc);
this.operation = operation;
this.args = args;
}
private static final Map<String, OperatorFactory> factories = Set.of(
new NormalOperatorFactory("*", 13, Operation.MULTIPLY),
new NormalOperatorFactory("/", 12, Operation.DIVIDE),
new NormalOperatorFactory("%", 12, Operation.MODULO),
new NormalOperatorFactory("-", 11, Operation.SUBTRACT),
new NormalOperatorFactory("+", 11, Operation.ADD),
new NormalOperatorFactory(">>", 10, Operation.SHIFT_RIGHT),
new NormalOperatorFactory("<<", 10, Operation.SHIFT_LEFT),
new NormalOperatorFactory(">>>", 10, Operation.USHIFT_RIGHT),
new NormalOperatorFactory(">", 9, Operation.GREATER),
new NormalOperatorFactory("<", 9, Operation.LESS),
new NormalOperatorFactory(">=", 9, Operation.GREATER_EQUALS),
new NormalOperatorFactory("<=", 9, Operation.LESS_EQUALS),
new NormalOperatorFactory("!=", 8, Operation.LOOSE_NOT_EQUALS),
new NormalOperatorFactory("!==", 8, Operation.NOT_EQUALS),
new NormalOperatorFactory("==", 8, Operation.LOOSE_EQUALS),
new NormalOperatorFactory("===", 8, Operation.EQUALS),
new NormalOperatorFactory("&", 7, Operation.AND),
new NormalOperatorFactory("^", 6, Operation.XOR),
new NormalOperatorFactory("|", 5, Operation.OR),
new AssignmentOperatorFactory("=", 2, null),
new AssignmentOperatorFactory("*=", 2, Operation.MULTIPLY),
new AssignmentOperatorFactory("/=", 2, Operation.DIVIDE),
new AssignmentOperatorFactory("%=", 2, Operation.MODULO),
new AssignmentOperatorFactory("-=", 2, Operation.SUBTRACT),
new AssignmentOperatorFactory("+=", 2, Operation.ADD),
new AssignmentOperatorFactory(">>=", 2, Operation.SHIFT_RIGHT),
new AssignmentOperatorFactory("<<=", 2, Operation.SHIFT_LEFT),
new AssignmentOperatorFactory(">>>=", 2, Operation.USHIFT_RIGHT),
new AssignmentOperatorFactory("&=", 2, Operation.AND),
new AssignmentOperatorFactory("^=", 2, Operation.XOR),
new AssignmentOperatorFactory("|=", 2, Operation.OR),
new LazyAndFactory(),
new LazyOrFactory()
).stream().collect(Collectors.toMap(v -> v.token(), v -> v));
private static final List<String> operatorsByLength = factories.keySet().stream().sorted((a, b) -> -a.compareTo(b)).collect(Collectors.toList());
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 (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();
n++;
var res = ES5.parseExpression(src, i + n, 14);
if (res.isSuccess()) return ParseRes.res(new OperationStatement(loc, operation, res.result), n + res.n);
else return res.chainError(src.loc(i + n), String.format("Expected a value after the unary operator '%s'.", op));
}
public static ParseRes<OperationStatement> parseInstanceof(Source src, int i, Statement prev, int precedence) {
if (precedence > 9) return ParseRes.failed();
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 = ES5.parseExpression(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(Source src, int i, Statement prev, int precedence) {
if (precedence > 9) return ParseRes.failed();
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 = ES5.parseExpression(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, valRes.result, prev), n);
}
public static ParseRes<? extends Statement> parseOperator(Source src, int i, Statement prev, int precedence) {
var n = Parsing.skipEmpty(src, i);
for (var token : operatorsByLength) {
var factory = factories.get(token);
if (!src.is(i + n, token)) continue;
if (factory.precedence() < precedence) ParseRes.failed();
n += token.length();
n += Parsing.skipEmpty(src, i + n);
var res = factory.construct(src, i + n, prev);
return res.addN(n);
// var res = Parsing.parseValue(src, i + n, prec + 1);
// if (!res.isSuccess()) return res.chainError(src.loc(i + n), String.format("Expected a value after the '%s' operator.", token));
// n += res.n;
// return ParseRes.res(new OperationStatement(loc, factories.get(token), prev, res.result), n);
}
return ParseRes.failed();
}
}

View File

@ -1,12 +1,14 @@
package me.topchetoeu.jscript.compilation.values; package me.topchetoeu.jscript.compilation.values.operations;
import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.common.parsing.ParseRes;
import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.ES5;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.parsing.ParseRes; import me.topchetoeu.jscript.compilation.values.VariableStatement;
import me.topchetoeu.jscript.compilation.parsing.Parsing;
import me.topchetoeu.jscript.compilation.parsing.Source;
public class TypeofStatement extends Statement { public class TypeofStatement extends Statement {
public final Statement value; public final Statement value;
@ -40,7 +42,7 @@ public class TypeofStatement extends Statement {
if (!Parsing.isIdentifier(src, i + n, "typeof")) return ParseRes.failed(); if (!Parsing.isIdentifier(src, i + n, "typeof")) return ParseRes.failed();
n += 6; n += 6;
var valRes = Parsing.parseValue(src, i + n, 15); var valRes = ES5.parseExpression(src, i + n, 15);
if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'typeof' keyword."); if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'typeof' keyword.");
n += valRes.n; n += valRes.n;

View File

@ -1,10 +1,11 @@
package me.topchetoeu.jscript.compilation.values; package me.topchetoeu.jscript.compilation.values.operations;
import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.Operation; import me.topchetoeu.jscript.common.Operation;
import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.values.FunctionStatement;
public class VariableAssignStatement extends Statement { public class VariableAssignStatement extends Statement {
public final String name; public final String name;

View File

@ -1,7 +1,7 @@
package me.topchetoeu.jscript.compilation.values; package me.topchetoeu.jscript.compilation.values.operations;
import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;

View File

@ -0,0 +1,13 @@
package me.topchetoeu.jscript.runtime;
import me.topchetoeu.jscript.runtime.values.Value;
import me.topchetoeu.jscript.runtime.values.objects.ArrayValue;
public class ArgumentsValue extends ArrayValue {
public final Frame frame;
public ArgumentsValue(Frame frame, Value... args) {
super(args);
this.frame = frame;
}
}

View File

@ -1,18 +1,19 @@
package me.topchetoeu.jscript.runtime; package me.topchetoeu.jscript.runtime;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
import java.util.concurrent.PriorityBlockingQueue; import java.util.concurrent.PriorityBlockingQueue;
import java.util.function.Supplier;
import me.topchetoeu.jscript.common.ResultRunnable;
import me.topchetoeu.jscript.common.events.DataNotifier;
import me.topchetoeu.jscript.runtime.exceptions.InterruptException; import me.topchetoeu.jscript.runtime.exceptions.InterruptException;
public class Engine implements EventLoop { public class Engine implements EventLoop {
private static class Task<T> implements Comparable<Task<?>> { private static class Task<T> implements Comparable<Task<?>> {
public final ResultRunnable<?> runnable; public final Supplier<?> runnable;
public final DataNotifier<T> notifier = new DataNotifier<>(); public final CompletableFuture<T> notifier = new CompletableFuture<T>();
public final boolean micro; public final boolean micro;
public Task(ResultRunnable<T> runnable, boolean micro) { public Task(Supplier<T> runnable, boolean micro) {
this.runnable = runnable; this.runnable = runnable;
this.micro = micro; this.micro = micro;
} }
@ -26,8 +27,7 @@ public class Engine implements EventLoop {
private PriorityBlockingQueue<Task<?>> tasks = new PriorityBlockingQueue<>(); private PriorityBlockingQueue<Task<?>> tasks = new PriorityBlockingQueue<>();
private Thread thread; private Thread thread;
@Override @Override public <T> Future<T> pushMsg(Supplier<T> runnable, boolean micro) {
public <T> DataNotifier<T> pushMsg(ResultRunnable<T> runnable, boolean micro) {
var msg = new Task<T>(runnable, micro); var msg = new Task<T>(runnable, micro);
tasks.add(msg); tasks.add(msg);
return msg.notifier; return msg.notifier;
@ -40,15 +40,15 @@ public class Engine implements EventLoop {
var task = tasks.take(); var task = tasks.take();
try { try {
((Task<Object>)task).notifier.next(task.runnable.run()); ((Task<Object>)task).notifier.complete(task.runnable.get());
} }
catch (RuntimeException e) { catch (RuntimeException e) {
if (e instanceof InterruptException) throw e; if (e instanceof InterruptException) throw e;
task.notifier.error(e); task.notifier.completeExceptionally(e);
} }
} }
catch (InterruptedException | InterruptException e) { catch (InterruptedException | InterruptException e) {
for (var msg : tasks) msg.notifier.error(new InterruptException(e)); for (var msg : tasks) msg.notifier.cancel(false);
break; break;
} }
} }

View File

@ -1,9 +1,10 @@
package me.topchetoeu.jscript.runtime; package me.topchetoeu.jscript.runtime;
import java.util.concurrent.Future;
import java.util.function.Supplier;
import me.topchetoeu.jscript.common.Compiler; import me.topchetoeu.jscript.common.Compiler;
import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.parsing.Filename;
import me.topchetoeu.jscript.common.ResultRunnable;
import me.topchetoeu.jscript.common.events.DataNotifier;
import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.environment.Key; import me.topchetoeu.jscript.runtime.environment.Key;
import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.exceptions.EngineException;
@ -16,21 +17,21 @@ public interface EventLoop {
public static EventLoop get(Environment ext) { public static EventLoop get(Environment ext) {
if (ext.hasNotNull(KEY)) return ext.get(KEY); if (ext.hasNotNull(KEY)) return ext.get(KEY);
else return new EventLoop() { else return new EventLoop() {
@Override public <T> DataNotifier<T> pushMsg(ResultRunnable<T> runnable, boolean micro) { @Override public <T> Future<T> pushMsg(Supplier<T> runnable, boolean micro) {
throw EngineException.ofError("No event loop attached to environment."); throw EngineException.ofError("No event loop attached to environment.");
} }
}; };
} }
public <T> DataNotifier<T> pushMsg(ResultRunnable<T> runnable, boolean micro); public <T> Future<T> pushMsg(Supplier<T> runnable, boolean micro);
public default DataNotifier<Void> pushMsg(Runnable runnable, boolean micro) { public default Future<Void> pushMsg(Runnable runnable, boolean micro) {
return pushMsg(() -> { runnable.run(); return null; }, micro); return pushMsg(() -> { runnable.run(); return null; }, micro);
} }
public default DataNotifier<Value> pushMsg(boolean micro, Environment env, FunctionValue func, Value thisArg, Value ...args) { public default Future<Value> pushMsg(boolean micro, Environment env, FunctionValue func, Value thisArg, Value ...args) {
return pushMsg(() -> func.call(env, thisArg, args), micro); return pushMsg(() -> func.call(env, thisArg, args), micro);
} }
public default DataNotifier<Value> pushMsg(boolean micro, Environment env, Filename filename, String raw, Value thisArg, Value ...args) { public default Future<Value> pushMsg(boolean micro, Environment env, Filename filename, String raw, Value thisArg, Value ...args) {
return pushMsg(() -> Compiler.compile(env, filename, raw).call(env, thisArg, args), micro); return pushMsg(() -> Compiler.compileFunc(env, filename, raw).call(env, thisArg, args), micro);
} }
} }

View File

@ -13,11 +13,11 @@ import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.exceptions.InterruptException; import me.topchetoeu.jscript.runtime.exceptions.InterruptException;
import me.topchetoeu.jscript.runtime.scope.LocalScope; import me.topchetoeu.jscript.runtime.scope.LocalScope;
import me.topchetoeu.jscript.runtime.scope.ValueVariable; import me.topchetoeu.jscript.runtime.scope.ValueVariable;
import me.topchetoeu.jscript.runtime.values.KeyCache;
import me.topchetoeu.jscript.runtime.values.Member; import me.topchetoeu.jscript.runtime.values.Member;
import me.topchetoeu.jscript.runtime.values.Value; import me.topchetoeu.jscript.runtime.values.Value;
import me.topchetoeu.jscript.runtime.values.Member.FieldMember; import me.topchetoeu.jscript.runtime.values.Member.FieldMember;
import me.topchetoeu.jscript.runtime.values.functions.CodeFunction; import me.topchetoeu.jscript.runtime.values.functions.CodeFunction;
import me.topchetoeu.jscript.runtime.values.objects.ArrayValue;
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
import me.topchetoeu.jscript.runtime.values.objects.ScopeValue; import me.topchetoeu.jscript.runtime.values.objects.ScopeValue;
import me.topchetoeu.jscript.runtime.values.primitives.VoidValue; import me.topchetoeu.jscript.runtime.values.primitives.VoidValue;
@ -101,6 +101,7 @@ public class Frame {
public final LocalScope scope; public final LocalScope scope;
public final Object thisArg; public final Object thisArg;
public final Object[] args; public final Object[] args;
public final boolean isNew;
public final Stack<TryCtx> tryStack = new Stack<>(); public final Stack<TryCtx> tryStack = new Stack<>();
public final CodeFunction function; public final CodeFunction function;
public final Environment env; public final Environment env;
@ -356,14 +357,14 @@ public class Frame {
*/ */
public ObjectValue getValStackScope() { public ObjectValue getValStackScope() {
return new ObjectValue() { return new ObjectValue() {
@Override public Member getOwnMember(Environment env, Value key) { @Override public Member getOwnMember(Environment env, KeyCache key) {
var res = super.getOwnMember(env, key); var res = super.getOwnMember(env, key);
if (res != null) return res; if (res != null) return res;
var f = key.toNumber(env).value; var num = key.toNumber(env);
var i = (int)f; var i = key.toInt(env);
if (i < 0 || i >= stackPtr) return null; if (num != i || i < 0 || i >= stackPtr) return null;
else return new FieldMember(false, true, true) { else return new FieldMember(false, true, true) {
@Override public Value get(Environment env, Value self) { return stack[i]; } @Override public Value get(Environment env, Value self) { return stack[i]; }
@Override public boolean set(Environment env, Value val, Value self) { @Override public boolean set(Environment env, Value val, Value self) {
@ -391,12 +392,13 @@ public class Frame {
}; };
} }
public Frame(Environment env, Value thisArg, Value[] args, CodeFunction func) { public Frame(Environment env, boolean isNew, Value thisArg, Value[] args, CodeFunction func) {
this.env = env; this.env = env;
this.args = args.clone(); this.args = args;
this.isNew = isNew;
this.scope = new LocalScope(func.body.localsN, func.captures); this.scope = new LocalScope(func.body.localsN, func.captures);
this.scope.get(0).set(thisArg); this.scope.get(0).set(thisArg);
this.scope.get(1).value = new ArrayValue(args); this.scope.get(1).value = new ArgumentsValue(this, args);
this.thisArg = thisArg; this.thisArg = thisArg;
this.function = func; this.function = func;

View File

@ -35,9 +35,18 @@ public class InstructionRunner {
private static Value execCall(Environment env, Instruction instr, Frame frame) { private static Value execCall(Environment env, Instruction instr, Frame frame) {
var callArgs = frame.take(instr.get(0)); var callArgs = frame.take(instr.get(0));
var func = frame.pop(); var func = frame.pop();
var thisArg = frame.pop();
frame.push(func.call(env, thisArg, callArgs)); frame.push(func.call(env, false, instr.get(1), VoidValue.UNDEFINED, callArgs));
frame.codePtr++;
return null;
}
private static Value execCallMember(Environment env, Instruction instr, Frame frame) {
var callArgs = frame.take(instr.get(0));
var index = frame.pop();
var obj = frame.pop();
frame.push(obj.getMember(env, index).call(env, false, instr.get(1), obj, callArgs));
frame.codePtr++; frame.codePtr++;
return null; return null;
@ -46,7 +55,7 @@ public class InstructionRunner {
var callArgs = frame.take(instr.get(0)); var callArgs = frame.take(instr.get(0));
var funcObj = frame.pop(); var funcObj = frame.pop();
frame.push(funcObj.callNew(env, callArgs)); frame.push(funcObj.callNew(env, instr.get(1), callArgs));
frame.codePtr++; frame.codePtr++;
return null; return null;
@ -61,7 +70,7 @@ public class InstructionRunner {
private static Value execDefProp(Environment env, Instruction instr, Frame frame) { private static Value execDefProp(Environment env, Instruction instr, Frame frame) {
var setterVal = frame.pop(); var setterVal = frame.pop();
var getterVal = frame.pop(); var getterVal = frame.pop();
var name = frame.pop(); var key = frame.pop();
var obj = frame.pop(); var obj = frame.pop();
FunctionValue getter, setter; FunctionValue getter, setter;
@ -74,7 +83,7 @@ public class InstructionRunner {
else if (setterVal instanceof FunctionValue) setter = (FunctionValue)setterVal; else if (setterVal instanceof FunctionValue) setter = (FunctionValue)setterVal;
else throw EngineException.ofType("Setter must be a function or undefined."); else throw EngineException.ofType("Setter must be a function or undefined.");
obj.defineOwnMember(env, name, new PropertyMember(getter, setter, true, true)); obj.defineOwnMember(env, key, new PropertyMember(getter, setter, true, true));
frame.push(obj); frame.push(obj);
frame.codePtr++; frame.codePtr++;
@ -90,7 +99,7 @@ public class InstructionRunner {
for (var el : members) { for (var el : members) {
var obj = new ObjectValue(); var obj = new ObjectValue();
obj.defineOwnMember(env, new StringValue("value"), FieldMember.of(new StringValue(el))); obj.defineOwnMember(env, "value", FieldMember.of(new StringValue(el)));
frame.push(obj); frame.push(obj);
} }
@ -147,7 +156,9 @@ public class InstructionRunner {
return null; return null;
} }
private static Value execLoadObj(Environment env, Instruction instr, Frame frame) { private static Value execLoadObj(Environment env, Instruction instr, Frame frame) {
frame.push(new ObjectValue()); var obj = new ObjectValue();
obj.setPrototype(Environment.OBJECT_PROTO);
frame.push(obj);
frame.codePtr++; frame.codePtr++;
return null; return null;
} }
@ -165,13 +176,14 @@ public class InstructionRunner {
} }
private static Value execLoadFunc(Environment env, Instruction instr, Frame frame) { private static Value execLoadFunc(Environment env, Instruction instr, Frame frame) {
int id = instr.get(0); int id = instr.get(0);
var captures = new ValueVariable[instr.params.length - 1]; String name = instr.get(1);
var captures = new ValueVariable[instr.params.length - 2];
for (var i = 1; i < instr.params.length; i++) { for (var i = 2; i < instr.params.length; i++) {
captures[i - 1] = frame.scope.get(instr.get(i)); captures[i - 2] = frame.scope.get(instr.get(i));
} }
var func = new CodeFunction(env, "", frame.function.body.children[id], captures); var func = new CodeFunction(env, name, frame.function.body.children[id], captures);
frame.push(func); frame.push(func);
@ -240,7 +252,7 @@ public class InstructionRunner {
return null; return null;
} }
private static Value execJmpIf(Environment env, Instruction instr, Frame frame) { private static Value execJmpIf(Environment env, Instruction instr, Frame frame) {
if (frame.pop().toBoolean().value) { if (frame.pop().toBoolean()) {
frame.codePtr += (int)instr.get(0); frame.codePtr += (int)instr.get(0);
frame.jumpFlag = true; frame.jumpFlag = true;
} }
@ -248,7 +260,7 @@ public class InstructionRunner {
return null; return null;
} }
private static Value execJmpIfNot(Environment env, Instruction instr, Frame frame) { private static Value execJmpIfNot(Environment env, Instruction instr, Frame frame) {
if (frame.pop().not().value) { if (!frame.pop().toBoolean()) {
frame.codePtr += (int)instr.get(0); frame.codePtr += (int)instr.get(0);
frame.jumpFlag = true; frame.jumpFlag = true;
} }
@ -306,6 +318,7 @@ public class InstructionRunner {
case THROW_SYNTAX: return execThrowSyntax(env, instr, frame); case THROW_SYNTAX: return execThrowSyntax(env, instr, frame);
case CALL: return execCall(env, instr, frame); case CALL: return execCall(env, instr, frame);
case CALL_NEW: return execCallNew(env, instr, frame); case CALL_NEW: return execCallNew(env, instr, frame);
case CALL_MEMBER: return execCallMember(env, instr, frame);
case TRY_START: return execTryStart(env, instr, frame); case TRY_START: return execTryStart(env, instr, frame);
case TRY_END: return execTryEnd(env, instr, frame); case TRY_END: return execTryEnd(env, instr, frame);

View File

@ -27,7 +27,7 @@ public class JSONConverter {
var res = new ObjectValue(); var res = new ObjectValue();
for (var el : val.map().entrySet()) { for (var el : val.map().entrySet()) {
res.defineOwnMember(null, new StringValue(el.getKey()), FieldMember.of(toJs(el.getValue()))); res.defineOwnMember(null, el.getKey(), FieldMember.of(toJs(el.getValue())));
} }
return res; return res;

View File

@ -3,12 +3,14 @@ package me.topchetoeu.jscript.runtime;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import me.topchetoeu.jscript.common.Compiler; import me.topchetoeu.jscript.common.Compiler;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.Metadata; import me.topchetoeu.jscript.common.Metadata;
import me.topchetoeu.jscript.common.Reading; import me.topchetoeu.jscript.common.Reading;
import me.topchetoeu.jscript.compilation.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Filename;
import me.topchetoeu.jscript.runtime.debug.DebugContext;
import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.exceptions.InterruptException; import me.topchetoeu.jscript.runtime.exceptions.InterruptException;
@ -16,6 +18,7 @@ import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
import me.topchetoeu.jscript.runtime.scope.GlobalScope; import me.topchetoeu.jscript.runtime.scope.GlobalScope;
import me.topchetoeu.jscript.runtime.values.Value; import me.topchetoeu.jscript.runtime.values.Value;
import me.topchetoeu.jscript.runtime.values.functions.NativeFunction; import me.topchetoeu.jscript.runtime.values.functions.NativeFunction;
import me.topchetoeu.jscript.runtime.values.primitives.StringValue;
import me.topchetoeu.jscript.runtime.values.primitives.VoidValue; import me.topchetoeu.jscript.runtime.values.primitives.VoidValue;
public class SimpleRepl { public class SimpleRepl {
@ -32,13 +35,16 @@ public class SimpleRepl {
try { try {
var file = Path.of(arg); var file = Path.of(arg);
var raw = Files.readString(file); var raw = Files.readString(file);
var res = engine.pushMsg(
false, environment,
Filename.fromFile(file.toFile()),
raw, null
).await();
System.err.println(res.toReadable(environment)); try {
var res = engine.pushMsg(
false, environment,
Filename.fromFile(file.toFile()), raw, null
).get();
System.err.println(res.toReadable(environment));
}
catch (ExecutionException e) { throw e.getCause(); }
} }
catch (EngineException | SyntaxException e) { System.err.println(Value.errorToReadable(e, null)); } catch (EngineException | SyntaxException e) { System.err.println(Value.errorToReadable(e, null)); }
} }
@ -48,9 +54,16 @@ public class SimpleRepl {
var raw = Reading.readline(); var raw = Reading.readline();
if (raw == null) break; if (raw == null) break;
var func = Compiler.compile(environment, new Filename("jscript", "repl/" + i + ".js"), raw);
var res = engine.pushMsg(false, environment, func, VoidValue.UNDEFINED).await(); try {
System.err.println(res.toReadable(environment)); var res = engine.pushMsg(
false, environment,
new Filename("jscript", "repl/" + i + ".js"), raw,
VoidValue.UNDEFINED
).get();
System.err.println(res.toReadable(environment));
}
catch (ExecutionException e) { throw e.getCause(); }
} }
catch (EngineException | SyntaxException e) { System.err.println(Value.errorToReadable(e, null)); } catch (EngineException | SyntaxException e) { System.err.println(Value.errorToReadable(e, null)); }
} }
@ -59,62 +72,36 @@ public class SimpleRepl {
System.out.println(e.toString()); System.out.println(e.toString());
engine.thread().interrupt(); engine.thread().interrupt();
} }
catch (RuntimeException ex) { catch (CancellationException | InterruptedException e) { return; }
if (ex instanceof InterruptException) return; catch (Throwable ex) {
else { System.out.println("Internal error ocurred:");
System.out.println("Internal error ocurred:"); ex.printStackTrace();
ex.printStackTrace();
}
} }
} }
private static void initEnv() { private static void initEnv() {
// glob.define(null, false, new NativeFunction("go", args -> {
// try {
// var f = Path.of("do.js");
// var func = Compiler.compile(args.env, new Filename("do", "do/" + j++ + ".js"), new String(Files.readAllBytes(f)));
// return func.call(args.env);
// }
// catch (IOException e) {
// throw new EngineException("Couldn't open do.js");
// }
// }));
// var fs = new RootFilesystem(PermissionsProvider.get(environment));
// fs.protocols.put("temp", new MemoryFilesystem(Mode.READ_WRITE));
// fs.protocols.put("file", new PhysicalFilesystem("."));
// fs.protocols.put("std", new STDFilesystem(System.in, System.out, System.err));
// environment.add(PermissionsProvider.KEY, PermissionsManager.ALL_PERMS);
// environment.add(Filesystem.KEY, fs);
// environment.add(ModuleRepo.KEY, ModuleRepo.ofFilesystem(fs));
// environment.add(Compiler.KEY, new JSCompiler(environment));
environment.add(EventLoop.KEY, engine); environment.add(EventLoop.KEY, engine);
environment.add(GlobalScope.KEY, new GlobalScope()); environment.add(GlobalScope.KEY, new GlobalScope());
// environment.add(EventLoop.KEY, engine); environment.add(DebugContext.KEY, new DebugContext());
environment.add(Compiler.KEY, (filename, source) -> { environment.add(Compiler.KEY, Compiler.DEFAULT);
return Parsing.compile(filename, source).body();
});
var glob = GlobalScope.get(environment); var glob = GlobalScope.get(environment);
glob.define(null, false, new NativeFunction("exit", args -> { glob.define(null, false, new NativeFunction("exit", args -> {
Thread.currentThread().interrupt();
throw new InterruptException(); throw new InterruptException();
})); }));
glob.define(null, false, new NativeFunction("log", args -> { glob.define(null, false, new NativeFunction("log", args -> {
for (var el : args.args) { for (var el : args.args) {
System.out.print(el.toReadable(args.env)); if (el instanceof StringValue) System.out.print(((StringValue)el).value);
else System.out.print(el.toReadable(args.env));
} }
return null; return null;
})); }));
} }
private static void initEngine() { private static void initEngine() {
// var ctx = new DebugContext();
// environment.add(DebugContext.KEY, ctx);
// debugServer.targets.put("target", (ws, req) -> new SimpleDebugger(ws).attach(ctx));
engineTask = engine.start(); engineTask = engine.start();
// debugTask = debugServer.start(new InetSocketAddress("127.0.0.1", 9229), true);
} }
public static void main(String args[]) throws InterruptedException { public static void main(String args[]) throws InterruptedException {

View File

@ -1,13 +0,0 @@
package me.topchetoeu.jscript.runtime;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.values.functions.FunctionValue;
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
public interface WrapperProvider {
public ObjectValue getProto(Class<?> obj);
public ObjectValue getNamespace(Class<?> obj);
public FunctionValue getConstr(Class<?> obj);
public WrapperProvider fork(Environment env);
}

View File

@ -5,11 +5,11 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.WeakHashMap; import java.util.WeakHashMap;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.FunctionBody; import me.topchetoeu.jscript.common.FunctionBody;
import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.mapping.FunctionMap; import me.topchetoeu.jscript.common.mapping.FunctionMap;
import me.topchetoeu.jscript.common.parsing.Filename;
import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.runtime.Frame; import me.topchetoeu.jscript.runtime.Frame;
import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.environment.Key; import me.topchetoeu.jscript.runtime.environment.Key;

View File

@ -2,10 +2,10 @@ package me.topchetoeu.jscript.runtime.debug;
import java.util.List; import java.util.List;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.FunctionBody; import me.topchetoeu.jscript.common.FunctionBody;
import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.mapping.FunctionMap; import me.topchetoeu.jscript.common.mapping.FunctionMap;
import me.topchetoeu.jscript.common.parsing.Filename;
import me.topchetoeu.jscript.runtime.Frame; import me.topchetoeu.jscript.runtime.Frame;
import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.exceptions.EngineException;

View File

@ -1,11 +0,0 @@
package me.topchetoeu.jscript.runtime.exceptions;
public class ConvertException extends RuntimeException {
public final String source, target;
public ConvertException(String source, String target) {
super(String.format("Cannot convert '%s' to '%s'.", source, target));
this.source = source;
this.target = target;
}
}

View File

@ -3,13 +3,14 @@ package me.topchetoeu.jscript.runtime.exceptions;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.values.Value; import me.topchetoeu.jscript.runtime.values.Value;
import me.topchetoeu.jscript.runtime.values.Member.FieldMember; import me.topchetoeu.jscript.runtime.values.Member.FieldMember;
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue.PrototypeProvider; import me.topchetoeu.jscript.runtime.values.objects.ObjectValue.PrototypeProvider;
import me.topchetoeu.jscript.runtime.values.primitives.StringValue; import me.topchetoeu.jscript.runtime.values.primitives.StringValue;
import me.topchetoeu.jscript.runtime.values.primitives.VoidValue;
public class EngineException extends RuntimeException { public class EngineException extends RuntimeException {
public static class StackElement { public static class StackElement {
@ -69,7 +70,19 @@ public class EngineException extends RuntimeException {
ss.append(value.toString(env)).append('\n'); ss.append(value.toString(env)).append('\n');
} }
catch (EngineException e) { catch (EngineException e) {
ss.append("[Error while stringifying]\n"); var name = value.getMember(env, "name");
var desc = value.getMember(env, "message");
if (name.isPrimitive() && desc.isPrimitive()) {
if (name instanceof VoidValue) ss.append("Error: ");
else ss.append(name.toString(env).value + ": ");
if (desc instanceof VoidValue) ss.append("An error occurred");
else ss.append(desc.toString(env).value);
ss.append("\n");
}
else ss.append("[Error while stringifying]\n");
} }
for (var line : stackTrace) { for (var line : stackTrace) {
if (line.visible()) ss.append(" ").append(line.toString()).append("\n"); if (line.visible()) ss.append(" ").append(line.toString()).append("\n");
@ -83,8 +96,8 @@ public class EngineException extends RuntimeException {
var res = new ObjectValue(); var res = new ObjectValue();
res.setPrototype(proto); res.setPrototype(proto);
if (name != null) res.defineOwnMember(Environment.empty(), new StringValue("name"), FieldMember.of(new StringValue(name))); if (name != null) res.defineOwnMember(Environment.empty(), "name", FieldMember.of(new StringValue(name)));
res.defineOwnMember(Environment.empty(), new StringValue("message"), FieldMember.of(new StringValue(msg))); res.defineOwnMember(Environment.empty(), "message", FieldMember.of(new StringValue(msg)));
return res; return res;
} }

View File

@ -1,6 +1,6 @@
package me.topchetoeu.jscript.runtime.exceptions; package me.topchetoeu.jscript.runtime.exceptions;
import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.parsing.Location;
public class SyntaxException extends RuntimeException { public class SyntaxException extends RuntimeException {
public final Location loc; public final Location loc;

View File

@ -29,10 +29,10 @@ public class GlobalScope {
} }
public void define(Environment ext, String name, Variable variable) { public void define(Environment ext, String name, Variable variable) {
object.defineOwnMember(ext, new StringValue(name), variable.toField(true, true)); object.defineOwnMember(ext, name, variable.toField(true, true));
} }
public void define(Environment ext, boolean readonly, String name, Value val) { public void define(Environment ext, boolean readonly, String name, Value val) {
object.defineOwnMember(ext, new StringValue(name), FieldMember.of(val, !readonly)); object.defineOwnMember(ext, name, FieldMember.of(val, !readonly));
} }
public void define(Environment ext, boolean readonly, String ...names) { public void define(Environment ext, boolean readonly, String ...names) {
for (var name : names) define(ext, name, new ValueVariable(readonly, VoidValue.UNDEFINED)); for (var name : names) define(ext, name, new ValueVariable(readonly, VoidValue.UNDEFINED));
@ -42,12 +42,12 @@ public class GlobalScope {
} }
public Value get(Environment env, String name) { public Value get(Environment env, String name) {
if (!object.hasMember(env, new StringValue(name), false)) throw EngineException.ofSyntax("The variable '" + name + "' doesn't exist."); if (!object.hasMember(env, name, false)) throw EngineException.ofSyntax(name + " is not defined");
else return object.getMember(env, new StringValue(name)); else return object.getMember(env, name);
} }
public void set(Environment ext, String name, Value val) { public void set(Environment ext, String name, Value val) {
if (!object.hasMember(ext, new StringValue(name), false)) throw EngineException.ofSyntax("The variable '" + name + "' doesn't exist."); if (!object.hasMember(ext, name, false)) throw EngineException.ofSyntax(name + " is not defined");
if (!object.setMember(ext, new StringValue(name), val)) throw EngineException.ofSyntax("The global '" + name + "' is read-only."); if (!object.setMember(ext, name, val)) throw EngineException.ofSyntax("Assignment to constant variable");
} }
public Set<String> keys() { public Set<String> keys() {

View File

@ -1,6 +0,0 @@
package me.topchetoeu.jscript.runtime.values;
public enum ConvertHint {
TOSTRING,
VALUEOF,
}

View File

@ -0,0 +1,59 @@
package me.topchetoeu.jscript.runtime.values;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.values.primitives.NumberValue;
import me.topchetoeu.jscript.runtime.values.primitives.StringValue;
import me.topchetoeu.jscript.runtime.values.primitives.SymbolValue;
public class KeyCache {
public final Value value;
private Integer intCache;
private Double doubleCache;
private Boolean booleanCache;
private String stringCache;
public String toString(Environment env) {
if (stringCache != null) return stringCache;
else return stringCache = value.toString(env).value;
}
public double toNumber(Environment env) {
if (doubleCache != null) return doubleCache;
else return doubleCache = value.toNumber(env).value;
}
public int toInt(Environment env) {
if (intCache != null) return intCache;
else return intCache = (int)toNumber(env);
}
public boolean toBoolean() {
if (booleanCache != null) return booleanCache;
else return booleanCache = value.toBoolean();
}
public SymbolValue toSymbol() {
if (value instanceof SymbolValue) return (SymbolValue)value;
else return null;
}
public boolean isSymbol() {
return value instanceof SymbolValue;
}
public KeyCache(Value value) {
this.value = value;
}
public KeyCache(String value) {
this.value = new StringValue(value);
this.stringCache = value;
this.booleanCache = !value.equals("");
}
public KeyCache(int value) {
this.value = new NumberValue(value);
this.intCache = value;
this.doubleCache = (double)value;
this.booleanCache = value != 0;
}
public KeyCache(double value) {
this.value = new NumberValue(value);
this.intCache = (int)value;
this.doubleCache = value;
this.booleanCache = value != 0;
}
}

View File

@ -4,7 +4,6 @@ import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.values.functions.FunctionValue; import me.topchetoeu.jscript.runtime.values.functions.FunctionValue;
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
import me.topchetoeu.jscript.runtime.values.primitives.BoolValue; import me.topchetoeu.jscript.runtime.values.primitives.BoolValue;
import me.topchetoeu.jscript.runtime.values.primitives.StringValue;
import me.topchetoeu.jscript.runtime.values.primitives.VoidValue; import me.topchetoeu.jscript.runtime.values.primitives.VoidValue;
public interface Member { public interface Member {
@ -42,14 +41,14 @@ public interface Member {
@Override public ObjectValue descriptor(Environment env, Value self) { @Override public ObjectValue descriptor(Environment env, Value self) {
var res = new ObjectValue(); var res = new ObjectValue();
if (getter == null) res.defineOwnMember(env, new StringValue("getter"), FieldMember.of(VoidValue.UNDEFINED)); if (getter == null) res.defineOwnMember(env, "getter", FieldMember.of(VoidValue.UNDEFINED));
else res.defineOwnMember(env, new StringValue("getter"), FieldMember.of(getter)); else res.defineOwnMember(env, "getter", FieldMember.of(getter));
if (setter == null) res.defineOwnMember(env, new StringValue("setter"), FieldMember.of(VoidValue.UNDEFINED)); if (setter == null) res.defineOwnMember(env, "setter", FieldMember.of(VoidValue.UNDEFINED));
else res.defineOwnMember(env, new StringValue("setter"), FieldMember.of(setter)); else res.defineOwnMember(env, "setter", FieldMember.of(setter));
res.defineOwnMember(env, new StringValue("enumerable"), FieldMember.of(BoolValue.of(enumerable))); res.defineOwnMember(env, "enumerable", FieldMember.of(BoolValue.of(enumerable)));
res.defineOwnMember(env, new StringValue("configurable"), FieldMember.of(BoolValue.of(configurable))); res.defineOwnMember(env, "configurable", FieldMember.of(BoolValue.of(configurable)));
return res; return res;
} }
@ -98,10 +97,10 @@ public interface Member {
@Override public ObjectValue descriptor(Environment env, Value self) { @Override public ObjectValue descriptor(Environment env, Value self) {
var res = new ObjectValue(); var res = new ObjectValue();
res.defineOwnMember(env, new StringValue("value"), FieldMember.of(get(env, self))); res.defineOwnMember(env, "value", FieldMember.of(get(env, self)));
res.defineOwnMember(env, new StringValue("writable"), FieldMember.of(BoolValue.of(writable))); res.defineOwnMember(env, "writable", FieldMember.of(BoolValue.of(writable)));
res.defineOwnMember(env, new StringValue("enumerable"), FieldMember.of(BoolValue.of(enumerable))); res.defineOwnMember(env, "enumerable", FieldMember.of(BoolValue.of(enumerable)));
res.defineOwnMember(env, new StringValue("configurable"), FieldMember.of(BoolValue.of(configurable))); res.defineOwnMember(env, "configurable", FieldMember.of(BoolValue.of(configurable)));
return res; return res;
} }

View File

@ -53,31 +53,41 @@ public abstract class Value {
public abstract StringValue type(); public abstract StringValue type();
public abstract boolean isPrimitive(); public abstract boolean isPrimitive();
public abstract BoolValue toBoolean();
public final boolean isNaN() { public final boolean isNaN() {
return this instanceof NumberValue && Double.isNaN(((NumberValue)this).value); return this instanceof NumberValue && Double.isNaN(((NumberValue)this).value);
} }
public Value call(Environment env, Value self, Value ...args) { public Value call(Environment env, boolean isNew, String name, Value self, Value ...args) {
throw EngineException.ofType("Tried to call a non-function value."); if (name == null || name.equals("")) name = "(intermediate value)";
if (isNew) throw EngineException.ofType(name + " is not a constructor");
else throw EngineException.ofType(name + " is not a function");
} }
public final Value callNew(Environment env, Value ...args) { public final Value callNew(Environment env, String name, Value ...args) {
var res = new ObjectValue(); var res = new ObjectValue();
var proto = getMember(env, new StringValue("prototype")); var proto = getMember(env, new StringValue("prototype"));
if (proto instanceof ObjectValue) res.setPrototype(env, (ObjectValue)proto); if (proto instanceof ObjectValue) res.setPrototype(env, (ObjectValue)proto);
else res.setPrototype(env, null); else res.setPrototype(env, null);
var ret = this.call(env, res, args); var ret = this.call(env, true, name, res, args);
if (!ret.isPrimitive()) return ret; if (!ret.isPrimitive()) return ret;
return res; return res;
} }
public final Value call(Environment env, Value self, Value ...args) {
return call(env, false, "", self, args);
}
public final Value callNew(Environment env, Value ...args) {
return callNew(env, "", args);
}
public abstract Value toPrimitive(Environment env); public abstract Value toPrimitive(Environment env);
public abstract NumberValue toNumber(Environment env); public abstract NumberValue toNumber(Environment env);
public abstract StringValue toString(Environment env); public abstract StringValue toString(Environment env);
public abstract boolean toBoolean();
public final int toInt(Environment env) { return (int)toNumber(env).value; } public final int toInt(Environment env) { return (int)toNumber(env).value; }
public final long toLong(Environment env) { return (long)toNumber(env).value; } public final long toLong(Environment env) { return (long)toNumber(env).value; }
@ -134,11 +144,11 @@ public abstract class Value {
} }
public CompareResult compare(Environment env, Value other) { public CompareResult compare(Environment env, Value other) {
return toPrimitive(env).compare(env, other); var a = this.toPrimitive(env);
} var b = other.toPrimitive(env);
public final BoolValue not() { if (a instanceof StringValue && b instanceof StringValue) return a.compare(env, b);
return toBoolean().value ? BoolValue.FALSE : BoolValue.TRUE; else return a.toNumber(env).compare(env, b.toNumber(env));
} }
public final boolean isInstanceOf(Environment env, Value proto) { public final boolean isInstanceOf(Environment env, Value proto) {
@ -172,7 +182,7 @@ public abstract class Value {
case LESS_EQUALS: return BoolValue.of(args[0].compare(env, args[1]).lessOrEqual()); case LESS_EQUALS: return BoolValue.of(args[0].compare(env, args[1]).lessOrEqual());
case INVERSE: return args[0].bitwiseNot(env); case INVERSE: return args[0].bitwiseNot(env);
case NOT: return args[0].not(); case NOT: return BoolValue.of(!args[0].toBoolean());
case POS: return args[0].toNumber(env); case POS: return args[0].toNumber(env);
case NEG: return args[0].negative(env); case NEG: return args[0].negative(env);
@ -187,16 +197,71 @@ public abstract class Value {
} }
} }
public abstract Member getOwnMember(Environment env, Value key); public abstract Member getOwnMember(Environment env, KeyCache key);
public abstract Map<String, Member> getOwnMembers(Environment env); public abstract Map<String, Member> getOwnMembers(Environment env);
public abstract Map<SymbolValue, Member> getOwnSymbolMembers(Environment env); public abstract Map<SymbolValue, Member> getOwnSymbolMembers(Environment env);
public abstract boolean defineOwnMember(Environment env, Value key, Member member); public abstract boolean defineOwnMember(Environment env, KeyCache key, Member member);
public abstract boolean deleteOwnMember(Environment env, Value key); public abstract boolean deleteOwnMember(Environment env, KeyCache key);
public abstract ObjectValue getPrototype(Environment env); public abstract ObjectValue getPrototype(Environment env);
public abstract boolean setPrototype(Environment env, ObjectValue val); public abstract boolean setPrototype(Environment env, ObjectValue val);
public final Value getMember(Environment env, Value key) { public final Member getOwnMember(Environment env, Value key) {
return getOwnMember(env, new KeyCache(key));
}
public final Member getOwnMember(Environment env, String key) {
return getOwnMember(env, new KeyCache(key));
}
public final Member getOwnMember(Environment env, int key) {
return getOwnMember(env, new KeyCache(key));
}
public final Member getOwnMember(Environment env, double key) {
return getOwnMember(env, new KeyCache(key));
}
public final boolean defineOwnMember(Environment env, Value key, Member member) {
return defineOwnMember(env, new KeyCache(key), member);
}
public final boolean defineOwnMember(Environment env, String key, Member member) {
return defineOwnMember(env, new KeyCache(key), member);
}
public final boolean defineOwnMember(Environment env, int key, Member member) {
return defineOwnMember(env, new KeyCache(key), member);
}
public final boolean defineOwnMember(Environment env, double key, Member member) {
return defineOwnMember(env, new KeyCache(key), member);
}
public final boolean defineOwnMember(Environment env, KeyCache key, Value val) {
return defineOwnMember(env, key, FieldMember.of(val));
}
public final boolean defineOwnMember(Environment env, Value key, Value val) {
return defineOwnMember(env, new KeyCache(key), FieldMember.of(val));
}
public final boolean defineOwnMember(Environment env, String key, Value val) {
return defineOwnMember(env, new KeyCache(key), FieldMember.of(val));
}
public final boolean defineOwnMember(Environment env, int key, Value val) {
return defineOwnMember(env, new KeyCache(key), FieldMember.of(val));
}
public final boolean defineOwnMember(Environment env, double key, Value val) {
return defineOwnMember(env, new KeyCache(key), FieldMember.of(val));
}
public final boolean deleteOwnMember(Environment env, Value key) {
return deleteOwnMember(env, new KeyCache(key));
}
public final boolean deleteOwnMember(Environment env, String key) {
return deleteOwnMember(env, new KeyCache(key));
}
public final boolean deleteOwnMember(Environment env, int key) {
return deleteOwnMember(env, new KeyCache(key));
}
public final boolean deleteOwnMember(Environment env, double key) {
return deleteOwnMember(env, new KeyCache(key));
}
public final Value getMember(Environment env, KeyCache key) {
for (Value obj = this; obj != null; obj = obj.getPrototype(env)) { for (Value obj = this; obj != null; obj = obj.getPrototype(env)) {
var member = obj.getOwnMember(env, key); var member = obj.getOwnMember(env, key);
if (member != null) return member.get(env, obj); if (member != null) return member.get(env, obj);
@ -204,15 +269,51 @@ public abstract class Value {
return VoidValue.UNDEFINED; return VoidValue.UNDEFINED;
} }
public final boolean setMember(Environment env, Value key, Value val) { public final Value getMember(Environment env, Value key) {
return getMember(env, new KeyCache(key));
}
public final Value getMember(Environment env, String key) {
return getMember(env, new KeyCache(key));
}
public final Value getMember(Environment env, int key) {
return getMember(env, new KeyCache(key));
}
public final Value getMember(Environment env, double key) {
return getMember(env, new KeyCache(key));
}
public final boolean setMember(Environment env, KeyCache key, Value val) {
for (Value obj = this; obj != null; obj = obj.getPrototype(env)) { for (Value obj = this; obj != null; obj = obj.getPrototype(env)) {
var member = obj.getOwnMember(env, key); var member = obj.getOwnMember(env, key);
if (member != null) return member.set(env, val, obj); if (member != null) {
if (member.set(env, val, obj)) {
if (val instanceof FunctionValue) ((FunctionValue)val).setName(key.toString(env));
return true;
}
else return false;
}
} }
return defineOwnMember(env, key, FieldMember.of(val)); if (defineOwnMember(env, key, FieldMember.of(val))) {
if (val instanceof FunctionValue) ((FunctionValue)val).setName(key.toString(env));
return true;
}
else return false;
} }
public final boolean hasMember(Environment env, Value key, boolean own) { public final boolean setMember(Environment env, Value key, Value val) {
return setMember(env, new KeyCache(key), val);
}
public final boolean setMember(Environment env, String key, Value val) {
return setMember(env, new KeyCache(key), val);
}
public final boolean setMember(Environment env, int key, Value val) {
return setMember(env, new KeyCache(key), val);
}
public final boolean setMember(Environment env, double key, Value val) {
return setMember(env, new KeyCache(key), val);
}
public final boolean hasMember(Environment env, KeyCache key, boolean own) {
for (Value obj = this; obj != null; obj = obj.getPrototype(env)) { for (Value obj = this; obj != null; obj = obj.getPrototype(env)) {
if (obj.getOwnMember(env, key) != null) return true; if (obj.getOwnMember(env, key) != null) return true;
if (own) return false; if (own) return false;
@ -220,10 +321,36 @@ public abstract class Value {
return false; return false;
} }
public final boolean deleteMember(Environment env, Value key) { public final boolean hasMember(Environment env, Value key, boolean own) {
return hasMember(env, new KeyCache(key), own);
}
public final boolean hasMember(Environment env, String key, boolean own) {
return hasMember(env, new KeyCache(key), own);
}
public final boolean hasMember(Environment env, int key, boolean own) {
return hasMember(env, new KeyCache(key), own);
}
public final boolean hasMember(Environment env, double key, boolean own) {
return hasMember(env, new KeyCache(key), own);
}
public final boolean deleteMember(Environment env, KeyCache key) {
if (!hasMember(env, key, true)) return true; if (!hasMember(env, key, true)) return true;
return deleteOwnMember(env, key); return deleteOwnMember(env, key);
} }
public final boolean deleteMember(Environment env, Value key) {
return deleteMember(env, new KeyCache(key));
}
public final boolean deleteMember(Environment env, String key) {
return deleteMember(env, new KeyCache(key));
}
public final boolean deleteMember(Environment env, int key) {
return deleteMember(env, new KeyCache(key));
}
public final boolean deleteMember(Environment env, double key) {
return deleteMember(env, new KeyCache(key));
}
public final Map<String, Member> getMembers(Environment env, boolean own, boolean onlyEnumerable) { public final Map<String, Member> getMembers(Environment env, boolean own, boolean onlyEnumerable) {
var res = new LinkedHashMap<String, Member>(); var res = new LinkedHashMap<String, Member>();
var protos = new ArrayList<Value>(); var protos = new ArrayList<Value>();
@ -277,7 +404,7 @@ public abstract class Value {
return res; return res;
} }
public final ObjectValue getMemberDescriptor(Environment env, Value key) { public final ObjectValue getMemberDescriptor(Environment env, Value key) {
var member = getOwnMember(env, key); var member = getOwnMember(env, new KeyCache(key));
if (member != null) return member.descriptor(env, this); if (member != null) return member.descriptor(env, this);
else return null; else return null;
@ -394,7 +521,7 @@ public abstract class Value {
var curr = supplier.call(env, VoidValue.UNDEFINED); var curr = supplier.call(env, VoidValue.UNDEFINED);
if (curr == null) { supplier = null; value = null; } if (curr == null) { supplier = null; value = null; }
if (curr.getMember(env, new StringValue("done")).toBoolean().value) { supplier = null; value = null; } if (curr.getMember(env, new StringValue("done")).toBoolean()) { supplier = null; value = null; }
else { else {
this.value = curr.getMember(env, new StringValue("value")); this.value = curr.getMember(env, new StringValue("value"));
consumed = false; consumed = false;
@ -425,8 +552,8 @@ public abstract class Value {
return new NativeFunction("", args -> { return new NativeFunction("", args -> {
var obj = new ObjectValue(); var obj = new ObjectValue();
if (!it.hasNext()) obj.defineOwnMember(args.env, new StringValue("done"), FieldMember.of(BoolValue.TRUE)); if (!it.hasNext()) obj.defineOwnMember(args.env, "done", FieldMember.of(BoolValue.TRUE));
else obj.defineOwnMember(args.env, new StringValue("value"), FieldMember.of(it.next())); else obj.defineOwnMember(args.env, "value", FieldMember.of(it.next()));
return obj; return obj;
}); });
@ -526,11 +653,11 @@ public abstract class Value {
} }
else if (this instanceof VoidValue) return ((VoidValue)this).name; else if (this instanceof VoidValue) return ((VoidValue)this).name;
else if (this instanceof StringValue) return JSON.stringify(JSONElement.string(((StringValue)this).value)); else if (this instanceof StringValue) return JSON.stringify(JSONElement.string(((StringValue)this).value));
else if (this instanceof SymbolValue) return this.toString();
else return this.toString(env).value; else return this.toString(env).value;
} }
public final String toReadable(Environment ext) { public final String toReadable(Environment ext) {
if (this instanceof StringValue) return ((StringValue)this).value;
return toReadable(ext, new HashSet<>(), 0); return toReadable(ext, new HashSet<>(), 0);
} }

View File

@ -1,765 +0,0 @@
package me.topchetoeu.jscript.runtime.values;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import me.topchetoeu.jscript.common.Operation;
// import me.topchetoeu.jscript.lib.PromiseLib;
import me.topchetoeu.jscript.runtime.debug.DebugContext;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.exceptions.ConvertException;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
import me.topchetoeu.jscript.runtime.values.functions.CodeFunction;
import me.topchetoeu.jscript.runtime.values.functions.FunctionValue;
import me.topchetoeu.jscript.runtime.values.functions.NativeFunction;
import me.topchetoeu.jscript.runtime.values.objects.ArrayValue;
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
import me.topchetoeu.jscript.utils.interop.NativeWrapperProvider;
public class Values {
public static enum CompareResult {
NOT_EQUAL,
EQUAL,
LESS,
GREATER;
public boolean less() { return this == LESS; }
public boolean greater() { return this == GREATER; }
public boolean lessOrEqual() { return this == LESS || this == EQUAL; }
public boolean greaterOrEqual() { return this == GREATER || this == EQUAL; }
public static CompareResult from(int cmp) {
if (cmp < 0) return LESS;
if (cmp > 0) return GREATER;
return EQUAL;
}
}
public static final Object NULL = new Object();
public static final Object NO_RETURN = new Object();
public static boolean isWrapper(Object val) { return val instanceof NativeWrapper; }
public static boolean isWrapper(Object val, Class<?> clazz) {
if (!isWrapper(val)) return false;
var res = (NativeWrapper)val;
return res != null && clazz.isInstance(res.wrapped);
}
public static boolean isNan(Object val) { return val instanceof Number && Double.isNaN(number(val)); }
public static double number(Object val) {
if (val instanceof Number) return ((Number)val).doubleValue();
else return Double.NaN;
}
@SuppressWarnings("unchecked")
public static <T> T wrapper(Object val, Class<T> clazz) {
if (isWrapper(val)) val = ((NativeWrapper)val).wrapped;
if (val != null && clazz.isInstance(val)) return (T)val;
else return null;
}
public static String type(Object val) {
if (val == null) return "undefined";
if (val instanceof String) return "string";
if (val instanceof Number) return "number";
if (val instanceof Boolean) return "boolean";
if (val instanceof SymbolValue) return "symbol";
if (val instanceof FunctionValue) return "function";
return "object";
}
private static Object tryCallConvertFunc(Environment ext, Object obj, String name) {
var func = getMember(ext, obj, name);
if (func instanceof FunctionValue) {
var res = Values.call(ext, func, obj);
if (isPrimitive(res)) return res;
}
throw EngineException.ofType("Value couldn't be converted to a primitive.");
}
public static boolean isPrimitive(Object obj) {
return
obj instanceof Number ||
obj instanceof String ||
obj instanceof Boolean ||
obj instanceof SymbolValue ||
obj == null ||
obj == NULL;
}
public static Object toPrimitive(Environment ext, Object obj, ConvertHint hint) {
obj = normalize(ext, obj);
if (isPrimitive(obj)) return obj;
var first = hint == ConvertHint.VALUEOF ? "valueOf" : "toString";
var second = hint == ConvertHint.VALUEOF ? "toString" : "valueOf";
if (ext != null) {
try { return tryCallConvertFunc(ext, obj, first); }
catch (EngineException unused) { return tryCallConvertFunc(ext, obj, second); }
}
throw EngineException.ofType("Value couldn't be converted to a primitive.");
}
public static boolean toBoolean(Object obj) {
if (obj == NULL || obj == null) return false;
if (obj instanceof Number && (number(obj) == 0 || Double.isNaN(number(obj)))) return false;
if (obj instanceof String && ((String)obj).equals("")) return false;
if (obj instanceof Boolean) return (Boolean)obj;
return true;
}
public static double toNumber(Environment ext, Object obj) {
var val = toPrimitive(ext, obj, ConvertHint.VALUEOF);
if (val instanceof Number) return number(val);
if (val instanceof Boolean) return ((Boolean)val) ? 1 : 0;
if (val instanceof String) {
try { return Double.parseDouble((String)val); }
catch (NumberFormatException e) { return Double.NaN; }
}
return Double.NaN;
}
public static String toString(Environment ext, Object obj) {
var val = toPrimitive(ext, obj, ConvertHint.VALUEOF);
if (val == null) return "undefined";
if (val == NULL) return "null";
if (val instanceof Number) {
var d = number(val);
if (d == Double.NEGATIVE_INFINITY) return "-Infinity";
if (d == Double.POSITIVE_INFINITY) return "Infinity";
if (Double.isNaN(d)) return "NaN";
return BigDecimal.valueOf(d).stripTrailingZeros().toPlainString();
}
if (val instanceof Boolean) return (Boolean)val ? "true" : "false";
if (val instanceof String) return (String)val;
if (val instanceof SymbolValue) return val.toString();
return "Unknown value";
}
public static Object add(Environment ext, Object a, Object b) {
if (a instanceof String || b instanceof String) return toString(ext, a) + toString(ext, b);
else return toNumber(ext, a) + toNumber(ext, b);
}
public static double subtract(Environment ext, Object a, Object b) {
return toNumber(ext, a) - toNumber(ext, b);
}
public static double multiply(Environment ext, Object a, Object b) {
return toNumber(ext, a) * toNumber(ext, b);
}
public static double divide(Environment ext, Object a, Object b) {
return toNumber(ext, a) / toNumber(ext, b);
}
public static double modulo(Environment ext, Object a, Object b) {
return toNumber(ext, a) % toNumber(ext, b);
}
public static double negative(Environment ext, Object obj) {
return -toNumber(ext, obj);
}
public static int and(Environment ext, Object a, Object b) {
return (int)toNumber(ext, a) & (int)toNumber(ext, b);
}
public static int or(Environment ext, Object a, Object b) {
return (int)toNumber(ext, a) | (int)toNumber(ext, b);
}
public static int xor(Environment ext, Object a, Object b) {
return (int)toNumber(ext, a) ^ (int)toNumber(ext, b);
}
public static int bitwiseNot(Environment ext, Object obj) {
return ~(int)toNumber(ext, obj);
}
public static int shiftLeft(Environment ext, Object a, Object b) {
return (int)toNumber(ext, a) << (int)toNumber(ext, b);
}
public static int shiftRight(Environment ext, Object a, Object b) {
return (int)toNumber(ext, a) >> (int)toNumber(ext, b);
}
public static long unsignedShiftRight(Environment ext, Object a, Object b) {
long _a = (long)toNumber(ext, a);
long _b = (long)toNumber(ext, b);
if (_a < 0) _a += 0x100000000l;
if (_b < 0) _b += 0x100000000l;
return _a >>> _b;
}
public static CompareResult compare(Environment ext, Object a, Object b) {
a = toPrimitive(ext, a, ConvertHint.VALUEOF);
b = toPrimitive(ext, b, ConvertHint.VALUEOF);
if (a instanceof String && b instanceof String) CompareResult.from(((String)a).compareTo((String)b));
var _a = toNumber(ext, a);
var _b = toNumber(ext, b);
if (Double.isNaN(_a) || Double.isNaN(_b)) return CompareResult.NOT_EQUAL;
return CompareResult.from(Double.compare(_a, _b));
}
public static boolean not(Object obj) {
return !toBoolean(obj);
}
public static boolean isInstanceOf(Environment ext, Object obj, Object proto) {
if (obj == null || obj == NULL || proto == null || proto == NULL) return false;
var val = getPrototype(ext, obj);
while (val != null) {
if (val.equals(proto)) return true;
val = val.getPrototype(ext);
}
return false;
}
public static Object operation(Environment ext, Operation op, Object ...args) {
switch (op) {
case ADD: return add(ext, args[0], args[1]);
case SUBTRACT: return subtract(ext, args[0], args[1]);
case DIVIDE: return divide(ext, args[0], args[1]);
case MULTIPLY: return multiply(ext, args[0], args[1]);
case MODULO: return modulo(ext, args[0], args[1]);
case AND: return and(ext, args[0], args[1]);
case OR: return or(ext, args[0], args[1]);
case XOR: return xor(ext, args[0], args[1]);
case EQUALS: return strictEquals(ext, args[0], args[1]);
case NOT_EQUALS: return !strictEquals(ext, args[0], args[1]);
case LOOSE_EQUALS: return looseEqual(ext, args[0], args[1]);
case LOOSE_NOT_EQUALS: return !looseEqual(ext, args[0], args[1]);
case GREATER: return compare(ext, args[0], args[1]).greater();
case GREATER_EQUALS: return compare(ext, args[0], args[1]).greaterOrEqual();
case LESS: return compare(ext, args[0], args[1]).less();
case LESS_EQUALS: return compare(ext, args[0], args[1]).lessOrEqual();
case INVERSE: return bitwiseNot(ext, args[0]);
case NOT: return not(args[0]);
case POS: return toNumber(ext, args[0]);
case NEG: return negative(ext, args[0]);
case SHIFT_LEFT: return shiftLeft(ext, args[0], args[1]);
case SHIFT_RIGHT: return shiftRight(ext, args[0], args[1]);
case USHIFT_RIGHT: return unsignedShiftRight(ext, args[0], args[1]);
case IN: return hasMember(ext, args[1], args[0], false);
case INSTANCEOF: {
var proto = getMember(ext, args[1], "prototype");
return isInstanceOf(ext, args[0], proto);
}
default: return null;
}
}
public static Object getMember(Environment ctx, Object obj, Object key) {
obj = normalize(ctx, obj); key = normalize(ctx, key);
if (obj == null) throw new IllegalArgumentException("Tried to access member of undefined.");
if (obj == NULL) throw new IllegalArgumentException("Tried to access member of null.");
if (obj instanceof ObjectValue) return ((ObjectValue)obj).getMember(ctx, key, obj);
if (obj instanceof String && key instanceof Number) {
var i = number(key);
var s = (String)obj;
if (i >= 0 && i < s.length() && i - Math.floor(i) == 0) {
return s.charAt((int)i) + "";
}
}
var proto = getPrototype(ctx, obj);
if (proto == null) return "__proto__".equals(key) ? NULL : null;
else if (key != null && "__proto__".equals(key)) return proto;
else return proto.getMember(ctx, key, obj);
}
public static Object getMemberPath(Environment ctx, Object obj, Object ...path) {
var res = obj;
for (var key : path) res = getMember(ctx, res, key);
return res;
}
public static boolean setMember(Environment ctx, Object obj, Object key, Object val) {
obj = normalize(ctx, obj); key = normalize(ctx, key); val = normalize(ctx, val);
if (obj == null) throw EngineException.ofType("Tried to access member of undefined.");
if (obj == NULL) throw EngineException.ofType("Tried to access member of null.");
if (key != null && "__proto__".equals(key)) return setPrototype(ctx, obj, val);
if (obj instanceof ObjectValue) return ((ObjectValue)obj).setMember(ctx, key, val, obj, false);
var proto = getPrototype(ctx, obj);
return proto.setMember(ctx, key, val, obj, true);
}
public static boolean hasMember(Environment ctx, Object obj, Object key, boolean own) {
if (obj == null || obj == NULL) return false;
obj = normalize(ctx, obj); key = normalize(ctx, key);
if ("__proto__".equals(key)) return true;
if (obj instanceof ObjectValue) return ((ObjectValue)obj).hasMember(ctx, key, own);
if (obj instanceof String && key instanceof Number) {
var i = number(key);
var s = (String)obj;
if (i >= 0 && i < s.length() && i - Math.floor(i) == 0) return true;
}
if (own) return false;
var proto = getPrototype(ctx, obj);
return proto != null && proto.hasMember(ctx, key, own);
}
public static boolean deleteMember(Environment ext, Object obj, Object key) {
if (obj == null || obj == NULL) return false;
obj = normalize(ext, obj); key = normalize(ext, key);
if (obj instanceof ObjectValue) return ((ObjectValue)obj).deleteMember(ext, key);
else return false;
}
public static ObjectValue getPrototype(Environment ext, Object obj) {
if (obj == null || obj == NULL) return null;
obj = normalize(ext, obj);
if (obj instanceof ObjectValue) return ((ObjectValue)obj).getPrototype(ext);
if (ext == null) return null;
if (obj instanceof String) return ext.get(Environment.STRING_PROTO);
else if (obj instanceof Number) return ext.get(Environment.NUMBER_PROTO);
else if (obj instanceof Boolean) return ext.get(Environment.BOOL_PROTO);
else if (obj instanceof SymbolValue) return ext.get(Environment.SYMBOL_PROTO);
return null;
}
public static boolean setPrototype(Environment ext, Object obj, Object proto) {
obj = normalize(ext, obj);
return obj instanceof ObjectValue && ((ObjectValue)obj).setPrototype(ext, proto);
}
public static void makePrototypeChain(Environment ext, Object... chain) {
for(var i = 1; i < chain.length; i++) {
setPrototype(ext, chain[i], chain[i - 1]);
}
}
public static List<Object> getMembers(Environment ext, Object obj, boolean own, boolean includeNonEnumerable) {
List<Object> res = new ArrayList<>();
if (obj instanceof ObjectValue) res = ((ObjectValue)obj).keys(includeNonEnumerable);
if (obj instanceof String) {
for (var i = 0; i < ((String)obj).length(); i++) res.add((double)i);
}
if (!own) {
var proto = getPrototype(ext, obj);
while (proto != null) {
res.addAll(proto.keys(includeNonEnumerable));
proto = getPrototype(ext, proto);
}
}
return res;
}
public static ObjectValue getMemberDescriptor(Environment ext, Object obj, Object key) {
if (obj instanceof ObjectValue) return ((ObjectValue)obj).getMemberDescriptor(ext, key);
else if (obj instanceof String && key instanceof Number) {
var i = ((Number)key).intValue();
var _i = ((Number)key).doubleValue();
if (i - _i != 0) return null;
if (i < 0 || i >= ((String)obj).length()) return null;
return new ObjectValue(ext, Map.of(
"value", ((String)obj).charAt(i) + "",
"writable", false,
"enumerable", true,
"configurable", false
));
}
else return null;
}
public static Object call(Environment ext, Object func, Object thisArg, Object ...args) {
if (!(func instanceof FunctionValue)) throw EngineException.ofType("Tried to call a non-function value.");
return ((FunctionValue)func).call(ext, thisArg, args);
}
public static Object callNew(Environment ext, Object func, Object ...args) {
var res = new ObjectValue();
try {
var proto = Values.getMember(ext, func, "prototype");
setPrototype(ext, res, proto);
var ret = call(ext, func, res, args);
if (!isPrimitive(ret)) return ret;
return res;
}
catch (IllegalArgumentException e) {
throw EngineException.ofType("Tried to call new on an invalid constructor.");
}
}
public static boolean strictEquals(Environment ext, Object a, Object b) {
a = normalize(ext, a);
b = normalize(ext, b);
if (a == null || b == null) return a == null && b == null;
if (isNan(a) || isNan(b)) return false;
if (a instanceof Number && number(a) == -0.) a = 0.;
if (b instanceof Number && number(b) == -0.) b = 0.;
return a == b || a.equals(b);
}
public static boolean looseEqual(Environment ext, Object a, Object b) {
a = normalize(ext, a); b = normalize(ext, b);
// In loose equality, null is equivalent to undefined
if (a == NULL) a = null;
if (b == NULL) b = null;
if (a == null || b == null) return a == null && b == null;
// If both are objects, just compare their references
if (!isPrimitive(a) && !isPrimitive(b)) return a == b;
// Convert values to primitives
a = toPrimitive(ext, a, ConvertHint.VALUEOF);
b = toPrimitive(ext, b, ConvertHint.VALUEOF);
// Compare symbols by reference
if (a instanceof SymbolValue || b instanceof SymbolValue) return a == b;
if (a instanceof Boolean || b instanceof Boolean) return toBoolean(a) == toBoolean(b);
if (a instanceof Number || b instanceof Number) return strictEquals(ext, toNumber(ext, a), toNumber(ext, b));
// Default to strings
return toString(ext, a).equals(toString(ext, b));
}
public static Object normalize(Environment ext, Object val) {
if (val instanceof Number) return number(val);
if (isPrimitive(val) || val instanceof ObjectValue) return val;
if (val instanceof Character) return val + "";
if (val instanceof Map) {
var res = new ObjectValue();
for (var entry : ((Map<?, ?>)val).entrySet()) {
res.defineProperty(ext, entry.getKey(), entry.getValue());
}
return res;
}
if (val instanceof Iterable) {
var res = new ArrayValue();
for (var entry : ((Iterable<?>)val)) {
res.set(ext, res.size(), entry);
}
return res;
}
if (val instanceof Class) {
if (ext == null) return null;
else return NativeWrapperProvider.get(ext).getConstr((Class<?>)val);
}
return NativeWrapper.of(ext, val);
}
@SuppressWarnings("unchecked")
public static <T> T convert(Environment ext, Object obj, Class<T> clazz) {
if (clazz == Void.class) return null;
if (obj instanceof NativeWrapper) {
var res = ((NativeWrapper)obj).wrapped;
if (clazz.isInstance(res)) return (T)res;
}
if (clazz == null || clazz == Object.class) return (T)obj;
if (obj instanceof ArrayValue) {
if (clazz.isAssignableFrom(ArrayList.class)) {
var raw = ((ArrayValue)obj).toArray();
var res = new ArrayList<>();
for (var i = 0; i < raw.length; i++) res.add(convert(ext, raw[i], Object.class));
return (T)new ArrayList<>(res);
}
if (clazz.isAssignableFrom(HashSet.class)) {
var raw = ((ArrayValue)obj).toArray();
var res = new HashSet<>();
for (var i = 0; i < raw.length; i++) res.add(convert(ext, raw[i], Object.class));
return (T)new HashSet<>(res);
}
if (clazz.isArray()) {
var raw = ((ArrayValue)obj).toArray();
Object res = Array.newInstance(clazz.getComponentType(), raw.length);
for (var i = 0; i < raw.length; i++) Array.set(res, i, convert(ext, raw[i], Object.class));
return (T)res;
}
}
if (obj instanceof ObjectValue && clazz.isAssignableFrom(HashMap.class)) {
var res = new HashMap<>();
for (var el : ((ObjectValue)obj).values.entrySet()) res.put(
convert(ext, el.getKey(), null),
convert(ext, el.getValue(), null)
);
return (T)res;
}
if (clazz == String.class) return (T)toString(ext, obj);
if (clazz == Boolean.class || clazz == Boolean.TYPE) return (T)(Boolean)toBoolean(obj);
if (clazz == Byte.class || clazz == byte.class) return (T)(Byte)(byte)toNumber(ext, obj);
if (clazz == Integer.class || clazz == int.class) return (T)(Integer)(int)toNumber(ext, obj);
if (clazz == Long.class || clazz == long.class) return (T)(Long)(long)toNumber(ext, obj);
if (clazz == Short.class || clazz == short.class) return (T)(Short)(short)toNumber(ext, obj);
if (clazz == Float.class || clazz == float.class) return (T)(Float)(float)toNumber(ext, obj);
if (clazz == Double.class || clazz == double.class) return (T)(Double)toNumber(ext, obj);
if (clazz == Character.class || clazz == char.class) {
if (obj instanceof Number) return (T)(Character)(char)number(obj);
else {
var res = toString(ext, obj);
if (res.length() == 0) throw new ConvertException("\"\"", "Character");
else return (T)(Character)res.charAt(0);
}
}
if (obj == null) return null;
if (clazz.isInstance(obj)) return (T)obj;
if (clazz.isAssignableFrom(NativeWrapper.class)) {
return (T)NativeWrapper.of(ext, obj);
}
throw new ConvertException(type(obj), clazz.getSimpleName());
}
public static Iterable<Object> fromJSIterator(Environment ext, Object obj) {
return () -> {
try {
var symbol = SymbolValue.get("Symbol.iterator");
var iteratorFunc = getMember(ext, obj, symbol);
if (!(iteratorFunc instanceof FunctionValue)) return Collections.emptyIterator();
var iterator = iteratorFunc instanceof FunctionValue ?
((FunctionValue)iteratorFunc).call(ext, obj, obj) :
iteratorFunc;
var nextFunc = getMember(ext, call(ext, iteratorFunc, obj), "next");
if (!(nextFunc instanceof FunctionValue)) return Collections.emptyIterator();
return new Iterator<Object>() {
private Object value = null;
public boolean consumed = true;
private FunctionValue next = (FunctionValue)nextFunc;
private void loadNext() {
if (next == null) value = null;
else if (consumed) {
var curr = next.call(ext, iterator);
if (curr == null) { next = null; value = null; }
if (toBoolean(Values.getMember(ext, curr, "done"))) { next = null; value = null; }
else {
this.value = Values.getMember(ext, curr, "value");
consumed = false;
}
}
}
@Override
public boolean hasNext() {
loadNext();
return next != null;
}
@Override
public Object next() {
loadNext();
var res = value;
value = null;
consumed = true;
return res;
}
};
}
catch (IllegalArgumentException | NullPointerException e) {
return Collections.emptyIterator();
}
};
}
public static ObjectValue toJSIterator(Environment ext, Iterator<?> it) {
var res = new ObjectValue();
try {
var key = getMember(ext, getMember(ext, ext.get(Environment.SYMBOL_PROTO), "constructor"), "iterator");
res.defineProperty(ext, key, new NativeFunction("", args -> args.self));
}
catch (IllegalArgumentException | NullPointerException e) { }
res.defineProperty(ext, "next", new NativeFunction("", args -> {
if (!it.hasNext()) return new ObjectValue(ext, Map.of("done", true));
else {
var obj = new ObjectValue();
obj.defineProperty(args.env, "value", it.next());
return obj;
}
}));
return res;
}
public static ObjectValue toJSIterator(Environment ext, Iterable<?> it) {
return toJSIterator(ext, it.iterator());
}
public static ObjectValue toJSAsyncIterator(Environment ext, Iterator<?> it) {
var res = new ObjectValue();
try {
var key = getMemberPath(ext, ext.get(Environment.SYMBOL_PROTO), "constructor", "asyncIterator");
res.defineProperty(ext, key, new NativeFunction("", args -> args.self));
}
catch (IllegalArgumentException | NullPointerException e) { }
res.defineProperty(ext, "next", new NativeFunction("", args -> {
return PromiseLib.await(args.env, () -> {
if (!it.hasNext()) return new ObjectValue(ext, Map.of("done", true));
else {
var obj = new ObjectValue();
object.defineProperty(args.env, "value", it.next());
return object;
}
});
}));
return res;
}
private static boolean isEmptyFunc(ObjectValue val) {
if (!(val instanceof FunctionValue)) return false;
if (!val.values.containsKey("prototype") || val.values.size() + val.properties.size() > 1) return false;
var proto = val.values.get("prototype");
if (!(proto instanceof ObjectValue)) return false;
var protoObj = (ObjectValue)proto;
if (protoObj.values.get("constructor") != val) return false;
if (protoObj.values.size() + protoObj.properties.size() != 1) return false;
return true;
}
private static String toReadable(Environment ext, Object val, HashSet<Object> passed, int tab) {
if (tab == 0 && val instanceof String) return (String)val;
if (passed.contains(val)) return "[circular]";
var printed = true;
var res = new StringBuilder();
var dbg = DebugContext.get(ext);
if (val instanceof FunctionValue) {
res.append(val.toString());
var loc = val instanceof CodeFunction ? dbg.getMapOrEmpty((CodeFunction)val).start() : null;
if (loc != null) res.append(" @ " + loc);
}
else if (val instanceof ArrayValue) {
res.append("[");
var obj = ((ArrayValue)val);
for (int i = 0; i < obj.size(); i++) {
if (i != 0) res.append(", ");
else res.append(" ");
if (obj.has(i)) res.append(toReadable(ext, obj.get(i), passed, tab));
else res.append("<empty>");
}
res.append(" ] ");
}
else if (val instanceof NativeWrapper) {
var obj = ((NativeWrapper)val).wrapped;
res.append("Native " + obj.toString() + " ");
}
else printed = false;
if (val instanceof ObjectValue) {
if (tab > 3) {
return "{...}";
}
passed.add(val);
var obj = (ObjectValue)val;
if (obj.values.size() + obj.properties.size() == 0 || isEmptyFunc(obj)) {
if (!printed) res.append("{}\n");
}
else {
res.append("{\n");
for (var el : obj.values.entrySet()) {
for (int i = 0; i < tab + 1; i++) res.append(" ");
res.append(toReadable(ext, el.getKey(), passed, tab + 1));
res.append(": ");
res.append(toReadable(ext, el.getValue(), passed, tab + 1));
res.append(",\n");
}
for (var el : obj.properties.entrySet()) {
for (int i = 0; i < tab + 1; i++) res.append(" ");
res.append(toReadable(ext, el.getKey(), passed, tab + 1));
res.append(": [prop],\n");
}
for (int i = 0; i < tab; i++) res.append(" ");
res.append("}");
}
passed.remove(val);
}
else if (val == null) return "undefined";
else if (val == Values.NULL) return "null";
else if (val instanceof String) return "'" + val + "'";
else return Values.toString(ext, val);
return res.toString();
}
public static String toReadable(Environment ext, Object val) {
return toReadable(ext, val, new HashSet<>(), 0);
}
public static String errorToReadable(RuntimeException err, String prefix) {
prefix = prefix == null ? "Uncaught" : "Uncaught " + prefix;
if (err instanceof EngineException) {
var ee = ((EngineException)err);
try {
return prefix + " " + ee.toString(ee.env);
}
catch (EngineException ex) {
return prefix + " " + toReadable(ee.env, ee.value);
}
}
else if (err instanceof SyntaxException) {
return prefix + " SyntaxError " + ((SyntaxException)err).msg;
}
else if (err.getCause() instanceof InterruptedException) return "";
else {
var str = new ByteArrayOutputStream();
err.printStackTrace(new PrintStream(str));
return prefix + " internal error " + str.toString();
}
}
public static void printValue(Environment ext, Object val) {
System.out.print(toReadable(ext, val));
}
public static void printError(RuntimeException err, String prefix) {
System.out.println(errorToReadable(err, prefix));
}
}

View File

@ -9,6 +9,7 @@ public class Arguments {
public final Value self; public final Value self;
public final Value[] args; public final Value[] args;
public final Environment env; public final Environment env;
public final boolean isNew;
public int n() { public int n() {
return args.length; return args.length;
@ -31,9 +32,10 @@ public class Arguments {
else return get(i); else return get(i);
} }
public Arguments(Environment env, Value thisArg, Value... args) { public Arguments(Environment env, boolean isNew, Value thisArg, Value... args) {
this.env = env; this.env = env;
this.args = args; this.args = args;
this.self = thisArg; this.self = thisArg;
this.isNew = isNew;
} }
} }

View File

@ -11,8 +11,8 @@ public class CodeFunction extends FunctionValue {
public final ValueVariable[] captures; public final ValueVariable[] captures;
public Environment env; public Environment env;
@Override public Value call(Environment env, Value thisArg, Value ...args) { @Override public Value onCall(Environment env, boolean isNew, String name, Value thisArg, Value ...args) {
var frame = new Frame(env, thisArg, args, this); var frame = new Frame(env, isNew, thisArg, args, this);
frame.onPush(); frame.onPush();
try { try {

View File

@ -1,6 +1,7 @@
package me.topchetoeu.jscript.runtime.values.functions; package me.topchetoeu.jscript.runtime.values.functions;
import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.values.KeyCache;
import me.topchetoeu.jscript.runtime.values.Member; import me.topchetoeu.jscript.runtime.values.Member;
import me.topchetoeu.jscript.runtime.values.Value; import me.topchetoeu.jscript.runtime.values.Value;
import me.topchetoeu.jscript.runtime.values.Member.FieldMember; import me.topchetoeu.jscript.runtime.values.Member.FieldMember;
@ -13,8 +14,12 @@ public abstract class FunctionValue extends ObjectValue {
public int length; public int length;
public Value prototype = new ObjectValue(); public Value prototype = new ObjectValue();
private final FieldMember nameField = new FieldMember(false, true, true) { public boolean enableCall = true;
public boolean enableNew = true;
private final FieldMember nameField = new FieldMember(true, false, false) {
@Override public Value get(Environment env, Value self) { @Override public Value get(Environment env, Value self) {
if (name == null) return new StringValue("");
return new StringValue(name); return new StringValue(name);
} }
@Override public boolean set(Environment env, Value val, Value self) { @Override public boolean set(Environment env, Value val, Value self) {
@ -22,7 +27,7 @@ public abstract class FunctionValue extends ObjectValue {
return true; return true;
} }
}; };
private final FieldMember lengthField = new FieldMember(false, true, false) { private final FieldMember lengthField = new FieldMember(true, false, false) {
@Override public Value get(Environment env, Value self) { @Override public Value get(Environment env, Value self) {
return new NumberValue(length); return new NumberValue(length);
} }
@ -30,7 +35,7 @@ public abstract class FunctionValue extends ObjectValue {
return false; return false;
} }
}; };
private final FieldMember prototypeField = new FieldMember(false, true, true) { private final FieldMember prototypeField = new FieldMember(false, false, true) {
@Override public Value get(Environment env, Value self) { @Override public Value get(Environment env, Value self) {
return prototype; return prototype;
} }
@ -40,28 +45,40 @@ public abstract class FunctionValue extends ObjectValue {
} }
}; };
protected abstract Value onCall(Environment ext, boolean isNew, String name, Value thisArg, Value ...args);
@Override public String toString() { return String.format("function %s(...)", name); } @Override public String toString() { return String.format("function %s(...)", name); }
@Override public abstract Value call(Environment ext, Value thisArg, Value ...args); @Override public Value call(Environment ext, boolean isNew, String name, Value thisArg, Value ...args) {
if (isNew && !enableNew) super.call(ext, isNew, name, thisArg, args);
if (!isNew && !enableCall) super.call(ext, isNew, name, thisArg, args);
@Override public Member getOwnMember(Environment env, Value key) { return onCall(ext, isNew, name, thisArg, args);
var el = key.toString(env).value;
if (el.equals("length")) return lengthField;
if (el.equals("name")) return nameField;
if (el.equals("prototype")) return prototypeField;
return super.getOwnMember(env, key);
} }
@Override public boolean deleteOwnMember(Environment env, Value key) {
if (!super.deleteOwnMember(env, key)) return false;
var el = key.toString(env).value; @Override public Member getOwnMember(Environment env, KeyCache key) {
switch (key.toString(env)) {
case "length": return lengthField;
case "name": return nameField;
case "prototype": return prototypeField;
default: return super.getOwnMember(env, key);
}
}
@Override public boolean deleteOwnMember(Environment env, KeyCache key) {
switch (key.toString(env)) {
case "length":
length = 0;
return true;
case "name":
name = "";
return true;
case "prototype":
return false;
default: return super.deleteOwnMember(env, key);
}
}
if (el.equals("length")) return false; public void setName(String val) {
if (el.equals("name")) return false; if (this.name == null || this.name.equals("")) this.name = val;
if (el.equals("prototype")) return false;
return true;
} }
public FunctionValue(String name, int length) { public FunctionValue(String name, int length) {
@ -71,7 +88,7 @@ public abstract class FunctionValue extends ObjectValue {
this.length = length; this.length = length;
this.name = name; this.name = name;
prototype.defineOwnMember(Environment.empty(), new StringValue("constructor"), FieldMember.of(this)); prototype.defineOwnMember(null, "constructor", FieldMember.of(this));
} }
} }

View File

@ -10,8 +10,8 @@ public class NativeFunction extends FunctionValue {
public final NativeFunctionRunner action; public final NativeFunctionRunner action;
@Override public Value call(Environment env, Value self, Value ...args) { @Override public Value onCall(Environment env, boolean isNew, String name, Value self, Value ...args) {
return action.run(new Arguments(env, self, args)); return action.run(new Arguments(env, isNew, self, args));
} }
public NativeFunction(String name, NativeFunctionRunner action) { public NativeFunction(String name, NativeFunctionRunner action) {

View File

@ -8,6 +8,7 @@ import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.values.KeyCache;
import me.topchetoeu.jscript.runtime.values.Member; import me.topchetoeu.jscript.runtime.values.Member;
import me.topchetoeu.jscript.runtime.values.Member.FieldMember; import me.topchetoeu.jscript.runtime.values.Member.FieldMember;
import me.topchetoeu.jscript.runtime.values.Value; import me.topchetoeu.jscript.runtime.values.Value;
@ -19,6 +20,16 @@ public class ArrayValue extends ObjectValue implements Iterable<Value> {
private Value[] values; private Value[] values;
private int size; private int size;
private final FieldMember lengthField = new FieldMember(false, false, true) {
@Override public Value get(Environment env, Value self) {
return new NumberValue(size);
}
@Override public boolean set(Environment env, Value val, Value self) {
size = val.toInt(env);
return true;
}
};
private class IndexField extends FieldMember { private class IndexField extends FieldMember {
private int i; private int i;
private ArrayValue arr; private ArrayValue arr;
@ -44,7 +55,7 @@ public class ArrayValue extends ObjectValue implements Iterable<Value> {
var arr = new Value[index]; var arr = new Value[index];
System.arraycopy(values, 0, arr, 0, values.length); System.arraycopy(values, 0, arr, 0, values.length);
return arr; return values = arr;
} }
public int size() { return size; } public int size() { return size; }
@ -95,11 +106,11 @@ public class ArrayValue extends ObjectValue implements Iterable<Value> {
} }
public void copyTo(Value[] arr, int sourceStart, int destStart, int count) { public void copyTo(Value[] arr, int sourceStart, int destStart, int count) {
var nullFill = values.length - destStart + count; var nullFill = Math.max(0, arr.length - size - destStart);
count -= nullFill; count -= nullFill;
System.arraycopy(values, sourceStart, arr, destStart, count); System.arraycopy(values, sourceStart, arr, destStart, count);
Arrays.fill(arr, count, nullFill, null); Arrays.fill(arr, count, nullFill + count, null);
} }
public void copyTo(ArrayValue arr, int sourceStart, int destStart, int count) { public void copyTo(ArrayValue arr, int sourceStart, int destStart, int count) {
if (arr == this) { if (arr == this) {
@ -138,36 +149,37 @@ public class ArrayValue extends ObjectValue implements Iterable<Value> {
}); });
} }
@Override public Member getOwnMember(Environment env, Value key) { @Override public Member getOwnMember(Environment env, KeyCache key) {
var res = super.getOwnMember(env, key); var res = super.getOwnMember(env, key);
if (res != null) return res; if (res != null) return res;
var num = key.toNumber(env); var num = key.toNumber(env);
var i = num.toInt(env); var i = key.toInt(env);
if (i == num.value && i >= 0 && i < size) return new IndexField(i, this); if (i == num && i >= 0 && i < size && has(i)) return new IndexField(i, this);
else if (key.toString(env).equals("length")) return lengthField;
else return null; else return null;
} }
@Override public boolean defineOwnMember(Environment env, Value key, Member member) { @Override public boolean defineOwnMember(Environment env, KeyCache key, Member member) {
if (!(member instanceof FieldMember) || hasMember(env, key, true)) return super.defineOwnMember(env, key, member); if (!(member instanceof FieldMember) || hasMember(env, key, true)) return super.defineOwnMember(env, key, member);
if (!extensible) return false; if (!extensible) return false;
var num = key.toNumber(env); var num = key.toNumber(env);
var i = num.toInt(env); var i = key.toInt(env);
if (i == num.value && i >= 0) { if (i == num && i >= 0) {
set(i, ((FieldMember)member).get(env, this)); set(i, ((FieldMember)member).get(env, this));
return true; return true;
} }
else return super.defineOwnMember(env, key, member); else return super.defineOwnMember(env, key, member);
} }
@Override public boolean deleteOwnMember(Environment env, Value key) { @Override public boolean deleteOwnMember(Environment env, KeyCache key) {
if (!super.deleteOwnMember(env, key)) return false; if (!super.deleteOwnMember(env, key)) return false;
var num = key.toNumber(env); var num = key.toNumber(env);
var i = num.toInt(env); var i = key.toInt(env);
if (i == num.value && i >= 0 && i < size) return super.deleteOwnMember(env, key); if (i == num && i >= 0 && i < size) return super.deleteOwnMember(env, key);
else return true; else return true;
} }
@ -175,9 +187,12 @@ public class ArrayValue extends ObjectValue implements Iterable<Value> {
var res = new LinkedHashMap<String, Member>(); var res = new LinkedHashMap<String, Member>();
for (var i = 0; i < size; i++) { for (var i = 0; i < size; i++) {
res.put(i + "", getOwnMember(env, new NumberValue(i))); var member = getOwnMember(env, i);
if (member != null) res.put(i + "", member);
} }
res.put("length", lengthField);
res.putAll(super.getOwnMembers(env)); res.putAll(super.getOwnMembers(env));
return res; return res;

View File

@ -7,10 +7,10 @@ import java.util.Map;
import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.environment.Key; import me.topchetoeu.jscript.runtime.environment.Key;
import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.values.KeyCache;
import me.topchetoeu.jscript.runtime.values.Member; import me.topchetoeu.jscript.runtime.values.Member;
import me.topchetoeu.jscript.runtime.values.Value; import me.topchetoeu.jscript.runtime.values.Value;
import me.topchetoeu.jscript.runtime.values.functions.FunctionValue; import me.topchetoeu.jscript.runtime.values.functions.FunctionValue;
import me.topchetoeu.jscript.runtime.values.primitives.BoolValue;
import me.topchetoeu.jscript.runtime.values.primitives.NumberValue; import me.topchetoeu.jscript.runtime.values.primitives.NumberValue;
import me.topchetoeu.jscript.runtime.values.primitives.StringValue; import me.topchetoeu.jscript.runtime.values.primitives.StringValue;
import me.topchetoeu.jscript.runtime.values.primitives.SymbolValue; import me.topchetoeu.jscript.runtime.values.primitives.SymbolValue;
@ -66,7 +66,7 @@ public class ObjectValue extends Value {
throw EngineException.ofType("Value couldn't be converted to a primitive."); throw EngineException.ofType("Value couldn't be converted to a primitive.");
} }
@Override public StringValue toString(Environment env) { return toPrimitive(env).toString(env); } @Override public StringValue toString(Environment env) { return toPrimitive(env).toString(env); }
@Override public BoolValue toBoolean() { return BoolValue.TRUE; } @Override public boolean toBoolean() { return true; }
@Override public NumberValue toNumber(Environment env) { return toPrimitive(env).toNumber(env); } @Override public NumberValue toNumber(Environment env) { return toPrimitive(env).toNumber(env); }
@Override public StringValue type() { return typeString; } @Override public StringValue type() { return typeString; }
@ -76,33 +76,29 @@ public class ObjectValue extends Value {
extensible = false; extensible = false;
} }
@Override public Member getOwnMember(Environment env, Value key) { @Override public Member getOwnMember(Environment env, KeyCache key) {
if (key instanceof SymbolValue) return symbolMembers.get(key); if (key.isSymbol()) return symbolMembers.get(key.toSymbol());
else return members.get(key.toString(env).value); else return members.get(key.toString(env));
} }
@Override public boolean defineOwnMember(Environment env, Value key, Member member) { @Override public boolean defineOwnMember(Environment env, KeyCache key, Member member) {
if (!(key instanceof SymbolValue)) key = key.toString(env);
var old = getOwnMember(env, key); var old = getOwnMember(env, key);
if (old != null && old.configure(env, member, this)) return true; if (old != null && old.configure(env, member, this)) return true;
if (old != null && !old.configurable()) return false; if (old != null && !old.configurable()) return false;
if (key instanceof SymbolValue) symbolMembers.put((SymbolValue)key, member); if (key.isSymbol()) symbolMembers.put(key.toSymbol(), member);
else members.put(key.toString(env).value, member); else members.put(key.toString(env), member);
return true; return true;
} }
@Override public boolean deleteOwnMember(Environment env, Value key) { @Override public boolean deleteOwnMember(Environment env, KeyCache key) {
if (!extensible) return false; if (!extensible) return false;
if (!(key instanceof SymbolValue)) key = key.toString(env);
var member = getOwnMember(env, key); var member = getOwnMember(env, key);
if (member == null) return true; if (member == null) return true;
if (member.configurable()) return false; if (member.configurable()) return false;
if (key instanceof SymbolValue) symbolMembers.remove(key); if (key.isSymbol()) symbolMembers.remove(key.toSymbol());
else members.remove(key.toString(env).value); else members.remove(key.toString(env));
return true; return true;
} }
@ -114,7 +110,7 @@ public class ObjectValue extends Value {
} }
@Override public ObjectValue getPrototype(Environment env) { @Override public ObjectValue getPrototype(Environment env) {
if (prototype == null) return null; if (prototype == null || env == null) return null;
else return prototype.get(env); else return prototype.get(env);
} }
@Override public final boolean setPrototype(Environment env, ObjectValue val) { @Override public final boolean setPrototype(Environment env, ObjectValue val) {

View File

@ -4,7 +4,6 @@ import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.scope.ValueVariable; import me.topchetoeu.jscript.runtime.scope.ValueVariable;
import me.topchetoeu.jscript.runtime.values.Value; import me.topchetoeu.jscript.runtime.values.Value;
import me.topchetoeu.jscript.runtime.values.Member.FieldMember; import me.topchetoeu.jscript.runtime.values.Member.FieldMember;
import me.topchetoeu.jscript.runtime.values.primitives.StringValue;
public class ScopeValue extends ObjectValue { public class ScopeValue extends ObjectValue {
private class VariableField extends FieldMember { private class VariableField extends FieldMember {
@ -29,7 +28,7 @@ public class ScopeValue extends ObjectValue {
public ScopeValue(ValueVariable[] variables, String[] names) { public ScopeValue(ValueVariable[] variables, String[] names) {
this.variables = variables; this.variables = variables;
for (var i = 0; i < names.length && i < variables.length; i++) { for (var i = 0; i < names.length && i < variables.length; i++) {
defineOwnMember(Environment.empty(), new StringValue(i + ""), new VariableField(i)); defineOwnMember(Environment.empty(), i, new VariableField(i));
} }
} }
} }

View File

@ -13,7 +13,7 @@ public final class BoolValue extends PrimitiveValue {
@Override public StringValue type() { return typeString; } @Override public StringValue type() { return typeString; }
@Override public BoolValue toBoolean() { return this; } @Override public boolean toBoolean() { return value; }
@Override public NumberValue toNumber(Environment ext) { @Override public NumberValue toNumber(Environment ext) {
return value ? new NumberValue(1) : new NumberValue(0); return value ? new NumberValue(1) : new NumberValue(0);
} }

View File

@ -1,7 +1,9 @@
package me.topchetoeu.jscript.runtime.values.primitives; package me.topchetoeu.jscript.runtime.values.primitives;
import java.math.BigDecimal; import me.topchetoeu.jscript.common.json.JSON;
import me.topchetoeu.jscript.common.json.JSONElement;
import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.values.Value; import me.topchetoeu.jscript.runtime.values.Value;
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
@ -14,21 +16,19 @@ public final class NumberValue extends PrimitiveValue {
@Override public StringValue type() { return typeString; } @Override public StringValue type() { return typeString; }
@Override public BoolValue toBoolean() { return BoolValue.of(value != 0); } @Override public boolean toBoolean() { return value != 0; }
@Override public NumberValue toNumber(Environment ext) { return this; } @Override public NumberValue toNumber(Environment ext) { return this; }
@Override public StringValue toString(Environment ext) { return new StringValue(toString()); } @Override public StringValue toString(Environment ext) { return new StringValue(toString()); }
@Override public String toString() { @Override public String toString() { return JSON.stringify(JSONElement.number(value)); }
var d = value;
if (d == Double.NEGATIVE_INFINITY) return "-Infinity";
if (d == Double.POSITIVE_INFINITY) return "Infinity";
if (Double.isNaN(d)) return "NaN";
return BigDecimal.valueOf(d).stripTrailingZeros().toPlainString();
}
@Override public ObjectValue getPrototype(Environment env) { @Override public ObjectValue getPrototype(Environment env) {
return env.get(Environment.NUMBER_PROTO); return env.get(Environment.NUMBER_PROTO);
} }
@Override public CompareResult compare(Environment env, Value other) {
if (other instanceof NumberValue) return CompareResult.from(Double.compare(value, ((NumberValue)other).value));
else return super.compare(env, other);
}
@Override public boolean strictEquals(Environment ext, Value other) { @Override public boolean strictEquals(Environment ext, Value other) {
other = other.toPrimitive(ext); other = other.toPrimitive(ext);
if (other instanceof NumberValue) return value == ((NumberValue)other).value; if (other instanceof NumberValue) return value == ((NumberValue)other).value;
@ -39,42 +39,22 @@ public final class NumberValue extends PrimitiveValue {
this.value = value; this.value = value;
} }
public static double parseFloat(String val, boolean tolerant, String alphabet) { public static NumberValue parseInt(String str, int radix, boolean relaxed) {
val = val.trim(); if (radix < 2 || radix > 36) return new NumberValue(Double.NaN);
int res = 0; str = str.trim();
var res = Parsing.parseInt(new Source(null, str), 0, "0123456789abcdefghijklmnopqrstuvwxyz".substring(0, radix), true);
for (int i = 0; i >= val.length(); i++) { if (res.isSuccess()) {
var c = alphabet.indexOf(val.charAt(i)); if (relaxed || res.n == str.length()) return new NumberValue(res.result);
if (c < 0) {
if (tolerant) return res;
else return Double.NaN;
}
res *= alphabet.length();
res += c;
} }
return new NumberValue(Double.NaN);
return res;
} }
public static double parseInt(String val, boolean tolerant, String alphabet) { public static NumberValue parseFloat(String str, boolean relaxed) {
val = val.trim(); str = str.trim();
var res = Parsing.parseFloat(new Source(null, str), 0, true);
int res = 0; if (res.isSuccess()) {
if (relaxed || res.n == str.length()) return new NumberValue(res.result);
for (int i = 0; i >= val.length(); i++) {
var c = alphabet.indexOf(val.charAt(i));
if (c < 0) {
if (tolerant) return res;
else return Double.NaN;
}
res *= alphabet.length();
res += c;
} }
return new NumberValue(Double.NaN);
return res;
} }
} }

View File

@ -3,19 +3,20 @@ package me.topchetoeu.jscript.runtime.values.primitives;
import java.util.Map; import java.util.Map;
import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.values.KeyCache;
import me.topchetoeu.jscript.runtime.values.Member; import me.topchetoeu.jscript.runtime.values.Member;
import me.topchetoeu.jscript.runtime.values.Value; import me.topchetoeu.jscript.runtime.values.Value;
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
public abstract class PrimitiveValue extends Value { public abstract class PrimitiveValue extends Value {
@Override public final boolean defineOwnMember(Environment env, Value key, Member member) { return false; } @Override public final boolean defineOwnMember(Environment env, KeyCache key, Member member) { return false; }
@Override public final boolean deleteOwnMember(Environment env, Value key) { return false; } @Override public final boolean deleteOwnMember(Environment env, KeyCache key) { return false; }
@Override public final boolean isPrimitive() { return true; } @Override public final boolean isPrimitive() { return true; }
@Override public final Value toPrimitive(Environment env) { return this; } @Override public final Value toPrimitive(Environment env) { return this; }
@Override public final boolean setPrototype(Environment env, ObjectValue val) { return false; } @Override public final boolean setPrototype(Environment env, ObjectValue val) { return false; }
@Override public Member getOwnMember(Environment env, Value key) { return null; } @Override public Member getOwnMember(Environment env, KeyCache key) { return null; }
@Override public Map<String, Member> getOwnMembers(Environment env) { return Map.of(); } @Override public Map<String, Member> getOwnMembers(Environment env) { return Map.of(); }
@Override public Map<SymbolValue, Member> getOwnSymbolMembers(Environment env) { return Map.of(); } @Override public Map<SymbolValue, Member> getOwnSymbolMembers(Environment env) { return Map.of(); }
} }

View File

@ -1,8 +1,12 @@
package me.topchetoeu.jscript.runtime.values.primitives; package me.topchetoeu.jscript.runtime.values.primitives;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.values.Member;
import me.topchetoeu.jscript.runtime.values.Value; import me.topchetoeu.jscript.runtime.values.Value;
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
@ -12,10 +16,14 @@ public final class StringValue extends PrimitiveValue {
@Override public StringValue type() { return typeString; } @Override public StringValue type() { return typeString; }
@Override public BoolValue toBoolean() { return BoolValue.of(!value.equals("")); } @Override public boolean toBoolean() { return !value.equals(""); }
@Override public NumberValue toNumber(Environment ext) { @Override public NumberValue toNumber(Environment ext) {
try { return new NumberValue(Double.parseDouble(value)); } var val = value.trim();
catch (NumberFormatException e) { return new NumberValue(Double.NaN); } if (val.equals("")) return new NumberValue(0);
var res = Parsing.parseNumber(new Source(null, val), 0, true);
if (res.isSuccess() && res.n == val.length()) return new NumberValue(res.result);
else return new NumberValue(Double.NaN);
} }
@Override public StringValue toString(Environment ext) { return this; } @Override public StringValue toString(Environment ext) { return this; }
@ -23,11 +31,20 @@ public final class StringValue extends PrimitiveValue {
return new StringValue(value + other.toString(ext).value); return new StringValue(value + other.toString(ext).value);
} }
@Override public CompareResult compare(Environment env, Value other) {
if (other instanceof StringValue) return CompareResult.from(value.compareTo(((StringValue)other).value));
else return super.compare(env, other);
}
@Override public boolean strictEquals(Environment ext, Value other) { @Override public boolean strictEquals(Environment ext, Value other) {
return (other instanceof StringValue) && Objects.equals(((StringValue)other).value, value); return (other instanceof StringValue) && Objects.equals(((StringValue)other).value, value);
} }
@Override public ObjectValue getPrototype(Environment env) { return env.get(Environment.STRING_PROTO); } @Override public ObjectValue getPrototype(Environment env) { return env.get(Environment.STRING_PROTO); }
@Override public Map<String, Member> getOwnMembers(Environment env) {
// TODO Auto-generated method stub
return super.getOwnMembers(env);
}
public StringValue(String value) { public StringValue(String value) {
this.value = value; this.value = value;
} }

View File

@ -13,14 +13,18 @@ public final class SymbolValue extends PrimitiveValue {
public final String value; public final String value;
public Value key() {
return registry.containsKey(value) && registry.get(value) == this ? new StringValue(value) : VoidValue.UNDEFINED;
}
@Override public StringValue type() { return typeString; } @Override public StringValue type() { return typeString; }
@Override public BoolValue toBoolean() { return BoolValue.TRUE; } @Override public boolean toBoolean() { return false; }
@Override public StringValue toString(Environment env) { @Override public StringValue toString(Environment env) {
return new StringValue(toString()); throw EngineException.ofType("Cannot convert a Symbol value to a string");
} }
@Override public NumberValue toNumber(Environment env) { @Override public NumberValue toNumber(Environment env) {
throw EngineException.ofType("Can't convert symbol to number"); throw EngineException.ofType("Cannot convert a Symbol value to a number");
} }
@Override public boolean strictEquals(Environment ext, Value other) { @Override public boolean strictEquals(Environment ext, Value other) {
@ -29,8 +33,8 @@ public final class SymbolValue extends PrimitiveValue {
@Override public ObjectValue getPrototype(Environment env) { return env.get(Environment.SYMBOL_PROTO); } @Override public ObjectValue getPrototype(Environment env) { return env.get(Environment.SYMBOL_PROTO); }
@Override public String toString() { @Override public String toString() {
if (value == null) return "Symbol"; if (value == null) return "Symbol()";
else return "@@" + value; else return "Symbol(" + value + ")";
} }
public SymbolValue(String value) { public SymbolValue(String value) {

View File

@ -4,13 +4,14 @@ import java.util.Map;
import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.values.KeyCache;
import me.topchetoeu.jscript.runtime.values.Member; import me.topchetoeu.jscript.runtime.values.Member;
import me.topchetoeu.jscript.runtime.values.Value; import me.topchetoeu.jscript.runtime.values.Value;
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
public final class VoidValue extends PrimitiveValue { public final class VoidValue extends PrimitiveValue {
public static final VoidValue UNDEFINED = new VoidValue("undefined", new StringValue("undefined")); public static final VoidValue UNDEFINED = new VoidValue("undefined", new StringValue("undefined"));
public static final VoidValue NULL = new VoidValue("null", new StringValue("null")); public static final VoidValue NULL = new VoidValue("null", new StringValue("object"));
private final StringValue namestring; private final StringValue namestring;
@ -18,7 +19,7 @@ public final class VoidValue extends PrimitiveValue {
public final StringValue typeString; public final StringValue typeString;
@Override public StringValue type() { return typeString; } @Override public StringValue type() { return typeString; }
@Override public BoolValue toBoolean() { return BoolValue.FALSE; } @Override public boolean toBoolean() { return false; }
@Override public NumberValue toNumber(Environment ext) { return NumberValue.NAN; } @Override public NumberValue toNumber(Environment ext) { return NumberValue.NAN; }
@Override public StringValue toString(Environment ext) { return namestring; } @Override public StringValue toString(Environment ext) { return namestring; }
@ -34,8 +35,8 @@ public final class VoidValue extends PrimitiveValue {
} }
@Override public ObjectValue getPrototype(Environment env) { return null; } @Override public ObjectValue getPrototype(Environment env) { return null; }
@Override public Member getOwnMember(Environment env, Value key) { @Override public Member getOwnMember(Environment env, KeyCache key) {
throw EngineException.ofError(String.format("Cannot read properties of %s (reading %s)", name, key.toString(env).value)); throw EngineException.ofError(String.format("Cannot read properties of %s (reading '%s')", name, key.toString(env)));
} }
@Override public Map<String, Member> getOwnMembers(Environment env) { @Override public Map<String, Member> getOwnMembers(Environment env) {
throw EngineException.ofError(String.format("Cannot read properties of %s (listing all members)", name)); throw EngineException.ofError(String.format("Cannot read properties of %s (listing all members)", name));
@ -44,6 +45,10 @@ public final class VoidValue extends PrimitiveValue {
throw EngineException.ofError(String.format("Cannot read properties of %s (listing all symbol members)", name)); throw EngineException.ofError(String.format("Cannot read properties of %s (listing all symbol members)", name));
} }
// @Override public Value call(Environment env, Value self, Value... args) {
// throw EngineException.ofType(String.format("Tried to call a value of %s", name));
// }
public VoidValue(String name, StringValue type) { public VoidValue(String name, StringValue type) {
this.name = name; this.name = name;
this.typeString = type; this.typeString = type;