@tilua/parser 5.0.0 → 5.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -353,7 +353,9 @@ than -1 — and `push`, `pop`, `shift`, `unshift`, `sort` and `reverse` change
353
353
  the array. Looking for a value is not adding one, so `includes` and `indexOf`
354
354
  take any value, not only one the array's type already holds — and `includes`
355
355
  is a guard: `if (modes:includes(s))` narrows `s` to a mode. `concat` may join
356
- arrays of other types (`(T | U)[]`). The ones that only read take a read-only
356
+ arrays of other types (`(T | U)[]`). `keys`, `values` and `entries` answer
357
+ arrays rather than JavaScript's iterators — `for (const [i, v] in
358
+ xs:entries())` — counting from 1. The ones that only read take a read-only
357
359
  list too (`readonly T[]`, what `as const` makes), as TypeScript's
358
360
  `ReadonlyArray` does; the ones that change it do not. A table's are `keys`, `values` and `entries`, which answer a tuple
359
361
  in the order the table's type wrote its members, not `string[]` as
@@ -693,7 +695,11 @@ Dog.ParentClass == Animal -- a class names the one it extends
693
695
  Animal.ParentClass == nil -- and a root class extends nothing
694
696
  ```
695
697
 
696
- Both live on the class table, so an instance carries neither.
698
+ Both live on the class table, so an instance carries neither. So does its
699
+ name, as a JavaScript class's `name`: `Dog.name` is `"Dog"`, and a class
700
+ written as a value takes the name of what holds it (`const Counter = class
701
+ { ... }` is `"Counter"`). A static, a method or an accessor cannot be called
702
+ `name`; an instance field can, and lives on the instance.
697
703
 
698
704
  **Generic classes** — `class Box<T> { ... }`. The name is then a generic type,
699
705
  and `Box<number>` and `Box<string>` are both `Box` but neither is the other:
package/dist/index.cjs CHANGED
@@ -226,6 +226,14 @@ function isAlphaNumeric(ch) {
226
226
  return isAlpha(ch) || isDigit(ch);
227
227
  }
228
228
  var KeywordSet = new Set(Keywords);
229
+ var TS_SPELLINGS = [
230
+ ["!==", "~=", "Operator"],
231
+ ["===", "==", "Operator"],
232
+ ["!=", "~=", "Operator"],
233
+ ["&&", "and", "Keyword"],
234
+ ["||", "or", "Keyword"],
235
+ ["!", "not", "Keyword"]
236
+ ];
229
237
  var OperatorSet = new Set(Operators);
230
238
  var SortedSymbols = [...Operators, ...Punctuators].sort((a, b) => b.length - a.length);
231
239
  var LexError = class extends Error {
@@ -686,6 +694,15 @@ function tokenize(source, options = {}) {
686
694
  tokens.push(readIdentifierOrKeyword());
687
695
  continue;
688
696
  }
697
+ const spelled = TS_SPELLINGS.find(([written]) => source.startsWith(written, cursor));
698
+ if (spelled) {
699
+ const [written, meant, kind] = spelled;
700
+ const startLine = line, startColumn = column;
701
+ fail(`tilua writes '${meant}', not '${written}'`, startLine, startColumn);
702
+ for (let k = 0; k < written.length; k++) advance();
703
+ tokens.push(kind === "Keyword" ? { type: "Keyword", value: meant, ...makeBase(startLine, startColumn) } : { type: "Operator", value: meant, ...makeBase(startLine, startColumn) });
704
+ continue;
705
+ }
689
706
  const symbol = readOperatorOrPunctuator();
690
707
  if (symbol) tokens.push(symbol);
691
708
  }
