@tilua/compiler 4.0.0 → 5.0.0

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/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as T from '@tilua/parser';
2
- import { LoweringPlugin, BuildTarget, TypeAnalysis, ScopeAnalysis, TiluaConfig, ConfigProblem } from '@tilua/parser';
2
+ import { LoweringPlugin, BuildTarget, TypeAnalysis, ScopeAnalysis, OutputMode, TiluaConfig, ConfigProblem } from '@tilua/parser';
3
3
  export { LoweringPlugin, MethodCall, MethodLowering } from '@tilua/parser';
4
4
  import * as L from 'luau-parser';
5
5
 
@@ -88,11 +88,15 @@ interface ModuleContext {
88
88
  /** The bundle's name for this module. */
89
89
  readonly name: string;
90
90
  /** The bundle's name for the module `specifier` imports, or `undefined`
91
- * when there is no such module (reported). */
92
- resolve(specifier: string): string | undefined;
91
+ * when there is no such module (reported). `dynamic` says an `import()`
92
+ * is asking: the module is needed when that runs, not before this one. */
93
+ resolve(specifier: string, dynamic?: boolean): string | undefined;
93
94
  /** The bundle's `require`: an expression that, called with a module's
94
95
  * name, returns its exports table. */
95
96
  readonly require: L.Expression;
97
+ /** What `import("./x")` calls, with the module's name: the bundle's
98
+ * wrapper around its `require`. Default: `require` itself. */
99
+ readonly import?: L.Expression;
96
100
  /** Luau reaching the bundle's line map — what a library's runtime reads,
97
101
  * as `__LINES__`, to name the file a frame was written in. */
98
102
  readonly lines?: string;
@@ -129,6 +133,9 @@ interface LowerOptions {
129
133
  * lowers what Luau adds to Lua and 5.1 has no syntax for — `a += b`,
130
134
  * `continue`, `if c then a else b` as an expression, `//`. */
131
135
  readonly target?: Target;
136
+ /** The project leaves Lua's standard library out (`"noLib": true`), so
137
+ * nothing of it is lowered either. */
138
+ readonly noLib?: boolean;
132
139
  }
133
140
  /** `"luau"` is Roblox's Lua. `"lua51"` is stock Lua 5.1, which has none of
134
141
  * Luau's additions — and no `goto` either, so `continue` has to be built out
@@ -189,6 +196,13 @@ interface TiluaConfigJson {
189
196
  * `a += b`, `continue`, `if c then a else b` as an expression, `//`,
190
197
  * digit separators and binary literals. */
191
198
  target?: Target;
199
+ /** How many files the output is. `"single"` (the default) is one file;
200
+ * `"chunks"` also writes a file for what only an `import()` reaches,
201
+ * loaded when it is first asked for; `"modules"` writes one per module. */
202
+ output?: OutputMode;
203
+ /** Leave out Lua's standard library — `print`, `string`, `pcall` — which
204
+ * is otherwise there for every file, as JavaScript's is in TypeScript. */
205
+ noLib?: boolean;
192
206
  }
193
207
  /** A config file's path, or the config itself. Relative paths — to a config
194
208
  * file, and inside an inline config — resolve from `cwd`. */
@@ -214,6 +228,23 @@ interface BundleOptions {
214
228
  readonly typeCheck?: boolean;
215
229
  /** Which Lua the output must run on. Overrides the config's `target`. */
216
230
  readonly target?: Target;
231
+ /** How many files to make. Overrides the config's `output`; `"single"`
232
+ * without either. */
233
+ readonly output?: OutputMode;
234
+ /** What the entry's file is called: `main.luau`. The other files of
235
+ * `"chunks"` are named after it (`main_shop.luau`), and every file takes
236
+ * its extension. Default: the entry's own name, with `.luau`. */
237
+ readonly outFile?: string;
238
+ }
239
+ /** One file of the output. */
240
+ interface BundleFile {
241
+ /** Where it goes, from the folder the output is written to: `main.luau`,
242
+ * `src/shop/buy.luau`. Files find each other by these, so they have to be
243
+ * written as they are named. */
244
+ readonly path: string;
245
+ readonly code: string;
246
+ /** The modules in it, by name. */
247
+ readonly modules: string[];
217
248
  }
