feat: dead simple Map and RegExp implementations

This commit is contained in:
TopchetoEU 2024-12-10 01:11:07 +02:00
parent ea158c1e60
commit 058d20b27f
Signed by: topchetoeu
GPG Key ID: 6531B8583E5F6ED4
2 changed files with 499 additions and 269 deletions

View File

@ -3,8 +3,11 @@ package me.topchetoeu.jscript.runtime;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.regex.Pattern;
import me.topchetoeu.jscript.common.Metadata;
import me.topchetoeu.jscript.common.Reading;
@ -26,6 +29,7 @@ import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
import me.topchetoeu.jscript.runtime.values.primitives.BoolValue;
import me.topchetoeu.jscript.runtime.values.primitives.StringValue;
import me.topchetoeu.jscript.runtime.values.primitives.SymbolValue;
import me.topchetoeu.jscript.runtime.values.primitives.UserValue;
import me.topchetoeu.jscript.runtime.values.primitives.VoidValue;
import me.topchetoeu.jscript.runtime.values.primitives.numbers.NumberValue;
@ -44,6 +48,8 @@ public class SimpleRepl {
}
catch (EngineException | SyntaxException e) { System.err.println(Value.errorToReadable(environment, e, null)); }
System.out.println(String.format("Running %s v%s by %s", Metadata.name(), Metadata.version(), Metadata.author()));
for (var arg : args) {
try {
var file = new File(arg);
@ -92,6 +98,94 @@ public class SimpleRepl {
}
}
@SuppressWarnings("unchecked")
private static ObjectValue mapPrimordials(Environment env) {
var res = new ObjectValue();
res.setPrototype(null, null);
var prototype = new ObjectValue[1];
NativeFunction mapConstr = new NativeFunction(args -> {
return UserValue.of(new LinkedHashMap<>(), prototype[0]);
});
mapConstr.prototype.defineOwnMember(env, "get", new NativeFunction(getArgs -> {
var map = getArgs.self(LinkedHashMap.class);
var key = getArgs.get(0);
var val = map.get(key);
return val == null ? Value.UNDEFINED : (Value)val;
}));
mapConstr.prototype.defineOwnMember(env, "set", new NativeFunction(getArgs -> {
var map = getArgs.self(LinkedHashMap.class);
var key = getArgs.get(0);
var val = getArgs.get(1);
map.put(key, val);
return Value.UNDEFINED;
}));
mapConstr.prototype.defineOwnMember(env, "has", new NativeFunction(getArgs -> {
var map = getArgs.self(LinkedHashMap.class);
var key = getArgs.get(0);
return BoolValue.of(map.containsKey(key));
}));
mapConstr.prototype.defineOwnMember(env, "delete", new NativeFunction(getArgs -> {
var map = getArgs.self(LinkedHashMap.class);
var key = getArgs.get(0);
map.remove(key);
return Value.UNDEFINED;
}));
mapConstr.prototype.defineOwnMember(env, "keys", new NativeFunction(getArgs -> {
var map = getArgs.self(LinkedHashMap.class);
return ArrayValue.of(map.keySet());
}));
prototype[0] = (ObjectValue)mapConstr.prototype;
return mapConstr;
}
private static ObjectValue regexPrimordials(Environment env) {
var res = new ObjectValue();
res.setPrototype(null, null);
var prototype = new ObjectValue[1];
NativeFunction mapConstr = new NativeFunction(args -> {
var pattern = Pattern.compile(args.get(0).toString(args.env));
return UserValue.of(pattern, prototype[0]);
});
mapConstr.prototype.defineOwnMember(env, "exec", new NativeFunction(args -> {
var pattern = args.self(Pattern.class);
var target = args.get(0).toString(args.env);
var offset = args.get(1).toNumber(args.env).getInt();
var index = args.get(2).toBoolean();
var matcher = pattern.matcher(target).region(offset, target.length());
var obj = new ArrayValue();
for (var i = 0; i < matcher.groupCount(); i++) {
obj.set(args.env, i, StringValue.of(matcher.group(i)));
}
obj.defineOwnMember(args.env, "index", NumberValue.of(matcher.start()));
obj.defineOwnMember(args.env, "input", StringValue.of(target));
if (index) {
var indices = new ArrayValue();
indices.setPrototype(args.env, null);
for (var i = 0; i < matcher.groupCount(); i++) {
obj.set(args.env, i, ArrayValue.of(Arrays.asList(
NumberValue.of(matcher.start(i)),
NumberValue.of(matcher.end(i))
)));
}
}
return Value.UNDEFINED;
// return val == null ? Value.UNDEFINED : (Value)val;
}));
prototype[0] = (ObjectValue)mapConstr.prototype;
return mapConstr;
}
private static ObjectValue symbolPrimordials(Environment env) {
var res = new ObjectValue();
res.setPrototype(null, null);
@ -238,7 +332,7 @@ public class SimpleRepl {
var func = (FunctionValue)args.get(0);
var funcArgs = (ArrayValue)args.get(1);
return func.construct(env, funcArgs.toArray());
return func.constructNoSelf(env, funcArgs.toArray());
}));
return res;
@ -275,6 +369,8 @@ public class SimpleRepl {
res.defineOwnMember(env, "object", objectPrimordials(env));
res.defineOwnMember(env, "function", functionPrimordials(env));
res.defineOwnMember(env, "json", jsonPrimordials(env));
res.defineOwnMember(env, "map", mapPrimordials(env));
res.defineOwnMember(env, "regex", regexPrimordials(env));
int[] i = new int[1];
@ -292,7 +388,10 @@ public class SimpleRepl {
setProto(args.env, env, Value.SYNTAX_ERR_PROTO, obj, "syntax");
setProto(args.env, env, Value.TYPE_ERR_PROTO, obj, "type");
setProto(args.env, env, Value.RANGE_ERR_PROTO, obj, "range");
var val = obj.getMember(env, "regex");
if (val instanceof FunctionValue func) {
args.env.add(Value.REGEX_CONSTR, func);
}
return Value.UNDEFINED;
}));
res.defineOwnMember(env, "setIntrinsic", new NativeFunction(args -> {
@ -342,12 +441,7 @@ public class SimpleRepl {
}));
}
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();
// debugTask = debugServer.start(new InetSocketAddress("127.0.0.1", 9229), true);
}
private static void initGlobals() throws InterruptedException, ExecutionException {
EventLoop.get(environment).pushMsg(
@ -355,11 +449,14 @@ public class SimpleRepl {
Filename.parse("jscript://init.js"), Reading.resourceToString("lib/index.js"),
Value.UNDEFINED, Value.global(environment), primordials(environment)
).get();
EventLoop.get(environment).pushMsg(
false, environment,
Filename.parse("jscript://ts.js"), Reading.resourceToString("lib/ts.js"),
Value.UNDEFINED
).get();
}
public static void main(String args[]) throws InterruptedException {
System.out.println(String.format("Running %s v%s by %s", Metadata.name(), Metadata.version(), Metadata.author()));
SimpleRepl.args = args;
var reader = new Thread(SimpleRepl::reader);
@ -371,7 +468,6 @@ public class SimpleRepl {
reader.start();
engine.thread().join();
// debugTask.interrupt();
engineTask.interrupt();
}
}

View File

@ -1,4 +1,8 @@
(function main() {
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
};
function extend(derived, base) {
if (base == null) {
object.setPrototype(derived.prototype, null);
@ -59,7 +63,9 @@
var json = primordials.json || {
stringify: function (val) { throw new Error("JSON stringify not polyfillable"); },
parse: function (val) { throw new Error("JSON parse not polyfillable"); },
}
};
var map = primordials.map;
var regex = primordials.regex;
var setGlobalPrototypes = primordials.setGlobalPrototypes;
var compile = primordials.compile;
@ -192,6 +198,27 @@
String.prototype.valueOf = function () {
return unwrapThis(this, "string", String, "String.prototype.valueOf");
};
String.prototype[Symbol.iterator] = function () {
var i = 0;
var arr = this;
var obj = {
next: function () {
if (arr == null) return { done: true, value: undefined };
if (i > arr.length) {
arr = undefined;
return { done: true, value: undefined };
}
else {
var val = arr[i++];
if (i >= arr.length) arr = undefined;
return { done: false, value: val };
}
}
};
obj[Symbol.iterator] = function () { return this; };
return obj;
}
String.fromCharCode = function () {
var res = [];
res[arguments.length] = 0;
@ -339,6 +366,30 @@
// TODO: Implement spreading
else throw new Error("Spreading not implemented");
}
Array.prototype[Symbol.iterator] = function () {
var i = 0;
var arr = this;
var obj = {
next: function () {
if (arr == null) return { done: true, value: undefined };
if (i > arr.length) {
arr = undefined;
return { done: true, value: undefined };
}
else {
var val = arr[i++];
if (i >= arr.length) arr = undefined;
return { done: false, value: val };
}
}
};
obj[Symbol.iterator] = function () { return this; };
return obj;
}
Array.isArray = function (val) {
return val instanceof Array;
}
func.setCallable(Array, true);
target.Array = Array;
@ -392,6 +443,88 @@
target.uint8 = primordials.uint8;
var mapKey = Symbol("Map.impl");
function Map(iterable) {
var _map = this[mapKey] = new map();
if (iterable != null) {
var it = iterable[Symbol.iterator]();
for (var val = it.next(); !val.done; val = it.next()) {
_map.set(val.value[0], val.value[1]);
}
}
}
Map.prototype.get = function (key) {
return this[mapKey].get(key);
}
Map.prototype.has = function (key) {
return this[mapKey].has(key);
}
Map.prototype.set = function (key, val) {
return this[mapKey].set(key, val);
}
Map.prototype.delete = function (key, val) {
return this[mapKey].delete(key, val);
}
Map.prototype.keys = function (key, val) {
return this[mapKey].keys(key, val);
}
Map.prototype.values = function (key, val) {
var res = this[mapKey].keys(key, val);
for (var i = 0; i < res.length; i++) res[i] = this[mapKey].get(res[i]);
return res;
}
Map.prototype.entries = function (key, val) {
var res = this[mapKey].keys(key, val);
for (var i = 0; i < res.length; i++) res[i] = [res[i], this[mapKey].get(res[i])];
return res;
}
func.setCallable(Map, false);
target.Map = Map;
var regexKey = Symbol("RegExp.impl");
function RegExp(source, flags) {
var _regex = this[regexKey] = new regex();
source = this.source = String("source" in source ? source.source : source);
flags = String(flags);
var indices = false, global = false, ignoreCase = false, multiline = false, dotall = false, unicode = false, unicodeSets = false, sticky = false;
for (var i = 0; i < flags.length; i++) {
switch (flags[i]) {
case "d": indices = true; break;
case "g": global = true; break;
case "i": ignoreCase = true; break;
case "m": multiline = true; break;
case "s": dotall = true; break;
case "u": unicode = true; break;
case "v": unicodeSets = true; break;
case "y": sticky = true; break;
}
}
flags = "";
if (indices) flags += "d";
if (global) flags += "g";
if (ignoreCase) flags += "i";
if (multiline) flags += "m";
if (dotall) flags += "s";
if (unicode) flags += "u";
if (unicodeSets) flags += "v";
if (sticky) flags += "y";
this.flags = flags;
}
target.RegExp = RegExp;
setGlobalPrototypes({
string: String.prototype,
number: Number.prototype,
@ -404,5 +537,6 @@
syntax: SyntaxError.prototype,
range: RangeError.prototype,
type: TypeError.prototype,
regex: RegExp,
});
})(arguments[0], arguments[1]);