@@ -4494,7 +4511,7 @@ declare function xpcall<A extends unknown[], T>(fn: (...args: A) => T, handler:
4494
4511
  declare function select(n: "#", ...args: unknown[]): number
4495
4512
  declare function select<T>(n: number, ...args: T[]): T[]
4496
4513
  -- A copy of the list, or of part of it.
4497
- declare function unpack<V>(list: V[], i?: number, j?: number): V[]
4514
+ declare function unpack<V>(list: readonly V[], i?: number, j?: number): V[]
4498
4515
  declare function rawget(t: unknown, key: unknown): unknown
4499
4516
  declare function rawset<T>(t: T, key: unknown, value: unknown): T
4500
4517
  declare function rawequal(a: unknown, b: unknown): boolean
@@ -4513,10 +4530,11 @@ type Iteration<S, T> = [(state: S, previous: T | nil) => T | nil, S, T | nil]
4513
4530
  -- \`for (const [k, v] in pairs(t))\` / \`ipairs(t)\`: each item is one \`[key,
4514
4531
  -- value]\` \u2014 a table, as every array is. The analyzer types it from \`t\` itself
4515
4532
  -- (a record's keys as literals, an array's elements); these declare the
4516
- -- names, and their shape for any other use.
4517
- declare function pairs<V>(t: V[]): Iteration<V[], [number, V]>
4533
+ -- names, and their shape for any other use. Walking a list only reads it, so
4534
+ -- a read-only one (\`as const\`, \`readonly T[]\`) is walked as well.
4535
+ declare function pairs<V>(t: readonly V[]): Iteration<readonly V[], [number, V]>
4518
4536
  declare function pairs<K, V>(t: { [K]: V }): Iteration<{ [K]: V }, [K, V]>
4519
- declare function ipairs<V>(t: V[]): Iteration<V[], [number, V]>
4537
+ declare function ipairs<V>(t: readonly V[]): Iteration<readonly V[], [number, V]>
4520
4538
 
4521
4539
  declare function collectgarbage(option: "count"): number
4522
4540
  declare function gcinfo(): number
@@ -4590,14 +4608,14 @@ declare table: {
4590
4608
  -- \`insert(t, v)\` appends; \`insert(t, pos, v)\` puts it there.
4591
4609
  insert: (<T>(t: T[], value: T) => nil) & (<T>(t: T[], pos: number, value: T) => nil),
4592
4610
  remove: <T>(t: T[], pos?: number) => T | nil,
4593
- concat: (t: (string | number)[], sep?: string, i?: number, j?: number) => string,
4611
+ concat: (t: readonly (string | number)[], sep?: string, i?: number, j?: number) => string,
4594
4612
  sort: <T>(t: T[], compare?: (a: T, b: T) => boolean) => nil,
4595
4613
  -- Lua 5.1's originals. Deprecated there and gone from 5.2 on, but Luau
4596
4614
  -- still answers to them, so code being ported still type-checks.
4597
- getn: <T>(t: T[]) => number,
4598
- maxn: <T>(t: T[]) => number,
4615
+ getn: <T>(t: readonly T[]) => number,
4616
+ maxn: <T>(t: readonly T[]) => number,
4599
4617
  foreach: <K, V, R>(t: { [K]: V }, f: (key: K, value: V) => R) => R | nil,
4600
- foreachi: <V, R>(t: V[], f: (index: number, value: V) => R) => R | nil,
4618
+ foreachi: <V, R>(t: readonly V[], f: (index: number, value: V) => R) => R | nil,
4601
4619
  }
4602
4620
 
4603
4621
  declare os: {
@@ -5490,6 +5508,11 @@ declare metatable<T> T[]: {
5490
5508
  concat: <U>(self: readonly T[], ...others: (readonly U[])[]) => (T | U)[],
5491
5509
  slice: (self: readonly T[], start?: number, stop?: number) => T[],
5492
5510
  flat: (self: readonly T[]) => T[],
5511
+ -- JavaScript's, as arrays rather than iterators: \`for (const [i, v] in
5512
+ -- xs:entries())\` walks positions and values, counting from 1.
5513
+ keys: (self: readonly T[]) => number[],
5514
+ values: (self: readonly T[]) => T[],
5515
+ entries: (self: readonly T[]) => [number, T][],
5493
5516
  reverse: (self: T[]) => T[],
5494
5517
  sort: (self: T[], compare?: (a: T, b: T) => boolean) => T[],
5495
5518
  push: (self: T[], ...args: T[]) => number,
@@ -7277,7 +7300,7 @@ function keepsLiterals(paramType) {
7277
7300
  const members = paramType.constraint.kind === "union" ? paramType.constraint.types : [paramType.constraint];
7278
7301
  return members.some((m) => m.kind === "literal" || m.kind === "primitive" && (m.name === "string" || m.name === "number"));
7279
7302
  }
7280
- var CLASS_LINKS = /* @__PURE__ */ new Set(["new", "ClassObject", "ParentClass"]);
7303
+ var CLASS_LINKS = /* @__PURE__ */ new Set(["new", "ClassObject", "ParentClass", "name"]);
7281
7304
  var CLASS_RESERVED = /* @__PURE__ */ new Set([
7282
7305
  "new",
7283
7306
  "ClassObject",
@@ -8035,6 +8058,7 @@ var TypeAnalyzer = class {
8035
8058
  });
8036
8059
  type.properties.set("new", construct);
8037
8060
  type.properties.set("ParentClass", { type: parent ?? nilType, optional: false, readonly: true });
8061
+ type.properties.set("name", { type: stmt.name ? literal(stmt.name.name) : stringType, optional: false, readonly: true });
8038
8062
  return type;
8039
8063
  }
8040
8064
  /** The value side of the class `superclass` names when it is not
@@ -8294,6 +8318,9 @@ var TypeAnalyzer = class {
8294
8318
  if (CLASS_RESERVED.has(member.name.name)) {
8295
8319
  report(member.name, `'${member.name.name}' is what the compiler calls part of a class; a member cannot be named that`);
8296
8320
  }
8321
+ if (member.name.name === "name" && !(member.type === "ClassField" && !member.isStatic)) {
8322
+ report(member.name, "'name' is the class's own name, `Dog.name`: a static, a method or an accessor cannot be called that \u2014 an instance field can");
8323
+ }
8297
8324
  }
8298
8325
  const seen = /* @__PURE__ */ new Map();
8299
8326
  for (const member of stmt.members) {
@@ -9134,6 +9161,13 @@ var TypeAnalyzer = class {
9134
9161
  case "array":
9135
9162
  case "tuple":
9136
9163
  return union([numberType, literal("length")]);
9164
+ // A string is not indexed by position in Lua (`s[1]` is nil), so
9165
+ // of its keys only `length` is there.
9166
+ case "primitive":
9167
+ return t.name === "string" ? literal("length") : neverType;
9168
+ case "literal":
9169
+ case "templateLiteral":
9170
+ return t.kind === "templateLiteral" || t.base === "string" ? literal("length") : neverType;
9137
9171
  case "union": {
9138
9172
  const sets = t.types.map((m) => {
9139
9173
  const keys = this.keysOf(m);
@@ -9604,7 +9638,7 @@ var TypeAnalyzer = class {
9604
9638
  this.loops.set(stmt, { walks: "iteration", viaIter: false });
9605
9639
  } else {
9606
9640
  const [item, form] = this.iterationOf(stmt.iterator, sourceType);
9607
- this.bindPattern(stmt.variable, item, bodyEnv, "widen");
9641
+ this.bindPattern(stmt.variable, item, bodyEnv, stmt.kind === "const" ? "keep" : "widen");
9608
9642
  this.loops.set(stmt, form);
9609
9643
  }
9610
9644
  this.visitBlock(stmt.body, bodyEnv);
@@ -11060,7 +11094,8 @@ var TypeAnalyzer = class {
11060
11094
  const name = source.callee.name;
11061
11095
  const src = this.expand(this.typeOf.get(source.arguments[0]) ?? unknownType);
11062
11096
  const iteration = { walks: "iteration", viaIter: false };
11063
- if (name === "ipairs") return [tuple([numberType, this.elementType(src, 0)]), iteration];
11097
+ const element = src.kind === "array" ? src.element : src.kind === "tuple" ? union(tupleMembers(src).map((m) => m.kind === "spread" ? this.spreadElement(m.type) ?? unknownType : m)) : void 0;
11098
+ if (name === "ipairs") return [tuple([numberType, element ?? this.elementType(src, 0)]), iteration];
11064
11099
  if (name === "pairs") {
11065
11100
  if (src.kind === "object") {
11066
11101
  return [tuple([
@@ -11068,7 +11103,7 @@ var TypeAnalyzer = class {
11068
11103
  src.indexer?.value ?? union([...src.properties.values()].map((p) => p.type))
11069
11104
  ]), iteration];
11070
11105
  }
11071
- if (src.kind === "array") return [tuple([numberType, src.element]), iteration];
11106
+ if (element) return [tuple([numberType, element]), iteration];
11072
11107
  }
11073
11108
  }
11074
11109
  const handsOut = (t2, viaIter) => {
package/dist/index.d.cts CHANGED
@@ -1756,7 +1756,7 @@ resolveModule?: (specifier: string) => ModuleExports | undefined): ModuleExports
1756
1756
  * `ObjectValues<T>` and `ObjectEntries<T>`, an object's members as tuples in
1757
1757
  * the order they were written.
1758
1758
  */
1759
- declare const PRELUDE_SOURCE = "\n-- In Luau only `nil` and `false` are falsy: `0` and `\"\"` are truthy.\n-- These are what truthiness narrowing computes, made available to write down.\ntype Falsy = nil | false\ntype Truthy<T> = Exclude<T, Falsy>\n\n-- `Exclude<T, U>` is the analyzer's own: what of `T` is not a `U`. Over a\n-- union it drops the members it takes whole; over `unknown`, `any`, a\n-- primitive or an unresolved parameter it is kept \u2014 `Exclude<string, \"a\">` is\n-- a string that is not `\"a\"` \u2014 unlike TypeScript's.\ntype Extract<T, U> = T extends U ? T : never\ntype NonNullable<T> = Exclude<T, nil>\n\ntype ReturnType<T> = T extends (...args: unknown[]) => infer R ? R : never\ntype Parameters<T> = T extends (...args: infer P) => unknown ? P : never\n\ntype Partial<T> = { [K in keyof T]?: T[K] }\ntype Required<T> = { [K in keyof T]-?: T[K] }\ntype Readonly<T> = { readonly [K in keyof T]: T[K] }\ntype Mutable<T> = { -readonly [K in keyof T]: T[K] }\n\n-- A class table builds with `new`: `InstanceType<typeof Dog>` is a `Dog`.\ntype InstanceType<T> = T extends { new: (...args: any[]) => infer R } ? R : never\ntype ConstructorParameters<T> = T extends { new: (...args: infer P) => any } ? P : never\n\ntype Pick<T, K> = { [P in K]: T[P] }\ntype Omit<T, K> = Pick<T, Exclude<keyof T, K>>\ntype Record<K, V> = { [P in K]: V }\n\n-- ============================================================\n-- The metatables of the language's own values\n-- ============================================================\n\n-- A string's methods. The first group is Lua's string library, which a\n-- string really answers to; where Lua answers several values, tilua answers\n-- one, as the global functions do (`find` is an array, `gsub` the new\n-- string). The second group is written the way JavaScript writes it.\n--\n-- Positions are Lua's: `sub` and `slice` count from 1, and `indexOf` is\n-- `nil` when the text is not there.\ndeclare metatable string: {\n __index: {\n upper: (self: string) => string,\n lower: (self: string) => string,\n len: (self: string) => number,\n sub: (self: string, i: number, j?: number) => string,\n rep: (self: string, n: number, separator?: string) => string,\n reverse: (self: string) => string,\n format: (self: string, ...args: unknown[]) => string,\n byte: ((self: string, i?: number) => number) & ((self: string, i: number, j: number) => number[]),\n find: (self: string, pattern: string, init?: number, plain?: boolean) => [number, number, ...string[]] | nil,\n match: (self: string, pattern: string, init?: number) => [...string[]] | nil,\n gmatch: (self: string, pattern: string) => () => [string, ...string[]] | nil,\n gsub: (self: string, pattern: string, replacement: unknown, n?: number) => string,\n\n trim: (self: string) => string,\n trimStart: (self: string) => string,\n trimEnd: (self: string) => string,\n startsWith: (self: string, text: string) => boolean,\n endsWith: (self: string, text: string) => boolean,\n includes: (self: string, text: string) => boolean,\n indexOf: (self: string, text: string, start?: number) => number | nil,\n slice: (self: string, start?: number, stop?: number) => string,\n replace: (self: string, text: string, replacement: string) => string,\n replaceAll: (self: string, text: string, replacement: string) => string,\n padStart: (self: string, length: number, padding?: string) => string,\n padEnd: (self: string, length: number, padding?: string) => string,\n },\n}\n\n-- An array's methods. Lua gives an array no metatable: the compiler lowers\n-- each call to a plain function, so they work on any array, one a Lua\n-- library made included.\n--\n-- Indices are Lua's: the first element is 1, and `indexOf` / `findIndex`\n-- answer `nil` rather than JavaScript's -1. `sort` takes Lua's comparator\n-- (true when `a` comes first). `push`, `pop`, `shift`, `unshift`,\n-- `sort` and `reverse` change the array; everything else returns a new one.\n-- The ones that only read take a `readonly T[]` too \u2014 `as const` lists, and\n-- what a function promised not to change \u2014 as TypeScript's `ReadonlyArray`.\n-- A `filter` whose test is a guard \u2014 `v is S`, or `v => v ~= nil` \u2014\n-- answers an array of what it let through.\ndeclare metatable<T> T[]: {\n __index: {\n find: (self: readonly T[], test: (value: T, index: number) => boolean) => T | nil,\n findIndex: (self: readonly T[], test: (value: T, index: number) => boolean) => number | nil,\n filter: (<S extends T>(self: readonly T[], test: (value: T, index: number) => value is S) => S[])\n & ((self: readonly T[], test: (value: T, index: number) => boolean) => T[]),\n map: <U>(self: readonly T[], transform: (value: T, index: number) => U) => U[],\n forEach: (self: readonly T[], visit: (value: T, index: number) => nil) => nil,\n some: (self: readonly T[], test: (value: T, index: number) => boolean) => boolean,\n every: (self: readonly T[], test: (value: T, index: number) => boolean) => boolean,\n reduce: <U>(self: readonly T[], step: (total: U, value: T, index: number) => U, initial: U) => U,\n -- Asking is not adding: any value may be looked for, and finding it\n -- says it is one of the array's \u2014 `if (names:includes(s))` narrows `s`.\n includes: (self: readonly T[], value: unknown) => value is T,\n indexOf: (self: readonly T[], value: unknown, start?: number) => number | nil,\n join: (self: readonly T[], separator?: string) => string,\n concat: <U>(self: readonly T[], ...others: (readonly U[])[]) => (T | U)[],\n slice: (self: readonly T[], start?: number, stop?: number) => T[],\n flat: (self: readonly T[]) => T[],\n reverse: (self: T[]) => T[],\n sort: (self: T[], compare?: (a: T, b: T) => boolean) => T[],\n push: (self: T[], ...args: T[]) => number,\n pop: (self: T[]) => T | nil,\n shift: (self: T[]) => T | nil,\n unshift: (self: T[], ...args: T[]) => number,\n },\n}\n\n-- A table's methods. Unlike JavaScript's `Object.keys`, they answer a\n-- tuple, in the order the table's type wrote its members:\n--\n-- const point = { x: 1, y: \"up\" }\n-- point:keys() -- [\"x\", \"y\"]\n-- point:values() -- [number, string]\n-- point:entries() -- [[\"x\", number], [\"y\", string]]\n--\n-- A Lua table has no order of its own, so the compiler hands its runtime the\n-- keys as the type lists them. An optional member keeps its place, holding\n-- `nil` when it is missing; an indexer's keys follow the named ones. A\n-- member of the table's own with one of these names is what a call reaches,\n-- and a class instance, which has a metatable of its own, has none of them.\n--\n-- The rest are JavaScript's `Object` functions, written as methods of the\n-- table they take first:\n--\n-- point:hasOwn(\"x\") -- Object.hasOwn(point, \"x\")\n-- point:assign({ z: 3 }) -- Object.assign(point, { z: 3 })\n-- point:freeze() -- Object.freeze(point)\n--\n-- `hasOwn` asks the table itself, not what its metatable's `__index`\n-- lends it. `assign` copies each source's members onto the table, later ones\n-- over earlier ones, skipping a `nil` source, and answers the table. `freeze`\n-- is Luau's `table.freeze`; under Lua 5.1, which cannot freeze a table, the\n-- table is answered as it is and `isFrozen` is false.\ndeclare metatable<T extends {}> T: {\n __index: {\n keys: (self: T) => ObjectKeys<T>,\n values: (self: T) => ObjectValues<T>,\n entries: (self: T) => ObjectEntries<T>,\n\n hasOwn: (self: T, key: unknown) => boolean,\n hasOwnProperty: (self: T, key: unknown) => boolean,\n assign: (<U>(self: T, source: U) => T & U)\n & (<U, V>(self: T, a: U, b: V) => T & U & V)\n & (<U, V, W>(self: T, a: U, b: V, c: W) => T & U & V & W)\n -- Generic like the rest: a plain signature would be tried first.\n & (<S extends {}>(self: T, ...sources: S[]) => T),\n freeze: (self: T) => Readonly<T>,\n isFrozen: (self: T) => boolean,\n },\n}\n";
1759
+ declare const PRELUDE_SOURCE = "\n-- In Luau only `nil` and `false` are falsy: `0` and `\"\"` are truthy.\n-- These are what truthiness narrowing computes, made available to write down.\ntype Falsy = nil | false\ntype Truthy<T> = Exclude<T, Falsy>\n\n-- `Exclude<T, U>` is the analyzer's own: what of `T` is not a `U`. Over a\n-- union it drops the members it takes whole; over `unknown`, `any`, a\n-- primitive or an unresolved parameter it is kept \u2014 `Exclude<string, \"a\">` is\n-- a string that is not `\"a\"` \u2014 unlike TypeScript's.\ntype Extract<T, U> = T extends U ? T : never\ntype NonNullable<T> = Exclude<T, nil>\n\ntype ReturnType<T> = T extends (...args: unknown[]) => infer R ? R : never\ntype Parameters<T> = T extends (...args: infer P) => unknown ? P : never\n\ntype Partial<T> = { [K in keyof T]?: T[K] }\ntype Required<T> = { [K in keyof T]-?: T[K] }\ntype Readonly<T> = { readonly [K in keyof T]: T[K] }\ntype Mutable<T> = { -readonly [K in keyof T]: T[K] }\n\n-- A class table builds with `new`: `InstanceType<typeof Dog>` is a `Dog`.\ntype InstanceType<T> = T extends { new: (...args: any[]) => infer R } ? R : never\ntype ConstructorParameters<T> = T extends { new: (...args: infer P) => any } ? P : never\n\ntype Pick<T, K> = { [P in K]: T[P] }\ntype Omit<T, K> = Pick<T, Exclude<keyof T, K>>\ntype Record<K, V> = { [P in K]: V }\n\n-- ============================================================\n-- The metatables of the language's own values\n-- ============================================================\n\n-- A string's methods. The first group is Lua's string library, which a\n-- string really answers to; where Lua answers several values, tilua answers\n-- one, as the global functions do (`find` is an array, `gsub` the new\n-- string). The second group is written the way JavaScript writes it.\n--\n-- Positions are Lua's: `sub` and `slice` count from 1, and `indexOf` is\n-- `nil` when the text is not there.\ndeclare metatable string: {\n __index: {\n upper: (self: string) => string,\n lower: (self: string) => string,\n len: (self: string) => number,\n sub: (self: string, i: number, j?: number) => string,\n rep: (self: string, n: number, separator?: string) => string,\n reverse: (self: string) => string,\n format: (self: string, ...args: unknown[]) => string,\n byte: ((self: string, i?: number) => number) & ((self: string, i: number, j: number) => number[]),\n find: (self: string, pattern: string, init?: number, plain?: boolean) => [number, number, ...string[]] | nil,\n match: (self: string, pattern: string, init?: number) => [...string[]] | nil,\n gmatch: (self: string, pattern: string) => () => [string, ...string[]] | nil,\n gsub: (self: string, pattern: string, replacement: unknown, n?: number) => string,\n\n trim: (self: string) => string,\n trimStart: (self: string) => string,\n trimEnd: (self: string) => string,\n startsWith: (self: string, text: string) => boolean,\n endsWith: (self: string, text: string) => boolean,\n includes: (self: string, text: string) => boolean,\n indexOf: (self: string, text: string, start?: number) => number | nil,\n slice: (self: string, start?: number, stop?: number) => string,\n replace: (self: string, text: string, replacement: string) => string,\n replaceAll: (self: string, text: string, replacement: string) => string,\n padStart: (self: string, length: number, padding?: string) => string,\n padEnd: (self: string, length: number, padding?: string) => string,\n },\n}\n\n-- An array's methods. Lua gives an array no metatable: the compiler lowers\n-- each call to a plain function, so they work on any array, one a Lua\n-- library made included.\n--\n-- Indices are Lua's: the first element is 1, and `indexOf` / `findIndex`\n-- answer `nil` rather than JavaScript's -1. `sort` takes Lua's comparator\n-- (true when `a` comes first). `push`, `pop`, `shift`, `unshift`,\n-- `sort` and `reverse` change the array; everything else returns a new one.\n-- The ones that only read take a `readonly T[]` too \u2014 `as const` lists, and\n-- what a function promised not to change \u2014 as TypeScript's `ReadonlyArray`.\n-- A `filter` whose test is a guard \u2014 `v is S`, or `v => v ~= nil` \u2014\n-- answers an array of what it let through.\ndeclare metatable<T> T[]: {\n __index: {\n find: (self: readonly T[], test: (value: T, index: number) => boolean) => T | nil,\n findIndex: (self: readonly T[], test: (value: T, index: number) => boolean) => number | nil,\n filter: (<S extends T>(self: readonly T[], test: (value: T, index: number) => value is S) => S[])\n & ((self: readonly T[], test: (value: T, index: number) => boolean) => T[]),\n map: <U>(self: readonly T[], transform: (value: T, index: number) => U) => U[],\n forEach: (self: readonly T[], visit: (value: T, index: number) => nil) => nil,\n some: (self: readonly T[], test: (value: T, index: number) => boolean) => boolean,\n every: (self: readonly T[], test: (value: T, index: number) => boolean) => boolean,\n reduce: <U>(self: readonly T[], step: (total: U, value: T, index: number) => U, initial: U) => U,\n -- Asking is not adding: any value may be looked for, and finding it\n -- says it is one of the array's \u2014 `if (names:includes(s))` narrows `s`.\n includes: (self: readonly T[], value: unknown) => value is T,\n indexOf: (self: readonly T[], value: unknown, start?: number) => number | nil,\n join: (self: readonly T[], separator?: string) => string,\n concat: <U>(self: readonly T[], ...others: (readonly U[])[]) => (T | U)[],\n slice: (self: readonly T[], start?: number, stop?: number) => T[],\n flat: (self: readonly T[]) => T[],\n -- JavaScript's, as arrays rather than iterators: `for (const [i, v] in\n -- xs:entries())` walks positions and values, counting from 1.\n keys: (self: readonly T[]) => number[],\n values: (self: readonly T[]) => T[],\n entries: (self: readonly T[]) => [number, T][],\n reverse: (self: T[]) => T[],\n sort: (self: T[], compare?: (a: T, b: T) => boolean) => T[],\n push: (self: T[], ...args: T[]) => number,\n pop: (self: T[]) => T | nil,\n shift: (self: T[]) => T | nil,\n unshift: (self: T[], ...args: T[]) => number,\n },\n}\n\n-- A table's methods. Unlike JavaScript's `Object.keys`, they answer a\n-- tuple, in the order the table's type wrote its members:\n--\n-- const point = { x: 1, y: \"up\" }\n-- point:keys() -- [\"x\", \"y\"]\n-- point:values() -- [number, string]\n-- point:entries() -- [[\"x\", number], [\"y\", string]]\n--\n-- A Lua table has no order of its own, so the compiler hands its runtime the\n-- keys as the type lists them. An optional member keeps its place, holding\n-- `nil` when it is missing; an indexer's keys follow the named ones. A\n-- member of the table's own with one of these names is what a call reaches,\n-- and a class instance, which has a metatable of its own, has none of them.\n--\n-- The rest are JavaScript's `Object` functions, written as methods of the\n-- table they take first:\n--\n-- point:hasOwn(\"x\") -- Object.hasOwn(point, \"x\")\n-- point:assign({ z: 3 }) -- Object.assign(point, { z: 3 })\n-- point:freeze() -- Object.freeze(point)\n--\n-- `hasOwn` asks the table itself, not what its metatable's `__index`\n-- lends it. `assign` copies each source's members onto the table, later ones\n-- over earlier ones, skipping a `nil` source, and answers the table. `freeze`\n-- is Luau's `table.freeze`; under Lua 5.1, which cannot freeze a table, the\n-- table is answered as it is and `isFrozen` is false.\ndeclare metatable<T extends {}> T: {\n __index: {\n keys: (self: T) => ObjectKeys<T>,\n values: (self: T) => ObjectValues<T>,\n entries: (self: T) => ObjectEntries<T>,\n\n hasOwn: (self: T, key: unknown) => boolean,\n hasOwnProperty: (self: T, key: unknown) => boolean,\n assign: (<U>(self: T, source: U) => T & U)\n & (<U, V>(self: T, a: U, b: V) => T & U & V)\n & (<U, V, W>(self: T, a: U, b: V, c: W) => T & U & V & W)\n -- Generic like the rest: a plain signature would be tried first.\n & (<S extends {}>(self: T, ...sources: S[]) => T),\n freeze: (self: T) => Readonly<T>,\n isFrozen: (self: T) => boolean,\n },\n}\n";
1760
1760
 
1761
1761
  /**
1762
1762
  * The Lua 5.1 standard library, always there — as TypeScript always has
@@ -1773,7 +1773,7 @@ declare const PRELUDE_SOURCE = "\n-- In Luau only `nil` and `false` are falsy: `
1773
1773
  * `@tilua-types/lua`, where this used to live, is not loaded when a project
1774
1774
  * still names it: it would declare everything twice.
1775
1775
  */
1776
- declare const STANDARD_LIBRARY_SOURCE = "\n-- The Lua 5.1 standard library, written in tilua.\n--\n-- This is the floor every tilua project stands on, the way TypeScript stands\n-- on JavaScript's: the functions and libraries Lua itself defines, there in\n-- every file without being asked for. Nothing here is Luau's or Roblox's \u2014\n-- `buffer`, `table.create` and the rest live one layer up in\n-- `@tilua-types/roblox`, which adds to it.\n--\n-- Lua's `type()` is not here: what a value is, is asked with the language's\n-- own `typeof v`, which also tells an array from an object.\n--\n-- Adding to it is literal: a name declared again in a later file keeps what\n-- earlier files gave it. `declare table: { find: ... }` in `@tilua-types/roblox` does\n-- not replace this `table`, it extends it, and overloads of a function\n-- accumulate the same way. So the layers really are layers.\n\n-- ============================================================\n-- Base library\n-- ============================================================\n\ndeclare _G: { [string]: unknown }\ndeclare _VERSION: string\n\ndeclare function require(module: unknown): unknown\ndeclare function tostring(value: unknown): string\ndeclare function tonumber(value: unknown, base?: number): number | nil\ndeclare function print(...args: unknown[]): nil\n-- `error` never returns, which is what lets\n-- `if v == nil then error(\"...\") end` narrow the rest of the block.\ndeclare function error(message: unknown, level?: number): never\n\n-- `asserts value` \u2014 narrows the *rest of the enclosing block*, the way\n-- TypeScript's assertion signatures do:\n--\n-- const part = workspace:FindFirstChild(\"Part\") -- Instance | nil\n-- assert(part)\n-- part.Name -- Instance, no `?`\n\ndeclare function assert<T>(value: T, message?: string): asserts value\n\n-- `pcall` and `xpcall` answer the way a JavaScript result object does:\n--\n-- const { success, data, error } = pcall(load, \"config\")\n-- if (success) { use(data) } else { warn(error) }\n--\n-- `data` is what the function returned. These are not Luau's own `pcall`:\n-- where Luau answers several values, tilua has one, and the compiler puts its\n-- own functions in place of those globals (runtime/result.luau) \u2014 for calls,\n-- and for the global read as a value alike. The same goes for `string.find`,\n-- `string.match`, `string.gsub`, `string.byte`, `math.modf`, `math.frexp`,\n-- `next`, `select`, `unpack`, `loadstring`, `pairs`, `ipairs` and\n-- `string.gmatch` below.\ntype PcallResult<T> = { success: true, data: T, error: nil } | { success: false, data: nil, error: unknown }\n\ndeclare function pcall<A extends unknown[], T>(fn: (...args: A) => T, ...args: A): PcallResult<T>\ndeclare function xpcall<A extends unknown[], T>(fn: (...args: A) => T, handler: (err: unknown) => unknown, ...args: A): PcallResult<T>\n-- `select(\"#\", ...args)` counts; `select(n, ...args)` is what follows the\n-- n-th, as an array.\ndeclare function select(n: \"#\", ...args: unknown[]): number\ndeclare function select<T>(n: number, ...args: T[]): T[]\n-- A copy of the list, or of part of it.\ndeclare function unpack<V>(list: V[], i?: number, j?: number): V[]\ndeclare function rawget(t: unknown, key: unknown): unknown\ndeclare function rawset<T>(t: T, key: unknown, value: unknown): T\ndeclare function rawequal(a: unknown, b: unknown): boolean\ndeclare function setmetatable<T>(t: T, mt: unknown): T\ndeclare function getmetatable(t: unknown): unknown\ndeclare function newproxy(addMeta?: boolean): unknown\n-- The entry after `key` \u2014 or nil, past the last one.\ndeclare function next<K, V>(t: { [K]: V }, key?: K): [K, V] | nil\n\n-- What `for (const item in source)` walks: `step` is called with the state\n-- and the item before it \u2014 `first` the first time \u2014 until it answers nil.\n-- An array, a table and an iterator function are walked too, so this is only\n-- what a source such as `pairs(t)` hands over.\ntype Iteration<S, T> = [(state: S, previous: T | nil) => T | nil, S, T | nil]\n\n-- `for (const [k, v] in pairs(t))` / `ipairs(t)`: each item is one `[key,\n-- value]` \u2014 a table, as every array is. The analyzer types it from `t` itself\n-- (a record's keys as literals, an array's elements); these declare the\n-- names, and their shape for any other use.\ndeclare function pairs<V>(t: V[]): Iteration<V[], [number, V]>\ndeclare function pairs<K, V>(t: { [K]: V }): Iteration<{ [K]: V }, [K, V]>\ndeclare function ipairs<V>(t: V[]): Iteration<V[], [number, V]>\n\ndeclare function collectgarbage(option: \"count\"): number\ndeclare function gcinfo(): number\ndeclare function getfenv(target?: unknown): { [string]: unknown }\ndeclare function setfenv<T>(target: T, env: { [string]: unknown }): T\ndeclare function loadstring(source: string, chunkname?: string): [((...args: unknown[]) => unknown) | nil, string | nil]\n\ndeclare debug: {\n traceback: (message?: string, level?: number) => string,\n}\n\ndeclare string: {\n format: (fmt: string, ...args: unknown[]) => string,\n sub: (s: string, i: number, j?: number) => string,\n len: (s: string) => number,\n upper: (s: string) => string,\n lower: (s: string) => string,\n rep: (s: string, n: number, sep?: string) => string,\n reverse: (s: string) => string,\n -- Where the pattern is, then what it captured \u2014 or nil.\n find: (s: string, pattern: string, init?: number, plain?: boolean) => [number, number, ...string[]] | nil,\n -- What it captured (the whole match when there are no captures), as\n -- JavaScript's `match` answers \u2014 or nil.\n match: (s: string, pattern: string, init?: number) => [...string[]] | nil,\n -- Each match's captures (the whole match when there are none), one\n -- array at a time: `for (const [word] in s:gmatch(\"%a+\"))`.\n gmatch: (s: string, pattern: string) => () => [string, ...string[]] | nil,\n -- The new string, as JavaScript's `replace` answers; Luau's count of\n -- replacements is left behind.\n gsub: (s: string, pattern: string, repl: unknown, n?: number) => string,\n -- One code for one position; an array of them for a range.\n byte: ((s: string, i?: number) => number) & ((s: string, i: number, j: number) => number[]),\n char: (...args: number[]) => string,\n}\n\ndeclare math: {\n abs: (x: number) => number,\n floor: (x: number) => number,\n ceil: (x: number) => number,\n fmod: (x: number, y: number) => number,\n modf: (x: number) => [number, number],\n sqrt: (x: number) => number,\n pow: (x: number, y: number) => number,\n exp: (x: number) => number,\n log: (x: number, base?: number) => number,\n log10: (x: number) => number,\n frexp: (x: number) => [number, number],\n ldexp: (m: number, e: number) => number,\n min: (...args: number[]) => number,\n max: (...args: number[]) => number, \n sin: (x: number) => number,\n cos: (x: number) => number,\n tan: (x: number) => number,\n asin: (x: number) => number,\n acos: (x: number) => number,\n atan: (x: number) => number,\n atan2: (y: number, x: number) => number,\n sinh: (x: number) => number,\n cosh: (x: number) => number,\n tanh: (x: number) => number,\n rad: (deg: number) => number,\n deg: (rad: number) => number,\n random: (m?: number, n?: number) => number,\n randomseed: (x: number) => nil,\n huge: number,\n pi: number,\n}\n\n\ndeclare table: {\n -- `insert(t, v)` appends; `insert(t, pos, v)` puts it there.\n insert: (<T>(t: T[], value: T) => nil) & (<T>(t: T[], pos: number, value: T) => nil),\n remove: <T>(t: T[], pos?: number) => T | nil,\n concat: (t: (string | number)[], sep?: string, i?: number, j?: number) => string,\n sort: <T>(t: T[], compare?: (a: T, b: T) => boolean) => nil,\n -- Lua 5.1's originals. Deprecated there and gone from 5.2 on, but Luau\n -- still answers to them, so code being ported still type-checks.\n getn: <T>(t: T[]) => number,\n maxn: <T>(t: T[]) => number,\n foreach: <K, V, R>(t: { [K]: V }, f: (key: K, value: V) => R) => R | nil,\n foreachi: <V, R>(t: V[], f: (index: number, value: V) => R) => R | nil,\n}\n\ndeclare os: {\n time: (t?: unknown) => number,\n clock: () => number,\n date: (format?: string, time?: number) => unknown,\n difftime: (a: number, b: number) => number,\n}\n\ndeclare coroutine: {\n create: (fn: (...args: unknown[]) => unknown) => thread,\n -- Answers like `pcall`: `{ success, data, error }`, `data` what the\n -- coroutine yielded or returned.\n resume: (co: thread, ...args: unknown[]) => PcallResult<unknown>,\n yield: (...args: unknown[]) => unknown,\n wrap: (fn: (...args: unknown[]) => unknown) => (...args: unknown[]) => unknown,\n status: (co: thread) => string,\n running: () => thread,\n}\n\n-- A string's, an array's and a table's methods \u2014 `text:trim()`,\n-- `names:filter(f)`, `point:keys()` \u2014 are the language's: their metatables\n-- are declared in @tilua/parser's prelude, and the compiler runs them. A\n-- library adds to one with `declare metatable` of the same target, as\n-- `@tilua-types/roblox` gives a string Luau's `split`.\n\n-- The console. Written with `:` \u2014 `console:log(value)` \u2014 and lowered by the\n-- compiler (standard.ts) into runtime/console.luau, not into `print`.\n--\n-- Every line starts with the place that called it: `[src/main:12]: ...`.\n-- `print` and `warn` are left as written; `error` puts `src/main.tilua:12:` in\n-- its message, as Lua does.\n--\n-- `log` expands what it is given the way a browser console does: a table is\n-- printed as its contents, not as an address. A function prints as the type it\n-- was inferred to have *and* the code it was written as, both of which the\n-- compiler passes along, since neither exists once the code is running.\n--\n-- `error` reports in the project's own files: a bundle is one Luau file, so\n-- Luau's own line numbers name a file nobody wrote. It does not raise on the\n-- calling thread \u2014 the error goes to `task.spawn`, so reporting a problem\n-- never becomes one.\ntype Console = {\n log: (self: Console, ...args: unknown[]) => nil,\n warn: (self: Console, ...args: unknown[]) => nil,\n error: (self: Console, ...args: unknown[]) => nil,\n}\n\ndeclare console: Console\n";
1776
+ declare const STANDARD_LIBRARY_SOURCE = "\n-- The Lua 5.1 standard library, written in tilua.\n--\n-- This is the floor every tilua project stands on, the way TypeScript stands\n-- on JavaScript's: the functions and libraries Lua itself defines, there in\n-- every file without being asked for. Nothing here is Luau's or Roblox's \u2014\n-- `buffer`, `table.create` and the rest live one layer up in\n-- `@tilua-types/roblox`, which adds to it.\n--\n-- Lua's `type()` is not here: what a value is, is asked with the language's\n-- own `typeof v`, which also tells an array from an object.\n--\n-- Adding to it is literal: a name declared again in a later file keeps what\n-- earlier files gave it. `declare table: { find: ... }` in `@tilua-types/roblox` does\n-- not replace this `table`, it extends it, and overloads of a function\n-- accumulate the same way. So the layers really are layers.\n\n-- ============================================================\n-- Base library\n-- ============================================================\n\ndeclare _G: { [string]: unknown }\ndeclare _VERSION: string\n\ndeclare function require(module: unknown): unknown\ndeclare function tostring(value: unknown): string\ndeclare function tonumber(value: unknown, base?: number): number | nil\ndeclare function print(...args: unknown[]): nil\n-- `error` never returns, which is what lets\n-- `if v == nil then error(\"...\") end` narrow the rest of the block.\ndeclare function error(message: unknown, level?: number): never\n\n-- `asserts value` \u2014 narrows the *rest of the enclosing block*, the way\n-- TypeScript's assertion signatures do:\n--\n-- const part = workspace:FindFirstChild(\"Part\") -- Instance | nil\n-- assert(part)\n-- part.Name -- Instance, no `?`\n\ndeclare function assert<T>(value: T, message?: string): asserts value\n\n-- `pcall` and `xpcall` answer the way a JavaScript result object does:\n--\n-- const { success, data, error } = pcall(load, \"config\")\n-- if (success) { use(data) } else { warn(error) }\n--\n-- `data` is what the function returned. These are not Luau's own `pcall`:\n-- where Luau answers several values, tilua has one, and the compiler puts its\n-- own functions in place of those globals (runtime/result.luau) \u2014 for calls,\n-- and for the global read as a value alike. The same goes for `string.find`,\n-- `string.match`, `string.gsub`, `string.byte`, `math.modf`, `math.frexp`,\n-- `next`, `select`, `unpack`, `loadstring`, `pairs`, `ipairs` and\n-- `string.gmatch` below.\ntype PcallResult<T> = { success: true, data: T, error: nil } | { success: false, data: nil, error: unknown }\n\ndeclare function pcall<A extends unknown[], T>(fn: (...args: A) => T, ...args: A): PcallResult<T>\ndeclare function xpcall<A extends unknown[], T>(fn: (...args: A) => T, handler: (err: unknown) => unknown, ...args: A): PcallResult<T>\n-- `select(\"#\", ...args)` counts; `select(n, ...args)` is what follows the\n-- n-th, as an array.\ndeclare function select(n: \"#\", ...args: unknown[]): number\ndeclare function select<T>(n: number, ...args: T[]): T[]\n-- A copy of the list, or of part of it.\ndeclare function unpack<V>(list: readonly V[], i?: number, j?: number): V[]\ndeclare function rawget(t: unknown, key: unknown): unknown\ndeclare function rawset<T>(t: T, key: unknown, value: unknown): T\ndeclare function rawequal(a: unknown, b: unknown): boolean\ndeclare function setmetatable<T>(t: T, mt: unknown): T\ndeclare function getmetatable(t: unknown): unknown\ndeclare function newproxy(addMeta?: boolean): unknown\n-- The entry after `key` \u2014 or nil, past the last one.\ndeclare function next<K, V>(t: { [K]: V }, key?: K): [K, V] | nil\n\n-- What `for (const item in source)` walks: `step` is called with the state\n-- and the item before it \u2014 `first` the first time \u2014 until it answers nil.\n-- An array, a table and an iterator function are walked too, so this is only\n-- what a source such as `pairs(t)` hands over.\ntype Iteration<S, T> = [(state: S, previous: T | nil) => T | nil, S, T | nil]\n\n-- `for (const [k, v] in pairs(t))` / `ipairs(t)`: each item is one `[key,\n-- value]` \u2014 a table, as every array is. The analyzer types it from `t` itself\n-- (a record's keys as literals, an array's elements); these declare the\n-- names, and their shape for any other use. Walking a list only reads it, so\n-- a read-only one (`as const`, `readonly T[]`) is walked as well.\ndeclare function pairs<V>(t: readonly V[]): Iteration<readonly V[], [number, V]>\ndeclare function pairs<K, V>(t: { [K]: V }): Iteration<{ [K]: V }, [K, V]>\ndeclare function ipairs<V>(t: readonly V[]): Iteration<readonly V[], [number, V]>\n\ndeclare function collectgarbage(option: \"count\"): number\ndeclare function gcinfo(): number\ndeclare function getfenv(target?: unknown): { [string]: unknown }\ndeclare function setfenv<T>(target: T, env: { [string]: unknown }): T\ndeclare function loadstring(source: string, chunkname?: string): [((...args: unknown[]) => unknown) | nil, string | nil]\n\ndeclare debug: {\n traceback: (message?: string, level?: number) => string,\n}\n\ndeclare string: {\n format: (fmt: string, ...args: unknown[]) => string,\n sub: (s: string, i: number, j?: number) => string,\n len: (s: string) => number,\n upper: (s: string) => string,\n lower: (s: string) => string,\n rep: (s: string, n: number, sep?: string) => string,\n reverse: (s: string) => string,\n -- Where the pattern is, then what it captured \u2014 or nil.\n find: (s: string, pattern: string, init?: number, plain?: boolean) => [number, number, ...string[]] | nil,\n -- What it captured (the whole match when there are no captures), as\n -- JavaScript's `match` answers \u2014 or nil.\n match: (s: string, pattern: string, init?: number) => [...string[]] | nil,\n -- Each match's captures (the whole match when there are none), one\n -- array at a time: `for (const [word] in s:gmatch(\"%a+\"))`.\n gmatch: (s: string, pattern: string) => () => [string, ...string[]] | nil,\n -- The new string, as JavaScript's `replace` answers; Luau's count of\n -- replacements is left behind.\n gsub: (s: string, pattern: string, repl: unknown, n?: number) => string,\n -- One code for one position; an array of them for a range.\n byte: ((s: string, i?: number) => number) & ((s: string, i: number, j: number) => number[]),\n char: (...args: number[]) => string,\n}\n\ndeclare math: {\n abs: (x: number) => number,\n floor: (x: number) => number,\n ceil: (x: number) => number,\n fmod: (x: number, y: number) => number,\n modf: (x: number) => [number, number],\n sqrt: (x: number) => number,\n pow: (x: number, y: number) => number,\n exp: (x: number) => number,\n log: (x: number, base?: number) => number,\n log10: (x: number) => number,\n frexp: (x: number) => [number, number],\n ldexp: (m: number, e: number) => number,\n min: (...args: number[]) => number,\n max: (...args: number[]) => number, \n sin: (x: number) => number,\n cos: (x: number) => number,\n tan: (x: number) => number,\n asin: (x: number) => number,\n acos: (x: number) => number,\n atan: (x: number) => number,\n atan2: (y: number, x: number) => number,\n sinh: (x: number) => number,\n cosh: (x: number) => number,\n tanh: (x: number) => number,\n rad: (deg: number) => number,\n deg: (rad: number) => number,\n random: (m?: number, n?: number) => number,\n randomseed: (x: number) => nil,\n huge: number,\n pi: number,\n}\n\n\ndeclare table: {\n -- `insert(t, v)` appends; `insert(t, pos, v)` puts it there.\n insert: (<T>(t: T[], value: T) => nil) & (<T>(t: T[], pos: number, value: T) => nil),\n remove: <T>(t: T[], pos?: number) => T | nil,\n concat: (t: readonly (string | number)[], sep?: string, i?: number, j?: number) => string,\n sort: <T>(t: T[], compare?: (a: T, b: T) => boolean) => nil,\n -- Lua 5.1's originals. Deprecated there and gone from 5.2 on, but Luau\n -- still answers to them, so code being ported still type-checks.\n getn: <T>(t: readonly T[]) => number,\n maxn: <T>(t: readonly T[]) => number,\n foreach: <K, V, R>(t: { [K]: V }, f: (key: K, value: V) => R) => R | nil,\n foreachi: <V, R>(t: readonly V[], f: (index: number, value: V) => R) => R | nil,\n}\n\ndeclare os: {\n time: (t?: unknown) => number,\n clock: () => number,\n date: (format?: string, time?: number) => unknown,\n difftime: (a: number, b: number) => number,\n}\n\ndeclare coroutine: {\n create: (fn: (...args: unknown[]) => unknown) => thread,\n -- Answers like `pcall`: `{ success, data, error }`, `data` what the\n -- coroutine yielded or returned.\n resume: (co: thread, ...args: unknown[]) => PcallResult<unknown>,\n yield: (...args: unknown[]) => unknown,\n wrap: (fn: (...args: unknown[]) => unknown) => (...args: unknown[]) => unknown,\n status: (co: thread) => string,\n running: () => thread,\n}\n\n-- A string's, an array's and a table's methods \u2014 `text:trim()`,\n-- `names:filter(f)`, `point:keys()` \u2014 are the language's: their metatables\n-- are declared in @tilua/parser's prelude, and the compiler runs them. A\n-- library adds to one with `declare metatable` of the same target, as\n-- `@tilua-types/roblox` gives a string Luau's `split`.\n\n-- The console. Written with `:` \u2014 `console:log(value)` \u2014 and lowered by the\n-- compiler (standard.ts) into runtime/console.luau, not into `print`.\n--\n-- Every line starts with the place that called it: `[src/main:12]: ...`.\n-- `print` and `warn` are left as written; `error` puts `src/main.tilua:12:` in\n-- its message, as Lua does.\n--\n-- `log` expands what it is given the way a browser console does: a table is\n-- printed as its contents, not as an address. A function prints as the type it\n-- was inferred to have *and* the code it was written as, both of which the\n-- compiler passes along, since neither exists once the code is running.\n--\n-- `error` reports in the project's own files: a bundle is one Luau file, so\n-- Luau's own line numbers name a file nobody wrote. It does not raise on the\n-- calling thread \u2014 the error goes to `task.spawn`, so reporting a problem\n-- never becomes one.\ntype Console = {\n log: (self: Console, ...args: unknown[]) => nil,\n warn: (self: Console, ...args: unknown[]) => nil,\n error: (self: Console, ...args: unknown[]) => nil,\n}\n\ndeclare console: Console\n";
1777
1777
  /** The standard library, parsed once. */
1778
1778
  declare function standardLibrary(): Program;
1779
1779
  /** The globals the standard library declares: `print`, `string`, ... */
package/dist/index.d.ts CHANGED
@@ -1756,7 +1756,7 @@ resolveModule?: (specifier: string) => ModuleExports | undefined): ModuleExports
1756
1756
  * `ObjectValues<T>` and `ObjectEntries<T>`, an object's members as tuples in