218
249
  interface BundleDiagnostic {
219
250
  /** Absolute path of the file it is about. */
@@ -227,9 +258,13 @@ interface BundleDiagnostic {
227
258
  readonly category: "syntax" | "scope" | "module" | "type" | "config";
228
259
  }
229
260
  interface BundleResult {
230
- /** The bundle, or `undefined` when a module could not be compiled. Type
231
- * errors are reported but do not stop it. */
261
+ /** The bundle — the entry's file, when there are several — or `undefined`
262
+ * when a module could not be compiled. Type errors are reported but do
263
+ * not stop it. */
232
264
  readonly code?: string;
265
+ /** Every file of the output, the entry's first. Empty when there is no
266
+ * bundle. */
267
+ readonly files: BundleFile[];
233
268
  /** The bundled modules' names, entry first. */
234
269
  readonly modules: string[];
235
270
  readonly diagnostics: BundleDiagnostic[];
@@ -248,4 +283,4 @@ interface CompileResult {
248
283
  }
249
284
  declare function compile(source: string, config?: ConfigInput): Promise<CompileResult>;
250
285
 
251
- export { type BundleDiagnostic, type BundleOptions, type BundleResult, type CompileResult, type ConfigInput, type Diagnostic, type LoadedLowering, type LowerDiagnostic, type LowerOptions, type LowerResult, type LoweringProblem, type ModuleContext, type ResolvedConfig, type TiluaConfigJson, bundle, compile, loadLowerings, lower, resolveConfig };
286
+ export { type BundleDiagnostic, type BundleFile, type BundleOptions, type BundleResult, type CompileResult, type ConfigInput, type Diagnostic, type LoadedLowering, type LowerDiagnostic, type LowerOptions, type LowerResult, type LoweringProblem, type ModuleContext, type ResolvedConfig, type TiluaConfigJson, bundle, compile, loadLowerings, lower, resolveConfig };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as T from '@tilua/parser';
2
- import { LoweringPlugin, BuildTarget, TypeAnalysis, ScopeAnalysis, TiluaConfig, ConfigProblem } from '@tilua/parser';
2
+ import { LoweringPlugin, BuildTarget, TypeAnalysis, ScopeAnalysis, OutputMode, TiluaConfig, ConfigProblem } from '@tilua/parser';
3
3
  export { LoweringPlugin, MethodCall, MethodLowering } from '@tilua/parser';
4
4
  import * as L from 'luau-parser';
5
5
 
@@ -88,11 +88,15 @@ interface ModuleContext {
88
88
  /** The bundle's name for this module. */
89
89
  readonly name: string;
90
90
  /** The bundle's name for the module `specifier` imports, or `undefined`
91
- * when there is no such module (reported). */
92
- resolve(specifier: string): string | undefined;
91
+ * when there is no such module (reported). `dynamic` says an `import()`
92
+ * is asking: the module is needed when that runs, not before this one. */
93
+ resolve(specifier: string, dynamic?: boolean): string | undefined;
93
94
  /** The bundle's `require`: an expression that, called with a module's
94
95
  * name, returns its exports table. */
95
96
  readonly require: L.Expression;
97
+ /** What `import("./x")` calls, with the module's name: the bundle's
98
+ * wrapper around its `require`. Default: `require` itself. */
99
+ readonly import?: L.Expression;
96
100
  /** Luau reaching the bundle's line map — what a library's runtime reads,
97
101
  * as `__LINES__`, to name the file a frame was written in. */
98
102
  readonly lines?: string;
@@ -129,6 +133,9 @@ interface LowerOptions {
129
133
  * lowers what Luau adds to Lua and 5.1 has no syntax for — `a += b`,
130
134
  * `continue`, `if c then a else b` as an expression, `//`. */
131
135
  readonly target?: Target;
136
+ /** The project leaves Lua's standard library out (`"noLib": true`), so
137
+ * nothing of it is lowered either. */
138
+ readonly noLib?: boolean;
132
139
  }
133
140
  /** `"luau"` is Roblox's Lua. `"lua51"` is stock Lua 5.1, which has none of
134
141
  * Luau's additions — and no `goto` either, so `continue` has to be built out
@@ -189,6 +196,13 @@ interface TiluaConfigJson {
189
196
  * `a += b`, `continue`, `if c then a else b` as an expression, `//`,
190
197
  * digit separators and binary literals. */
191
198
  target?: Target;
199
+ /** How many files the output is. `"single"` (the default) is one file;
200
+ * `"chunks"` also writes a file for what only an `import()` reaches,
201
+ * loaded when it is first asked for; `"modules"` writes one per module. */
202
+ output?: OutputMode;
203
+ /** Leave out Lua's standard library — `print`, `string`, `pcall` — which
204
+ * is otherwise there for every file, as JavaScript's is in TypeScript. */
205
+ noLib?: boolean;
192
206
  }
193
207
  /** A config file's path, or the config itself. Relative paths — to a config
194
208
  * file, and inside an inline config — resolve from `cwd`. */
@@ -214,6 +228,23 @@ interface BundleOptions {
214
228
  readonly typeCheck?: boolean;
215
229
  /** Which Lua the output must run on. Overrides the config's `target`. */
216
230
  readonly target?: Target;
231
+ /** How many files to make. Overrides the config's `output`; `"single"`
232
+ * without either. */
233
+ readonly output?: OutputMode;
234
+ /** What the entry's file is called: `main.luau`. The other files of
235
+ * `"chunks"` are named after it (`main_shop.luau`), and every file takes
236
+ * its extension. Default: the entry's own name, with `.luau`. */
237
+ readonly outFile?: string;
238
+ }
239
+ /** One file of the output. */
240
+ interface BundleFile {
241
+ /** Where it goes, from the folder the output is written to: `main.luau`,
242
+ * `src/shop/buy.luau`. Files find each other by these, so they have to be
243
+ * written as they are named. */
244
+ readonly path: string;
245
+ readonly code: string;
246
+ /** The modules in it, by name. */
247
+ readonly modules: string[];
217
248
  }
218
249
  interface BundleDiagnostic {
219
250
  /** Absolute path of the file it is about. */
@@ -227,9 +258,13 @@ interface BundleDiagnostic {
227
258
  readonly category: "syntax" | "scope" | "module" | "type" | "config";
228
259
  }
229
260
  interface BundleResult {
230
- /** The bundle, or `undefined` when a module could not be compiled. Type
231
- * errors are reported but do not stop it. */
261
+ /** The bundle — the entry's file, when there are several — or `undefined`
262
+ * when a module could not be compiled. Type errors are reported but do
263
+ * not stop it. */
232
264
  readonly code?: string;
265
+ /** Every file of the output, the entry's first. Empty when there is no
266
+ * bundle. */
267
+ readonly files: BundleFile[];
233
268
  /** The bundled modules' names, entry first. */
234
269
  readonly modules: string[];
235
270
  readonly diagnostics: BundleDiagnostic[];
@@ -248,4 +283,4 @@ interface CompileResult {
248
283
  }
249
284
  declare function compile(source: string, config?: ConfigInput): Promise<CompileResult>;
250
285
 
251
- export { type BundleDiagnostic, type BundleOptions, type BundleResult, type CompileResult, type ConfigInput, type Diagnostic, type LoadedLowering, type LowerDiagnostic, type LowerOptions, type LowerResult, type LoweringProblem, type ModuleContext, type ResolvedConfig, type TiluaConfigJson, bundle, compile, loadLowerings, lower, resolveConfig };
286
+ export { type BundleDiagnostic, type BundleFile, type BundleOptions, type BundleResult, type CompileResult, type ConfigInput, type Diagnostic, type LoadedLowering, type LowerDiagnostic, type LowerOptions, type LowerResult, type LoweringProblem, type ModuleContext, type ResolvedConfig, type TiluaConfigJson, bundle, compile, loadLowerings, lower, resolveConfig };
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  lower,
5
5
  program,
6
6
  resolveConfig
7
- } from "./chunk-VZ7XC4BG.js";
7
+ } from "./chunk-3J2QMA4W.js";
8
8
 
9
9
  // src/compile.ts
10
10
  import { readFileSync } from "fs";
@@ -22,13 +22,14 @@ async function compile(source, config) {
22
22
  }
23
23
  throw error;
24
24
  }
25
- const scopes = analyzeScopes(program2);
25
+ const { config: resolved, problems } = resolveConfig(config, resolve("main.tilua"));
26
+ const noLib = resolved?.noLib ?? false;
27
+ const scopes = analyzeScopes(program2, { noLib });
26
28
  const diagnostics = scopes.diagnostics.map((d) => ({
27
29
  message: d.message,
28
30
  line: d.node.line.start,
29
31
  column: d.node.column.start
30
32
  }));
31
- const { config: resolved, problems } = resolveConfig(config, resolve("main.tilua"));
32
33
  const libraries = resolved ? resolveTypeLibraries(resolved) : { files: [], lowerings: [], problems: [] };
33
34
  const { lowerings, problems: loweringProblems } = await loadLowerings(libraries.lowerings);
34
35
  for (const p of [...problems, ...libraries.problems]) {
@@ -37,7 +38,8 @@ async function compile(source, config) {
37
38
  for (const p of loweringProblems) diagnostics.push({ message: p.message, line: 1, column: 1 });
38
39
  const libs = libraries.files.map((file) => parse(readFileSync(file, "utf8")));
39
40
  const lowered = lower(program2, scopes, {
40
- types: analyzeTypes(program2, scopes, { libs, diagnostics: false }),
41
+ types: analyzeTypes(program2, scopes, { libs, noLib, diagnostics: false }),
42
+ noLib,
41
43
  lowerings,
42
44
  target: resolved?.target,
43
45
  source
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tilua/compiler",
3
- "version": "4.0.0",
3
+ "version": "5.0.0",
4
4
  "description": "Compiles tilua to Luau: lowers the @tilua/parser AST to a luau-parser AST and prints it",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -38,7 +38,7 @@
38
38
  "type": "module",
39
39
  "dependencies": {
40
40
  "luau-parser": "^1.0.3",
41
- "@tilua/parser": "^4.0.0"
41
+ "@tilua/parser": "^5.0.0"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@types/node": "^22.20.2",
@@ -1,5 +1,13 @@
1
1
  local __NAME__ = {}
2
2
 
3
+ -- `table.move`, which Lua 5.1 does not have.
4
+ local __NAME___move = table.move or function(from, first, last, at, to)
5
+ for i = first, last do
6
+ to[at + i - first] = from[i]
7
+ end
8
+ return to
9
+ end
10
+
3
11
  function __NAME__.find(t, test)
4
12
  for i = 1, #t do
5
13
  if test(t[i], i) then
@@ -96,10 +104,10 @@ end
96
104
 
97
105
  function __NAME__.concat(t, ...)
98
106
  local out = __ARRAY__({})
99
- table.move(t, 1, #t, 1, out)
107
+ __NAME___move(t, 1, #t, 1, out)
100
108
  for i = 1, select("#", ...) do
101
109
  local part = select(i, ...)
102
- table.move(part, 1, #part, #out + 1, out)
110
+ __NAME___move(part, 1, #part, #out + 1, out)
103
111
  end
104
112
  return out
105
113
  end
@@ -127,7 +135,7 @@ function __NAME__.flat(t)
127
135
  for i = 1, #t do
128
136
  local value = t[i]
129
137
  if type(value) == "table" then
130
- table.move(value, 1, #value, #out + 1, out)
138
+ __NAME___move(value, 1, #value, #out + 1, out)
131
139
  else
132
140
  out[#out + 1] = value
133
141
  end
@@ -137,7 +145,7 @@ end
137
145
 
138
146
  function __NAME__.reverse(t)
139
147
  local n = #t
140
- for i = 1, n // 2 do
148
+ for i = 1, math.floor(n / 2) do
141
149
  t[i], t[n - i + 1] = t[n - i + 1], t[i]
142
150
  end
143
151
  return t
@@ -0,0 +1,227 @@
1
+ -- The runtime behind `console:log`, `console:warn`, `console:error`, and the
2
+ -- `error` a project calls.
3
+ --
4
+ -- Every entry takes where it was called from as its first argument, written
5
+ -- by the compiler at the call, and puts it in front of what it prints, the way
6
+ -- a browser console names the line that logged: `[src/shop/buy:12]: ...`. A
7
+ -- bundle is one Luau file, so a line Luau reports names a file nobody wrote;
8
+ -- this is the place the author can open. `error` puts it inside the message
9
+ -- instead, as Lua does: `src/shop/buy.tilua:12: ...`.
10
+ --
11
+ -- A call to `print` or `warn` needs none of this: the compiler passes the
12
+ -- place to the real function as an extra argument. Read as values
13
+ -- (`local p = print`) there is no call site to be given, so the `*Value`
14
+ -- forms work it out from the stack and the bundle's line map instead.
15
+
16
+ local __NAME__ = {}
17
+
18
+ local MAX_DEPTH = 4
19
+ local ESCAPES = { ["\n"] = "\\n", ["\r"] = "\\r", ["\t"] = "\\t", ['"'] = '\\"', ["\\"] = "\\\\" }
20
+
21
+ local function quote(text)
22
+ return '"' .. text:gsub('[%c"\\]', function(c)
23
+ return ESCAPES[c] or ("\\%d"):format(c:byte())
24
+ end) .. '"'
25
+ end
26
+
27
+ -- A table with 1..n and nothing else prints as a list, as it was written.
28
+ local function isArray(value)
29
+ local count = 0
30
+ for key in pairs(value) do
31
+ if type(key) ~= "number" then
32
+ return false
33
+ end
34
+ count = count + 1
35
+ end
36
+ return count == #value
37
+ end
38
+
39
+ local function isIdentifier(key)
40
+ return type(key) == "string" and key:match("^[A-Za-z_][A-Za-z0-9_]*$") ~= nil
41
+ end
42
+
43
+ local render
44
+
45
+ local function renderTable(value, seen, depth, indent)
46
+ if seen[value] then
47
+ return "<cycle>"
48
+ end
49
+ if depth > MAX_DEPTH then
50
+ return "..."
51
+ end
52
+ seen[value] = true
53
+ local inner = indent .. " "
54
+ local parts = {}
55
+ local array = isArray(value)
56
+ if array then
57
+ for i = 1, #value do
58
+ parts[#parts + 1] = inner .. render(value[i], nil, seen, depth + 1, inner)
59
+ end
60
+ else
61
+ local keys = {}
62
+ for key in pairs(value) do
63
+ keys[#keys + 1] = key
64
+ end
65
+ table.sort(keys, function(a, b)
66
+ return tostring(a) < tostring(b)
67
+ end)
68
+ for _, key in ipairs(keys) do
69
+ local name = isIdentifier(key) and key or ("[" .. render(key, nil, seen, depth + 1, inner) .. "]")
70
+ parts[#parts + 1] = inner .. name .. " = " .. render(value[key], nil, seen, depth + 1, inner)
71
+ end
72
+ end
73
+ seen[value] = nil
74
+ if #parts == 0 then
75
+ return "{}"
76
+ end
77
+ local open = array and "[" or "{"
78
+ local close = array and "]" or "}"
79
+ return open .. "\n" .. table.concat(parts, ",\n") .. "\n" .. indent .. close
80
+ end
81
+
82
+ -- `meta` is what the compiler knew about this value and the runtime cannot:
83
+ -- a function's type, and the code it was written as.
84
+ render = function(value, meta, seen, depth, indent)
85
+ local kind = type(value)
86
+ if kind == "string" then
87
+ return quote(value)
88
+ end
89
+ if kind == "number" or kind == "boolean" or kind == "nil" then
90
+ return tostring(value)
91
+ end
92
+ if kind == "function" then
93
+ if meta == nil then
94
+ return "function"
95
+ end
96
+ if meta[2] == nil or meta[2] == "" then
97
+ return meta[1]
98
+ end
99
+ return meta[1] .. " " .. meta[2]
100
+ end
101
+ if kind == "table" then
102
+ -- A value with its own `__tostring` (a Roblox Vector3, a class
103
+ -- instance that says how it reads) is trusted to describe itself.
104
+ local metatable = getmetatable(value)
105
+ if type(metatable) == "table" and rawget(metatable, "__tostring") ~= nil then
106
+ return tostring(value)
107
+ end
108
+ return renderTable(value, seen, depth, indent)
109
+ end
110
+ return tostring(value)
111
+ end
112
+
113
+ local function join(meta, count, ...)
114
+ local parts = {}
115
+ for i = 1, count do
116
+ parts[#parts + 1] = render((select(i, ...)), meta ~= nil and meta[i] or nil, {}, 1, "")
117
+ end
118
+ return table.concat(parts, " ")
119
+ end
120
+
121
+ local function prefixed(at, text)
122
+ if at == nil then
123
+ return text
124
+ end
125
+ return at .. ": " .. text
126
+ end
127
+
128
+ -- A line of the bundle, as the place in the project that wrote it.
129
+ local lines = __LINES__
130
+ local function place(line)
131
+ local origin = lines ~= nil and lines[tonumber(line)]
132
+ if not origin then
133
+ return nil
134
+ end
135
+ return origin[1] .. ":" .. tostring(origin[2])
136
+ end
137
+
138
+ -- The place of the function `level` frames above the caller of this, or nil.
139
+ local function caller(level)
140
+ local line
141
+ if debug.info ~= nil then
142
+ line = debug.info(level + 1, "l")
143
+ elseif debug.getinfo ~= nil then
144
+ local info = debug.getinfo(level + 1, "l")
145
+ line = info and info.currentline
146
+ end
147
+ if line == nil or line < 0 then
148
+ return nil
149
+ end
150
+ return place(line)
151
+ end
152
+
153
+ -- Luau names the bundle in every frame; the project's files are what the
154
+ -- reader knows. Frames the map has nothing for are dropped: they are this
155
+ -- runtime's own, and the bundle's.
156
+ local function trace(level)
157
+ local out = {}
158
+ for frame in debug.traceback("", level + 1):gmatch("[^\n]+") do
159
+ local line = frame:match(":(%d+)")
160
+ local at = line ~= nil and place(line) or nil
161
+ if at ~= nil then
162
+ local name = frame:match("function ([%w_.:]+)")
163
+ out[#out + 1] = " at " .. at .. (name ~= nil and (" (" .. name .. ")") or "")
164
+ end
165
+ end
166
+ return table.concat(out, "\n")
167
+ end
168
+
169
+ -- Never on the caller's thread: reporting a problem must not become one.
170
+ local detach = (task ~= nil and task.spawn) or function(f)
171
+ local ok, problem = pcall(f)
172
+ if not ok then
173
+ print(problem)
174
+ end
175
+ end
176
+
177
+ -- `tag` is how the line starts, `[src/a:12]:`, passed to `print` and `warn`
178
+ -- as an argument of its own so the host joins it the way it joins the rest.
179
+ function __NAME__.log(tag, meta, ...)
180
+ print(tag, join(meta, select("#", ...), ...))
181
+ end
182
+
183
+ function __NAME__.warn(tag, meta, ...)
184
+ local text = join(meta, select("#", ...), ...)
185
+ if warn ~= nil then
186
+ warn(tag, text)
187
+ else
188
+ print(tag, "[warn]", text)
189
+ end
190
+ end
191
+
192
+ function __NAME__.error(tag, meta, ...)
193
+ local message = tag .. " " .. join(meta, select("#", ...), ...)
194
+ local where = trace(2)
195
+ detach(function()
196
+ error(where ~= "" and (message .. "\n" .. where) or message, 0)
197
+ end)
198
+ end
199
+
200
+ -- `error(message, level)`, raised for real — `pcall` still catches it — with
201
+ -- the position already in the project's files. Level 1, the default, is the
202
+ -- call itself, which the compiler wrote down; a higher level is a caller the
203
+ -- compiler could not see, so it is read off the stack. A message that is not
204
+ -- a string, or level 0, carries no position in Lua either.
205
+ function __NAME__.raise(at, message, level)
206
+ if level == nil then
207
+ level = 1
208
+ end
209
+ if type(message) ~= "string" or level == 0 then
210
+ error(message, 0)
211
+ end
212
+ local where = at
213
+ if level > 1 then
214
+ where = caller(level + 1)
215
+ end
216
+ error(prefixed(where, message), 0)
217
+ end
218
+
219
+ function __NAME__.raiseValue(message, level)
220
+ if level == nil then
221
+ level = 1
222
+ end
223
+ if type(message) ~= "string" or level == 0 then
224
+ error(message, 0)
225
+ end
226
+ error(prefixed(caller(level + 1), message), 0)
227
+ end
@@ -0,0 +1,122 @@
1
+ local __NAME__ = {}
2
+
3
+ -- The globals whose Luau answer is several values, as tilua has them: one
4
+ -- value — an array, or an object. The compiler calls these instead of the
5
+ -- globals they stand for, and reading one of those globals as a value
6
+ -- (`local p = pcall`) gives the function here, so the two never disagree.
7
+
8
+ -- `pcall`'s `ok, ...` as `{ success, data, error }`. `data` is what the
9
+ -- function returned: one value, since a tilua function returns one.
10
+ local function result(ok, ...)
11
+ if ok then
12
+ return { success = true, data = (...) }
13
+ end
14
+ return { success = false, error = (...) }
15
+ end
16
+
17
+ function __NAME__.pcall(f, ...)
18
+ return result(pcall(f, ...))
19
+ end
20
+
21
+ function __NAME__.xpcall(f, handler, ...)
22
+ return result(xpcall(f, handler, ...))
23
+ end
24
+
25
+ function __NAME__.resume(co, ...)
26
+ return result(coroutine.resume(co, ...))
27
+ end
28
+
29
+ -- Several values as an array — or nil, when all there was is nil.
30
+ local function values(...)
31
+ if select("#", ...) <= 1 and (...) == nil then
32
+ return nil
33
+ end
34
+ return { ... }
35
+ end
36
+
37
+ function __NAME__.find(s, pattern, init, plain)
38
+ return values(string.find(s, pattern, init, plain))
39
+ end
40
+
41
+ function __NAME__.match(s, pattern, init)
42
+ return values(string.match(s, pattern, init))
43
+ end
44
+
45
+ function __NAME__.gsub(s, pattern, replacement, n)
46
+ return (string.gsub(s, pattern, replacement, n))
47
+ end
48
+
49
+ function __NAME__.byte(s, i, j)
50
+ if j == nil then
51
+ return (string.byte(s, i))
52
+ end
53
+ return { string.byte(s, i, j) }
54
+ end
55
+
56
+ function __NAME__.modf(x)
57
+ return { math.modf(x) }
58
+ end
59
+
60
+ function __NAME__.frexp(x)
61
+ return { math.frexp(x) }
62
+ end
63
+
64
+ function __NAME__.next(t, key)
65
+ local k, v = next(t, key)
66
+ if k == nil then
67
+ return nil
68
+ end
69
+ return { k, v }
70
+ end
71
+
72
+ function __NAME__.select(n, ...)
73
+ if n == "#" then
74
+ return select("#", ...)
75
+ end
76
+ return { select(n, ...) }
77
+ end
78
+
79
+ function __NAME__.unpack(list, i, j)
80
+ return { (table.unpack or unpack)(list, i or 1, j or #list) }
81
+ end
82
+
83
+ -- `pairs(t)` and `ipairs(t)` as an iteration — `{ step, state, first }`,
84
+ -- which a loop walks with. Each step answers one `{ key, value }`, and is
85
+ -- handed the one before it.
86
+ local function pairsStep(t, previous)
87
+ local k, v = next(t, previous ~= nil and previous[1] or nil)
88
+ if k == nil then
89
+ return nil
90
+ end
91
+ return { k, v }
92
+ end
93
+
94
+ function __NAME__.pairs(t)
95
+ return { pairsStep, t, nil }
96
+ end
97
+
98
+ local function ipairsStep(t, previous)
99
+ local i = (previous ~= nil and previous[1] or 0) + 1
100
+ local v = t[i]
101
+ if v == nil then
102
+ return nil
103
+ end
104
+ return { i, v }
105
+ end
106
+
107
+ function __NAME__.ipairs(t)
108
+ return { ipairsStep, t, nil }
109
+ end
110
+
111
+ -- An iterator function: each call is one match's captures, as an array.
112
+ function __NAME__.gmatch(s, pattern)
113
+ local step = string.gmatch(s, pattern)
114
+ return function()
115
+ return values(step())
116
+ end
117
+ end
118
+
119
+ function __NAME__.loadstring(source, chunkname)
120
+ local f, message = loadstring(source, chunkname)
121
+ return { f, message }
122
+ end