everything all at once
This commit is contained in:
parent
f09feae08f
commit
d0ccf00f14
@ -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]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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() {
|
||||||
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.common;
|
|
||||||
|
|
||||||
public interface ResultRunnable<T> {
|
|
||||||
T run();
|
|
||||||
}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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); }
|
|
||||||
}
|
|
||||||
}
|
|
@ -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()) {
|
||||||
|
@ -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 {
|
||||||
|
@ -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;
|
@ -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;
|
@ -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(); }
|
383
src/java/me/topchetoeu/jscript/common/parsing/Parsing.java
Normal file
383
src/java/me/topchetoeu/jscript/common/parsing/Parsing.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
@ -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;
|
@ -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);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
302
src/java/me/topchetoeu/jscript/compilation/ES5.java
Normal file
302
src/java/me/topchetoeu/jscript/compilation/ES5.java
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.parsing;
|
|
||||||
|
|
||||||
enum TokenType {
|
|
||||||
REGEX,
|
|
||||||
STRING,
|
|
||||||
NUMBER,
|
|
||||||
LITERAL,
|
|
||||||
OPERATOR,
|
|
||||||
}
|
|
@ -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);
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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(
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
@ -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;
|
||||||
|
@ -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.");
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -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.");
|
||||||
|
|
@ -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;
|
||||||
|
|
@ -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;
|
||||||
|
|
@ -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);
|
@ -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;
|
||||||
|
|
@ -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;
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
|
@ -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;
|
@ -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;
|
||||||
|
|
13
src/java/me/topchetoeu/jscript/runtime/ArgumentsValue.java
Normal file
13
src/java/me/topchetoeu/jscript/runtime/ArgumentsValue.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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 {
|
||||||
|
@ -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);
|
|
||||||
}
|
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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() {
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.runtime.values;
|
|
||||||
|
|
||||||
public enum ConvertHint {
|
|
||||||
TOSTRING,
|
|
||||||
VALUEOF,
|
|
||||||
}
|
|
59
src/java/me/topchetoeu/jscript/runtime/values/KeyCache.java
Normal file
59
src/java/me/topchetoeu/jscript/runtime/values/KeyCache.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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));
|
|
||||||
}
|
|
||||||
}
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
@ -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;
|
||||||
|
@ -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) {
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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(); }
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user