1757
1757
  * the order they were written.
1758
1758
  */
1759
- declare const PRELUDE_SOURCE = "\n-- In Luau only `nil` and `false` are falsy: `0` and `\"\"` are truthy.\n-- These are what truthiness narrowing computes, made available to write down.\ntype Falsy = nil | false\ntype Truthy<T> = Exclude<T, Falsy>\n\n-- `Exclude<T, U>` is the analyzer's own: what of `T` is not a `U`. Over a\n-- union it drops the members it takes whole; over `unknown`, `any`, a\n-- primitive or an unresolved parameter it is kept \u2014 `Exclude<string, \"a\">` is\n-- a string that is not `\"a\"` \u2014 unlike TypeScript's.\ntype Extract<T, U> = T extends U ? T : never\ntype NonNullable<T> = Exclude<T, nil>\n\ntype ReturnType<T> = T extends (...args: unknown[]) => infer R ? R : never\ntype Parameters<T> = T extends (...args: infer P) => unknown ? P : never\n\ntype Partial<T> = { [K in keyof T]?: T[K] }\ntype Required<T> = { [K in keyof T]-?: T[K] }\ntype Readonly<T> = { readonly [K in keyof T]: T[K] }\ntype Mutable<T> = { -readonly [K in keyof T]: T[K] }\n\n-- A class table builds with `new`: `InstanceType<typeof Dog>` is a `Dog`.\ntype InstanceType<T> = T extends { new: (...args: any[]) => infer R } ? R : never\ntype ConstructorParameters<T> = T extends { new: (...args: infer P) => any } ? P : never\n\ntype Pick<T, K> = { [P in K]: T[P] }\ntype Omit<T, K> = Pick<T, Exclude<keyof T, K>>\ntype Record<K, V> = { [P in K]: V }\n\n-- ============================================================\n-- The metatables of the language's own values\n-- ============================================================\n\n-- A string's methods. The first group is Lua's string library, which a\n-- string really answers to; where Lua answers several values, tilua answers\n-- one, as the global functions do (`find` is an array, `gsub` the new\n-- string). The second group is written the way JavaScript writes it.\n--\n-- Positions are Lua's: `sub` and `slice` count from 1, and `indexOf` is\n-- `nil` when the text is not there.\ndeclare metatable string: {\n __index: {\n upper: (self: string) => string,\n lower: (self: string) => string,\n len: (self: string) => number,\n sub: (self: string, i: number, j?: number) => string,\n rep: (self: string, n: number, separator?: string) => string,\n reverse: (self: string) => string,\n format: (self: string, ...args: unknown[]) => string,\n byte: ((self: string, i?: number) => number) & ((self: string, i: number, j: number) => number[]),\n find: (self: string, pattern: string, init?: number, plain?: boolean) => [number, number, ...string[]] | nil,\n match: (self: string, pattern: string, init?: number) => [...string[]] | nil,\n gmatch: (self: string, pattern: string) => () => [string, ...string[]] | nil,\n gsub: (self: string, pattern: string, replacement: unknown, n?: number) => string,\n\n trim: (self: string) => string,\n trimStart: (self: string) => string,\n trimEnd: (self: string) => string,\n startsWith: (self: string, text: string) => boolean,\n endsWith: (self: string, text: string) => boolean,\n includes: (self: string, text: string) => boolean,\n indexOf: (self: string, text: string, start?: number) => number | nil,\n slice: (self: string, start?: number, stop?: number) => string,\n replace: (self: string, text: string, replacement: string) => string,\n replaceAll: (self: string, text: string, replacement: string) => string,\n padStart: (self: string, length: number, padding?: string) => string,\n padEnd: (self: string, length: number, padding?: string) => string,\n },\n}\n\n-- An array's methods. Lua gives an array no metatable: the compiler lowers\n-- each call to a plain function, so they work on any array, one a Lua\n-- library made included.\n--\n-- Indices are Lua's: the first element is 1, and `indexOf` / `findIndex`\n-- answer `nil` rather than JavaScript's -1. `sort` takes Lua's comparator\n-- (true when `a` comes first). `push`, `pop`, `shift`, `unshift`,\n-- `sort` and `reverse` change the array; everything else returns a new one.\n-- The ones that only read take a `readonly T[]` too \u2014 `as const` lists, and\n-- what a function promised not to change \u2014 as TypeScript's `ReadonlyArray`.\n-- A `filter` whose test is a guard \u2014 `v is S`, or `v => v ~= nil` \u2014\n-- answers an array of what it let through.\ndeclare metatable<T> T[]: {\n __index: {\n find: (self: readonly T[], test: (value: T, index: number) => boolean) => T | nil,\n findIndex: (self: readonly T[], test: (value: T, index: number) => boolean) => number | nil,\n filter: (<S extends T>(self: readonly T[], test: (value: T, index: number) => value is S) => S[])\n & ((self: readonly T[], test: (value: T, index: number) => boolean) => T[]),\n map: <U>(self: readonly T[], transform: (value: T, index: number) => U) => U[],\n forEach: (self: readonly T[], visit: (value: T, index: number) => nil) => nil,\n some: (self: readonly T[], test: (value: T, index: number) => boolean) => boolean,\n every: (self: readonly T[], test: (value: T, index: number) => boolean) => boolean,\n reduce: <U>(self: readonly T[], step: (total: U, value: T, index: number) => U, initial: U) => U,\n -- Asking is not adding: any value may be looked for, and finding it\n -- says it is one of the array's \u2014 `if (names:includes(s))` narrows `s`.\n includes: (self: readonly T[], value: unknown) => value is T,\n indexOf: (self: readonly T[], value: unknown, start?: number) => number | nil,\n join: (self: readonly T[], separator?: string) => string,\n concat: <U>(self: readonly T[], ...others: (readonly U[])[]) => (T | U)[],\n slice: (self: readonly T[], start?: number, stop?: number) => T[],\n flat: (self: readonly T[]) => T[],\n reverse: (self: T[]) => T[],\n sort: (self: T[], compare?: (a: T, b: T) => boolean) => T[],\n push: (self: T[], ...args: T[]) => number,\n pop: (self: T[]) => T | nil,\n shift: (self: T[]) => T | nil,\n unshift: (self: T[], ...args: T[]) => number,\n },\n}\n\n-- A table's methods. Unlike JavaScript's `Object.keys`, they answer a\n-- tuple, in the order the table's type wrote its members:\n--\n-- const point = { x: 1, y: \"up\" }\n-- point:keys() -- [\"x\", \"y\"]\n-- point:values() -- [number, string]\n-- point:entries() -- [[\"x\", number], [\"y\", string]]\n--\n-- A Lua table has no order of its own, so the compiler hands its runtime the\n-- keys as the type lists them. An optional member keeps its place, holding\n-- `nil` when it is missing; an indexer's keys follow the named ones. A\n-- member of the table's own with one of these names is what a call reaches,\n-- and a class instance, which has a metatable of its own, has none of them.\n--\n-- The rest are JavaScript's `Object` functions, written as methods of the\n-- table they take first:\n--\n-- point:hasOwn(\"x\") -- Object.hasOwn(point, \"x\")\n-- point:assign({ z: 3 }) -- Object.assign(point, { z: 3 })\n-- point:freeze() -- Object.freeze(point)\n--\n-- `hasOwn` asks the table itself, not what its metatable's `__index`\n-- lends it. `assign` copies each source's members onto the table, later ones\n-- over earlier ones, skipping a `nil` source, and answers the table. `freeze`\n-- is Luau's `table.freeze`; under Lua 5.1, which cannot freeze a table, the\n-- table is answered as it is and `isFrozen` is false.\ndeclare metatable<T extends {}> T: {\n __index: {\n keys: (self: T) => ObjectKeys<T>,\n values: (self: T) => ObjectValues<T>,\n entries: (self: T) => ObjectEntries<T>,\n\n hasOwn: (self: T, key: unknown) => boolean,\n hasOwnProperty: (self: T, key: unknown) => boolean,\n assign: (<U>(self: T, source: U) => T & U)\n & (<U, V>(self: T, a: U, b: V) => T & U & V)\n & (<U, V, W>(self: T, a: U, b: V, c: W) => T & U & V & W)\n -- Generic like the rest: a plain signature would be tried first.\n & (<S extends {}>(self: T, ...sources: S[]) => T),\n freeze: (self: T) => Readonly<T>,\n isFrozen: (self: T) => boolean,\n },\n}\n";
1759
+ declare const PRELUDE_SOURCE = "\n-- In Luau only `nil` and `false` are falsy: `0` and `\"\"` are truthy.\n-- These are what truthiness narrowing computes, made available to write down.\ntype Falsy = nil | false\ntype Truthy<T> = Exclude<T, Falsy>\n\n-- `Exclude<T, U>` is the analyzer's own: what of `T` is not a `U`. Over a\n-- union it drops the members it takes whole; over `unknown`, `any`, a\n-- primitive or an unresolved parameter it is kept \u2014 `Exclude<string, \"a\">` is\n-- a string that is not `\"a\"` \u2014 unlike TypeScript's.\ntype Extract<T, U> = T extends U ? T : never\ntype NonNullable<T> = Exclude<T, nil>\n\ntype ReturnType<T> = T extends (...args: unknown[]) => infer R ? R : never\ntype Parameters<T> = T extends (...args: infer P) => unknown ? P : never\n\ntype Partial<T> = { [K in keyof T]?: T[K] }\ntype Required<T> = { [K in keyof T]-?: T[K] }\ntype Readonly<T> = { readonly [K in keyof T]: T[K] }\ntype Mutable<T> = { -readonly [K in keyof T]: T[K] }\n\n-- A class table builds with `new`: `InstanceType<typeof Dog>` is a `Dog`.\ntype InstanceType<T> = T extends { new: (...args: any[]) => infer R } ? R : never\ntype ConstructorParameters<T> = T extends { new: (...args: infer P) => any } ? P : never\n\ntype Pick<T, K> = { [P in K]: T[P] }\ntype Omit<T, K> = Pick<T, Exclude<keyof T, K>>\ntype Record<K, V> = { [P in K]: V }\n\n-- ============================================================\n-- The metatables of the language's own values\n-- ============================================================\n\n-- A string's methods. The first group is Lua's string library, which a\n-- string really answers to; where Lua answers several values, tilua answers\n-- one, as the global functions do (`find` is an array, `gsub` the new\n-- string). The second group is written the way JavaScript writes it.\n--\n-- Positions are Lua's: `sub` and `slice` count from 1, and `indexOf` is\n-- `nil` when the text is not there.\ndeclare metatable string: {\n __index: {\n upper: (self: string) => string,\n lower: (self: string) => string,\n len: (self: string) => number,\n sub: (self: string, i: number, j?: number) => string,\n rep: (self: string, n: number, separator?: string) => string,\n reverse: (self: string) => string,\n format: (self: string, ...args: unknown[]) => string,\n byte: ((self: string, i?: number) => number) & ((self: string, i: number, j: number) => number[]),\n find: (self: string, pattern: string, init?: number, plain?: boolean) => [number, number, ...string[]] | nil,\n match: (self: string, pattern: string, init?: number) => [...string[]] | nil,\n gmatch: (self: string, pattern: string) => () => [string, ...string[]] | nil,\n gsub: (self: string, pattern: string, replacement: unknown, n?: number) => string,\n\n trim: (self: string) => string,\n trimStart: (self: string) => string,\n trimEnd: (self: string) => string,\n startsWith: (self: string, text: string) => boolean,\n endsWith: (self: string, text: string) => boolean,\n includes: (self: string, text: string) => boolean,\n indexOf: (self: string, text: string, start?: number) => number | nil,\n slice: (self: string, start?: number, stop?: number) => string,\n replace: (self: string, text: string, replacement: string) => string,\n replaceAll: (self: string, text: string, replacement: string) => string,\n padStart: (self: string, length: number, padding?: string) => string,\n padEnd: (self: string, length: number, padding?: string) => string,\n },\n}\n\n-- An array's methods. Lua gives an array no metatable: the compiler lowers\n-- each call to a plain function, so they work on any array, one a Lua\n-- library made included.\n--\n-- Indices are Lua's: the first element is 1, and `indexOf` / `findIndex`\n-- answer `nil` rather than JavaScript's -1. `sort` takes Lua's comparator\n-- (true when `a` comes first). `push`, `pop`, `shift`, `unshift`,\n-- `sort` and `reverse` change the array; everything else returns a new one.\n-- The ones that only read take a `readonly T[]` too \u2014 `as const` lists, and\n-- what a function promised not to change \u2014 as TypeScript's `ReadonlyArray`.\n-- A `filter` whose test is a guard \u2014 `v is S`, or `v => v ~= nil` \u2014\n-- answers an array of what it let through.\ndeclare metatable<T> T[]: {\n __index: {\n find: (self: readonly T[], test: (value: T, index: number) => boolean) => T | nil,\n findIndex: (self: readonly T[], test: (value: T, index: number) => boolean) => number | nil,\n filter: (<S extends T>(self: readonly T[], test: (value: T, index: number) => value is S) => S[])\n & ((self: readonly T[], test: (value: T, index: number) => boolean) => T[]),\n map: <U>(self: readonly T[], transform: (value: T, index: number) => U) => U[],\n forEach: (self: readonly T[], visit: (value: T, index: number) => nil) => nil,\n some: (self: readonly T[], test: (value: T, index: number) => boolean) => boolean,\n every: (self: readonly T[], test: (value: T, index: number) => boolean) => boolean,\n reduce: <U>(self: readonly T[], step: (total: U, value: T, index: number) => U, initial: U) => U,\n -- Asking is not adding: any value may be looked for, and finding it\n -- says it is one of the array's \u2014 `if (names:includes(s))` narrows `s`.\n includes: (self: readonly T[], value: unknown) => value is T,\n indexOf: (self: readonly T[], value: unknown, start?: number) => number | nil,\n join: (self: readonly T[], separator?: string) => string,\n concat: <U>(self: readonly T[], ...others: (readonly U[])[]) => (T | U)[],\n slice: (self: readonly T[], start?: number, stop?: number) => T[],\n flat: (self: readonly T[]) => T[],\n -- JavaScript's, as arrays rather than iterators: `for (const [i, v] in\n -- xs:entries())` walks positions and values, counting from 1.\n keys: (self: readonly T[]) => number[],\n values: (self: readonly T[]) => T[],\n entries: (self: readonly T[]) => [number, T][],\n reverse: (self: T[]) => T[],\n sort: (self: T[], compare?: (a: T, b: T) => boolean) => T[],\n push: (self: T[], ...args: T[]) => number,\n pop: (self: T[]) => T | nil,\n shift: (self: T[]) => T | nil,\n unshift: (self: T[], ...args: T[]) => number,\n },\n}\n\n-- A table's methods. Unlike JavaScript's `Object.keys`, they answer a\n-- tuple, in the order the table's type wrote its members:\n--\n-- const point = { x: 1, y: \"up\" }\n-- point:keys() -- [\"x\", \"y\"]\n-- point:values() -- [number, string]\n-- point:entries() -- [[\"x\", number], [\"y\", string]]\n--\n-- A Lua table has no order of its own, so the compiler hands its runtime the\n-- keys as the type lists them. An optional member keeps its place, holding\n-- `nil` when it is missing; an indexer's keys follow the named ones. A\n-- member of the table's own with one of these names is what a call reaches,\n-- and a class instance, which has a metatable of its own, has none of them.\n--\n-- The rest are JavaScript's `Object` functions, written as methods of the\n-- table they take first:\n--\n-- point:hasOwn(\"x\") -- Object.hasOwn(point, \"x\")\n-- point:assign({ z: 3 }) -- Object.assign(point, { z: 3 })\n-- point:freeze() -- Object.freeze(point)\n--\n-- `hasOwn` asks the table itself, not what its metatable's `__index`\n-- lends it. `assign` copies each source's members onto the table, later ones\n-- over earlier ones, skipping a `nil` source, and answers the table. `freeze`\n-- is Luau's `table.freeze`; under Lua 5.1, which cannot freeze a table, the\n-- table is answered as it is and `isFrozen` is false.\ndeclare metatable<T extends {}> T: {\n __index: {\n keys: (self: T) => ObjectKeys<T>,\n values: (self: T) => ObjectValues<T>,\n entries: (self: T) => ObjectEntries<T>,\n\n hasOwn: (self: T, key: unknown) => boolean,\n hasOwnProperty: (self: T, key: unknown) => boolean,\n assign: (<U>(self: T, source: U) => T & U)\n & (<U, V>(self: T, a: U, b: V) => T & U & V)\n & (<U, V, W>(self: T, a: U, b: V, c: W) => T & U & V & W)\n -- Generic like the rest: a plain signature would be tried first.\n & (<S extends {}>(self: T, ...sources: S[]) => T),\n freeze: (self: T) => Readonly<T>,\n isFrozen: (self: T) => boolean,\n },\n}\n";
1760
1760
 
