From c5a4167c942f9d3093101ecd34647bebe5b6c227 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Fri, 25 Aug 2023 23:48:40 +0300 Subject: [PATCH 1/5] feat: make generator function --- lib/core.ts | 4 +- lib/values/array.ts | 2 +- lib/values/function.ts | 26 ++++- src/me/topchetoeu/jscript/Main.java | 2 +- .../jscript/engine/frame/CodeFrame.java | 41 +++++--- .../topchetoeu/jscript/parsing/Parsing.java | 16 +-- .../jscript/polyfills/AsyncFunction.java | 3 +- .../jscript/polyfills/GeneratorFunction.java | 99 +++++++++++++++++++ .../jscript/polyfills/Internals.java | 10 ++ 9 files changed, 166 insertions(+), 37 deletions(-) create mode 100644 src/me/topchetoeu/jscript/polyfills/GeneratorFunction.java diff --git a/lib/core.ts b/lib/core.ts index e3e39d1..c1d51a2 100644 --- a/lib/core.ts +++ b/lib/core.ts @@ -74,8 +74,8 @@ interface MathObject { //@ts-ignore declare const arguments: IArguments; declare const Math: MathObject; -declare const NaN: number; -declare const Infinity: number; +const NaN = 0 / 0; +const Infinity = 1 / 0; declare var setTimeout: (handle: (...args: [ ...T, ...any[] ]) => void, delay?: number, ...args: T) => number; declare var setInterval: (handle: (...args: [ ...T, ...any[] ]) => void, delay?: number, ...args: T) => number; diff --git a/lib/values/array.ts b/lib/values/array.ts index 5144630..dc3eceb 100644 --- a/lib/values/array.ts +++ b/lib/values/array.ts @@ -365,5 +365,5 @@ setProps(Array.prototype, { }); setProps(Array, { - isArray(val: any) { return (val instanceof Array); } + isArray(val: any) { return internals.isArr(val); } }); diff --git a/lib/values/function.ts b/lib/values/function.ts index 81e4be0..b77b475 100644 --- a/lib/values/function.ts +++ b/lib/values/function.ts @@ -14,7 +14,15 @@ interface FunctionConstructor extends Function { (...args: string[]): (...args: any[]) => any; new (...args: string[]): (...args: any[]) => any; prototype: Function; - async(func: (await: (val: T) => Awaited) => (...args: ArgsT) => RetT): (...args: ArgsT) => Promise; + async( + func: (await: (val: T) => Awaited) => (...args: ArgsT) => RetT + ): (...args: ArgsT) => Promise; + asyncGenerator( + func: (await: (val: T) => Awaited, _yield: (val: T) => void) => (...args: ArgsT) => RetT + ): (...args: ArgsT) => AsyncGenerator; + generator( + func: (_yield: (val: T) => TNext) => (...args: ArgsT) => RetT + ): (...args: ArgsT) => Generator; } interface CallableFunction extends Function { @@ -43,11 +51,15 @@ setProps(Function.prototype, { apply(thisArg, args) { if (typeof args !== 'object') throw 'Expected arguments to be an array-like object.'; var len = args.length - 0; - var newArgs: any[] = []; + let newArgs: any[]; + if (Array.isArray(args)) newArgs = args; + else { + newArgs = []; - while (len >= 0) { - len--; - newArgs[len] = args[len]; + while (len >= 0) { + len--; + newArgs[len] = args[len]; + } } return internals.apply(this, thisArg, newArgs); @@ -81,5 +93,9 @@ setProps(Function, { async(func) { if (typeof func !== 'function') throw new TypeError('Expected func to be function.'); return internals.makeAsync(func); + }, + generator(func) { + if (typeof func !== 'function') throw new TypeError('Expected func to be function.'); + return internals.makeGenerator(func); } }) \ No newline at end of file diff --git a/src/me/topchetoeu/jscript/Main.java b/src/me/topchetoeu/jscript/Main.java index 34a2426..aa92604 100644 --- a/src/me/topchetoeu/jscript/Main.java +++ b/src/me/topchetoeu/jscript/Main.java @@ -54,7 +54,7 @@ public class Main { public static void main(String args[]) { var in = new BufferedReader(new InputStreamReader(System.in)); - engine = new TypescriptEngine(new File(".")); + engine = new PolyfillEngine(new File(".")); var scope = engine.global().globalChild(); var exited = new boolean[1]; diff --git a/src/me/topchetoeu/jscript/engine/frame/CodeFrame.java b/src/me/topchetoeu/jscript/engine/frame/CodeFrame.java index 92a8a4d..1101c25 100644 --- a/src/me/topchetoeu/jscript/engine/frame/CodeFrame.java +++ b/src/me/topchetoeu/jscript/engine/frame/CodeFrame.java @@ -157,32 +157,47 @@ public class CodeFrame { } } - public Object next(CallContext ctx, EngineException prevError) throws InterruptedException { + public Object next(CallContext ctx, Object prevReturn, Object prevError) throws InterruptedException { TryCtx tryCtx = null; - var handled = prevError == null; + if (prevError != Runners.NO_RETURN) prevReturn = Runners.NO_RETURN; while (!tryStack.isEmpty()) { var tmp = tryStack.get(tryStack.size() - 1); var remove = false; - if (tmp.state == TryCtx.STATE_TRY) { - if (prevError != null) { + if (prevError != Runners.NO_RETURN) { + remove = true; + if (tmp.state == TryCtx.STATE_TRY) { tmp.jumpPtr = tmp.end; if (tmp.hasCatch) { tmp.state = TryCtx.STATE_CATCH; scope.catchVars.add(new ValueVariable(false, prevError)); + prevError = Runners.NO_RETURN; codePtr = tmp.catchStart; - handled = true; + remove = false; } else if (tmp.hasFinally) { tmp.state = TryCtx.STATE_FINALLY_THREW; - tmp.err = prevError; + tmp.err = new EngineException(prevError); + prevError = Runners.NO_RETURN; codePtr = tmp.finallyStart; - handled = true; + remove = false; } } - else if (codePtr < tmp.tryStart || codePtr >= tmp.catchStart) { + } + else if (prevReturn != Runners.NO_RETURN) { + remove = true; + if (tmp.hasFinally && tmp.state <= TryCtx.STATE_CATCH) { + tmp.state = TryCtx.STATE_FINALLY_RETURNED; + tmp.retVal = prevReturn; + prevReturn = Runners.NO_RETURN; + codePtr = tmp.finallyStart; + remove = false; + } + } + else if (tmp.state == TryCtx.STATE_TRY) { + if (codePtr < tmp.tryStart || codePtr >= tmp.catchStart) { if (jumpFlag) tmp.jumpPtr = codePtr; else tmp.jumpPtr = tmp.end; @@ -218,7 +233,6 @@ public class CodeFrame { remove = true; } - if (!handled) throw prevError; if (remove) tryStack.remove(tryStack.size() - 1); else { tryCtx = tmp; @@ -226,7 +240,8 @@ public class CodeFrame { } } - if (!handled) throw prevError; + if (prevError != Runners.NO_RETURN) throw new EngineException(prevError); + if (prevReturn != Runners.NO_RETURN) return prevReturn; if (tryCtx == null) return nextNoTry(ctx); else if (tryCtx.state == TryCtx.STATE_TRY) { @@ -279,15 +294,11 @@ public class CodeFrame { else return nextNoTry(ctx); } - public void handleReturn(Object value) { - - } - public Object run(CallContext ctx) throws InterruptedException { try { start(ctx); while (true) { - var res = next(ctx, null); + var res = next(ctx, Runners.NO_RETURN, Runners.NO_RETURN); if (res != Runners.NO_RETURN) return res; } } diff --git a/src/me/topchetoeu/jscript/parsing/Parsing.java b/src/me/topchetoeu/jscript/parsing/Parsing.java index 27804db..6c816dc 100644 --- a/src/me/topchetoeu/jscript/parsing/Parsing.java +++ b/src/me/topchetoeu/jscript/parsing/Parsing.java @@ -35,8 +35,6 @@ public class Parsing { reserved.add("void"); reserved.add("null"); reserved.add("this"); - reserved.add("NaN"); - reserved.add("Infinity"); reserved.add("if"); reserved.add("else"); reserved.add("try"); @@ -58,7 +56,6 @@ public class Parsing { reserved.add("break"); reserved.add("continue"); reserved.add("debug"); - reserved.add("let"); reserved.add("implements"); reserved.add("interface"); reserved.add("package"); @@ -66,17 +63,18 @@ public class Parsing { reserved.add("protected"); reserved.add("public"); reserved.add("static"); - reserved.add("yield"); - // Although the standards allow it, these are keywords in newer ES, so we won't allow them + // Although ES5 allow these, we will comply to ES6 here reserved.add("const"); - // reserved.add("await"); + reserved.add("let"); reserved.add("async"); + reserved.add("super"); // These are allowed too, however our parser considers them keywords reserved.add("undefined"); reserved.add("arguments"); reserved.add("globalThis"); reserved.add("window"); reserved.add("self"); + // We allow yield and await, because they're part of the custom async and generator functions } @@ -1040,12 +1038,6 @@ public class Parsing { if (id.result.equals("null")) { return ParseRes.res(new ConstantStatement(loc, Values.NULL), 1); } - if (id.result.equals("NaN")) { - return ParseRes.res(new ConstantStatement(loc, Double.NaN), 1); - } - if (id.result.equals("Infinity")) { - return ParseRes.res(new ConstantStatement(loc, Double.POSITIVE_INFINITY), 1); - } if (id.result.equals("this")) { return ParseRes.res(new VariableIndexStatement(loc, 0), 1); } diff --git a/src/me/topchetoeu/jscript/polyfills/AsyncFunction.java b/src/me/topchetoeu/jscript/polyfills/AsyncFunction.java index 627ff18..df4afc7 100644 --- a/src/me/topchetoeu/jscript/polyfills/AsyncFunction.java +++ b/src/me/topchetoeu/jscript/polyfills/AsyncFunction.java @@ -40,7 +40,8 @@ public class AsyncFunction extends FunctionValue { awaited = null; try { - var res = frame.next(ctx, err == Runners.NO_RETURN ? null : new EngineException(err)); + var res = frame.next(ctx, val, err); + err = Runners.NO_RETURN; if (res != Runners.NO_RETURN) { promise.fulfill(ctx, res); break; diff --git a/src/me/topchetoeu/jscript/polyfills/GeneratorFunction.java b/src/me/topchetoeu/jscript/polyfills/GeneratorFunction.java new file mode 100644 index 0000000..b59940b --- /dev/null +++ b/src/me/topchetoeu/jscript/polyfills/GeneratorFunction.java @@ -0,0 +1,99 @@ +package me.topchetoeu.jscript.polyfills; + +import me.topchetoeu.jscript.engine.CallContext; +import me.topchetoeu.jscript.engine.frame.CodeFrame; +import me.topchetoeu.jscript.engine.frame.Runners; +import me.topchetoeu.jscript.engine.values.CodeFunction; +import me.topchetoeu.jscript.engine.values.FunctionValue; +import me.topchetoeu.jscript.engine.values.NativeFunction; +import me.topchetoeu.jscript.engine.values.ObjectValue; +import me.topchetoeu.jscript.exceptions.EngineException; +import me.topchetoeu.jscript.interop.Native; + +public class GeneratorFunction extends FunctionValue { + public final CodeFunction factory; + + public static class Generator { + private boolean yielding = true; + private boolean done = false; + public CodeFrame frame; + + private ObjectValue next(CallContext ctx, Object inducedValue, Object inducedReturn, Object inducedError) throws InterruptedException { + if (done) { + if (inducedError != Runners.NO_RETURN) throw new EngineException(inducedError); + var res = new ObjectValue(); + res.defineProperty("done", true); + res.defineProperty("value", inducedReturn == Runners.NO_RETURN ? null : inducedReturn); + return res; + } + + Object res = null; + if (inducedValue != Runners.NO_RETURN) frame.push(inducedValue); + frame.start(ctx); + yielding = false; + while (!yielding) { + try { + res = frame.next(ctx, inducedReturn, inducedError); + inducedReturn = inducedError = Runners.NO_RETURN; + if (res != Runners.NO_RETURN) { + done = true; + break; + } + } + catch (EngineException e) { + done = true; + throw e; + } + } + + frame.end(ctx); + if (done) frame = null; + else res = frame.pop(); + + var obj = new ObjectValue(); + obj.defineProperty("done", done); + obj.defineProperty("value", res); + return obj; + } + + @Native + public ObjectValue next(CallContext ctx, Object... args) throws InterruptedException { + if (args.length == 0) return next(ctx, Runners.NO_RETURN, Runners.NO_RETURN, Runners.NO_RETURN); + else return next(ctx, args[0], Runners.NO_RETURN, Runners.NO_RETURN); + } + @Native("throw") + public ObjectValue _throw(CallContext ctx, Object error) throws InterruptedException { + return next(ctx, Runners.NO_RETURN, Runners.NO_RETURN, error); + } + @Native("return") + public ObjectValue _return(CallContext ctx, Object value) throws InterruptedException { + return next(ctx, Runners.NO_RETURN, value, Runners.NO_RETURN); + } + + @Override + public String toString() { + if (done) return "Generator [closed]"; + if (yielding) return "Generator [suspended]"; + return "Generator " + (done ? "[closed]" : "[suspended]"); + } + + public Object yield(CallContext ctx, Object thisArg, Object[] args) { + this.yielding = true; + return args.length > 0 ? args[0] : null; + } + } + + @Override + public Object call(CallContext _ctx, Object thisArg, Object... args) throws InterruptedException { + var handler = new Generator(); + var func = factory.call(_ctx, thisArg, new NativeFunction("yield", handler::yield)); + if (!(func instanceof CodeFunction)) throw EngineException.ofType("Return value of argument must be a js function."); + handler.frame = new CodeFrame(thisArg, args, (CodeFunction)func); + return handler; + } + + public GeneratorFunction(CodeFunction factory) { + super(factory.name, factory.length); + this.factory = factory; + } +} diff --git a/src/me/topchetoeu/jscript/polyfills/Internals.java b/src/me/topchetoeu/jscript/polyfills/Internals.java index 9aa7446..9a5d42e 100644 --- a/src/me/topchetoeu/jscript/polyfills/Internals.java +++ b/src/me/topchetoeu/jscript/polyfills/Internals.java @@ -24,6 +24,11 @@ public class Internals { return Values.toNumber(ctx, val); } + @Native + public boolean isArr(Object val) { + return val instanceof ArrayValue; + } + @NativeGetter("symbolProto") public ObjectValue symbolProto(CallContext ctx) { return ctx.engine().symbolProto(); @@ -241,6 +246,11 @@ public class Internals { if (func instanceof CodeFunction) return new AsyncFunction((CodeFunction)func); else throw EngineException.ofType("Can't create an async function with a non-js function."); } + @Native + public GeneratorFunction makeGenerator(FunctionValue func) { + if (func instanceof CodeFunction) return new GeneratorFunction((CodeFunction)func); + else throw EngineException.ofType("Can't create a generator with a non-js function."); + } @NativeGetter("err") public ObjectValue errProto(CallContext ctx) { From d49c54e15a51af54f9e151df7637f35fbe56e0c3 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Fri, 25 Aug 2023 23:48:57 +0300 Subject: [PATCH 2/5] feat: implement async function with generator --- lib/values/function.ts | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/lib/values/function.ts b/lib/values/function.ts index b77b475..577c95d 100644 --- a/lib/values/function.ts +++ b/lib/values/function.ts @@ -90,9 +90,36 @@ setProps(Function.prototype, { }, }); setProps(Function, { - async(func) { + async(func: (_yield: (val: T) => Awaited) => (...args: ArgsT) => RetT) { if (typeof func !== 'function') throw new TypeError('Expected func to be function.'); - return internals.makeAsync(func); + + return function (this: any) { + const args = arguments; + + return new Promise((res, rej) => { + const gen = Function.generator(func as any).apply(this, args as any); + + (function next(type: 'none' | 'err' | 'ret', val?: any) { + try { + let result; + + switch (type) { + case 'err': result = gen.throw(val); break; + case 'ret': result = gen.next(val); break; + case 'none': result = gen.next(); break; + } + if (result.done) res(result.value); + else Promise.resolve(result.value).then( + v => next('ret', v), + v => next('err', v) + ) + } + catch (e) { + rej(e); + } + })('none'); + }); + }; }, generator(func) { if (typeof func !== 'function') throw new TypeError('Expected func to be function.'); From 8a0d309f0f571b5dd5439e9bcedb1d9c4d3366a9 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Fri, 25 Aug 2023 23:49:31 +0300 Subject: [PATCH 3/5] feat: fully remove async functions --- .../jscript/polyfills/AsyncFunction.java | 102 ------------------ .../jscript/polyfills/Internals.java | 5 - 2 files changed, 107 deletions(-) delete mode 100644 src/me/topchetoeu/jscript/polyfills/AsyncFunction.java diff --git a/src/me/topchetoeu/jscript/polyfills/AsyncFunction.java b/src/me/topchetoeu/jscript/polyfills/AsyncFunction.java deleted file mode 100644 index df4afc7..0000000 --- a/src/me/topchetoeu/jscript/polyfills/AsyncFunction.java +++ /dev/null @@ -1,102 +0,0 @@ -package me.topchetoeu.jscript.polyfills; - -import me.topchetoeu.jscript.engine.CallContext; -import me.topchetoeu.jscript.engine.frame.CodeFrame; -import me.topchetoeu.jscript.engine.frame.Runners; -import me.topchetoeu.jscript.engine.values.CodeFunction; -import me.topchetoeu.jscript.engine.values.FunctionValue; -import me.topchetoeu.jscript.engine.values.NativeFunction; -import me.topchetoeu.jscript.engine.values.Values; -import me.topchetoeu.jscript.exceptions.EngineException; - -public class AsyncFunction extends FunctionValue { - public final CodeFunction body; - - private class CallHandler { - private boolean awaiting = false; - private Object awaited = null; - public final Promise promise = new Promise(); - public CodeFrame frame; - private final NativeFunction fulfillFunc = new NativeFunction("", (ctx, thisArg, args) -> { - if (args.length == 0) exec(ctx, null, Runners.NO_RETURN); - else exec(ctx, args[0], Runners.NO_RETURN); - - return null; - }); - private final NativeFunction rejectFunc = new NativeFunction("", (ctx, thisArg, args) -> { - if (args.length == 0) exec(ctx, Runners.NO_RETURN, null); - else exec(ctx, Runners.NO_RETURN, args[0]); - - return null; - }); - - public Object exec(CallContext ctx, Object val, Object err) throws InterruptedException { - if (val != Runners.NO_RETURN) frame.push(val); - - frame.start(ctx); - - while (true) { - awaiting = false; - awaited = null; - - try { - var res = frame.next(ctx, val, err); - err = Runners.NO_RETURN; - if (res != Runners.NO_RETURN) { - promise.fulfill(ctx, res); - break; - } - } - catch (EngineException e) { - promise.reject(e.value); - break; - } - - if (!awaiting) continue; - - frame.pop(); - - if (awaited instanceof Promise) ((Promise)awaited).then(ctx, fulfillFunc, rejectFunc); - else if (Values.isPrimitive(awaited)) frame.push(awaited); - else { - try { - var res = Values.getMember(ctx, awaited, "then"); - if (res instanceof FunctionValue) { - Values.function(res).call(ctx, awaited, fulfillFunc, rejectFunc); - break; - } - else frame.push(awaited); - } - catch (EngineException e) { - promise.reject(e); - break; - } - } - } - - frame.end(ctx); - return null; - } - - public Object await(CallContext ctx, Object thisArg, Object[] args) { - this.awaiting = true; - this.awaited = args[0]; - return null; - } - } - - @Override - public Object call(CallContext _ctx, Object thisArg, Object... args) throws InterruptedException { - var handler = new CallHandler(); - var func = body.call(_ctx, thisArg, new NativeFunction("await", handler::await)); - if (!(func instanceof CodeFunction)) throw EngineException.ofType("Return value of argument must be a js function."); - handler.frame = new CodeFrame(thisArg, args, (CodeFunction)func); - handler.exec(_ctx, Runners.NO_RETURN, Runners.NO_RETURN); - return handler.promise; - } - - public AsyncFunction(CodeFunction body) { - super(body.name, body.length); - this.body = body; - } -} diff --git a/src/me/topchetoeu/jscript/polyfills/Internals.java b/src/me/topchetoeu/jscript/polyfills/Internals.java index 9a5d42e..02ed113 100644 --- a/src/me/topchetoeu/jscript/polyfills/Internals.java +++ b/src/me/topchetoeu/jscript/polyfills/Internals.java @@ -241,11 +241,6 @@ public class Internals { return res.exports(); } - @Native - public AsyncFunction makeAsync(FunctionValue func) { - if (func instanceof CodeFunction) return new AsyncFunction((CodeFunction)func); - else throw EngineException.ofType("Can't create an async function with a non-js function."); - } @Native public GeneratorFunction makeGenerator(FunctionValue func) { if (func instanceof CodeFunction) return new GeneratorFunction((CodeFunction)func); From 7a745faacf5fb494364df32a8de2c512cfec71ef Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sat, 26 Aug 2023 00:41:28 +0300 Subject: [PATCH 4/5] feat: implement async generators --- lib/values/function.ts | 51 +++++++++++++++++-- .../topchetoeu/jscript/parsing/Parsing.java | 18 ++++--- 2 files changed, 58 insertions(+), 11 deletions(-) diff --git a/lib/values/function.ts b/lib/values/function.ts index 577c95d..52bbf43 100644 --- a/lib/values/function.ts +++ b/lib/values/function.ts @@ -20,7 +20,7 @@ interface FunctionConstructor extends Function { asyncGenerator( func: (await: (val: T) => Awaited, _yield: (val: T) => void) => (...args: ArgsT) => RetT ): (...args: ArgsT) => AsyncGenerator; - generator( + generator( func: (_yield: (val: T) => TNext) => (...args: ArgsT) => RetT ): (...args: ArgsT) => Generator; } @@ -90,7 +90,7 @@ setProps(Function.prototype, { }, }); setProps(Function, { - async(func: (_yield: (val: T) => Awaited) => (...args: ArgsT) => RetT) { + async(func) { if (typeof func !== 'function') throw new TypeError('Expected func to be function.'); return function (this: any) { @@ -121,8 +121,53 @@ setProps(Function, { }); }; }, + asyncGenerator(func) { + if (typeof func !== 'function') throw new TypeError('Expected func to be function.'); + + + return function(this: any) { + const gen = Function.generator((_yield) => func( + val => _yield(['await', val]) as any, + val => _yield(['yield', val]) + )).apply(this, arguments as any); + + const next = (resolve: Function, reject: Function, type: 'none' | 'val' | 'ret' | 'err', val?: any) => { + let res; + + try { + switch (type) { + case 'val': res = gen.next(val); break; + case 'ret': res = gen.return(val); break; + case 'err': res = gen.throw(val); break; + default: res = gen.next(); break; + } + } + catch (e) { return reject(e); } + + if (res.done) return { done: true, res: res }; + else if (res.value[0] === 'await') Promise.resolve(res.value[1]).then( + v => next(resolve, reject, 'val', v), + v => next(resolve, reject, 'err', v), + ) + else resolve({ done: false, value: res.value[1] }); + }; + + return { + next() { + const args = arguments; + if (arguments.length === 0) return new Promise((res, rej) => next(res, rej, 'none')); + else return new Promise((res, rej) => next(res, rej, 'val', args[0])); + }, + return: (value) => new Promise((res, rej) => next(res, rej, 'ret', value)), + throw: (value) => new Promise((res, rej) => next(res, rej, 'err', value)), + [Symbol.asyncIterator]() { return this; } + } + } + }, generator(func) { if (typeof func !== 'function') throw new TypeError('Expected func to be function.'); - return internals.makeGenerator(func); + return Object.assign(internals.makeGenerator(func), { + [Symbol.iterator]() { return this; } + }); } }) \ No newline at end of file diff --git a/src/me/topchetoeu/jscript/parsing/Parsing.java b/src/me/topchetoeu/jscript/parsing/Parsing.java index 6c816dc..dde815b 100644 --- a/src/me/topchetoeu/jscript/parsing/Parsing.java +++ b/src/me/topchetoeu/jscript/parsing/Parsing.java @@ -981,7 +981,14 @@ public class Parsing { } @SuppressWarnings("all") public static ParseRes parseSimple(String filename, List tokens, int i, boolean statement) { - var res = new ArrayList<>(List.of( + var res = new ArrayList<>(); + + if (!statement) { + res.add(parseObject(filename, tokens, i)); + res.add(parseFunction(filename, tokens, i, false)); + } + + res.addAll(List.of( parseVariable(filename, tokens, i), parseLiteral(filename, tokens, i), parseString(filename, tokens, i), @@ -997,11 +1004,6 @@ public class Parsing { parseDelete(filename, tokens, i) )); - if (!statement) { - res.add(parseObject(filename, tokens, i)); - res.add(parseFunction(filename, tokens, i, false)); - } - return ParseRes.any(res.toArray(ParseRes[]::new)); } @@ -1795,7 +1797,6 @@ public class Parsing { parseContinue(filename, tokens, i), parseBreak(filename, tokens, i), parseDebug(filename, tokens, i), - parseValueStatement(filename, tokens, i), parseIf(filename, tokens, i), parseWhile(filename, tokens, i), parseSwitch(filename, tokens, i), @@ -1804,7 +1805,8 @@ public class Parsing { parseDoWhile(filename, tokens, i), parseCatch(filename, tokens, i), parseCompound(filename, tokens, i), - parseFunction(filename, tokens, i, true) + parseFunction(filename, tokens, i, true), + parseValueStatement(filename, tokens, i) ); } From 12093eda14d3195439ad9aa6d523cc4b48caf93c Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sat, 26 Aug 2023 00:44:28 +0300 Subject: [PATCH 5/5] fix: fully encapsulate native generator --- lib/values/function.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/values/function.ts b/lib/values/function.ts index 52bbf43..7bf7178 100644 --- a/lib/values/function.ts +++ b/lib/values/function.ts @@ -166,8 +166,16 @@ setProps(Function, { }, generator(func) { if (typeof func !== 'function') throw new TypeError('Expected func to be function.'); - return Object.assign(internals.makeGenerator(func), { - [Symbol.iterator]() { return this; } - }); + const gen = internals.makeGenerator(func); + return (...args: any[]) => { + const it = gen(args); + + return { + next: it.next, + return: it.return, + throw: it.throw, + [Symbol.iterator]() { return this; } + } + } } }) \ No newline at end of file