From 549fc70a6946c158b3d0b87ca3d8bc67eea0ed2d Mon Sep 17 00:00:00 2001 From: GlassBricks <24237065+GlassBricks@users.noreply.github.com> Date: Sat, 19 Mar 2022 16:32:55 -0700 Subject: [PATCH] Implement Array.from and Array.of --- src/LuaLib.ts | 1 + src/lualib/ArrayFrom.ts | 36 ++++++++++++++++++++++++++++ src/lualib/tsconfig.json | 3 ++- src/transformation/builtins/array.ts | 7 +++++- test/unit/builtins/array.spec.ts | 14 +++++++++++ 5 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 src/lualib/ArrayFrom.ts diff --git a/src/LuaLib.ts b/src/LuaLib.ts index e5df8927a..b9beddab9 100644 --- a/src/LuaLib.ts +++ b/src/LuaLib.ts @@ -10,6 +10,7 @@ export enum LuaLibFeature { ArrayForEach = "ArrayForEach", ArrayFind = "ArrayFind", ArrayFindIndex = "ArrayFindIndex", + ArrayFrom = "ArrayFrom", ArrayIncludes = "ArrayIncludes", ArrayIndexOf = "ArrayIndexOf", ArrayIsArray = "ArrayIsArray", diff --git a/src/lualib/ArrayFrom.ts b/src/lualib/ArrayFrom.ts new file mode 100644 index 000000000..fda55580f --- /dev/null +++ b/src/lualib/ArrayFrom.ts @@ -0,0 +1,36 @@ +/** @noSelfInFile */ + +import { __TS__Iterator } from "./Iterator"; + +function arrayLikeStep(this: ArrayLike, index: number): LuaMultiReturn<[number, unknown] | []> { + index += 1; + if (index > this.length) return $multi(); + return $multi(index, this[index]); +} + +const arrayLikeIterator: ( + this: void, + arr: ArrayLike | Iterable +) => LuaIterable> = (arr => { + if (typeof arr.length === "number") return $multi(arrayLikeStep, arr, 0); + return __TS__Iterator(arr); +}) as any; + +export function __TS__ArrayFrom( + this: void, + arrayLike: ArrayLike | Iterable, + mapFn?: (this: unknown, element: unknown, index: number) => unknown, + thisArg?: unknown +): unknown[] { + const result = []; + if (mapFn === undefined) { + for (const [, v] of arrayLikeIterator(arrayLike)) { + result.push(v); + } + } else { + for (const [i, v] of arrayLikeIterator(arrayLike)) { + result.push(mapFn.call(thisArg, v, i - 1)); + } + } + return result; +} diff --git a/src/lualib/tsconfig.json b/src/lualib/tsconfig.json index 8acde4cca..246e46b90 100644 --- a/src/lualib/tsconfig.json +++ b/src/lualib/tsconfig.json @@ -13,5 +13,6 @@ "luaLibImport": "none", "noHeader": true, "luaPlugins": [{ "name": "../../dist/lualib-build/plugin.js" }] - } + }, + "include": [".", "../../language-extensions/index.d.ts"] } diff --git a/src/transformation/builtins/array.ts b/src/transformation/builtins/array.ts index 7bbd4cf98..3242cd57a 100644 --- a/src/transformation/builtins/array.ts +++ b/src/transformation/builtins/array.ts @@ -5,19 +5,24 @@ import { unsupportedProperty } from "../utils/diagnostics"; import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib"; import { PropertyCallExpression, transformArguments, transformCallAndArguments } from "../visitors/call"; import { isStringType, isNumberType } from "../utils/typescript"; +import { wrapInTable } from "../utils/lua-ast"; export function transformArrayConstructorCall( context: TransformationContext, node: PropertyCallExpression -): lua.CallExpression | undefined { +): lua.Expression | undefined { const expression = node.expression; const signature = context.checker.getResolvedSignature(node); const params = transformArguments(context, node.arguments, signature); const expressionName = expression.name.text; switch (expressionName) { + case "from": + return transformLuaLibFunction(context, LuaLibFeature.ArrayFrom, node, ...params); case "isArray": return transformLuaLibFunction(context, LuaLibFeature.ArrayIsArray, node, ...params); + case "of": + return wrapInTable(...params); default: context.diagnostics.push(unsupportedProperty(expression.name, "Array", expressionName)); } diff --git a/test/unit/builtins/array.spec.ts b/test/unit/builtins/array.spec.ts index eaeddf08e..98b749e3b 100644 --- a/test/unit/builtins/array.spec.ts +++ b/test/unit/builtins/array.spec.ts @@ -671,6 +671,20 @@ test("Array.isArray returns true for empty objects", () => { util.testExpression`Array.isArray({})`.expectToEqual(true); }); +test.each([ + "[1, 2, 3]", + "(new Set([1, 2, 3])).values()", + "[1, 2, 3], value => value * 2", + "{ length: 3 }, (_, index) => index + 1", +])("Array.from(%p)", valueString => { + util.testExpression`Array.from(${valueString})`.expectToMatchJsResult(); +}); + +// Array.of +test.each(["1, 2, 3", "", "...[1, 2, 3], 4, 5, 6"])("Array.of(%p)", valueString => { + util.testExpression`Array.of(${valueString})`.expectToMatchJsResult(); +}); + // Test fix for https://github.com/TypeScriptToLua/TypeScriptToLua/issues/738 test("array.prototype.concat issue #738", () => { util.testExpression`([] as any[]).concat(13, 323, {x: 3}, [2, 3])`.expectToMatchJsResult();