1761
1761
  /**
1762
1762
  * The Lua 5.1 standard library, always there — as TypeScript always has
@@ -1773,7 +1773,7 @@ declare const PRELUDE_SOURCE = "\n-- In Luau only `nil` and `false` are falsy: `
1773
1773
  * `@tilua-types/lua`, where this used to live, is not loaded when a project
1774
1774
  * still names it: it would declare everything twice.
1775
1775
  */
1776
- declare const STANDARD_LIBRARY_SOURCE = "\n-- The Lua 5.1 standard library, written in tilua.\n--\n-- This is the floor every tilua project stands on, the way TypeScript stands\n-- on JavaScript's: the functions and libraries Lua itself defines, there in\n-- every file without being asked for. Nothing here is Luau's or Roblox's \u2014\n-- `buffer`, `table.create` and the rest live one layer up in\n-- `@tilua-types/roblox`, which adds to it.\n--\n-- Lua's `type()` is not here: what a value is, is asked with the language's\n-- own `typeof v`, which also tells an array from an object.\n--\n-- Adding to it is literal: a name declared again in a later file keeps what\n-- earlier files gave it. `declare table: { find: ... }` in `@tilua-types/roblox` does\n-- not replace this `table`, it extends it, and overloads of a function\n-- accumulate the same way. So the layers really are layers.\n\n-- ============================================================\n-- Base library\n-- ============================================================\n\ndeclare _G: { [string]: unknown }\ndeclare _VERSION: string\n\ndeclare function require(module: unknown): unknown\ndeclare function tostring(value: unknown): string\ndeclare function tonumber(value: unknown, base?: number): number | nil\ndeclare function print(...args: unknown[]): nil\n-- `error` never returns, which is what lets\n-- `if v == nil then error(\"...\") end` narrow the rest of the block.\ndeclare function error(message: unknown, level?: number): never\n\n-- `asserts value` \u2014 narrows the *rest of the enclosing block*, the way\n-- TypeScript's assertion signatures do:\n--\n-- const part = workspace:FindFirstChild(\"Part\") -- Instance | nil\n-- assert(part)\n-- part.Name -- Instance, no `?`\n\ndeclare function assert<T>(value: T, message?: string): asserts value\n\n-- `pcall` and `xpcall` answer the way a JavaScript result object does:\n--\n-- const { success, data, error } = pcall(load, \"config\")\n-- if (success) { use(data) } else { warn(error) }\n--\n-- `data` is what the function returned. These are not Luau's own `pcall`:\n-- where Luau answers several values, tilua has one, and the compiler puts its\n-- own functions in place of those globals (runtime/result.luau) \u2014 for calls,\n-- and for the global read as a value alike. The same goes for `string.find`,\n-- `string.match`, `string.gsub`, `string.byte`, `math.modf`, `math.frexp`,\n-- `next`, `select`, `unpack`, `loadstring`, `pairs`, `ipairs` and\n-- `string.gmatch` below.\ntype PcallResult<T> = { success: true, data: T, error: nil } | { success: false, data: nil, error: unknown }\n\ndeclare function pcall<A extends unknown[], T>(fn: (...args: A) => T, ...args: A): PcallResult<T>\ndeclare function xpcall<A extends unknown[], T>(fn: (...args: A) => T, handler: (err: unknown) => unknown, ...args: A): PcallResult<T>\n-- `select(\"#\", ...args)` counts; `select(n, ...args)` is what follows the\n-- n-th, as an array.\ndeclare function select(n: \"#\", ...args: unknown[]): number\ndeclare function select<T>(n: number, ...args: T[]): T[]\n-- A copy of the list, or of part of it.\ndeclare function unpack<V>(list: V[], i?: number, j?: number): V[]\ndeclare function rawget(t: unknown, key: unknown): unknown\ndeclare function rawset<T>(t: T, key: unknown, value: unknown): T\ndeclare function rawequal(a: unknown, b: unknown): boolean\ndeclare function setmetatable<T>(t: T, mt: unknown): T\ndeclare function getmetatable(t: unknown): unknown\ndeclare function newproxy(addMeta?: boolean): unknown\n-- The entry after `key` \u2014 or nil, past the last one.\ndeclare function next<K, V>(t: { [K]: V }, key?: K): [K, V] | nil\n\n-- What `for (const item in source)` walks: `step` is called with the state\n-- and the item before it \u2014 `first` the first time \u2014 until it answers nil.\n-- An array, a table and an iterator function are walked too, so this is only\n-- what a source such as `pairs(t)` hands over.\ntype Iteration<S, T> = [(state: S, previous: T | nil) => T | nil, S, T | nil]\n\n-- `for (const [k, v] in pairs(t))` / `ipairs(t)`: each item is one `[key,\n-- value]` \u2014 a table, as every array is. The analyzer types it from `t` itself\n-- (a record's keys as literals, an array's elements); these declare the\n-- names, and their shape for any other use.\ndeclare function pairs<V>(t: V[]): Iteration<V[], [number, V]>\ndeclare function pairs<K, V>(t: { [K]: V }): Iteration<{ [K]: V }, [K, V]>\ndeclare function ipairs<V>(t: V[]): Iteration<V[], [number, V]>\n\ndeclare function collectgarbage(option: \"count\"): number\ndeclare function gcinfo(): number\ndeclare function getfenv(target?: unknown): { [string]: unknown }\ndeclare function setfenv<T>(target: T, env: { [string]: unknown }): T\ndeclare function loadstring(source: string, chunkname?: string): [((...args: unknown[]) => unknown) | nil, string | nil]\n\ndeclare debug: {\n traceback: (message?: string, level?: number) => string,\n}\n\ndeclare string: {\n format: (fmt: string, ...args: unknown[]) => string,\n sub: (s: string, i: number, j?: number) => string,\n len: (s: string) => number,\n upper: (s: string) => string,\n lower: (s: string) => string,\n rep: (s: string, n: number, sep?: string) => string,\n reverse: (s: string) => string,\n -- Where the pattern is, then what it captured \u2014 or nil.\n find: (s: string, pattern: string, init?: number, plain?: boolean) => [number, number, ...string[]] | nil,\n -- What it captured (the whole match when there are no captures), as\n -- JavaScript's `match` answers \u2014 or nil.\n match: (s: string, pattern: string, init?: number) => [...string[]] | nil,\n -- Each match's captures (the whole match when there are none), one\n -- array at a time: `for (const [word] in s:gmatch(\"%a+\"))`.\n gmatch: (s: string, pattern: string) => () => [string, ...string[]] | nil,\n -- The new string, as JavaScript's `replace` answers; Luau's count of\n -- replacements is left behind.\n gsub: (s: string, pattern: string, repl: unknown, n?: number) => string,\n -- One code for one position; an array of them for a range.\n byte: ((s: string, i?: number) => number) & ((s: string, i: number, j: number) => number[]),\n char: (...args: number[]) => string,\n}\n\ndeclare math: {\n abs: (x: number) => number,\n floor: (x: number) => number,\n ceil: (x: number) => number,\n fmod: (x: number, y: number) => number,\n modf: (x: number) => [number, number],\n sqrt: (x: number) => number,\n pow: (x: number, y: number) => number,\n exp: (x: number) => number,\n log: (x: number, base?: number) => number,\n log10: (x: number) => number,\n frexp: (x: number) => [number, number],\n ldexp: (m: number, e: number) => number,\n min: (...args: number[]) => number,\n max: (...args: number[]) => number, \n sin: (x: number) => number,\n cos: (x: number) => number,\n tan: (x: number) => number,\n asin: (x: number) => number,\n acos: (x: number) => number,\n atan: (x: number) => number,\n atan2: (y: number, x: number) => number,\n sinh: (x: number) => number,\n cosh: (x: number) => number,\n tanh: (x: number) => number,\n rad: (deg: number) => number,\n deg: (rad: number) => number,\n random: (m?: number, n?: number) => number,\n randomseed: (x: number) => nil,\n huge: number,\n pi: number,\n}\n\n\ndeclare table: {\n -- `insert(t, v)` appends; `insert(t, pos, v)` puts it there.\n insert: (<T>(t: T[], value: T) => nil) & (<T>(t: T[], pos: number, value: T) => nil),\n remove: <T>(t: T[], pos?: number) => T | nil,\n concat: (t: (string | number)[], sep?: string, i?: number, j?: number) => string,\n sort: <T>(t: T[], compare?: (a: T, b: T) => boolean) => nil,\n -- Lua 5.1's originals. Deprecated there and gone from 5.2 on, but Luau\n -- still answers to them, so code being ported still type-checks.\n getn: <T>(t: T[]) => number,\n maxn: <T>(t: T[]) => number,\n foreach: <K, V, R>(t: { [K]: V }, f: (key: K, value: V) => R) => R | nil,\n foreachi: <V, R>(t: V[], f: (index: number, value: V) => R) => R | nil,\n}\n\ndeclare os: {\n time: (t?: unknown) => number,\n clock: () => number,\n date: (format?: string, time?: number) => unknown,\n difftime: (a: number, b: number) => number,\n}\n\ndeclare coroutine: {\n create: (fn: (...args: unknown[]) => unknown) => thread,\n -- Answers like `pcall`: `{ success, data, error }`, `data` what the\n -- coroutine yielded or returned.\n resume: (co: thread, ...args: unknown[]) => PcallResult<unknown>,\n yield: (...args: unknown[]) => unknown,\n wrap: (fn: (...args: unknown[]) => unknown) => (...args: unknown[]) => unknown,\n status: (co: thread) => string,\n running: () => thread,\n}\n\n-- A string's, an array's and a table's methods \u2014 `text:trim()`,\n-- `names:filter(f)`, `point:keys()` \u2014 are the language's: their metatables\n-- are declared in @tilua/parser's prelude, and the compiler runs them. A\n-- library adds to one with `declare metatable` of the same target, as\n-- `@tilua-types/roblox` gives a string Luau's `split`.\n\n-- The console. Written with `:` \u2014 `console:log(value)` \u2014 and lowered by the\n-- compiler (standard.ts) into runtime/console.luau, not into `print`.\n--\n-- Every line starts with the place that called it: `[src/main:12]: ...`.\n-- `print` and `warn` are left as written; `error` puts `src/main.tilua:12:` in\n-- its message, as Lua does.\n--\n-- `log` expands what it is given the way a browser console does: a table is\n-- printed as its contents, not as an address. A function prints as the type it\n-- was inferred to have *and* the code it was written as, both of which the\n-- compiler passes along, since neither exists once the code is running.\n--\n-- `error` reports in the project's own files: a bundle is one Luau file, so\n-- Luau's own line numbers name a file nobody wrote. It does not raise on the\n-- calling thread \u2014 the error goes to `task.spawn`, so reporting a problem\n-- never becomes one.\ntype Console = {\n log: (self: Console, ...args: unknown[]) => nil,\n warn: (self: Console, ...args: unknown[]) => nil,\n error: (self: Console, ...args: unknown[]) => nil,\n}\n\ndeclare console: Console\n";
1776
+ declare const STANDARD_LIBRARY_SOURCE = "\n-- The Lua 5.1 standard library, written in tilua.\n--\n-- This is the floor every tilua project stands on, the way TypeScript stands\n-- on JavaScript's: the functions and libraries Lua itself defines, there in\n-- every file without being asked for. Nothing here is Luau's or Roblox's \u2014\n-- `buffer`, `table.create` and the rest live one layer up in\n-- `@tilua-types/roblox`, which adds to it.\n--\n-- Lua's `type()` is not here: what a value is, is asked with the language's\n-- own `typeof v`, which also tells an array from an object.\n--\n-- Adding to it is literal: a name declared again in a later file keeps what\n-- earlier files gave it. `declare table: { find: ... }` in `@tilua-types/roblox` does\n-- not replace this `table`, it extends it, and overloads of a function\n-- accumulate the same way. So the layers really are layers.\n\n-- ============================================================\n-- Base library\n-- ============================================================\n\ndeclare _G: { [string]: unknown }\ndeclare _VERSION: string\n\ndeclare function require(module: unknown): unknown\ndeclare function tostring(value: unknown): string\ndeclare function tonumber(value: unknown, base?: number): number | nil\ndeclare function print(...args: unknown[]): nil\n-- `error` never returns, which is what lets\n-- `if v == nil then error(\"...\") end` narrow the rest of the block.\ndeclare function error(message: unknown, level?: number): never\n\n-- `asserts value` \u2014 narrows the *rest of the enclosing block*, the way\n-- TypeScript's assertion signatures do:\n--\n-- const part = workspace:FindFirstChild(\"Part\") -- Instance | nil\n-- assert(part)\n-- part.Name -- Instance, no `?`\n\ndeclare function assert<T>(value: T, message?: string): asserts value\n\n-- `pcall` and `xpcall` answer the way a JavaScript result object does:\n--\n-- const { success, data, error } = pcall(load, \"config\")\n-- if (success) { use(data) } else { warn(error) }\n--\n-- `data` is what the function returned. These are not Luau's own `pcall`:\n-- where Luau answers several values, tilua has one, and the compiler puts its\n-- own functions in place of those globals (runtime/result.luau) \u2014 for calls,\n-- and for the global read as a value alike. The same goes for `string.find`,\n-- `string.match`, `string.gsub`, `string.byte`, `math.modf`, `math.frexp`,\n-- `next`, `select`, `unpack`, `loadstring`, `pairs`, `ipairs` and\n-- `string.gmatch` below.\ntype PcallResult<T> = { success: true, data: T, error: nil } | { success: false, data: nil, error: unknown }\n\ndeclare function pcall<A extends unknown[], T>(fn: (...args: A) => T, ...args: A): PcallResult<T>\ndeclare function xpcall<A extends unknown[], T>(fn: (...args: A) => T, handler: (err: unknown) => unknown, ...args: A): PcallResult<T>\n-- `select(\"#\", ...args)` counts; `select(n, ...args)` is what follows the\n-- n-th, as an array.\ndeclare function select(n: \"#\", ...args: unknown[]): number\ndeclare function select<T>(n: number, ...args: T[]): T[]\n-- A copy of the list, or of part of it.\ndeclare function unpack<V>(list: readonly V[], i?: number, j?: number): V[]\ndeclare function rawget(t: unknown, key: unknown): unknown\ndeclare function rawset<T>(t: T, key: unknown, value: unknown): T\ndeclare function rawequal(a: unknown, b: unknown): boolean\ndeclare function setmetatable<T>(t: T, mt: unknown): T\ndeclare function getmetatable(t: unknown): unknown\ndeclare function newproxy(addMeta?: boolean): unknown\n-- The entry after `key` \u2014 or nil, past the last one.\ndeclare function next<K, V>(t: { [K]: V }, key?: K): [K, V] | nil\n\n-- What `for (const item in source)` walks: `step` is called with the state\n-- and the item before it \u2014 `first` the first time \u2014 until it answers nil.\n-- An array, a table and an iterator function are walked too, so this is only\n-- what a source such as `pairs(t)` hands over.\ntype Iteration<S, T> = [(state: S, previous: T | nil) => T | nil, S, T | nil]\n\n-- `for (const [k, v] in pairs(t))` / `ipairs(t)`: each item is one `[key,\n-- value]` \u2014 a table, as every array is. The analyzer types it from `t` itself\n-- (a record's keys as literals, an array's elements); these declare the\n-- names, and their shape for any other use. Walking a list only reads it, so\n-- a read-only one (`as const`, `readonly T[]`) is walked as well.\ndeclare function pairs<V>(t: readonly V[]): Iteration<readonly V[], [number, V]>\ndeclare function pairs<K, V>(t: { [K]: V }): Iteration<{ [K]: V }, [K, V]>\ndeclare function ipairs<V>(t: readonly V[]): Iteration<readonly V[], [number, V]>\n\ndeclare function collectgarbage(option: \"count\"): number\ndeclare function gcinfo(): number\ndeclare function getfenv(target?: unknown): { [string]: unknown }\ndeclare function setfenv<T>(target: T, env: { [string]: unknown }): T\ndeclare function loadstring(source: string, chunkname?: string): [((...args: unknown[]) => unknown) | nil, string | nil]\n\ndeclare debug: {\n traceback: (message?: string, level?: number) => string,\n}\n\ndeclare string: {\n format: (fmt: string, ...args: unknown[]) => string,\n sub: (s: string, i: number, j?: number) => string,\n len: (s: string) => number,\n upper: (s: string) => string,\n lower: (s: string) => string,\n rep: (s: string, n: number, sep?: string) => string,\n reverse: (s: string) => string,\n -- Where the pattern is, then what it captured \u2014 or nil.\n find: (s: string, pattern: string, init?: number, plain?: boolean) => [number, number, ...string[]] | nil,\n -- What it captured (the whole match when there are no captures), as\n -- JavaScript's `match` answers \u2014 or nil.\n match: (s: string, pattern: string, init?: number) => [...string[]] | nil,\n -- Each match's captures (the whole match when there are none), one\n -- array at a time: `for (const [word] in s:gmatch(\"%a+\"))`.\n gmatch: (s: string, pattern: string) => () => [string, ...string[]] | nil,\n -- The new string, as JavaScript's `replace` answers; Luau's count of\n -- replacements is left behind.\n gsub: (s: string, pattern: string, repl: unknown, n?: number) => string,\n -- One code for one position; an array of them for a range.\n byte: ((s: string, i?: number) => number) & ((s: string, i: number, j: number) => number[]),\n char: (...args: number[]) => string,\n}\n\ndeclare math: {\n abs: (x: number) => number,\n floor: (x: number) => number,\n ceil: (x: number) => number,\n fmod: (x: number, y: number) => number,\n modf: (x: number) => [number, number],\n sqrt: (x: number) => number,\n pow: (x: number, y: number) => number,\n exp: (x: number) => number,\n log: (x: number, base?: number) => number,\n log10: (x: number) => number,\n frexp: (x: number) => [number, number],\n ldexp: (m: number, e: number) => number,\n min: (...args: number[]) => number,\n max: (...args: number[]) => number, \n sin: (x: number) => number,\n cos: (x: number) => number,\n tan: (x: number) => number,\n asin: (x: number) => number,\n acos: (x: number) => number,\n atan: (x: number) => number,\n atan2: (y: number, x: number) => number,\n sinh: (x: number) => number,\n cosh: (x: number) => number,\n tanh: (x: number) => number,\n rad: (deg: number) => number,\n deg: (rad: number) => number,\n random: (m?: number, n?: number) => number,\n randomseed: (x: number) => nil,\n huge: number,\n pi: number,\n}\n\n\ndeclare table: {\n -- `insert(t, v)` appends; `insert(t, pos, v)` puts it there.\n insert: (<T>(t: T[], value: T) => nil) & (<T>(t: T[], pos: number, value: T) => nil),\n remove: <T>(t: T[], pos?: number) => T | nil,\n concat: (t: readonly (string | number)[], sep?: string, i?: number, j?: number) => string,\n sort: <T>(t: T[], compare?: (a: T, b: T) => boolean) => nil,\n -- Lua 5.1's originals. Deprecated there and gone from 5.2 on, but Luau\n -- still answers to them, so code being ported still type-checks.\n getn: <T>(t: readonly T[]) => number,\n maxn: <T>(t: readonly T[]) => number,\n foreach: <K, V, R>(t: { [K]: V }, f: (key: K, value: V) => R) => R | nil,\n foreachi: <V, R>(t: readonly V[], f: (index: number, value: V) => R) => R | nil,\n}\n\ndeclare os: {\n time: (t?: unknown) => number,\n clock: () => number,\n date: (format?: string, time?: number) => unknown,\n difftime: (a: number, b: number) => number,\n}\n\ndeclare coroutine: {\n create: (fn: (...args: unknown[]) => unknown) => thread,\n -- Answers like `pcall`: `{ success, data, error }`, `data` what the\n -- coroutine yielded or returned.\n resume: (co: thread, ...args: unknown[]) => PcallResult<unknown>,\n yield: (...args: unknown[]) => unknown,\n wrap: (fn: (...args: unknown[]) => unknown) => (...args: unknown[]) => unknown,\n status: (co: thread) => string,\n running: () => thread,\n}\n\n-- A string's, an array's and a table's methods \u2014 `text:trim()`,\n-- `names:filter(f)`, `point:keys()` \u2014 are the language's: their metatables\n-- are declared in @tilua/parser's prelude, and the compiler runs them. A\n-- library adds to one with `declare metatable` of the same target, as\n-- `@tilua-types/roblox` gives a string Luau's `split`.\n\n-- The console. Written with `:` \u2014 `console:log(value)` \u2014 and lowered by the\n-- compiler (standard.ts) into runtime/console.luau, not into `print`.\n--\n-- Every line starts with the place that called it: `[src/main:12]: ...`.\n-- `print` and `warn` are left as written; `error` puts `src/main.tilua:12:` in\n-- its message, as Lua does.\n--\n-- `log` expands what it is given the way a browser console does: a table is\n-- printed as its contents, not as an address. A function prints as the type it\n-- was inferred to have *and* the code it was written as, both of which the\n-- compiler passes along, since neither exists once the code is running.\n--\n-- `error` reports in the project's own files: a bundle is one Luau file, so\n-- Luau's own line numbers name a file nobody wrote. It does not raise on the\n-- calling thread \u2014 the error goes to `task.spawn`, so reporting a problem\n-- never becomes one.\ntype Console = {\n log: (self: Console, ...args: unknown[]) => nil,\n warn: (self: Console, ...args: unknown[]) => nil,\n error: (self: Console, ...args: unknown[]) => nil,\n}\n\ndeclare console: Console\n";
1777
1777
  /** The standard library, parsed once. */
