Docs for stdlib

This commit is contained in:
Zef Hemel
2026-07-15 17:55:18 +02:00
parent 23f338dc14
commit 599798c831
11 changed files with 1713 additions and 404 deletions
+8
View File
@@ -6,5 +6,13 @@ export const cryptoApi = new LuaTable({
(_sf, s: string | Uint8Array): Promise<string> => {
return hashSHA256(s);
},
{
kind: "builtin",
description: "Computes the SHA-256 digest of a string or byte buffer.",
parameters: [
{ name: "data", type: "string|bytes", description: "Data to hash." },
],
returns: [{ type: "string", description: "Hexadecimal SHA-256 digest." }],
},
),
});
+39 -9
View File
@@ -6,14 +6,44 @@ export const encodingApi = new LuaTable({
(_sf, s: string | Uint8Array): string => {
return base64Encode(s);
},
{
kind: "builtin",
description: "Encodes a string or byte buffer as Base64.",
parameters: [{ name: "data", type: "string|bytes" }],
returns: [{ type: "string", description: "Base64-encoded data." }],
},
),
base64Decode: new LuaBuiltinFunction(
(_sf, s: string): Uint8Array => {
return base64Decode(s);
},
{
kind: "builtin",
description: "Decodes a Base64 string into a byte buffer.",
parameters: [{ name: "encoded", type: "string" }],
returns: [{ type: "bytes", description: "Decoded bytes." }],
},
),
utf8Encode: new LuaBuiltinFunction(
(_sf, s: string): Uint8Array => {
return new TextEncoder().encode(s);
},
{
kind: "builtin",
description: "Encodes a UTF-8 string into a byte buffer.",
parameters: [{ name: "value", type: "string" }],
returns: [{ type: "bytes", description: "UTF-8 encoded bytes." }],
},
),
utf8Decode: new LuaBuiltinFunction(
(_sf, data: Uint8Array): string => {
return new TextDecoder().decode(data);
},
{
kind: "builtin",
description: "Decodes a UTF-8 byte buffer into a string.",
parameters: [{ name: "data", type: "bytes" }],
returns: [{ type: "string", description: "Decoded text." }],
},
),
base64Decode: new LuaBuiltinFunction((_sf, s: string): Uint8Array => {
return base64Decode(s);
}),
utf8Encode: new LuaBuiltinFunction((_sf, s: string): Uint8Array => {
return new TextEncoder().encode(s);
}),
utf8Decode: new LuaBuiltinFunction((_sf, data: Uint8Array): string => {
return new TextDecoder().decode(data);
}),
});
+150 -36
View File
@@ -13,22 +13,60 @@ export const jsApi = new LuaTable({
* @param args - The arguments to pass to the constructor.
* @returns The new instance.
*/
new: new LuaBuiltinFunction((sf, constructorFn: any, ...args) => {
return new constructorFn(...args.map((v) => luaValueToJS(v, sf)));
}),
new: new LuaBuiltinFunction(
(sf, constructorFn: any, ...args) => {
return new constructorFn(...args.map((v) => luaValueToJS(v, sf)));
},
{
kind: "builtin",
description: "Creates an instance of a JavaScript class.",
signatures: ["js.new(constructor, ...): userdata"],
parameters: [
{
name: "constructor",
type: "userdata",
description: "JavaScript constructor function.",
},
{
name: "...",
description: "Constructor arguments converted to JavaScript values.",
},
],
returns: [{ type: "userdata", description: "New JavaScript instance." }],
examples: [
{ code: 'local value = js.new(js.window.Date, "2024-03-14")' },
],
},
),
/**
* Imports a JavaScript module.
* @param url - The URL of the module to import.
* @returns The imported module.
*/
import: new LuaBuiltinFunction(async (_sf, url) => {
let m = await import(url);
// Unwrap default if it exists
if (Object.keys(m).length === 1 && m.default) {
m = m.default;
}
return m;
}),
import: new LuaBuiltinFunction(
async (_sf, url) => {
let m = await import(url);
// Unwrap default if it exists
if (Object.keys(m).length === 1 && m.default) {
m = m.default;
}
return m;
},
{
kind: "builtin",
description: "Dynamically imports a JavaScript module from a URL.",
parameters: [{ name: "url", type: "string", description: "Module URL." }],
returns: [
{
type: "userdata",
description: "Imported module, with a sole default export unwrapped.",
},
],
examples: [
{ code: 'local lib = js.import("https://esm.sh/lodash@4.17.21")' },
],
},
),
/**
* Like `js.import`, but takes a path to a file in the current space (e.g.
* "Library/foo/bar.js") and resolves it to its full same-origin `/.fs` URL
@@ -36,51 +74,127 @@ export const jsApi = new LuaTable({
* @param path - Space-relative path to the JS module (leading "/" optional).
* @returns The imported module (with a sole `default` export unwrapped).
*/
importFromSpace: new LuaBuiltinFunction(async (_sf, path: string) => {
const base = document.baseURI.replace(/\/*$/, "/");
const rel = String(path).replace(/^\/+/, "");
let m = await import(base + fsEndpoint.slice(1) + "/" + rel);
// Unwrap default if it exists (same contract as `js.import`).
if (Object.keys(m).length === 1 && m.default) {
m = m.default;
}
return m;
}),
eachIterable: new LuaBuiltinFunction((_sf, val) => {
const iterator = val[Symbol.asyncIterator]();
return async () => {
const result = await iterator.next();
if (result.done) {
return;
importFromSpace: new LuaBuiltinFunction(
async (_sf, path: string) => {
const base = document.baseURI.replace(/\/*$/, "/");
const rel = String(path).replace(/^\/+/, "");
let m = await import(base + fsEndpoint.slice(1) + "/" + rel);
// Unwrap default if it exists (same contract as `js.import`).
if (Object.keys(m).length === 1 && m.default) {
m = m.default;
}
return result.value;
};
}),
return m;
},
{
kind: "builtin",
description:
"Imports a JavaScript module from a file in the current space.",
parameters: [
{
name: "path",
type: "string",
description:
"Space-relative module path, with an optional leading slash.",
},
],
returns: [
{
type: "userdata",
description: "Imported module, with a sole default export unwrapped.",
},
],
examples: [
{ code: 'local acme = js.importFromSpace("Library/acme/acme.js")' },
],
},
),
eachIterable: new LuaBuiltinFunction(
(_sf, val) => {
const iterator = val[Symbol.asyncIterator]();
return async () => {
const result = await iterator.next();
if (result.done) {
return;
}
return result.value;
};
},
{
kind: "builtin",
description: "Creates a Lua iterator over a JavaScript async iterable.",
parameters: [
{
name: "iterable",
type: "userdata",
description: "JavaScript async iterable.",
},
],
returns: [
{
type: "function",
description: "Iterator yielding successive JavaScript values.",
},
],
examples: [
{
code: "for value in js.eachIterable(someJsAsyncIterable) do\n print(value)\nend",
},
],
},
),
/**
* Converts a JavaScript value to a Lua value.
* @param val - The JavaScript value to convert.
* @returns The Lua value.
*/
tolua: new LuaBuiltinFunction((_sf, val) => jsToLuaValue(val)),
tolua: new LuaBuiltinFunction((_sf, val) => jsToLuaValue(val), {
kind: "builtin",
description: "Converts a JavaScript value to its Lua representation.",
parameters: [
{ name: "value", description: "JavaScript value to convert." },
],
returns: [{ description: "Converted Lua value." }],
examples: [{ code: "local luaTable = js.tolua(jsArray)" }],
}),
/**
* Converts a Lua value to a JavaScript value.
* @param val - The Lua value to convert.
* @returns The JavaScript value.
*/
tojs: new LuaBuiltinFunction((sf, val) => luaValueToJS(val, sf)),
tojs: new LuaBuiltinFunction((sf, val) => luaValueToJS(val, sf), {
kind: "builtin",
description: "Converts a Lua value to its JavaScript representation.",
parameters: [{ name: "value", description: "Lua value to convert." }],
returns: [{ description: "Converted JavaScript value." }],
examples: [{ code: "local jsArray = js.tojs({1, 2, 3})" }],
}),
/**
* Logs a message to the console.
* @param args - The arguments to log.
*/
log: new LuaBuiltinFunction((_sf, ...args) => {
console.log(...args);
}),
log: new LuaBuiltinFunction(
(_sf, ...args) => {
console.log(...args);
},
{
kind: "builtin",
description: "Logs values to the JavaScript console.",
parameters: [{ name: "...", description: "Values to log." }],
examples: [{ code: 'js.log("User data:", {name = "Ada"})' }],
},
),
/**
* Converts a Lua value to a JSON string.
* @param val - The Lua value to convert.
* @returns The JSON string.
*/
stringify: new LuaBuiltinFunction((_sf, val) => JSON.stringify(val)),
stringify: new LuaBuiltinFunction((_sf, val) => JSON.stringify(val), {
kind: "builtin",
description: "Serializes a value as JSON using JavaScript semantics.",
parameters: [{ name: "value", description: "Value to serialize." }],
returns: [{ type: "string", description: "JSON representation." }],
examples: [{ code: "print(js.stringify({1, 2, 3})) -- [1,2,3]" }],
}),
// Expose the global window object
window: globalThis,
+28 -16
View File
@@ -24,23 +24,35 @@ export function luaLoad(code: LuaValue, sf: LuaStackFrame): LuaValue {
const globalEnv: LuaEnv = globalEnvMaybe as LuaEnv;
const runner = new LuaBuiltinFunction(async (innerSf: LuaStackFrame) => {
const res = await evalStatement(block, globalEnv, innerSf, true);
if (res === undefined) {
return null;
} else {
if (res && typeof res === "object" && (res as any).ctrl === "return") {
return new LuaMultiRes((res as any).values);
const runner = new LuaBuiltinFunction(
async (innerSf: LuaStackFrame) => {
const res = await evalStatement(block, globalEnv, innerSf, true);
if (res === undefined) {
return null;
} else {
if (
res &&
typeof res === "object" &&
(res as any).ctrl === "return"
) {
return new LuaMultiRes((res as any).values);
}
if (res && typeof res === "object" && (res as any).ctrl === "break") {
throw new Error("break outside loop");
}
if (res && typeof res === "object" && (res as any).ctrl === "goto") {
throw new Error("unexpected goto signal");
}
return null;
}
if (res && typeof res === "object" && (res as any).ctrl === "break") {
throw new Error("break outside loop");
}
if (res && typeof res === "object" && (res as any).ctrl === "goto") {
throw new Error("unexpected goto signal");
}
return null;
}
});
},
{
kind: "builtin",
description: "Executes the Lua chunk compiled by `load`.",
signatures: ["function(...)"],
returns: [{ description: "Values returned by the compiled chunk." }],
},
);
return runner;
} catch (e: any) {
+369 -107
View File
@@ -23,49 +23,71 @@ export const mathApi = new LuaTable({
pi: Math.PI,
// math.type(x) => "integer" | "float" | nil
type: new LuaBuiltinFunction((_sf, x?: any) => {
if (x === undefined) {
throw new LuaRuntimeError(
"bad argument #1 to 'math.type' (value expected)",
_sf,
);
}
if (isTaggedFloat(x)) {
return "float";
}
if (typeof x === "number") {
if (!Number.isFinite(x) || isNegativeZero(x)) {
type: new LuaBuiltinFunction(
(_sf, x?: any) => {
if (x === undefined) {
throw new LuaRuntimeError(
"bad argument #1 to 'math.type' (value expected)",
_sf,
);
}
if (isTaggedFloat(x)) {
return "float";
}
return Number.isInteger(x) ? "integer" : "float";
}
if (typeof x === "bigint") {
return "integer";
}
return null;
}),
if (typeof x === "number") {
if (!Number.isFinite(x) || isNegativeZero(x)) {
return "float";
}
return Number.isInteger(x) ? "integer" : "float";
}
if (typeof x === "bigint") {
return "integer";
}
return null;
},
{
kind: "builtin",
description:
"Returns `integer` or `float` for a number, or `nil` for other values.",
parameters: [{ name: "x", description: "Value to inspect." }],
returns: [
{ type: "string|nil", description: "Numeric subtype or `nil`." },
],
},
),
/**
* If the value x is representable as a Lua integer, returns an integer
* with that value. Otherwise returns nil.
* Strings are NOT accepted — only Lua number values.
*/
tointeger: new LuaBuiltinFunction((_sf, x?: any) => {
if (typeof x === "number") {
return Number.isInteger(x) && Number.isFinite(x) ? x : null;
}
if (isTaggedFloat(x)) {
const n = x.value;
return Number.isInteger(n) && Number.isFinite(n) ? n : null;
}
if (typeof x === "string") {
const n = untagNumber(x); // Number(x) coerces the string
if (Number.isNaN(n) || !Number.isFinite(n) || !Number.isInteger(n))
return null;
return n;
}
return null;
}),
tointeger: new LuaBuiltinFunction(
(_sf, x?: any) => {
if (typeof x === "number") {
return Number.isInteger(x) && Number.isFinite(x) ? x : null;
}
if (isTaggedFloat(x)) {
const n = x.value;
return Number.isInteger(n) && Number.isFinite(n) ? n : null;
}
if (typeof x === "string") {
const n = untagNumber(x); // Number(x) coerces the string
if (Number.isNaN(n) || !Number.isFinite(n) || !Number.isInteger(n))
return null;
return n;
}
return null;
},
{
kind: "builtin",
description:
"Converts a value to an integer when it has an exact finite integral representation.",
parameters: [{ name: "x", description: "Value to convert." }],
returns: [
{ type: "integer|nil", description: "Converted integer or `nil`." },
],
},
),
/**
* When called without arguments, returns a pseudo-random float with
@@ -76,118 +98,345 @@ export const mathApi = new LuaTable({
* math.random(1,n). The call math.random(0) produces an integer
* with all bits (pseudo)random.
*/
random: new LuaBuiltinFunction((_sf, m?: number, n?: number) => {
if (m !== undefined) m = untagNumber(m);
if (n !== undefined) n = untagNumber(n);
try {
return prng.random(m, n);
} catch (e: any) {
throw new LuaRuntimeError(e.message, _sf);
}
}),
random: new LuaBuiltinFunction(
(_sf, m?: number, n?: number) => {
if (m !== undefined) m = untagNumber(m);
if (n !== undefined) n = untagNumber(n);
try {
return prng.random(m, n);
} catch (e: any) {
throw new LuaRuntimeError(e.message, _sf);
}
},
{
kind: "builtin",
description:
"Returns a pseudo-random float or an integer in a requested inclusive range.",
signatures: [
"math.random(): number",
"math.random(n): integer",
"math.random(m, n): integer",
],
parameters: [
{ name: "m", type: "integer", optional: true },
{ name: "n", type: "integer", optional: true },
],
returns: [{ type: "number", description: "Pseudo-random result." }],
examples: [
{
code: "print(math.random())\nprint(math.random(10))\nprint(math.random(5, 10))",
},
],
},
),
/**
* Seeds the pseudo-random generator. With no arguments, uses a
* time-based seed. Returns the two seed integers used (Lua 5.4 contract).
*/
randomseed: new LuaBuiltinFunction((_sf, x?: number, y?: number) => {
if (x !== undefined) x = untagNumber(x);
if (y !== undefined) y = untagNumber(y);
const [s1, s2] = prng.randomseed(x, y);
return new LuaMultiRes([s1, s2]);
}),
randomseed: new LuaBuiltinFunction(
(_sf, x?: number, y?: number) => {
if (x !== undefined) x = untagNumber(x);
if (y !== undefined) y = untagNumber(y);
const [s1, s2] = prng.randomseed(x, y);
return new LuaMultiRes([s1, s2]);
},
{
kind: "builtin",
description:
"Seeds the pseudo-random generator and returns the two seeds used.",
signatures: [
"math.randomseed(): integer, integer",
"math.randomseed(x, y): integer, integer",
],
parameters: [
{ name: "x", type: "integer", optional: true },
{ name: "y", type: "integer", optional: true },
],
returns: [
{ type: "integer", description: "First seed." },
{ type: "integer", description: "Second seed." },
],
},
),
// Basic functions
abs: new LuaBuiltinFunction((_sf, x: number) => Math.abs(untagNumber(x))),
ceil: new LuaBuiltinFunction((_sf, x: number) => Math.ceil(untagNumber(x))),
floor: new LuaBuiltinFunction((_sf, x: number) => Math.floor(untagNumber(x))),
max: new LuaBuiltinFunction((_sf, ...args: number[]) =>
Math.max(...args.map(untagNumber)),
abs: new LuaBuiltinFunction((_sf, x: number) => Math.abs(untagNumber(x)), {
kind: "builtin",
description: "Returns the absolute value of `x`.",
parameters: [{ name: "x", type: "number" }],
returns: [{ type: "number" }],
}),
ceil: new LuaBuiltinFunction((_sf, x: number) => Math.ceil(untagNumber(x)), {
kind: "builtin",
description: "Returns the smallest integer greater than or equal to `x`.",
parameters: [{ name: "x", type: "number" }],
returns: [{ type: "integer" }],
}),
floor: new LuaBuiltinFunction(
(_sf, x: number) => Math.floor(untagNumber(x)),
{
kind: "builtin",
description: "Returns the largest integer less than or equal to `x`.",
parameters: [{ name: "x", type: "number" }],
returns: [{ type: "integer" }],
},
),
min: new LuaBuiltinFunction((_sf, ...args: number[]) =>
Math.min(...args.map(untagNumber)),
max: new LuaBuiltinFunction(
(_sf, ...args: number[]) => Math.max(...args.map(untagNumber)),
{
kind: "builtin",
description: "Returns the greatest of its arguments.",
signatures: ["math.max(x, ...): number"],
returns: [{ type: "number" }],
},
),
min: new LuaBuiltinFunction(
(_sf, ...args: number[]) => Math.min(...args.map(untagNumber)),
{
kind: "builtin",
description: "Returns the least of its arguments.",
signatures: ["math.min(x, ...): number"],
returns: [{ type: "number" }],
},
),
// Rounding and remainder
fmod: new LuaBuiltinFunction(
(_sf, x: number, y: number) => untagNumber(x) % untagNumber(y),
{
kind: "builtin",
description:
"Returns the remainder of `x / y` with the quotient rounded toward zero.",
parameters: [
{ name: "x", type: "number" },
{ name: "y", type: "number" },
],
returns: [{ type: "number" }],
},
),
modf: new LuaBuiltinFunction(
(_sf, x: number) => {
const xn = untagNumber(x);
const int = Math.trunc(xn);
// Guarantee that the `frac` part is always Lua float
const frac = makeLuaFloat(xn - int);
return new LuaMultiRes([int, frac]);
},
{
kind: "builtin",
description: "Splits `x` into its integral and fractional parts.",
parameters: [{ name: "x", type: "number" }],
returns: [
{ type: "integer", description: "Integral part." },
{ type: "float", description: "Fractional part." },
],
examples: [{ code: "local integer, fraction = math.modf(3.14)" }],
},
),
modf: new LuaBuiltinFunction((_sf, x: number) => {
const xn = untagNumber(x);
const int = Math.trunc(xn);
// Guarantee that the `frac` part is always Lua float
const frac = makeLuaFloat(xn - int);
return new LuaMultiRes([int, frac]);
}),
// Returns m and e such that x = m * 2^e, 0.5 <= |m| < 1 (or m=0 when x=0).
// e is an integer. Mirrors C99/Lua.
// Special cases: frexp(0) = (0, 0); frexp(+-inf/nan) = (x, 0).
frexp: new LuaBuiltinFunction((_sf, x: number) => {
const xn = untagNumber(x);
if (xn === 0 || !Number.isFinite(xn) || Number.isNaN(xn)) {
return new LuaMultiRes([xn, 0]);
}
const abs = Math.abs(xn);
let e = Math.floor(Math.log2(abs)) + 1;
let m = xn / 2 ** e;
if (Math.abs(m) >= 1.0) {
e += 1;
m /= 2;
}
if (Math.abs(m) < 0.5) {
e -= 1;
m *= 2;
}
return new LuaMultiRes([m, e]);
}),
frexp: new LuaBuiltinFunction(
(_sf, x: number) => {
const xn = untagNumber(x);
if (xn === 0 || !Number.isFinite(xn) || Number.isNaN(xn)) {
return new LuaMultiRes([xn, 0]);
}
const abs = Math.abs(xn);
let e = Math.floor(Math.log2(abs)) + 1;
let m = xn / 2 ** e;
if (Math.abs(m) >= 1.0) {
e += 1;
m /= 2;
}
if (Math.abs(m) < 0.5) {
e -= 1;
m *= 2;
}
return new LuaMultiRes([m, e]);
},
{
kind: "builtin",
description:
"Decomposes `x` into a normalized fraction and a power-of-two exponent.",
parameters: [{ name: "x", type: "number" }],
returns: [
{ type: "number", description: "Fraction." },
{ type: "integer", description: "Exponent." },
],
},
),
// Returns m * 2^e (the inverse of frexp). Mirrors C99/Lua.
ldexp: new LuaBuiltinFunction(
(_sf, m: number, e: number) => untagNumber(m) * 2 ** untagNumber(e),
{
kind: "builtin",
description: "Returns `m * 2^e`, the inverse of `math.frexp`.",
parameters: [
{ name: "m", type: "number" },
{ name: "e", type: "integer" },
],
returns: [{ type: "number" }],
},
),
// Power and logarithms
exp: new LuaBuiltinFunction((_sf, x: number) => Math.exp(untagNumber(x))),
log: new LuaBuiltinFunction((_sf, x: number, base?: number) => {
if (base === undefined) {
return Math.log(untagNumber(x));
}
return Math.log(untagNumber(x)) / Math.log(untagNumber(base));
exp: new LuaBuiltinFunction((_sf, x: number) => Math.exp(untagNumber(x)), {
kind: "builtin",
description: "Returns `e` raised to `x`.",
parameters: [{ name: "x", type: "number" }],
returns: [{ type: "number" }],
}),
log: new LuaBuiltinFunction(
(_sf, x: number, base?: number) => {
if (base === undefined) {
return Math.log(untagNumber(x));
}
return Math.log(untagNumber(x)) / Math.log(untagNumber(base));
},
{
kind: "builtin",
description:
"Returns the logarithm of `x`, using the natural base unless another base is supplied.",
parameters: [
{ name: "x", type: "number" },
{ name: "base", type: "number", optional: true },
],
returns: [{ type: "number" }],
examples: [{ code: "print(math.log(100, 10)) -- 2" }],
},
),
// Power function (deprecated in Lua 5.4 but retained for compatibility)
pow: new LuaBuiltinFunction(
(_sf, x: number, y: number) => untagNumber(x) ** untagNumber(y),
{
kind: "builtin",
description: "Returns `x` raised to the power `y`.",
parameters: [
{ name: "x", type: "number" },
{ name: "y", type: "number" },
],
returns: [{ type: "number" }],
deprecated: "Use the `^` operator instead.",
},
),
sqrt: new LuaBuiltinFunction((_sf, x: number) => Math.sqrt(untagNumber(x))),
// Trigonometric functions
cos: new LuaBuiltinFunction((_sf, x: number) => Math.cos(untagNumber(x))),
sin: new LuaBuiltinFunction((_sf, x: number) => Math.sin(untagNumber(x))),
tan: new LuaBuiltinFunction((_sf, x: number) => Math.tan(untagNumber(x))),
acos: new LuaBuiltinFunction((_sf, x: number) => Math.acos(untagNumber(x))),
asin: new LuaBuiltinFunction((_sf, x: number) => Math.asin(untagNumber(x))),
atan: new LuaBuiltinFunction((_sf, y: number, x?: number) => {
if (x === undefined) {
return Math.atan(untagNumber(y));
}
return Math.atan2(untagNumber(y), untagNumber(x));
sqrt: new LuaBuiltinFunction((_sf, x: number) => Math.sqrt(untagNumber(x)), {
kind: "builtin",
description: "Returns the square root of `x`.",
parameters: [{ name: "x", type: "number" }],
returns: [{ type: "number" }],
}),
// Trigonometric functions
cos: new LuaBuiltinFunction((_sf, x: number) => Math.cos(untagNumber(x)), {
kind: "builtin",
description: "Returns the cosine of `x` radians.",
parameters: [{ name: "x", type: "number" }],
returns: [{ type: "number" }],
}),
sin: new LuaBuiltinFunction((_sf, x: number) => Math.sin(untagNumber(x)), {
kind: "builtin",
description: "Returns the sine of `x` radians.",
parameters: [{ name: "x", type: "number" }],
returns: [{ type: "number" }],
}),
tan: new LuaBuiltinFunction((_sf, x: number) => Math.tan(untagNumber(x)), {
kind: "builtin",
description: "Returns the tangent of `x` radians.",
parameters: [{ name: "x", type: "number" }],
returns: [{ type: "number" }],
}),
acos: new LuaBuiltinFunction((_sf, x: number) => Math.acos(untagNumber(x)), {
kind: "builtin",
description: "Returns the arc cosine of `x` in radians.",
parameters: [{ name: "x", type: "number" }],
returns: [{ type: "number" }],
}),
asin: new LuaBuiltinFunction((_sf, x: number) => Math.asin(untagNumber(x)), {
kind: "builtin",
description: "Returns the arc sine of `x` in radians.",
parameters: [{ name: "x", type: "number" }],
returns: [{ type: "number" }],
}),
atan: new LuaBuiltinFunction(
(_sf, y: number, x?: number) => {
if (x === undefined) {
return Math.atan(untagNumber(y));
}
return Math.atan2(untagNumber(y), untagNumber(x));
},
{
kind: "builtin",
description:
"Returns the arc tangent of `y/x` in radians, using `1` for omitted `x`.",
parameters: [
{ name: "y", type: "number" },
{ name: "x", type: "number", optional: true },
],
returns: [{ type: "number" }],
},
),
// Hyperbolic functions (deprecated in Lua 5.4 but retained for compatibility)
cosh: new LuaBuiltinFunction((_sf, x: number) => Math.cosh(untagNumber(x))),
sinh: new LuaBuiltinFunction((_sf, x: number) => Math.sinh(untagNumber(x))),
tanh: new LuaBuiltinFunction((_sf, x: number) => Math.tanh(untagNumber(x))),
cosh: new LuaBuiltinFunction((_sf, x: number) => Math.cosh(untagNumber(x)), {
kind: "builtin",
description: "Returns the hyperbolic cosine of `x`.",
parameters: [{ name: "x", type: "number" }],
returns: [{ type: "number" }],
deprecated: "Retained for compatibility with older Lua versions.",
}),
sinh: new LuaBuiltinFunction((_sf, x: number) => Math.sinh(untagNumber(x)), {
kind: "builtin",
description: "Returns the hyperbolic sine of `x`.",
parameters: [{ name: "x", type: "number" }],
returns: [{ type: "number" }],
deprecated: "Retained for compatibility with older Lua versions.",
}),
tanh: new LuaBuiltinFunction((_sf, x: number) => Math.tanh(untagNumber(x)), {
kind: "builtin",
description: "Returns the hyperbolic tangent of `x`.",
parameters: [{ name: "x", type: "number" }],
returns: [{ type: "number" }],
deprecated: "Retained for compatibility with older Lua versions.",
}),
// Additional utility
deg: new LuaBuiltinFunction(
(_sf, x: number) => (untagNumber(x) * 180) / Math.PI,
{
kind: "builtin",
description: "Converts an angle from radians to degrees.",
parameters: [{ name: "x", type: "number" }],
returns: [{ type: "number" }],
examples: [{ code: "print(math.deg(math.pi)) -- 180" }],
},
),
rad: new LuaBuiltinFunction(
(_sf, x: number) => (untagNumber(x) * Math.PI) / 180,
{
kind: "builtin",
description: "Converts an angle from degrees to radians.",
parameters: [{ name: "x", type: "number" }],
returns: [{ type: "number" }],
},
),
ult: new LuaBuiltinFunction(
(_sf, m: number, n: number) => {
return untagNumber(m) >>> 0 < untagNumber(n) >>> 0;
},
{
kind: "builtin",
description: "Compares two integers as unsigned 32-bit values.",
parameters: [
{ name: "m", type: "integer" },
{ name: "n", type: "integer" },
],
returns: [{ type: "boolean" }],
examples: [{ code: "print(math.ult(2, 3)) -- true" }],
},
),
ult: new LuaBuiltinFunction((_sf, m: number, n: number) => {
return untagNumber(m) >>> 0 < untagNumber(n) >>> 0;
}),
// Keep the cosineSimilarity utility function
cosineSimilarity: new LuaBuiltinFunction(
@@ -215,5 +464,18 @@ export const mathApi = new LuaTable({
return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
},
{
kind: "builtin",
description:
"Returns the cosine similarity between two equal-length numeric vectors.",
parameters: [
{ name: "vecA", type: "table" },
{ name: "vecB", type: "table" },
],
returns: [{ type: "number", description: "Cosine similarity." }],
examples: [
{ code: "print(math.cosineSimilarity({1, 2, 3}, {4, 5, 6}))" },
],
},
),
});
+54
View File
@@ -70,6 +70,29 @@ export const netApi = new LuaTable({
body: body,
};
},
{
kind: "builtin",
description:
"Performs an HTTP request through the SilverBullet server to avoid browser CORS restrictions.",
signatures: ["net.proxyFetch(url, options?)"],
parameters: [
{ name: "url", type: "string", description: "URL to request." },
{
name: "options",
type: "table",
description:
"Optional method, headers, body, and responseEncoding values.",
optional: true,
},
],
returns: [
{
type: "table",
description: "Response status, headers, decoded body, and ok flag.",
},
],
see: "API/net",
},
),
readURI: new LuaNativeJSFunction(
(uri: string, options: { uri?: string; encoding?: string } = {}) => {
@@ -79,6 +102,22 @@ export const netApi = new LuaTable({
options,
);
},
{
kind: "builtin",
description: "Reads content from a URI using the best matching service.",
signatures: ["net.readURI(uri, options?)"],
parameters: [
{ name: "uri", type: "string", description: "URI to read." },
{
name: "options",
type: "table",
description: "Optional service-specific values such as encoding.",
optional: true,
},
],
returns: [{ description: "Content returned by the matching service." }],
see: "API/net",
},
),
writeURI: new LuaNativeJSFunction(
(uri: string, content: string | Uint8Array) => {
@@ -87,6 +126,21 @@ export const netApi = new LuaTable({
{ uri, content },
);
},
{
kind: "builtin",
description: "Writes content to a URI using the best matching service.",
signatures: ["net.writeURI(uri, content)"],
parameters: [
{ name: "uri", type: "string", description: "URI to write." },
{
name: "content",
type: "string|userdata",
description: "Text or binary content to write.",
},
],
returns: [{ description: "Result returned by the matching service." }],
see: "API/net",
},
),
});
+126 -47
View File
@@ -282,39 +282,78 @@ function luaFormatTime(fmt: string, d: Date, utc: boolean): string {
}
export const osApi = new LuaTable({
time: new LuaBuiltinFunction((_sf, tbl?: LuaTable) => {
if (tbl) {
if (!tbl.has("year")) {
throw new Error("time(): year is required");
time: new LuaBuiltinFunction(
(_sf, tbl?: LuaTable) => {
if (tbl) {
if (!tbl.has("year")) {
throw new Error("time(): year is required");
}
if (!tbl.has("month")) {
throw new Error("time(): month is required");
}
if (!tbl.has("day")) {
throw new Error("time(): day is required");
}
const year = tbl.get("year");
const month = tbl.get("month");
const day = tbl.get("day");
const hour = tbl.get("hour") ?? 12;
const min = tbl.get("min") ?? 0;
const sec = tbl.get("sec") ?? 0;
const date = new Date(year, month - 1, day, hour, min, sec);
return Math.floor(date.getTime() / 1000);
}
if (!tbl.has("month")) {
throw new Error("time(): month is required");
}
if (!tbl.has("day")) {
throw new Error("time(): day is required");
}
const year = tbl.get("year");
const month = tbl.get("month");
const day = tbl.get("day");
const hour = tbl.get("hour") ?? 12;
const min = tbl.get("min") ?? 0;
const sec = tbl.get("sec") ?? 0;
const date = new Date(year, month - 1, day, hour, min, sec);
return Math.floor(date.getTime() / 1000);
}
return Math.floor(Date.now() / 1000);
}),
return Math.floor(Date.now() / 1000);
},
{
kind: "builtin",
description:
"Returns the current Unix timestamp or one built from a local date table.",
signatures: ["os.time(): integer", "os.time(dateTable): integer"],
parameters: [
{
name: "dateTable",
type: "table",
description:
"Local date fields `year`, `month`, `day`, and optional `hour`, `min`, and `sec`.",
optional: true,
},
],
returns: [
{ type: "integer", description: "Seconds since the Unix epoch." },
],
examples: [
{
code: "local timestamp = os.time({year = 2020, month = 1, day = 1})",
},
],
},
),
// Returns the difference, from time `t1` to time `t2` in seconds
// In POSIX and some other systems, this value is exactly $t2-t1$.
difftime: new LuaBuiltinFunction((_sf, t2: number, t1: number): number => {
return t2 - t1;
}),
difftime: new LuaBuiltinFunction(
(_sf, t2: number, t1: number): number => {
return t2 - t1;
},
{
kind: "builtin",
description:
"Returns the difference in seconds from timestamp `t1` to `t2`.",
parameters: [
{ name: "t2", type: "number" },
{ name: "t1", type: "number" },
],
returns: [
{ type: "number", description: "The value `t2 - t1` in seconds." },
],
},
),
// Returns a string or a table containing date and time, formatted
// according to the given string format.
@@ -336,29 +375,69 @@ export const osApi = new LuaTable({
// Otherwise, format specifiers follow ISO C `strftime`.
//
// If format is absent, it defaults to `%c`.
date: new LuaBuiltinFunction((_sf, format?: string, timestamp?: number) => {
let fmt = format ?? "%c";
let utc = false;
date: new LuaBuiltinFunction(
(_sf, format?: string, timestamp?: number) => {
let fmt = format ?? "%c";
let utc = false;
if (fmt.startsWith("!")) {
utc = true;
fmt = fmt.slice(1);
}
if (fmt.startsWith("!")) {
utc = true;
fmt = fmt.slice(1);
}
const d =
timestamp !== undefined && timestamp !== null
? new Date(timestamp * 1000)
: new Date();
const d =
timestamp !== undefined && timestamp !== null
? new Date(timestamp * 1000)
: new Date();
if (fmt === "*t") {
return dateTable(d, utc);
}
if (fmt === "*t") {
return dateTable(d, utc);
}
return luaFormatTime(fmt, d, utc);
}),
return luaFormatTime(fmt, d, utc);
},
{
kind: "builtin",
description:
"Formats a timestamp as a date string or date table, optionally in UTC.",
parameters: [
{
name: "format",
type: "string",
description:
"`strftime`-style format, `*t` for a table, and optional leading `!` for UTC.",
optional: true,
},
{
name: "timestamp",
type: "number",
description: "Unix timestamp; defaults to the current time.",
optional: true,
},
],
returns: [
{ type: "string|table", description: "Formatted date or date fields." },
],
examples: [
{ code: 'print(os.date("%Y-%m-%d"))\nlocal utc = os.date("!*t")' },
],
},
),
// Returns an approximation of CPU time used by the program in seconds.
clock: new LuaBuiltinFunction((_sf): number => {
return performance.now() / 1000.0;
}),
clock: new LuaBuiltinFunction(
(_sf): number => {
return performance.now() / 1000.0;
},
{
kind: "builtin",
description: "Returns a high-resolution elapsed time value in seconds.",
returns: [
{
type: "number",
description: "Browser performance timer in seconds.",
},
],
},
),
});
+325 -99
View File
@@ -28,20 +28,39 @@ function capturesToLua(caps: CaptureResult[]): any {
}
export const stringApi = new LuaTable({
byte: new LuaBuiltinFunction((_sf, s: string, i?: number, j?: number) => {
i = i ?? 1;
j = j ?? i;
if (j > s.length) j = s.length;
if (i < 1) i = 1;
const result = [];
for (let k = i; k <= j; k++) {
result.push(s.charCodeAt(k - 1));
}
return new LuaMultiRes(result);
}),
char: new LuaBuiltinFunction((_sf, ...args: number[]) => {
return String.fromCharCode(...args);
}),
byte: new LuaBuiltinFunction(
(_sf, s: string, i?: number, j?: number) => {
i = i ?? 1;
j = j ?? i;
if (j > s.length) j = s.length;
if (i < 1) i = 1;
const result = [];
for (let k = i; k <= j; k++) {
result.push(s.charCodeAt(k - 1));
}
return new LuaMultiRes(result);
},
{
kind: "builtin",
description:
"Returns the numeric character codes in the inclusive range from `i` to `j`.",
parameters: [
{ name: "s", type: "string" },
{ name: "i", type: "integer", optional: true },
{ name: "j", type: "integer", optional: true },
],
returns: [{ type: "integer", description: "One result per character." }],
},
),
char: new LuaBuiltinFunction(
(_sf, ...args: number[]) => String.fromCharCode(...args),
{
kind: "builtin",
description: "Creates a string from numeric character codes.",
signatures: ["string.char(...): string"],
returns: [{ type: "string" }],
},
),
find: new LuaBuiltinFunction(
(_sf, s: string, pattern: string, init = 1, plain = false) => {
const r = patternFind(s, pattern, init, plain);
@@ -52,13 +71,46 @@ export const stringApi = new LuaTable({
}
return new LuaMultiRes(result);
},
{
kind: "builtin",
description:
"Finds the first Lua-pattern match and returns its bounds followed by captures.",
parameters: [
{ name: "s", type: "string" },
{ name: "pattern", type: "string" },
{ name: "init", type: "integer", optional: true },
{ name: "plain", type: "boolean", optional: true },
],
returns: [
{ type: "integer|nil", description: "Start index or `nil`." },
{ type: "integer", description: "End index." },
],
},
),
format: new LuaBuiltinFunction(
(_sf, format: string, ...args: any[]) => {
for (let i = 0; i < args.length; i++) {
args[i] = untagNumber(args[i]);
}
return luaFormat(format, ...args);
},
{
kind: "builtin",
description: "Formats values according to a C-style format string.",
signatures: ["string.format(format, ...): string"],
parameters: [
{ name: "format", type: "string" },
{
name: "...",
description: "Values consumed by conversion specifiers.",
},
],
returns: [{ type: "string" }],
examples: [
{ code: 'print(string.format("Name: %s, score: %.1f", "Ada", 9.5))' },
],
},
),
format: new LuaBuiltinFunction((_sf, format: string, ...args: any[]) => {
for (let i = 0; i < args.length; i++) {
args[i] = untagNumber(args[i]);
}
return luaFormat(format, ...args);
}),
gmatch: new LuaBuiltinFunction(
(_sf, s: string, pattern: string, init = 1) => {
const iter = patternGmatch(s, pattern, init);
@@ -68,6 +120,22 @@ export const stringApi = new LuaTable({
return capturesToLua(caps);
};
},
{
kind: "builtin",
description:
"Returns an iterator over successive Lua-pattern matches and captures.",
parameters: [
{ name: "s", type: "string" },
{ name: "pattern", type: "string" },
{ name: "init", type: "integer", optional: true },
],
returns: [{ type: "function", description: "Match iterator." }],
examples: [
{
code: 'for word in string.gmatch("hello world", "%w+") do\n print(word)\nend',
},
],
},
),
gsub: new LuaBuiltinFunction(
async (sf, s: string, pattern: string, repl: any, n?: number) => {
@@ -103,61 +171,156 @@ export const stringApi = new LuaTable({
const [result, count] = await patternGsub(s, pattern, callbacks, n);
return new LuaMultiRes([result, count]);
},
{
kind: "builtin",
description:
"Replaces Lua-pattern matches using a string, table, or function replacement.",
parameters: [
{ name: "s", type: "string" },
{ name: "pattern", type: "string" },
{ name: "replacement", type: "string|table|function" },
{ name: "n", type: "integer", optional: true },
],
returns: [
{ type: "string", description: "Result string." },
{ type: "integer", description: "Number of replacements." },
],
examples: [
{
code: 'local result, count = string.gsub("hello hello", "hello", "hi", 1)\nprint(result, count) -- hi hello 1',
},
],
},
),
len: new LuaBuiltinFunction((_sf, s: string) => {
return s.length;
}),
lower: new LuaBuiltinFunction((_sf, s: string) => {
return luaToString(s.toLowerCase());
}),
upper: new LuaBuiltinFunction((_sf, s: string) => {
return luaToString(s.toUpperCase());
}),
match: new LuaBuiltinFunction((_sf, s: string, pattern: string, init = 1) => {
const caps = patternMatch(s, pattern, init);
if (!caps) return null;
return capturesToLua(caps);
}),
rep: new LuaBuiltinFunction((_sf, s: string, n: number, sep?: string) => {
if (n <= 0) return "";
sep = sep ?? "";
const parts: string[] = [];
for (let i = 0; i < n; i++) {
parts.push(s);
}
return parts.join(sep);
}),
reverse: new LuaBuiltinFunction((_sf, s: string) => {
return s.split("").reverse().join("");
}),
sub: new LuaBuiltinFunction((_sf, s: string, i: number, j?: number) => {
const len = s.length;
let start: number;
if (i > 0) {
start = i;
} else if (i < -len) {
start = 1;
} else {
start = i === 0 ? 1 : len + i + 1;
}
let end: number;
if (j === undefined || j === null || j > len) {
end = len;
} else if (j >= 0) {
end = j;
} else if (j < -len) {
end = 0;
} else {
end = len + j + 1;
}
if (start <= end) {
return s.substring(start - 1, end);
}
return "";
len: new LuaBuiltinFunction((_sf, s: string) => s.length, {
kind: "builtin",
description: "Returns the length of a string.",
parameters: [{ name: "s", type: "string" }],
returns: [{ type: "integer" }],
}),
lower: new LuaBuiltinFunction(
(_sf, s: string) => luaToString(s.toLowerCase()),
{
kind: "builtin",
description: "Returns a copy of a string converted to lowercase.",
parameters: [{ name: "s", type: "string" }],
returns: [{ type: "string" }],
},
),
upper: new LuaBuiltinFunction(
(_sf, s: string) => luaToString(s.toUpperCase()),
{
kind: "builtin",
description: "Returns a copy of a string converted to uppercase.",
parameters: [{ name: "s", type: "string" }],
returns: [{ type: "string" }],
},
),
match: new LuaBuiltinFunction(
(_sf, s: string, pattern: string, init = 1) => {
const caps = patternMatch(s, pattern, init);
if (!caps) return null;
return capturesToLua(caps);
},
{
kind: "builtin",
description:
"Returns captures from the first Lua-pattern match, or `nil` when none is found.",
parameters: [
{ name: "s", type: "string" },
{ name: "pattern", type: "string" },
{ name: "init", type: "integer", optional: true },
],
returns: [{ description: "Pattern captures, whole match, or `nil`." }],
examples: [
{ code: 'local year, month = string.match("2024-03", "(%d+)%-(%d+)")' },
],
},
),
rep: new LuaBuiltinFunction(
(_sf, s: string, n: number, sep?: string) => {
if (n <= 0) return "";
sep = sep ?? "";
const parts: string[] = [];
for (let i = 0; i < n; i++) {
parts.push(s);
}
return parts.join(sep);
},
{
kind: "builtin",
description:
"Returns `n` copies of a string joined by an optional separator.",
parameters: [
{ name: "s", type: "string" },
{ name: "n", type: "integer" },
{ name: "sep", type: "string", optional: true },
],
returns: [{ type: "string" }],
},
),
reverse: new LuaBuiltinFunction(
(_sf, s: string) => s.split("").reverse().join(""),
{
kind: "builtin",
description: "Returns a string with its characters in reverse order.",
parameters: [{ name: "s", type: "string" }],
returns: [{ type: "string" }],
},
),
sub: new LuaBuiltinFunction(
(_sf, s: string, i: number, j?: number) => {
const len = s.length;
let start: number;
if (i > 0) {
start = i;
} else if (i < -len) {
start = 1;
} else {
start = i === 0 ? 1 : len + i + 1;
}
let end: number;
if (j === undefined || j === null || j > len) {
end = len;
} else if (j >= 0) {
end = j;
} else if (j < -len) {
end = 0;
} else {
end = len + j + 1;
}
if (start <= end) {
return s.substring(start - 1, end);
}
return "";
},
{
kind: "builtin",
description:
"Returns the substring from inclusive index `i` through `j`, supporting negative indices.",
parameters: [
{ name: "s", type: "string" },
{ name: "i", type: "integer" },
{ name: "j", type: "integer", optional: true },
],
returns: [{ type: "string" }],
},
),
split: new LuaBuiltinFunction((_sf, s: string, sep: string) => {
return s.split(sep);
split: new LuaBuiltinFunction((_sf, s: string, sep: string) => s.split(sep), {
kind: "builtin",
description:
"Splits a string on a literal separator and returns the substrings.",
parameters: [
{ name: "s", type: "string" },
{ name: "sep", type: "string" },
],
returns: [{ type: "table" }],
examples: [
{
code: 'for part in each(string.split("a,b,c", ",")) do\n print(part)\nend',
},
],
}),
pack: strPackFn,
@@ -165,34 +328,97 @@ export const stringApi = new LuaTable({
packsize: strPackSizeFn,
// Non-standard extensions
startsWith: new LuaBuiltinFunction((_sf, s: string, prefix: string) => {
return s.startsWith(prefix);
startsWith: new LuaBuiltinFunction(
(_sf, s: string, prefix: string) => s.startsWith(prefix),
{
kind: "builtin",
description: "Returns whether a string starts with a literal prefix.",
parameters: [
{ name: "s", type: "string" },
{ name: "prefix", type: "string" },
],
returns: [{ type: "boolean" }],
},
),
endsWith: new LuaBuiltinFunction(
(_sf, s: string, suffix: string) => s.endsWith(suffix),
{
kind: "builtin",
description: "Returns whether a string ends with a literal suffix.",
parameters: [
{ name: "s", type: "string" },
{ name: "suffix", type: "string" },
],
returns: [{ type: "boolean" }],
},
),
trim: new LuaBuiltinFunction((_sf, s: string) => s.trim(), {
kind: "builtin",
description: "Removes whitespace from both ends of a string.",
parameters: [{ name: "s", type: "string" }],
returns: [{ type: "string" }],
}),
endsWith: new LuaBuiltinFunction((_sf, s: string, suffix: string) => {
return s.endsWith(suffix);
trimStart: new LuaBuiltinFunction((_sf, s: string) => s.trimStart(), {
kind: "builtin",
description: "Removes whitespace from the beginning of a string.",
parameters: [{ name: "s", type: "string" }],
returns: [{ type: "string" }],
}),
trim: new LuaBuiltinFunction((_sf, s: string) => {
return s.trim();
}),
trimStart: new LuaBuiltinFunction((_sf, s: string) => {
return s.trimStart();
}),
trimEnd: new LuaBuiltinFunction((_sf, s: string) => {
return s.trimEnd();
}),
matchRegex: new LuaBuiltinFunction((_sf, s: string, pattern: string) => {
const regex = new RegExp(pattern);
const result = s.match(regex);
return jsToLuaValue(result);
}),
matchRegexAll: new LuaBuiltinFunction((_sf, s: string, pattern: string) => {
const regex = new RegExp(pattern, "g");
return () => {
const match = regex.exec(s);
if (!match) {
return;
}
return jsToLuaValue(match);
};
trimEnd: new LuaBuiltinFunction((_sf, s: string) => s.trimEnd(), {
kind: "builtin",
description: "Removes whitespace from the end of a string.",
parameters: [{ name: "s", type: "string" }],
returns: [{ type: "string" }],
}),
matchRegex: new LuaBuiltinFunction(
(_sf, s: string, pattern: string) => {
const regex = new RegExp(pattern);
const result = s.match(regex);
return jsToLuaValue(result);
},
{
kind: "builtin",
description:
"Matches a string with a JavaScript regular expression and returns the match array.",
parameters: [
{ name: "s", type: "string" },
{ name: "pattern", type: "string" },
],
returns: [{ type: "table|nil" }],
examples: [
{
code: 'local match = string.matchRegex("hello123", "([a-z]+)([0-9]+)")\nprint(match[1], match[2], match[3])',
},
],
},
),
matchRegexAll: new LuaBuiltinFunction(
(_sf, s: string, pattern: string) => {
const regex = new RegExp(pattern, "g");
return () => {
const match = regex.exec(s);
if (!match) {
return;
}
return jsToLuaValue(match);
};
},
{
kind: "builtin",
description:
"Returns an iterator over all JavaScript regular-expression matches.",
parameters: [
{ name: "s", type: "string" },
{ name: "pattern", type: "string" },
],
returns: [
{ type: "function", description: "Iterator yielding match arrays." },
],
examples: [
{
code: 'for match in string.matchRegexAll("a1b2", "([a-z])([0-9])") do\n print(match[1], match[2], match[3])\nend',
},
],
},
),
});
+62 -14
View File
@@ -364,6 +364,17 @@ export const strPackFn = new LuaBuiltinFunction(
for (let i = 0; i < out.length; i++) result += String.fromCharCode(out[i]);
return result;
},
{
kind: "builtin",
description:
"Packs values into a binary string according to a Lua 5.4 format string.",
signatures: ["string.pack(format, ...): string"],
parameters: [
{ name: "format", type: "string", description: "Binary packing format." },
{ name: "...", description: "Values consumed by the format options." },
],
returns: [{ type: "string", description: "Packed binary string." }],
},
);
export const strUnpackFn = new LuaBuiltinFunction(
@@ -468,23 +479,60 @@ export const strUnpackFn = new LuaBuiltinFunction(
results.push(pos + 1);
return new LuaMultiRes(results);
},
{
kind: "builtin",
description:
"Unpacks values from a binary string according to a Lua 5.4 format string.",
parameters: [
{
name: "format",
type: "string",
description: "Binary unpacking format.",
},
{ name: "data", type: "string", description: "Packed binary string." },
{
name: "init",
type: "integer",
description: "One-based starting position.",
optional: true,
},
],
returns: [
{ description: "Unpacked values followed by the next unread position." },
],
},
);
export const strPackSizeFn = new LuaBuiltinFunction((_sf, fmt: string) => {
const h = makeHeader();
let totalsize = 0;
let pos = 0;
export const strPackSizeFn = new LuaBuiltinFunction(
(_sf, fmt: string) => {
const h = makeHeader();
let totalsize = 0;
let pos = 0;
while (pos < fmt.length) {
let opt: ParsedOption;
[opt, pos] = getDetails(fmt, pos, h, totalsize);
while (pos < fmt.length) {
let opt: ParsedOption;
[opt, pos] = getDetails(fmt, pos, h, totalsize);
if (opt.opt === "string" || opt.opt === "zstr") {
throw new LuaRuntimeError("variable-length format", _sf);
if (opt.opt === "string" || opt.opt === "zstr") {
throw new LuaRuntimeError("variable-length format", _sf);
}
totalsize += opt.ntoalign + opt.size;
}
totalsize += opt.ntoalign + opt.size;
}
return totalsize;
});
return totalsize;
},
{
kind: "builtin",
description:
"Returns the byte size of a fixed-length Lua 5.4 packing format.",
parameters: [
{
name: "format",
type: "string",
description: "Fixed-length binary packing format.",
},
],
returns: [{ type: "integer", description: "Packed byte count." }],
},
);
+195 -15
View File
@@ -108,6 +108,18 @@ export const tableApi = new LuaTable({
}
return out.join(sep);
},
{
kind: "builtin",
description:
"Concatenates table elements from `i` through `j` using an optional separator.",
parameters: [
{ name: "table", type: "table" },
{ name: "sep", type: "string", optional: true },
{ name: "i", type: "integer", optional: true },
{ name: "j", type: "integer", optional: true },
],
returns: [{ type: "string" }],
},
),
/**
@@ -159,6 +171,32 @@ export const tableApi = new LuaTable({
await luaSet(tbl, pos, v, sf);
},
{
kind: "builtin",
description:
"Inserts a value at a position, shifting later elements, or appends it when no position is supplied.",
signatures: [
"table.insert(table, value)",
"table.insert(table, pos, value)",
],
parameters: [
{ name: "table", type: "table" },
{
name: "posOrValue",
description: "Insertion position or appended value.",
},
{
name: "value",
description: "Value for positional insertion.",
optional: true,
},
],
examples: [
{
code: 'local fruits = {"apple", "orange"}\ntable.insert(fruits, 2, "banana")\nprint(table.concat(fruits, ", "))',
},
],
},
),
/**
@@ -202,6 +240,21 @@ export const tableApi = new LuaTable({
return v;
},
{
kind: "builtin",
description:
"Removes and returns an element, shifting later elements down.",
parameters: [
{ name: "table", type: "table" },
{
name: "pos",
type: "integer",
description: "Position; defaults to the last element.",
optional: true,
},
],
returns: [{ description: "Removed value." }],
},
),
/**
@@ -252,6 +305,24 @@ export const tableApi = new LuaTable({
return a2;
},
{
kind: "builtin",
description:
"Moves an inclusive element range to a destination table while handling overlaps.",
parameters: [
{ name: "a1", type: "table", description: "Source table." },
{ name: "f", type: "integer", description: "First source index." },
{ name: "e", type: "integer", description: "Last source index." },
{ name: "t", type: "integer", description: "Destination start index." },
{
name: "a2",
type: "table",
description: "Destination table; defaults to `a1`.",
optional: true,
},
],
returns: [{ type: "table", description: "Destination table." }],
},
),
/**
@@ -311,6 +382,23 @@ export const tableApi = new LuaTable({
return tbl;
},
{
kind: "builtin",
description:
"Sorts a table in place using ascending order or an optional comparison function.",
parameters: [
{ name: "table", type: "table" },
{ name: "comp", type: "function", optional: true },
],
returns: [
{ type: "table", description: "The sorted table in Space Lua." },
],
examples: [
{
code: "local numbers = {3, 1, 2}\ntable.sort(numbers, function(a, b) return a > b end)",
},
],
},
),
/**
@@ -319,12 +407,21 @@ export const tableApi = new LuaTable({
* @param tbl - The table to get the keys from.
* @returns The keys of the table.
*/
keys: new LuaBuiltinFunction((_sf, tbl: LuaTable | LuaEnv | any) => {
if (tbl.keys) {
return tbl.keys();
}
return Object.keys(tbl);
}),
keys: new LuaBuiltinFunction(
(_sf, tbl: LuaTable | LuaEnv | any) => {
if (tbl.keys) {
return tbl.keys();
}
return Object.keys(tbl);
},
{
kind: "builtin",
description:
"Returns an array containing all keys of a table or JavaScript object.",
parameters: [{ name: "table", type: "table" }],
returns: [{ type: "table", description: "Array of keys." }],
},
),
/**
* Checks if a table (used as an array) contains a value.
@@ -355,6 +452,16 @@ export const tableApi = new LuaTable({
sf,
);
},
{
kind: "builtin",
description:
"Returns whether any table value is Lua-equal to a requested value.",
parameters: [
{ name: "table", type: "table" },
{ name: "value", description: "Value to find." },
],
returns: [{ type: "boolean" }],
},
),
/**
@@ -383,21 +490,57 @@ export const tableApi = new LuaTable({
}
return resultTable;
},
{
kind: "builtin",
description: "Copies selected keys from a table into a new table.",
signatures: [
"table.select(table, ...keys): table",
"table.select(table, keys): table",
],
parameters: [
{ name: "table", type: "table" },
{
name: "keys",
description: "Individual keys or one array-like table of keys.",
},
],
returns: [{ type: "table" }],
examples: [
{
code: '${query[[\n from p = index.pages()\n limit 3\n select table.select(p, "name", "lastModified")\n]]}',
language: "markdown",
},
],
},
),
/**
* Returns a new table with all arguments stored in keys 1, 2, ..., n
* and t.n = n (the total number of arguments).
*/
pack: new LuaBuiltinFunction(async (sf, ...args: any[]) => {
const tbl = new LuaTable();
const n = args.length;
for (let i = 0; i < n; i++) {
await luaSet(tbl, i + 1, args[i], sf);
}
void tbl.rawSet("n", n);
return tbl;
}),
pack: new LuaBuiltinFunction(
async (sf, ...args: any[]) => {
const tbl = new LuaTable();
const n = args.length;
for (let i = 0; i < n; i++) {
await luaSet(tbl, i + 1, args[i], sf);
}
void tbl.rawSet("n", n);
return tbl;
},
{
kind: "builtin",
description:
"Packs all arguments into a table with a count stored in field `n`.",
signatures: ["table.pack(...): table"],
returns: [
{
type: "table",
description: "Arguments at integer keys plus field `n`.",
},
],
},
),
/**
* Returns all values t[i], t[i+1], ..., t[j].
@@ -425,6 +568,20 @@ export const tableApi = new LuaTable({
}
return new LuaMultiRes(result);
},
{
kind: "builtin",
description:
"Returns the table values from index `i` through `j` as separate results.",
parameters: [
{ name: "table", type: "table" },
{ name: "i", type: "integer", optional: true },
{ name: "j", type: "integer", optional: true },
],
returns: [{ description: "One result per selected element." }],
examples: [
{ code: 'local second, third = table.unpack({"a", "b", "c"}, 2, 3)' },
],
},
),
// Non-standard Lua functions
@@ -457,5 +614,28 @@ export const tableApi = new LuaTable({
}
return null;
},
{
kind: "builtin",
description:
"Finds the first array element accepted by a predicate and returns its index and value.",
parameters: [
{ name: "table", type: "table" },
{
name: "criteriaFn",
type: "function",
description: "Predicate called with each value.",
},
{ name: "fromIndex", type: "integer", optional: true },
],
returns: [
{ type: "integer|nil", description: "Matching index or `nil`." },
{ description: "Matching value." },
],
examples: [
{
code: "local index, value = table.find({1, 2, 3, 4}, function(n) return n % 2 == 0 end)",
},
],
},
),
});