1778
1778
  declare function standardLibrary(): Program;
1779
1779
  /** The globals the standard library declares: `print`, `string`, ... */
package/dist/index.js CHANGED
@@ -94,6 +94,14 @@ function isAlphaNumeric(ch) {
94
94
  return isAlpha(ch) || isDigit(ch);
95
95
  }
96
96
  var KeywordSet = new Set(Keywords);
97
+ var TS_SPELLINGS = [
98
+ ["!==", "~=", "Operator"],
99
+ ["===", "==", "Operator"],
100
+ ["!=", "~=", "Operator"],
101
+ ["&&", "and", "Keyword"],
102
+ ["||", "or", "Keyword"],
103
+ ["!", "not", "Keyword"]
104
+ ];
97
105
  var OperatorSet = new Set(Operators);
98
106
  var SortedSymbols = [...Operators, ...Punctuators].sort((a, b) => b.length - a.length);
99
107
  var LexError = class extends Error {
@@ -554,6 +562,15 @@ function tokenize(source, options = {}) {
554
562
  tokens.push(readIdentifierOrKeyword());
555
563
  continue;
556
564
  }
565
+ const spelled = TS_SPELLINGS.find(([written]) => source.startsWith(written, cursor));
566
+ if (spelled) {
567
+ const [written, meant, kind] = spelled;
568
+ const startLine = line, startColumn = column;
569
+ fail(`tilua writes '${meant}', not '${written}'`, startLine, startColumn);
570
+ for (let k = 0; k < written.length; k++) advance();
571
+ tokens.push(kind === "Keyword" ? { type: "Keyword", value: meant, ...makeBase(startLine, startColumn) } : { type: "Operator", value: meant, ...makeBase(startLine, startColumn) });
572
+ continue;
573
+ }
557
574
  const symbol = readOperatorOrPunctuator();
558
575
  if (symbol) tokens.push(symbol);
559
576
  }
@@ -4362,7 +4379,7 @@ declare function xpcall<A extends unknown[], T>(fn: (...args: A) => T, handler:
4362
4379
  declare function select(n: "#", ...args: unknown[]): number
4363
4380
  declare function select<T>(n: number, ...args: T[]): T[]
4364
4381
  -- A copy of the list, or of part of it.
4365
- declare function unpack<V>(list: V[], i?: number, j?: number): V[]
4382
+ declare function unpack<V>(list: readonly V[], i?: number, j?: number): V[]
4366
4383
  declare function rawget(t: unknown, key: unknown): unknown
4367
4384
  declare function rawset<T>(t: T, key: unknown, value: unknown): T
4368
4385
  declare function rawequal(a: unknown, b: unknown): boolean
@@ -4381,10 +4398,11 @@ type Iteration<S, T> = [(state: S, previous: T | nil) => T | nil, S, T | nil]
4381
4398
  -- \`for (const [k, v] in pairs(t))\` / \`ipairs(t)\`: each item is one \`[key,
4382
4399
  -- value]\` \u2014 a table, as every array is. The analyzer types it from \`t\` itself
4383
4400
  -- (a record's keys as literals, an array's elements); these declare the
4384
- -- names, and their shape for any other use.
4385
- declare function pairs<V>(t: V[]): Iteration<V[], [number, V]>
4401
+ -- names, and their shape for any other use. Walking a list only reads it, so
4402
+ -- a read-only one (\`as const\`, \`readonly T[]\`) is walked as well.
4403
+ declare function pairs<V>(t: readonly V[]): Iteration<readonly V[], [number, V]>
4386
4404
  declare function pairs<K, V>(t: { [K]: V }): Iteration<{ [K]: V }, [K, V]>
4387
- declare function ipairs<V>(t: V[]): Iteration<V[], [number, V]>
4405
+ declare function ipairs<V>(t: readonly V[]): Iteration<readonly V[], [number, V]>
4388
4406
 
4389
4407
  declare function collectgarbage(option: "count"): number
4390
4408
  declare function gcinfo(): number
@@ -4458,14 +4476,14 @@ declare table: {
4458
4476
  -- \`insert(t, v)\` appends; \`insert(t, pos, v)\` puts it there.
4459
4477
  insert: (<T>(t: T[], value: T) => nil) & (<T>(t: T[], pos: number, value: T) => nil),
4460
4478
  remove: <T>(t: T[], pos?: number) => T | nil,
4461
- concat: (t: (string | number)[], sep?: string, i?: number, j?: number) => string,
4479
+ concat: (t: readonly (string | number)[], sep?: string, i?: number, j?: number) => string,
4462
4480
  sort: <T>(t: T[], compare?: (a: T, b: T) => boolean) => nil,
4463
4481
  -- Lua 5.1's originals. Deprecated there and gone from 5.2 on, but Luau
4464
4482
  -- still answers to them, so code being ported still type-checks.
4465
- getn: <T>(t: T[]) => number,
4466
- maxn: <T>(t: T[]) => number,
4483
+ getn: <T>(t: readonly T[]) => number,
4484
+ maxn: <T>(t: readonly T[]) => number,
4467
4485
  foreach: <K, V, R>(t: { [K]: V }, f: (key: K, value: V) => R) => R | nil,
4468
- foreachi: <V, R>(t: V[], f: (index: number, value: V) => R) => R | nil,
4486
+ foreachi: <V, R>(t: readonly V[], f: (index: number, value: V) => R) => R | nil,
4469
4487
  }
4470
4488
 
4471
4489
  declare os: {
@@ -5358,6 +5376,11 @@ declare metatable<T> T[]: {
5358
5376
  concat: <U>(self: readonly T[], ...others: (readonly U[])[]) => (T | U)[],
5359
5377
  slice: (self: readonly T[], start?: number, stop?: number) => T[],
5360
5378
  flat: (self: readonly T[]) => T[],
5379
+ -- JavaScript's, as arrays rather than iterators: \`for (const [i, v] in
5380
+ -- xs:entries())\` walks positions and values, counting from 1.
5381
+ keys: (self: readonly T[]) => number[],
5382
+ values: (self: readonly T[]) => T[],
5383
+ entries: (self: readonly T[]) => [number, T][],
5361
5384
  reverse: (self: T[]) => T[],
5362
5385
  sort: (self: T[], compare?: (a: T, b: T) => boolean) => T[],
5363
5386
  push: (self: T[], ...args: T[]) => number,
@@ -7145,7 +7168,7 @@ function keepsLiterals(paramType) {
7145
7168
  const members = paramType.constraint.kind === "union" ? paramType.constraint.types : [paramType.constraint];
7146
7169
  return members.some((m) => m.kind === "literal" || m.kind === "primitive" && (m.name === "string" || m.name === "number"));
7147
7170
  }
7148
- var CLASS_LINKS = /* @__PURE__ */ new Set(["new", "ClassObject", "ParentClass"]);
7171
+ var CLASS_LINKS = /* @__PURE__ */ new Set(["new", "ClassObject", "ParentClass", "name"]);
7149
7172
  var CLASS_RESERVED = /* @__PURE__ */ new Set([
7150
7173
  "new",
7151
7174
  "ClassObject",
@@ -7903,6 +7926,7 @@ var TypeAnalyzer = class {
7903
7926
  });
7904
7927
  type.properties.set("new", construct);
7905
7928
  type.properties.set("ParentClass", { type: parent ?? nilType, optional: false, readonly: true });
7929
+ type.properties.set("name", { type: stmt.name ? literal(stmt.name.name) : stringType, optional: false, readonly: true });
7906
7930
  return type;
7907
7931
  }
7908
7932
  /** The value side of the class `superclass` names when it is not
@@ -8162,6 +8186,9 @@ var TypeAnalyzer = class {
8162
8186
  if (CLASS_RESERVED.has(member.name.name)) {
8163
8187
  report(member.name, `'${member.name.name}' is what the compiler calls part of a class; a member cannot be named that`);
8164
8188
  }
8189
+ if (member.name.name === "name" && !(member.type === "ClassField" && !member.isStatic)) {
8190
+ report(member.name, "'name' is the class's own name, `Dog.name`: a static, a method or an accessor cannot be called that \u2014 an instance field can");
8191
+ }
8165
8192
  }
8166
8193
  const seen = /* @__PURE__ */ new Map();
8167
8194
  for (const member of stmt.members) {
@@ -9002,6 +9029,13 @@ var TypeAnalyzer = class {
9002
9029
  case "array":
9003
9030
  case "tuple":
9004
9031
  return union([numberType, literal("length")]);
9032
+ // A string is not indexed by position in Lua (`s[1]` is nil), so
9033
+ // of its keys only `length` is there.
9034
+ case "primitive":
9035
+ return t.name === "string" ? literal("length") : neverType;
9036
+ case "literal":
9037
+ case "templateLiteral":
9038
+ return t.kind === "templateLiteral" || t.base === "string" ? literal("length") : neverType;
9005
9039
  case "union": {
9006
9040
  const sets = t.types.map((m) => {
9007
9041
  const keys = this.keysOf(m);
@@ -9472,7 +9506,7 @@ var TypeAnalyzer = class {
9472
9506
  this.loops.set(stmt, { walks: "iteration", viaIter: false });
9473
9507
  } else {
9474
9508
  const [item, form] = this.iterationOf(stmt.iterator, sourceType);
9475
- this.bindPattern(stmt.variable, item, bodyEnv, "widen");
9509
+ this.bindPattern(stmt.variable, item, bodyEnv, stmt.kind === "const" ? "keep" : "widen");
9476
9510
  this.loops.set(stmt, form);
9477
9511
  }
9478
9512
  this.visitBlock(stmt.body, bodyEnv);
@@ -10928,7 +10962,8 @@ var TypeAnalyzer = class {
10928
10962
  const name = source.callee.name;
10929
10963
  const src = this.expand(this.typeOf.get(source.arguments[0]) ?? unknownType);
10930
10964
  const iteration = { walks: "iteration", viaIter: false };
10931
- if (name === "ipairs") return [tuple([numberType, this.elementType(src, 0)]), iteration];
10965
+ const element = src.kind === "array" ? src.element : src.kind === "tuple" ? union(tupleMembers(src).map((m) => m.kind === "spread" ? this.spreadElement(m.type) ?? unknownType : m)) : void 0;
10966
+ if (name === "ipairs") return [tuple([numberType, element ?? this.elementType(src, 0)]), iteration];
10932
10967
  if (name === "pairs") {
10933
10968
  if (src.kind === "object") {
10934
10969
  return [tuple([
@@ -10936,7 +10971,7 @@ var TypeAnalyzer = class {
10936
10971
  src.indexer?.value ?? union([...src.properties.values()].map((p) => p.type))
10937
10972
  ]), iteration];
10938
10973
  }
10939
- if (src.kind === "array") return [tuple([numberType, src.element]), iteration];
10974
+ if (element) return [tuple([numberType, element]), iteration];
10940
10975
  }
10941
10976
  }
10942
10977
  const handsOut = (t2, viaIter) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tilua/parser",
3
- "version": "5.0.0",
3
+ "version": "5.0.1",
4
4
  "description": "tilua parser for roblox",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",