diff --git a/client/space_lua/runtime.test.ts b/client/space_lua/runtime.test.ts index a37683fb..dd35929a 100644 --- a/client/space_lua/runtime.test.ts +++ b/client/space_lua/runtime.test.ts @@ -1,8 +1,10 @@ import { expect, test } from "vitest"; import { jsToLuaValue, + LuaBuiltinFunction, luaLen, LuaMultiRes, + LuaNativeJSFunction, LuaStackFrame, luaToString, } from "./runtime.ts"; @@ -52,3 +54,32 @@ test("Test Lua Rutime", async () => { circular.self = circular; expect(await luaToString(circular)).toEqual("{self = }"); }); + +test("Lua functions accept documented definition objects", () => { + const builtin = new LuaBuiltinFunction({ + callback: (_sf, value) => value, + documentation: { + description: "Returns the provided value.", + }, + }); + expect(builtin.call(LuaStackFrame.lostFrame, "value")).toBe("value"); + expect(builtin.info).toEqual({ + kind: "builtin", + description: "Returns the provided value.", + }); + + const native = new LuaNativeJSFunction({ + callback: (value) => value, + documentation: { + kind: "syscall", + name: "demo.echo", + description: "Returns the provided value.", + }, + }); + expect(native.call(LuaStackFrame.lostFrame, "value")).toBe("value"); + expect(native.info).toEqual({ + kind: "syscall", + name: "demo.echo", + description: "Returns the provided value.", + }); +}); diff --git a/client/space_lua/runtime.ts b/client/space_lua/runtime.ts index d0952af6..21bac0d3 100644 --- a/client/space_lua/runtime.ts +++ b/client/space_lua/runtime.ts @@ -5,7 +5,10 @@ import { isPromise, rpAll } from "./rp.ts"; import { isNegativeZero, isTaggedFloat } from "./numeric.ts"; import { isSqlNull } from "./sliq_null.ts"; import { luaFormat } from "./stdlib/format.ts"; -import type { LuaFunctionInfo } from "../../plug-api/types/index.ts"; +import type { + LuaFunctionDocumentation, + LuaFunctionInfo, +} from "../../plug-api/types/index.ts"; export type LuaType = | "nil" @@ -21,6 +24,14 @@ export type LuaType = export type LuaValue = any; export type JSValue = any; +type LuaFunctionDefinitionDocumentation = LuaFunctionDocumentation & + Partial>; + +export type LuaFunctionDefinition = { + callback: Callback; + documentation?: LuaFunctionDefinitionDocumentation; +}; + export interface ILuaFunction { call(sf: LuaStackFrame, ...args: LuaValue[]): Promise | LuaValue; @@ -578,10 +589,25 @@ function mapFunctionReturnValue(values: any[]): any { } export class LuaNativeJSFunction implements ILuaFunction { + readonly fn: (...args: JSValue[]) => JSValue; + public info: LuaFunctionInfo; + constructor( - readonly fn: (...args: JSValue[]) => JSValue, - public info?: LuaFunctionInfo, - ) {} + definition: + | ((...args: JSValue[]) => JSValue) + | LuaFunctionDefinition<(...args: JSValue[]) => JSValue>, + ) { + if (typeof definition === "function") { + this.fn = definition; + this.info = { kind: "builtin" }; + } else { + this.fn = definition.callback; + this.info = { + kind: "builtin", + ...definition.documentation, + }; + } + } // Performs automatic conversion between Lua and JS values for arguments, but not for return values call(sf: LuaStackFrame, ...args: LuaValue[]): Promise | LuaValue { @@ -611,10 +637,27 @@ export class LuaNativeJSFunction implements ILuaFunction { } export class LuaBuiltinFunction implements ILuaFunction { + readonly fn: (sf: LuaStackFrame, ...args: LuaValue[]) => LuaValue; + public info: LuaFunctionInfo; + constructor( - readonly fn: (sf: LuaStackFrame, ...args: LuaValue[]) => LuaValue, - public info: LuaFunctionInfo = { kind: "builtin" }, - ) {} + definition: + | ((sf: LuaStackFrame, ...args: LuaValue[]) => LuaValue) + | LuaFunctionDefinition< + (sf: LuaStackFrame, ...args: LuaValue[]) => LuaValue + >, + ) { + if (typeof definition === "function") { + this.fn = definition; + this.info = { kind: "builtin" }; + } else { + this.fn = definition.callback; + this.info = { + kind: "builtin", + ...definition.documentation, + }; + } + } call(sf: LuaStackFrame, ...args: LuaValue[]): Promise | LuaValue { // _CTX is already available via the stack frame diff --git a/client/space_lua/stdlib.ts b/client/space_lua/stdlib.ts index efad38fb..2f90aea1 100644 --- a/client/space_lua/stdlib.ts +++ b/client/space_lua/stdlib.ts @@ -37,31 +37,29 @@ import { isTaggedFloat, makeLuaFloat } from "./numeric.ts"; import { isPromise } from "./rp.ts"; import { isSqlNull } from "./sliq_null.ts"; -const printFunction = new LuaBuiltinFunction( - async (_sf, ...args) => { +const printFunction = new LuaBuiltinFunction({ + callback: async (_sf, ...args) => { console.log( "[Lua]", ...(await Promise.all(args.map((v) => luaToString(v)))), ); }, - { - kind: "builtin", + documentation: { description: "Prints string representations of its arguments to the runtime log.", signatures: ["print(...)"], parameters: [{ name: "...", description: "Values to print." }], examples: [{ code: 'print("Hello, world!")' }], }, -); +}); -const assertFunction = new LuaBuiltinFunction( - async (sf, value: any, message?: string) => { +const assertFunction = new LuaBuiltinFunction({ + callback: async (sf, value: any, message?: string) => { if (!(await value)) { throw new LuaRuntimeError(`Assertion failed: ${message}`, sf); } }, - { - kind: "builtin", + documentation: { description: "Raises an error when a value is falsy; otherwise completes successfully.", parameters: [ @@ -75,10 +73,10 @@ const assertFunction = new LuaBuiltinFunction( ], examples: [{ code: 'assert(user ~= nil, "user is required")' }], }, -); +}); -const ipairsFunction = new LuaBuiltinFunction( - (sf, t: LuaTable | any[]) => { +const ipairsFunction = new LuaBuiltinFunction({ + callback: (sf, t: LuaTable | any[]) => { let i = 0; return async () => { @@ -92,8 +90,7 @@ const ipairsFunction = new LuaBuiltinFunction( return new LuaMultiRes([i, v]); }; }, - { - kind: "builtin", + documentation: { description: "Returns an iterator over consecutive integer keys starting at 1 and stopping at the first `nil`.", parameters: [{ name: "table", type: "table" }], @@ -106,10 +103,10 @@ const ipairsFunction = new LuaBuiltinFunction( }, ], }, -); +}); -const pairsFunction = new LuaBuiltinFunction( - (sf, t: LuaTable | any[] | Record) => { +const pairsFunction = new LuaBuiltinFunction({ + callback: (sf, t: LuaTable | any[] | Record) => { // Respect `__pairs` metamethod for Lua tables if (isLuaTable(t)) { const mt = (t as any).metatable as LuaTable | null | undefined; @@ -149,8 +146,7 @@ const pairsFunction = new LuaBuiltinFunction( // Must return (iter, state, control) for generic for return new LuaMultiRes([iter, t, null]); }, - { - kind: "builtin", + documentation: { description: "Returns an iterator over all table key-value pairs, respecting `__pairs`.", parameters: [{ name: "table", type: "table" }], @@ -166,10 +162,10 @@ const pairsFunction = new LuaBuiltinFunction( }, ], }, -); +}); -export const eachFunction = new LuaBuiltinFunction( - (sf, ar: LuaTable | any[]) => { +export const eachFunction = new LuaBuiltinFunction({ + callback: (sf, ar: LuaTable | any[]) => { let i = 1; const length = (ar as any).length; return async () => { @@ -181,8 +177,7 @@ export const eachFunction = new LuaBuiltinFunction( return result; }; }, - { - kind: "builtin", + documentation: { description: "Returns a Space Lua iterator over array-like values without yielding indices.", parameters: [{ name: "table", type: "table" }], @@ -193,24 +188,23 @@ export const eachFunction = new LuaBuiltinFunction( }, ], }, -); +}); -const typeFunction = new LuaBuiltinFunction( - (_sf, value: LuaValue): string | Promise => { +const typeFunction = new LuaBuiltinFunction({ + callback: (_sf, value: LuaValue): string | Promise => { return luaTypeOf(value); }, - { - kind: "builtin", + documentation: { description: "Returns the Lua type name of a value.", parameters: [{ name: "value" }], returns: [{ type: "string" }], }, -); +}); // tostring() checks `__tostring` metamethod first (with live SF), then // falls back to the default `luaToString` representation. -const tostringFunction = new LuaBuiltinFunction( - (sf, value: any): string | Promise => { +const tostringFunction = new LuaBuiltinFunction({ + callback: (sf, value: any): string | Promise => { const mt = getMetatable(value, sf); if (mt) { const mm = mt.rawGet("__tostring"); @@ -232,17 +226,16 @@ const tostringFunction = new LuaBuiltinFunction( } return luaToString(value); }, - { - kind: "builtin", + documentation: { description: "Converts a value to a string, respecting its `__tostring` metamethod.", parameters: [{ name: "value" }], returns: [{ type: "string" }], }, -); +}); -const tonumberFunction = new LuaBuiltinFunction( - (sf, value: LuaValue, base?: number) => { +const tonumberFunction = new LuaBuiltinFunction({ + callback: (sf, value: LuaValue, base?: number) => { if (base !== undefined) { if (!(typeof base === "number" && base >= 2 && base <= 36)) { throw new LuaRuntimeError( @@ -274,8 +267,7 @@ const tonumberFunction = new LuaBuiltinFunction( return result.value; }, - { - kind: "builtin", + documentation: { description: "Converts a number or numeric string to a Lua number, optionally in a base from 2 through 36.", signatures: [ @@ -289,18 +281,17 @@ const tonumberFunction = new LuaBuiltinFunction( returns: [{ type: "number|nil" }], examples: [{ code: 'print(tonumber("2a", 16)) -- 42' }], }, -); +}); -const errorFunction = new LuaBuiltinFunction( - (sf, message: string) => { +const errorFunction = new LuaBuiltinFunction({ + callback: (sf, message: string) => { throw new LuaRuntimeError(message, sf); }, - { - kind: "builtin", + documentation: { description: "Raises a Lua runtime error with the supplied message.", parameters: [{ name: "message", type: "string" }], }, -); +}); async function pcallBoundary( sf: LuaStackFrame, @@ -329,8 +320,8 @@ async function pcallBoundary( } } -const pcallFunction = new LuaBuiltinFunction( - async (sf, fn: ILuaFunction, ...args) => { +const pcallFunction = new LuaBuiltinFunction({ + callback: async (sf, fn: ILuaFunction, ...args) => { // To-be-closed variables must be closed when unwinding to the // protected call boundary. Space Lua uses a per-thread close // stack, so we snapshot its length and close anything pushed @@ -349,8 +340,7 @@ const pcallFunction = new LuaBuiltinFunction( } return new LuaMultiRes([false, res.message]); }, - { - kind: "builtin", + documentation: { description: "Calls a function in protected mode and returns a success flag followed by results or an error message.", signatures: ["pcall(function, ...): boolean, ..."], @@ -366,10 +356,15 @@ const pcallFunction = new LuaBuiltinFunction( { code: "local ok, result = pcall(function() return mightFail() end)" }, ], }, -); +}); -const xpcallFunction = new LuaBuiltinFunction( - async (sf, fn: ILuaFunction, errorHandler: ILuaFunction, ...args) => { +const xpcallFunction = new LuaBuiltinFunction({ + callback: async ( + sf, + fn: ILuaFunction, + errorHandler: ILuaFunction, + ...args + ) => { // Same semantic as `pcall` (see comments there) const res = await pcallBoundary(sf, fn, args); if (res.ok) { @@ -379,8 +374,7 @@ const xpcallFunction = new LuaBuiltinFunction( const outVals = hr instanceof LuaMultiRes ? hr.flatten().values : [hr]; return new LuaMultiRes([false, ...outVals]); }, - { - kind: "builtin", + documentation: { description: "Calls a function in protected mode and transforms any error with an error handler.", signatures: ["xpcall(function, errorHandler, ...): boolean, ..."], @@ -399,18 +393,17 @@ const xpcallFunction = new LuaBuiltinFunction( }, ], }, -); +}); -const setmetatableFunction = new LuaBuiltinFunction( - (sf, table: LuaTable, metatable: LuaTable) => { +const setmetatableFunction = new LuaBuiltinFunction({ + callback: (sf, table: LuaTable, metatable: LuaTable) => { if (!metatable) { throw new LuaRuntimeError("metatable cannot be set to nil", sf); } table.metatable = metatable; return table; }, - { - kind: "builtin", + documentation: { description: "Sets a table's metatable and returns the table.", parameters: [ { name: "table", type: "table" }, @@ -418,24 +411,22 @@ const setmetatableFunction = new LuaBuiltinFunction( ], returns: [{ type: "table" }], }, -); +}); -const rawlenFunction = new LuaBuiltinFunction( - (_sf, value: LuaValue) => luaLen(value, _sf, true), - { - kind: "builtin", +const rawlenFunction = new LuaBuiltinFunction({ + callback: (_sf, value: LuaValue) => luaLen(value, _sf, true), + documentation: { description: "Returns a string or table length without invoking `__len`.", parameters: [{ name: "value", type: "string|table" }], returns: [{ type: "integer" }], }, -); +}); -const rawsetFunction = new LuaBuiltinFunction( - (_sf, table: LuaTable, key: LuaValue, value: LuaValue) => { +const rawsetFunction = new LuaBuiltinFunction({ + callback: (_sf, table: LuaTable, key: LuaValue, value: LuaValue) => { return (table as any).rawSet(key, value); }, - { - kind: "builtin", + documentation: { description: "Sets a table key without invoking `__newindex` and returns the table.", parameters: [ @@ -450,10 +441,10 @@ const rawsetFunction = new LuaBuiltinFunction( }, ], }, -); +}); -const rawgetFunction = new LuaBuiltinFunction( - (_sf, table: any, key: LuaValue) => { +const rawgetFunction = new LuaBuiltinFunction({ + callback: (_sf, table: any, key: LuaValue) => { const isArray = Array.isArray(table); const isPlainObj = @@ -504,40 +495,37 @@ const rawgetFunction = new LuaBuiltinFunction( const v = (table as Record)[k as any]; return v === undefined ? null : v; }, - { - kind: "builtin", + documentation: { description: "Reads a table key without invoking `__index`.", parameters: [{ name: "table", type: "table" }, { name: "key" }], returns: [{ description: "Stored value or `nil`." }], }, -); +}); -const rawequalFunction = new LuaBuiltinFunction( - (_sf, a: any, b: any) => { +const rawequalFunction = new LuaBuiltinFunction({ + callback: (_sf, a: any, b: any) => { const av = isTaggedFloat(a) ? a.value : a; const bv = isTaggedFloat(b) ? b.value : b; return av === bv; }, - { - kind: "builtin", + documentation: { description: "Tests two values for equality without invoking `__eq`.", parameters: [{ name: "a" }, { name: "b" }], returns: [{ type: "boolean" }], }, -); +}); -const getmetatableFunction = new LuaBuiltinFunction( - (_sf, table: LuaTable) => (table as any).metatable, - { - kind: "builtin", +const getmetatableFunction = new LuaBuiltinFunction({ + callback: (_sf, table: LuaTable) => (table as any).metatable, + documentation: { description: "Returns a table's metatable, or `nil` when none is set.", parameters: [{ name: "table", type: "table" }], returns: [{ type: "table|nil" }], }, -); +}); -const dofileFunction = new LuaBuiltinFunction( - async (sf, filename: string) => { +const dofileFunction = new LuaBuiltinFunction({ + callback: async (sf, filename: string) => { const global = sf.threadLocal.get("_GLOBAL") as LuaEnv; const file = (await luaCall( (global.get("space") as any).get("readFile"), @@ -557,8 +545,7 @@ const dofileFunction = new LuaBuiltinFunction( ); } }, - { - kind: "builtin", + documentation: { description: "Reads and executes a Lua source file from the current space.", parameters: [ { @@ -568,7 +555,7 @@ const dofileFunction = new LuaBuiltinFunction( }, ], }, -); +}); /** * From the Lua docs: @@ -578,8 +565,8 @@ const dofileFunction = new LuaBuiltinFunction( * argument). Otherwise, index must be the string "#", and select * returns the total number of extra arguments it received. */ -const selectFunction = new LuaBuiltinFunction( - (_sf, index: number | "#", ...args: LuaValue[]) => { +const selectFunction = new LuaBuiltinFunction({ + callback: (_sf, index: number | "#", ...args: LuaValue[]) => { if (index === "#") { return args.length; } @@ -590,8 +577,7 @@ const selectFunction = new LuaBuiltinFunction( return new LuaMultiRes(args.slice(args.length + index)); } }, - { - kind: "builtin", + documentation: { description: "Returns the count of extra arguments or all arguments from a selected position onward.", signatures: ['select("#", ...): integer', "select(index, ...): ..."], @@ -605,7 +591,7 @@ const selectFunction = new LuaBuiltinFunction( ], returns: [{ description: "Argument count or selected argument values." }], }, -); +}); /** * From the Lua docs: @@ -627,8 +613,12 @@ const selectFunction = new LuaBuiltinFunction( * during its traversal. You may however modify existing fields. In * particular, you may set existing fields to nil. */ -const nextFunction = new LuaBuiltinFunction( - (sf, table: LuaTable | Record, index: number | null = null) => { +const nextFunction = new LuaBuiltinFunction({ + callback: ( + sf, + table: LuaTable | Record, + index: number | null = null, + ) => { if (!table) { // When nil value return null; @@ -658,8 +648,7 @@ const nextFunction = new LuaBuiltinFunction( } return new LuaMultiRes([key, luaGet(table, key, sf.astCtx ?? null, sf)]); }, - { - kind: "builtin", + documentation: { description: "Returns the next table key and value after a given key, or the first pair when the key is omitted.", parameters: [ @@ -671,11 +660,11 @@ const nextFunction = new LuaBuiltinFunction( { description: "Value at the next key." }, ], }, -); +}); // Non-standard, but useful -const someFunction = new LuaBuiltinFunction( - async (_sf, value: any) => { +const someFunction = new LuaBuiltinFunction({ + callback: async (_sf, value: any) => { switch (await luaTypeOf(value)) { case "number": if (!Number.isFinite(value)) return null; @@ -688,8 +677,7 @@ const someFunction = new LuaBuiltinFunction( } return value; }, - { - kind: "builtin", + documentation: { description: "Returns `nil` for empty Space Lua values and otherwise returns the value unchanged.", parameters: [ @@ -706,19 +694,21 @@ const someFunction = new LuaBuiltinFunction( }, ], }, -); +}); -const loadFunction = new LuaBuiltinFunction((sf, s) => luaLoad(s, sf), { - kind: "builtin", - description: - "Compiles Lua source into a callable chunk without executing it.", - parameters: [ - { name: "chunk", type: "string", description: "Lua source code." }, - ], - returns: [ - { type: "function|nil", description: "Compiled chunk or `nil`." }, - { type: "string", description: "Compilation error when unsuccessful." }, - ], +const loadFunction = new LuaBuiltinFunction({ + callback: (sf, s) => luaLoad(s, sf), + documentation: { + description: + "Compiles Lua source into a callable chunk without executing it.", + parameters: [ + { name: "chunk", type: "string", description: "Lua source code." }, + ], + returns: [ + { type: "function|nil", description: "Compiled chunk or `nil`." }, + { type: "string", description: "Compilation error when unsuccessful." }, + ], + }, }); function annotateBuiltinApi( diff --git a/client/space_lua/stdlib/crypto.ts b/client/space_lua/stdlib/crypto.ts index cb7cff07..f6332004 100644 --- a/client/space_lua/stdlib/crypto.ts +++ b/client/space_lua/stdlib/crypto.ts @@ -2,17 +2,16 @@ import { LuaBuiltinFunction, LuaTable } from "../runtime.ts"; import { hashSHA256 } from "../../../plug-api/lib/crypto.ts"; export const cryptoApi = new LuaTable({ - sha256: new LuaBuiltinFunction( - (_sf, s: string | Uint8Array): Promise => { + sha256: new LuaBuiltinFunction({ + callback: (_sf, s: string | Uint8Array): Promise => { return hashSHA256(s); }, - { - kind: "builtin", + documentation: { 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." }], }, - ), + }), }); diff --git a/client/space_lua/stdlib/encoding.ts b/client/space_lua/stdlib/encoding.ts index 2055cb97..6c8bb6c3 100644 --- a/client/space_lua/stdlib/encoding.ts +++ b/client/space_lua/stdlib/encoding.ts @@ -2,48 +2,44 @@ import { LuaBuiltinFunction, LuaTable } from "../runtime.ts"; import { base64Decode, base64Encode } from "../../../plug-api/lib/crypto.ts"; export const encodingApi = new LuaTable({ - base64Encode: new LuaBuiltinFunction( - (_sf, s: string | Uint8Array): string => { + base64Encode: new LuaBuiltinFunction({ + callback: (_sf, s: string | Uint8Array): string => { return base64Encode(s); }, - { - kind: "builtin", + documentation: { 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 => { + }), + base64Decode: new LuaBuiltinFunction({ + callback: (_sf, s: string): Uint8Array => { return base64Decode(s); }, - { - kind: "builtin", + documentation: { 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 => { + }), + utf8Encode: new LuaBuiltinFunction({ + callback: (_sf, s: string): Uint8Array => { return new TextEncoder().encode(s); }, - { - kind: "builtin", + documentation: { 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 => { + }), + utf8Decode: new LuaBuiltinFunction({ + callback: (_sf, data: Uint8Array): string => { return new TextDecoder().decode(data); }, - { - kind: "builtin", + documentation: { description: "Decodes a UTF-8 byte buffer into a string.", parameters: [{ name: "data", type: "bytes" }], returns: [{ type: "string", description: "Decoded text." }], }, - ), + }), }); diff --git a/client/space_lua/stdlib/js.ts b/client/space_lua/stdlib/js.ts index e2ddd182..e4681093 100644 --- a/client/space_lua/stdlib/js.ts +++ b/client/space_lua/stdlib/js.ts @@ -13,12 +13,11 @@ 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) => { + new: new LuaBuiltinFunction({ + callback: (sf, constructorFn: any, ...args) => { return new constructorFn(...args.map((v) => luaValueToJS(v, sf))); }, - { - kind: "builtin", + documentation: { description: "Creates an instance of a JavaScript class.", signatures: ["js.new(constructor, ...): userdata"], parameters: [ @@ -37,14 +36,14 @@ export const jsApi = new LuaTable({ { 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) => { + import: new LuaBuiltinFunction({ + callback: async (_sf, url) => { let m = await import(url); // Unwrap default if it exists if (Object.keys(m).length === 1 && m.default) { @@ -52,8 +51,7 @@ export const jsApi = new LuaTable({ } return m; }, - { - kind: "builtin", + documentation: { description: "Dynamically imports a JavaScript module from a URL.", parameters: [{ name: "url", type: "string", description: "Module URL." }], returns: [ @@ -66,7 +64,7 @@ export const jsApi = new LuaTable({ { 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 @@ -74,8 +72,8 @@ 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) => { + importFromSpace: new LuaBuiltinFunction({ + callback: async (_sf, path: string) => { const base = document.baseURI.replace(/\/*$/, "/"); const rel = String(path).replace(/^\/+/, ""); let m = await import(base + fsEndpoint.slice(1) + "/" + rel); @@ -85,8 +83,7 @@ export const jsApi = new LuaTable({ } return m; }, - { - kind: "builtin", + documentation: { description: "Imports a JavaScript module from a file in the current space.", parameters: [ @@ -107,9 +104,9 @@ export const jsApi = new LuaTable({ { code: 'local acme = js.importFromSpace("Library/acme/acme.js")' }, ], }, - ), - eachIterable: new LuaBuiltinFunction( - (_sf, val) => { + }), + eachIterable: new LuaBuiltinFunction({ + callback: (_sf, val) => { const iterator = val[Symbol.asyncIterator](); return async () => { const result = await iterator.next(); @@ -119,8 +116,7 @@ export const jsApi = new LuaTable({ return result.value; }; }, - { - kind: "builtin", + documentation: { description: "Creates a Lua iterator over a JavaScript async iterable.", parameters: [ { @@ -141,59 +137,64 @@ export const jsApi = new LuaTable({ }, ], }, - ), + }), /** * 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), { - 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)" }], + tolua: new LuaBuiltinFunction({ + callback: (_sf, val) => jsToLuaValue(val), + documentation: { + 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), { - 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})" }], + tojs: new LuaBuiltinFunction({ + callback: (sf, val) => luaValueToJS(val, sf), + documentation: { + 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) => { + log: new LuaBuiltinFunction({ + callback: (_sf, ...args) => { console.log(...args); }, - { - kind: "builtin", + documentation: { 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), { - 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]" }], + stringify: new LuaBuiltinFunction({ + callback: (_sf, val) => JSON.stringify(val), + documentation: { + 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 diff --git a/client/space_lua/stdlib/load.ts b/client/space_lua/stdlib/load.ts index 50eabcc7..d00bb70a 100644 --- a/client/space_lua/stdlib/load.ts +++ b/client/space_lua/stdlib/load.ts @@ -24,8 +24,8 @@ export function luaLoad(code: LuaValue, sf: LuaStackFrame): LuaValue { const globalEnv: LuaEnv = globalEnvMaybe as LuaEnv; - const runner = new LuaBuiltinFunction( - async (innerSf: LuaStackFrame) => { + const runner = new LuaBuiltinFunction({ + callback: async (innerSf: LuaStackFrame) => { const res = await evalStatement(block, globalEnv, innerSf, true); if (res === undefined) { return null; @@ -46,13 +46,12 @@ export function luaLoad(code: LuaValue, sf: LuaStackFrame): LuaValue { return null; } }, - { - kind: "builtin", + documentation: { description: "Executes the Lua chunk compiled by `load`.", signatures: ["function(...)"], returns: [{ description: "Values returned by the compiled chunk." }], }, - ); + }); return runner; } catch (e: any) { diff --git a/client/space_lua/stdlib/math.ts b/client/space_lua/stdlib/math.ts index ed4f1252..198c98ba 100644 --- a/client/space_lua/stdlib/math.ts +++ b/client/space_lua/stdlib/math.ts @@ -23,8 +23,8 @@ export const mathApi = new LuaTable({ pi: Math.PI, // math.type(x) => "integer" | "float" | nil - type: new LuaBuiltinFunction( - (_sf, x?: any) => { + type: new LuaBuiltinFunction({ + callback: (_sf, x?: any) => { if (x === undefined) { throw new LuaRuntimeError( "bad argument #1 to 'math.type' (value expected)", @@ -45,8 +45,7 @@ export const mathApi = new LuaTable({ } return null; }, - { - kind: "builtin", + documentation: { description: "Returns `integer` or `float` for a number, or `nil` for other values.", parameters: [{ name: "x", description: "Value to inspect." }], @@ -54,15 +53,15 @@ export const mathApi = new LuaTable({ { 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) => { + tointeger: new LuaBuiltinFunction({ + callback: (_sf, x?: any) => { if (typeof x === "number") { return Number.isInteger(x) && Number.isFinite(x) ? x : null; } @@ -78,8 +77,7 @@ export const mathApi = new LuaTable({ } return null; }, - { - kind: "builtin", + documentation: { description: "Converts a value to an integer when it has an exact finite integral representation.", parameters: [{ name: "x", description: "Value to convert." }], @@ -87,7 +85,7 @@ export const mathApi = new LuaTable({ { type: "integer|nil", description: "Converted integer or `nil`." }, ], }, - ), + }), /** * When called without arguments, returns a pseudo-random float with @@ -98,8 +96,8 @@ 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) => { + random: new LuaBuiltinFunction({ + callback: (_sf, m?: number, n?: number) => { if (m !== undefined) m = untagNumber(m); if (n !== undefined) n = untagNumber(n); try { @@ -108,8 +106,7 @@ export const mathApi = new LuaTable({ throw new LuaRuntimeError(e.message, _sf); } }, - { - kind: "builtin", + documentation: { description: "Returns a pseudo-random float or an integer in a requested inclusive range.", signatures: [ @@ -128,20 +125,19 @@ export const mathApi = new LuaTable({ }, ], }, - ), + }), /** * 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) => { + randomseed: new LuaBuiltinFunction({ + callback: (_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", + documentation: { description: "Seeds the pseudo-random generator and returns the two seeds used.", signatures: [ @@ -157,54 +153,54 @@ export const mathApi = new LuaTable({ { type: "integer", description: "Second seed." }, ], }, - ), + }), // Basic functions - 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" }], + abs: new LuaBuiltinFunction({ + callback: (_sf, x: number) => Math.abs(untagNumber(x)), + documentation: { + 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" }], + ceil: new LuaBuiltinFunction({ + callback: (_sf, x: number) => Math.ceil(untagNumber(x)), + documentation: { + 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", + floor: new LuaBuiltinFunction({ + callback: (_sf, x: number) => Math.floor(untagNumber(x)), + documentation: { description: "Returns the largest integer less than or equal to `x`.", parameters: [{ name: "x", type: "number" }], returns: [{ type: "integer" }], }, - ), - max: new LuaBuiltinFunction( - (_sf, ...args: number[]) => Math.max(...args.map(untagNumber)), - { - kind: "builtin", + }), + max: new LuaBuiltinFunction({ + callback: (_sf, ...args: number[]) => Math.max(...args.map(untagNumber)), + documentation: { 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", + }), + min: new LuaBuiltinFunction({ + callback: (_sf, ...args: number[]) => Math.min(...args.map(untagNumber)), + documentation: { 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", + fmod: new LuaBuiltinFunction({ + callback: (_sf, x: number, y: number) => untagNumber(x) % untagNumber(y), + documentation: { description: "Returns the remainder of `x / y` with the quotient rounded toward zero.", parameters: [ @@ -213,17 +209,16 @@ export const mathApi = new LuaTable({ ], returns: [{ type: "number" }], }, - ), - modf: new LuaBuiltinFunction( - (_sf, x: number) => { + }), + modf: new LuaBuiltinFunction({ + callback: (_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", + documentation: { description: "Splits `x` into its integral and fractional parts.", parameters: [{ name: "x", type: "number" }], returns: [ @@ -232,13 +227,13 @@ export const mathApi = new LuaTable({ ], examples: [{ code: "local integer, fraction = math.modf(3.14)" }], }, - ), + }), // 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) => { + frexp: new LuaBuiltinFunction({ + callback: (_sf, x: number) => { const xn = untagNumber(x); if (xn === 0 || !Number.isFinite(xn) || Number.isNaN(xn)) { return new LuaMultiRes([xn, 0]); @@ -256,8 +251,7 @@ export const mathApi = new LuaTable({ } return new LuaMultiRes([m, e]); }, - { - kind: "builtin", + documentation: { description: "Decomposes `x` into a normalized fraction and a power-of-two exponent.", parameters: [{ name: "x", type: "number" }], @@ -266,13 +260,13 @@ export const mathApi = new LuaTable({ { 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", + ldexp: new LuaBuiltinFunction({ + callback: (_sf, m: number, e: number) => + untagNumber(m) * 2 ** untagNumber(e), + documentation: { description: "Returns `m * 2^e`, the inverse of `math.frexp`.", parameters: [ { name: "m", type: "number" }, @@ -280,24 +274,25 @@ export const mathApi = new LuaTable({ ], returns: [{ type: "number" }], }, - ), + }), // Power and logarithms - 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" }], + exp: new LuaBuiltinFunction({ + callback: (_sf, x: number) => Math.exp(untagNumber(x)), + documentation: { + description: "Returns `e` raised to `x`.", + parameters: [{ name: "x", type: "number" }], + returns: [{ type: "number" }], + }, }), - log: new LuaBuiltinFunction( - (_sf, x: number, base?: number) => { + log: new LuaBuiltinFunction({ + callback: (_sf, x: number, base?: number) => { if (base === undefined) { return Math.log(untagNumber(x)); } return Math.log(untagNumber(x)) / Math.log(untagNumber(base)); }, - { - kind: "builtin", + documentation: { description: "Returns the logarithm of `x`, using the natural base unless another base is supplied.", parameters: [ @@ -307,12 +302,11 @@ export const mathApi = new LuaTable({ 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", + pow: new LuaBuiltinFunction({ + callback: (_sf, x: number, y: number) => untagNumber(x) ** untagNumber(y), + documentation: { description: "Returns `x` raised to the power `y`.", parameters: [ { name: "x", type: "number" }, @@ -321,54 +315,65 @@ export const mathApi = new LuaTable({ returns: [{ type: "number" }], deprecated: "Use the `^` operator instead.", }, - ), - 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" }], + }), + sqrt: new LuaBuiltinFunction({ + callback: (_sf, x: number) => Math.sqrt(untagNumber(x)), + documentation: { + 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" }], + cos: new LuaBuiltinFunction({ + callback: (_sf, x: number) => Math.cos(untagNumber(x)), + documentation: { + 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" }], + sin: new LuaBuiltinFunction({ + callback: (_sf, x: number) => Math.sin(untagNumber(x)), + documentation: { + 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" }], + tan: new LuaBuiltinFunction({ + callback: (_sf, x: number) => Math.tan(untagNumber(x)), + documentation: { + 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" }], + acos: new LuaBuiltinFunction({ + callback: (_sf, x: number) => Math.acos(untagNumber(x)), + documentation: { + 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" }], + asin: new LuaBuiltinFunction({ + callback: (_sf, x: number) => Math.asin(untagNumber(x)), + documentation: { + 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) => { + atan: new LuaBuiltinFunction({ + callback: (_sf, y: number, x?: number) => { if (x === undefined) { return Math.atan(untagNumber(y)); } return Math.atan2(untagNumber(y), untagNumber(x)); }, - { - kind: "builtin", + documentation: { description: "Returns the arc tangent of `y/x` in radians, using `1` for omitted `x`.", parameters: [ @@ -377,57 +382,60 @@ export const mathApi = new LuaTable({ ], returns: [{ type: "number" }], }, - ), + }), // Hyperbolic functions (deprecated in Lua 5.4 but retained for compatibility) - 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.", + cosh: new LuaBuiltinFunction({ + callback: (_sf, x: number) => Math.cosh(untagNumber(x)), + documentation: { + 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.", + sinh: new LuaBuiltinFunction({ + callback: (_sf, x: number) => Math.sinh(untagNumber(x)), + documentation: { + 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.", + tanh: new LuaBuiltinFunction({ + callback: (_sf, x: number) => Math.tanh(untagNumber(x)), + documentation: { + 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", + deg: new LuaBuiltinFunction({ + callback: (_sf, x: number) => (untagNumber(x) * 180) / Math.PI, + documentation: { 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", + }), + rad: new LuaBuiltinFunction({ + callback: (_sf, x: number) => (untagNumber(x) * Math.PI) / 180, + documentation: { description: "Converts an angle from degrees to radians.", parameters: [{ name: "x", type: "number" }], returns: [{ type: "number" }], }, - ), - ult: new LuaBuiltinFunction( - (_sf, m: number, n: number) => { + }), + ult: new LuaBuiltinFunction({ + callback: (_sf, m: number, n: number) => { return untagNumber(m) >>> 0 < untagNumber(n) >>> 0; }, - { - kind: "builtin", + documentation: { description: "Compares two integers as unsigned 32-bit values.", parameters: [ { name: "m", type: "integer" }, @@ -436,11 +444,11 @@ export const mathApi = new LuaTable({ returns: [{ type: "boolean" }], examples: [{ code: "print(math.ult(2, 3)) -- true" }], }, - ), + }), // Keep the cosineSimilarity utility function - cosineSimilarity: new LuaBuiltinFunction( - (sf, vecA: LuaTable | number[], vecB: LuaTable | number[]) => { + cosineSimilarity: new LuaBuiltinFunction({ + callback: (sf, vecA: LuaTable | number[], vecB: LuaTable | number[]) => { if (vecA instanceof LuaTable) { vecA = vecA.toJSArray(); } @@ -464,8 +472,7 @@ export const mathApi = new LuaTable({ return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB)); }, - { - kind: "builtin", + documentation: { description: "Returns the cosine similarity between two equal-length numeric vectors.", parameters: [ @@ -477,5 +484,5 @@ export const mathApi = new LuaTable({ { code: "print(math.cosineSimilarity({1, 2, 3}, {4, 5, 6}))" }, ], }, - ), + }), }); diff --git a/client/space_lua/stdlib/net.ts b/client/space_lua/stdlib/net.ts index c7190248..6f188c4d 100644 --- a/client/space_lua/stdlib/net.ts +++ b/client/space_lua/stdlib/net.ts @@ -7,8 +7,8 @@ import { fsEndpoint } from "../../spaces/constants.ts"; import { LuaNativeJSFunction, LuaTable } from "../runtime.ts"; export const netApi = new LuaTable({ - proxyFetch: new LuaNativeJSFunction( - async ( + proxyFetch: new LuaNativeJSFunction({ + callback: async ( url: string, options: ProxyFetchRequest = {}, ): Promise => { @@ -70,8 +70,7 @@ export const netApi = new LuaTable({ body: body, }; }, - { - kind: "builtin", + documentation: { description: "Performs an HTTP request through the SilverBullet server to avoid browser CORS restrictions.", signatures: ["net.proxyFetch(url, options?)"], @@ -93,17 +92,19 @@ export const netApi = new LuaTable({ ], see: "API/net", }, - ), - readURI: new LuaNativeJSFunction( - (uri: string, options: { uri?: string; encoding?: string } = {}) => { + }), + readURI: new LuaNativeJSFunction({ + callback: ( + uri: string, + options: { uri?: string; encoding?: string } = {}, + ) => { options.uri = uri; return client.clientSystem.serviceRegistry.invokeBestMatch( `net.readURI:${uri}`, options, ); }, - { - kind: "builtin", + documentation: { description: "Reads content from a URI using the best matching service.", signatures: ["net.readURI(uri, options?)"], parameters: [ @@ -118,16 +119,15 @@ export const netApi = new LuaTable({ returns: [{ description: "Content returned by the matching service." }], see: "API/net", }, - ), - writeURI: new LuaNativeJSFunction( - (uri: string, content: string | Uint8Array) => { + }), + writeURI: new LuaNativeJSFunction({ + callback: (uri: string, content: string | Uint8Array) => { return client.clientSystem.serviceRegistry.invokeBestMatch( `net.writeURI:${uri}`, { uri, content }, ); }, - { - kind: "builtin", + documentation: { description: "Writes content to a URI using the best matching service.", signatures: ["net.writeURI(uri, content)"], parameters: [ @@ -141,7 +141,7 @@ export const netApi = new LuaTable({ returns: [{ description: "Result returned by the matching service." }], see: "API/net", }, - ), + }), }); // Utility functions diff --git a/client/space_lua/stdlib/os.ts b/client/space_lua/stdlib/os.ts index 9f88e221..ca652b06 100644 --- a/client/space_lua/stdlib/os.ts +++ b/client/space_lua/stdlib/os.ts @@ -282,8 +282,8 @@ function luaFormatTime(fmt: string, d: Date, utc: boolean): string { } export const osApi = new LuaTable({ - time: new LuaBuiltinFunction( - (_sf, tbl?: LuaTable) => { + time: new LuaBuiltinFunction({ + callback: (_sf, tbl?: LuaTable) => { if (tbl) { if (!tbl.has("year")) { throw new Error("time(): year is required"); @@ -310,8 +310,7 @@ export const osApi = new LuaTable({ return Math.floor(Date.now() / 1000); }, - { - kind: "builtin", + documentation: { description: "Returns the current Unix timestamp or one built from a local date table.", signatures: ["os.time(): integer", "os.time(dateTable): integer"], @@ -333,16 +332,15 @@ export const osApi = new LuaTable({ }, ], }, - ), + }), // 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 => { + difftime: new LuaBuiltinFunction({ + callback: (_sf, t2: number, t1: number): number => { return t2 - t1; }, - { - kind: "builtin", + documentation: { description: "Returns the difference in seconds from timestamp `t1` to `t2`.", parameters: [ @@ -353,7 +351,7 @@ export const osApi = new LuaTable({ { 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. @@ -375,8 +373,8 @@ 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) => { + date: new LuaBuiltinFunction({ + callback: (_sf, format?: string, timestamp?: number) => { let fmt = format ?? "%c"; let utc = false; @@ -396,8 +394,7 @@ export const osApi = new LuaTable({ return luaFormatTime(fmt, d, utc); }, - { - kind: "builtin", + documentation: { description: "Formats a timestamp as a date string or date table, optionally in UTC.", parameters: [ @@ -422,15 +419,14 @@ export const osApi = new LuaTable({ { 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 => { + clock: new LuaBuiltinFunction({ + callback: (_sf): number => { return performance.now() / 1000.0; }, - { - kind: "builtin", + documentation: { description: "Returns a high-resolution elapsed time value in seconds.", returns: [ { @@ -439,5 +435,5 @@ export const osApi = new LuaTable({ }, ], }, - ), + }), }); diff --git a/client/space_lua/stdlib/space_lua.ts b/client/space_lua/stdlib/space_lua.ts index 5c0ca2b2..a995ca71 100644 --- a/client/space_lua/stdlib/space_lua.ts +++ b/client/space_lua/stdlib/space_lua.ts @@ -236,8 +236,8 @@ function toPrintOptions( } export const spaceluaApi = new LuaTable({ - describe: new LuaBuiltinFunction( - (sf, target: ILuaFunction | string) => { + describe: new LuaBuiltinFunction({ + callback: (sf, target: ILuaFunction | string) => { const value = typeof target === "string" ? resolveApiValue(sf, target) : target; return describeFunction( @@ -245,8 +245,7 @@ export const spaceluaApi = new LuaTable({ typeof target === "string" ? target : undefined, ); }, - { - kind: "builtin", + documentation: { description: "Returns structured documentation for a Lua function value or dotted API name.", parameters: [ @@ -270,12 +269,11 @@ export const spaceluaApi = new LuaTable({ ], see: "API/spacelua", }, - ), - listFunctions: new LuaBuiltinFunction( - (sf, target?: LuaTable | string) => + }), + listFunctions: new LuaBuiltinFunction({ + callback: (sf, target?: LuaTable | string) => jsToLuaValue(listFunctionInfo(sf, target)), - { - kind: "builtin", + documentation: { description: "Lists documented functions in the global environment or an API namespace.", parameters: [ @@ -294,17 +292,16 @@ export const spaceluaApi = new LuaTable({ ], see: "API/spacelua", }, - ), - renderApiDocumentation: new LuaBuiltinFunction( - (sf, target?: ILuaFunction | LuaTable | string): string => { + }), + renderApiDocumentation: new LuaBuiltinFunction({ + callback: (sf, target?: ILuaFunction | LuaTable | string): string => { const selection = apiDocumentationTarget(sf, target); return renderApiDocumentationMarkdown( selection.functions, selection.context, ); }, - { - kind: "builtin", + documentation: { description: "Renders API documentation for a function, namespace, or the global environment as Markdown.", parameters: [ @@ -331,7 +328,7 @@ export const spaceluaApi = new LuaTable({ ], see: "API/spacelua", }, - ), + }), /** * Parses a lua expression and returns the parsed expression. * @@ -339,12 +336,11 @@ export const spaceluaApi = new LuaTable({ * @param luaExpression - The lua expression to parse. * @returns The parsed expression. */ - parseExpression: new LuaBuiltinFunction( - (_sf, luaExpression: string) => { + parseExpression: new LuaBuiltinFunction({ + callback: (_sf, luaExpression: string) => { return parseExpressionString(luaExpression); }, - { - kind: "builtin", + documentation: { description: "Parses a Lua expression and returns its AST.", parameters: [ { @@ -361,7 +357,7 @@ export const spaceluaApi = new LuaTable({ ], see: "API/spacelua", }, - ), + }), /** * Parses a lua chunk (block) and returns the parsed AST block. * @@ -369,12 +365,11 @@ export const spaceluaApi = new LuaTable({ * @param code - The lua code to parse. * @returns The parsed block. */ - parseBlock: new LuaBuiltinFunction( - (_sf, code: string): LuaBlock => { + parseBlock: new LuaBuiltinFunction({ + callback: (_sf, code: string): LuaBlock => { return parseBlock(code); }, - { - kind: "builtin", + documentation: { description: "Parses a Lua chunk and returns its AST. Blocks retain comments in source order with their exact text, kind, and source range.", parameters: [ @@ -388,7 +383,7 @@ export const spaceluaApi = new LuaTable({ ], see: "API/spacelua", }, - ), + }), /** * Pretty-prints a parsed lua block AST back to formatted source. * @@ -397,12 +392,11 @@ export const spaceluaApi = new LuaTable({ * @param opts - Optional formatting options. * @returns The formatted lua source. */ - prettyPrintBlock: new LuaBuiltinFunction( - (sf, block: LuaBlock, opts?: LuaTable): string => { + prettyPrintBlock: new LuaBuiltinFunction({ + callback: (sf, block: LuaBlock, opts?: LuaTable): string => { return prettyPrintBlock(block, toPrintOptions(sf, opts)); }, - { - kind: "builtin", + documentation: { description: "Pretty-prints a parsed Lua block AST. Comments are preserved while their placement and indentation are normalized.", parameters: [ @@ -423,7 +417,7 @@ export const spaceluaApi = new LuaTable({ ], see: "API/spacelua", }, - ), + }), /** * Pretty-prints a parsed lua expression AST back to formatted source. * @@ -432,12 +426,11 @@ export const spaceluaApi = new LuaTable({ * @param opts - Optional formatting options. * @returns The formatted lua source. */ - prettyPrintExpression: new LuaBuiltinFunction( - (sf, expr: LuaExpression, opts?: LuaTable): string => { + prettyPrintExpression: new LuaBuiltinFunction({ + callback: (sf, expr: LuaExpression, opts?: LuaTable): string => { return prettyPrintExpression(expr, toPrintOptions(sf, opts)); }, - { - kind: "builtin", + documentation: { description: "Pretty-prints a parsed Lua expression AST.", parameters: [ { @@ -461,7 +454,7 @@ export const spaceluaApi = new LuaTable({ ], see: "API/spacelua", }, - ), + }), /** * Evaluates a parsed lua expression and returns the result. * @@ -470,13 +463,16 @@ export const spaceluaApi = new LuaTable({ * @param envAugmentation - An optional environment to augment the global environment with. * @returns The result of the evaluated expression. */ - evalExpression: new LuaBuiltinFunction( - async (sf, parsedExpr: LuaExpression, envAugmentation?: LuaTable) => { + evalExpression: new LuaBuiltinFunction({ + callback: async ( + sf, + parsedExpr: LuaExpression, + envAugmentation?: LuaTable, + ) => { const env = createAugmentedEnv(sf, envAugmentation); return luaValueToJS(await evalExpression(parsedExpr, env, sf), sf); }, - { - kind: "builtin", + documentation: { description: "Evaluates a parsed Lua expression, optionally with additional environment values.", parameters: [ @@ -500,19 +496,18 @@ export const spaceluaApi = new LuaTable({ ], see: "API/spacelua", }, - ), + }), /** * Interpolates a string with lua expressions and returns the result. */ - interpolate: new LuaBuiltinFunction( - (sf, template: string, envAugmentation?: LuaTable | any) => { + interpolate: new LuaBuiltinFunction({ + callback: (sf, template: string, envAugmentation?: LuaTable | any) => { if (envAugmentation && !(envAugmentation instanceof LuaTable)) { envAugmentation = jsToLuaValue(envAugmentation); } return interpolateLuaString(sf, template, envAugmentation); }, - { - kind: "builtin", + documentation: { description: "Interpolates `${...}` Lua expressions in a string, optionally with additional environment values.", parameters: [ @@ -536,22 +531,21 @@ export const spaceluaApi = new LuaTable({ ], see: "API/spacelua", }, - ), + }), /** * Returns your SilverBullet instance's base URL */ - baseUrl: new LuaBuiltinFunction( - () => { + baseUrl: new LuaBuiltinFunction({ + callback: () => { //NOTE: Removing trailing slash to stay compatible with original code: `location.protocol + "//" + location.host;` return document.baseURI.replace(/\/*$/, ""); }, - { - kind: "builtin", + documentation: { description: "Returns the SilverBullet instance's base URL, or `nil` when run on the server.", returns: [{ type: "string|nil" }], examples: [{ code: "local url = spacelua.baseUrl()\nprint(url)" }], see: "API/spacelua", }, - ), + }), }); diff --git a/client/space_lua/stdlib/string.ts b/client/space_lua/stdlib/string.ts index e5f782c2..8ea4d845 100644 --- a/client/space_lua/stdlib/string.ts +++ b/client/space_lua/stdlib/string.ts @@ -28,8 +28,8 @@ function capturesToLua(caps: CaptureResult[]): any { } export const stringApi = new LuaTable({ - byte: new LuaBuiltinFunction( - (_sf, s: string, i?: number, j?: number) => { + byte: new LuaBuiltinFunction({ + callback: (_sf, s: string, i?: number, j?: number) => { i = i ?? 1; j = j ?? i; if (j > s.length) j = s.length; @@ -40,8 +40,7 @@ export const stringApi = new LuaTable({ } return new LuaMultiRes(result); }, - { - kind: "builtin", + documentation: { description: "Returns the numeric character codes in the inclusive range from `i` to `j`.", parameters: [ @@ -51,18 +50,17 @@ export const stringApi = new LuaTable({ ], returns: [{ type: "integer", description: "One result per character." }], }, - ), - char: new LuaBuiltinFunction( - (_sf, ...args: number[]) => String.fromCharCode(...args), - { - kind: "builtin", + }), + char: new LuaBuiltinFunction({ + callback: (_sf, ...args: number[]) => String.fromCharCode(...args), + documentation: { 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) => { + }), + find: new LuaBuiltinFunction({ + callback: (_sf, s: string, pattern: string, init = 1, plain = false) => { const r = patternFind(s, pattern, init, plain); if (!r) return null; const result: any[] = [r.start, r.end]; @@ -71,8 +69,7 @@ export const stringApi = new LuaTable({ } return new LuaMultiRes(result); }, - { - kind: "builtin", + documentation: { description: "Finds the first Lua-pattern match and returns its bounds followed by captures.", parameters: [ @@ -86,16 +83,15 @@ export const stringApi = new LuaTable({ { type: "integer", description: "End index." }, ], }, - ), - format: new LuaBuiltinFunction( - (_sf, format: string, ...args: any[]) => { + }), + format: new LuaBuiltinFunction({ + callback: (_sf, format: string, ...args: any[]) => { for (let i = 0; i < args.length; i++) { args[i] = untagNumber(args[i]); } return luaFormat(format, ...args); }, - { - kind: "builtin", + documentation: { description: "Formats values according to a C-style format string.", signatures: ["string.format(format, ...): string"], parameters: [ @@ -110,9 +106,9 @@ export const stringApi = new LuaTable({ { code: 'print(string.format("Name: %s, score: %.1f", "Ada", 9.5))' }, ], }, - ), - gmatch: new LuaBuiltinFunction( - (_sf, s: string, pattern: string, init = 1) => { + }), + gmatch: new LuaBuiltinFunction({ + callback: (_sf, s: string, pattern: string, init = 1) => { const iter = patternGmatch(s, pattern, init); return () => { const caps = iter(); @@ -120,8 +116,7 @@ export const stringApi = new LuaTable({ return capturesToLua(caps); }; }, - { - kind: "builtin", + documentation: { description: "Returns an iterator over successive Lua-pattern matches and captures.", parameters: [ @@ -136,9 +131,9 @@ export const stringApi = new LuaTable({ }, ], }, - ), - gsub: new LuaBuiltinFunction( - async (sf, s: string, pattern: string, repl: any, n?: number) => { + }), + gsub: new LuaBuiltinFunction({ + callback: async (sf, s: string, pattern: string, repl: any, n?: number) => { const callbacks: GsubCallbacks = {}; if (typeof repl === "string") { callbacks.replString = repl; @@ -171,8 +166,7 @@ export const stringApi = new LuaTable({ const [result, count] = await patternGsub(s, pattern, callbacks, n); return new LuaMultiRes([result, count]); }, - { - kind: "builtin", + documentation: { description: "Replaces Lua-pattern matches using a string, table, or function replacement.", parameters: [ @@ -191,39 +185,38 @@ export const stringApi = new LuaTable({ }, ], }, - ), - 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", + len: new LuaBuiltinFunction({ + callback: (_sf, s: string) => s.length, + documentation: { + description: "Returns the length of a string.", + parameters: [{ name: "s", type: "string" }], + returns: [{ type: "integer" }], + }, + }), + lower: new LuaBuiltinFunction({ + callback: (_sf, s: string) => luaToString(s.toLowerCase()), + documentation: { 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", + }), + upper: new LuaBuiltinFunction({ + callback: (_sf, s: string) => luaToString(s.toUpperCase()), + documentation: { 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) => { + }), + match: new LuaBuiltinFunction({ + callback: (_sf, s: string, pattern: string, init = 1) => { const caps = patternMatch(s, pattern, init); if (!caps) return null; return capturesToLua(caps); }, - { - kind: "builtin", + documentation: { description: "Returns captures from the first Lua-pattern match, or `nil` when none is found.", parameters: [ @@ -236,9 +229,9 @@ export const stringApi = new LuaTable({ { code: 'local year, month = string.match("2024-03", "(%d+)%-(%d+)")' }, ], }, - ), - rep: new LuaBuiltinFunction( - (_sf, s: string, n: number, sep?: string) => { + }), + rep: new LuaBuiltinFunction({ + callback: (_sf, s: string, n: number, sep?: string) => { if (n <= 0) return ""; sep = sep ?? ""; const parts: string[] = []; @@ -247,8 +240,7 @@ export const stringApi = new LuaTable({ } return parts.join(sep); }, - { - kind: "builtin", + documentation: { description: "Returns `n` copies of a string joined by an optional separator.", parameters: [ @@ -258,18 +250,17 @@ export const stringApi = new LuaTable({ ], returns: [{ type: "string" }], }, - ), - reverse: new LuaBuiltinFunction( - (_sf, s: string) => s.split("").reverse().join(""), - { - kind: "builtin", + }), + reverse: new LuaBuiltinFunction({ + callback: (_sf, s: string) => s.split("").reverse().join(""), + documentation: { 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) => { + }), + sub: new LuaBuiltinFunction({ + callback: (_sf, s: string, i: number, j?: number) => { const len = s.length; let start: number; if (i > 0) { @@ -294,8 +285,7 @@ export const stringApi = new LuaTable({ } return ""; }, - { - kind: "builtin", + documentation: { description: "Returns the substring from inclusive index `i` through `j`, supporting negative indices.", parameters: [ @@ -305,22 +295,24 @@ export const stringApi = new LuaTable({ ], returns: [{ type: "string" }], }, - ), + }), - 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', - }, - ], + split: new LuaBuiltinFunction({ + callback: (_sf, s: string, sep: string) => s.split(sep), + documentation: { + 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, @@ -328,10 +320,9 @@ export const stringApi = new LuaTable({ packsize: strPackSizeFn, // Non-standard extensions - startsWith: new LuaBuiltinFunction( - (_sf, s: string, prefix: string) => s.startsWith(prefix), - { - kind: "builtin", + startsWith: new LuaBuiltinFunction({ + callback: (_sf, s: string, prefix: string) => s.startsWith(prefix), + documentation: { description: "Returns whether a string starts with a literal prefix.", parameters: [ { name: "s", type: "string" }, @@ -339,11 +330,10 @@ export const stringApi = new LuaTable({ ], returns: [{ type: "boolean" }], }, - ), - endsWith: new LuaBuiltinFunction( - (_sf, s: string, suffix: string) => s.endsWith(suffix), - { - kind: "builtin", + }), + endsWith: new LuaBuiltinFunction({ + callback: (_sf, s: string, suffix: string) => s.endsWith(suffix), + documentation: { description: "Returns whether a string ends with a literal suffix.", parameters: [ { name: "s", type: "string" }, @@ -351,33 +341,38 @@ export const stringApi = new LuaTable({ ], 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" }], }), - 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({ + callback: (_sf, s: string) => s.trim(), + documentation: { + description: "Removes whitespace from both ends of a string.", + parameters: [{ name: "s", type: "string" }], + returns: [{ type: "string" }], + }, }), - 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" }], + trimStart: new LuaBuiltinFunction({ + callback: (_sf, s: string) => s.trimStart(), + documentation: { + description: "Removes whitespace from the beginning of a string.", + parameters: [{ name: "s", type: "string" }], + returns: [{ type: "string" }], + }, }), - matchRegex: new LuaBuiltinFunction( - (_sf, s: string, pattern: string) => { + trimEnd: new LuaBuiltinFunction({ + callback: (_sf, s: string) => s.trimEnd(), + documentation: { + description: "Removes whitespace from the end of a string.", + parameters: [{ name: "s", type: "string" }], + returns: [{ type: "string" }], + }, + }), + matchRegex: new LuaBuiltinFunction({ + callback: (_sf, s: string, pattern: string) => { const regex = new RegExp(pattern); const result = s.match(regex); return jsToLuaValue(result); }, - { - kind: "builtin", + documentation: { description: "Matches a string with a JavaScript regular expression and returns the match array.", parameters: [ @@ -391,9 +386,9 @@ export const stringApi = new LuaTable({ }, ], }, - ), - matchRegexAll: new LuaBuiltinFunction( - (_sf, s: string, pattern: string) => { + }), + matchRegexAll: new LuaBuiltinFunction({ + callback: (_sf, s: string, pattern: string) => { const regex = new RegExp(pattern, "g"); return () => { const match = regex.exec(s); @@ -403,8 +398,7 @@ export const stringApi = new LuaTable({ return jsToLuaValue(match); }; }, - { - kind: "builtin", + documentation: { description: "Returns an iterator over all JavaScript regular-expression matches.", parameters: [ @@ -420,5 +414,5 @@ export const stringApi = new LuaTable({ }, ], }, - ), + }), }); diff --git a/client/space_lua/stdlib/string_pack.ts b/client/space_lua/stdlib/string_pack.ts index 4da5fb70..0abf9c5a 100644 --- a/client/space_lua/stdlib/string_pack.ts +++ b/client/space_lua/stdlib/string_pack.ts @@ -252,8 +252,8 @@ function unpackFloat64( ); } -export const strPackFn = new LuaBuiltinFunction( - (sf, fmt: string, ...args: any[]) => { +export const strPackFn = new LuaBuiltinFunction({ + callback: (sf, fmt: string, ...args: any[]) => { const h = makeHeader(); const parts: Uint8Array[] = []; let totalsize = 0; @@ -364,8 +364,7 @@ export const strPackFn = new LuaBuiltinFunction( for (let i = 0; i < out.length; i++) result += String.fromCharCode(out[i]); return result; }, - { - kind: "builtin", + documentation: { description: "Packs values into a binary string according to a Lua 5.4 format string.", signatures: ["string.pack(format, ...): string"], @@ -375,10 +374,10 @@ export const strPackFn = new LuaBuiltinFunction( ], returns: [{ type: "string", description: "Packed binary string." }], }, -); +}); -export const strUnpackFn = new LuaBuiltinFunction( - (sf, fmt: string, data: string, init?: number) => { +export const strUnpackFn = new LuaBuiltinFunction({ + callback: (sf, fmt: string, data: string, init?: number) => { const h = makeHeader(); const buf = new Uint8Array(data.length); @@ -479,8 +478,7 @@ export const strUnpackFn = new LuaBuiltinFunction( results.push(pos + 1); return new LuaMultiRes(results); }, - { - kind: "builtin", + documentation: { description: "Unpacks values from a binary string according to a Lua 5.4 format string.", parameters: [ @@ -501,10 +499,10 @@ export const strUnpackFn = new LuaBuiltinFunction( { description: "Unpacked values followed by the next unread position." }, ], }, -); +}); -export const strPackSizeFn = new LuaBuiltinFunction( - (_sf, fmt: string) => { +export const strPackSizeFn = new LuaBuiltinFunction({ + callback: (_sf, fmt: string) => { const h = makeHeader(); let totalsize = 0; let pos = 0; @@ -522,8 +520,7 @@ export const strPackSizeFn = new LuaBuiltinFunction( return totalsize; }, - { - kind: "builtin", + documentation: { description: "Returns the byte size of a fixed-length Lua 5.4 packing format.", parameters: [ @@ -535,4 +532,4 @@ export const strPackSizeFn = new LuaBuiltinFunction( ], returns: [{ type: "integer", description: "Packed byte count." }], }, -); +}); diff --git a/client/space_lua/stdlib/table.ts b/client/space_lua/stdlib/table.ts index ee03bb33..12ce3fa1 100644 --- a/client/space_lua/stdlib/table.ts +++ b/client/space_lua/stdlib/table.ts @@ -58,8 +58,14 @@ export const tableApi = new LuaTable({ * @param j - The end index. * @returns The concatenated string. */ - concat: new LuaBuiltinFunction( - async (sf, tbl: LuaTable | any[], sep?: string, i?: number, j?: number) => { + concat: new LuaBuiltinFunction({ + callback: async ( + sf, + tbl: LuaTable | any[], + sep?: string, + i?: number, + j?: number, + ) => { sep = sep ?? ""; i = i ?? 1; if (j === undefined || j === null) { @@ -108,8 +114,7 @@ export const tableApi = new LuaTable({ } return out.join(sep); }, - { - kind: "builtin", + documentation: { description: "Concatenates table elements from `i` through `j` using an optional separator.", parameters: [ @@ -120,7 +125,7 @@ export const tableApi = new LuaTable({ ], returns: [{ type: "string" }], }, - ), + }), /** * Inserts an element into a table at a specified position. @@ -128,8 +133,8 @@ export const tableApi = new LuaTable({ * @param posOrValue - The position or value to insert. * @param value - The value to insert. */ - insert: new LuaBuiltinFunction( - async ( + insert: new LuaBuiltinFunction({ + callback: async ( sf, tbl: LuaTable | any[], posOrValue: number | any, @@ -171,8 +176,7 @@ export const tableApi = new LuaTable({ await luaSet(tbl, pos, v, sf); }, - { - kind: "builtin", + documentation: { description: "Inserts a value at a position, shifting later elements, or appends it when no position is supplied.", signatures: [ @@ -197,15 +201,15 @@ export const tableApi = new LuaTable({ }, ], }, - ), + }), /** * Removes an element from a table at a specified position. * @param tbl - The table to remove the element from. * @param pos - The position of the element to remove. */ - remove: new LuaBuiltinFunction( - async (sf, tbl: LuaTable | any[], pos?: number) => { + remove: new LuaBuiltinFunction({ + callback: async (sf, tbl: LuaTable | any[], pos?: number) => { if (Array.isArray(tbl)) { const n = tbl.length; const p = pos ?? n; @@ -240,8 +244,7 @@ export const tableApi = new LuaTable({ return v; }, - { - kind: "builtin", + documentation: { description: "Removes and returns an element, shifting later elements down.", parameters: [ @@ -255,7 +258,7 @@ export const tableApi = new LuaTable({ ], returns: [{ description: "Removed value." }], }, - ), + }), /** * Moves elements from table a1 into table a2 (defaults to a1). @@ -268,8 +271,8 @@ export const tableApi = new LuaTable({ * @param a2 - Destination table (defaults to a1). * @returns a2. */ - move: new LuaBuiltinFunction( - async ( + move: new LuaBuiltinFunction({ + callback: async ( sf, a1: LuaTable | any[], f: number, @@ -305,8 +308,7 @@ export const tableApi = new LuaTable({ return a2; }, - { - kind: "builtin", + documentation: { description: "Moves an inclusive element range to a destination table while handling overlaps.", parameters: [ @@ -323,7 +325,7 @@ export const tableApi = new LuaTable({ ], returns: [{ type: "table", description: "Destination table." }], }, - ), + }), /** * Sorts a table. @@ -331,8 +333,8 @@ export const tableApi = new LuaTable({ * @param comp - The comparison function. * @returns The sorted table. */ - sort: new LuaBuiltinFunction( - async (sf, tbl: LuaTable | any[], comp?: ILuaFunction) => { + sort: new LuaBuiltinFunction({ + callback: async (sf, tbl: LuaTable | any[], comp?: ILuaFunction) => { if (Array.isArray(tbl)) { return await asyncQuickSort(tbl, async (a, b) => { if (comp) { @@ -382,8 +384,7 @@ export const tableApi = new LuaTable({ return tbl; }, - { - kind: "builtin", + documentation: { description: "Sorts a table in place using ascending order or an optional comparison function.", parameters: [ @@ -399,7 +400,7 @@ export const tableApi = new LuaTable({ }, ], }, - ), + }), /** * Returns the keys of a table. @@ -407,21 +408,20 @@ 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) => { + keys: new LuaBuiltinFunction({ + callback: (_sf, tbl: LuaTable | LuaEnv | any) => { if (tbl.keys) { return tbl.keys(); } return Object.keys(tbl); }, - { - kind: "builtin", + documentation: { 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. @@ -430,8 +430,8 @@ export const tableApi = new LuaTable({ * @param value - The value to check for. * @returns True if the value is in the table, false otherwise. */ - includes: new LuaBuiltinFunction( - (sf, tbl: LuaTable | any[], value: LuaValue) => { + includes: new LuaBuiltinFunction({ + callback: (sf, tbl: LuaTable | any[], value: LuaValue) => { if (!tbl) { return false; } @@ -452,8 +452,7 @@ export const tableApi = new LuaTable({ sf, ); }, - { - kind: "builtin", + documentation: { description: "Returns whether any table value is Lua-equal to a requested value.", parameters: [ @@ -462,7 +461,7 @@ export const tableApi = new LuaTable({ ], returns: [{ type: "boolean" }], }, - ), + }), /** * Returns a new table from an old one, only with selected keys @@ -470,8 +469,12 @@ export const tableApi = new LuaTable({ * @param keys a list of keys to select from the table, if keys[0] is a table or array, assumed to contain the keys to select * @returns a new table with only the selected keys */ - select: new LuaBuiltinFunction( - (sf, tbl: LuaTable | Record, ...keys: LuaValue[]) => { + select: new LuaBuiltinFunction({ + callback: ( + sf, + tbl: LuaTable | Record, + ...keys: LuaValue[] + ) => { // Normalize arguments if (Array.isArray(keys[0])) { // First argument is key array, let's unpack @@ -490,8 +493,7 @@ export const tableApi = new LuaTable({ } return resultTable; }, - { - kind: "builtin", + documentation: { description: "Copies selected keys from a table into a new table.", signatures: [ "table.select(table, ...keys): table", @@ -512,14 +514,14 @@ export const tableApi = new LuaTable({ }, ], }, - ), + }), /** * 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[]) => { + pack: new LuaBuiltinFunction({ + callback: async (sf, ...args: any[]) => { const tbl = new LuaTable(); const n = args.length; for (let i = 0; i < n; i++) { @@ -528,8 +530,7 @@ export const tableApi = new LuaTable({ void tbl.rawSet("n", n); return tbl; }, - { - kind: "builtin", + documentation: { description: "Packs all arguments into a table with a count stored in field `n`.", signatures: ["table.pack(...): table"], @@ -540,15 +541,15 @@ export const tableApi = new LuaTable({ }, ], }, - ), + }), /** * Returns all values t[i], t[i+1], ..., t[j]. * i defaults to 1, j defaults to #t (honours __len). * Empty range returns no values (null), not an empty multi-res. */ - unpack: new LuaBuiltinFunction( - async (sf, tbl: LuaTable | any[], i?: number, j?: number) => { + unpack: new LuaBuiltinFunction({ + callback: async (sf, tbl: LuaTable | any[], i?: number, j?: number) => { i = i === undefined || i === null ? 1 : i; if (j === undefined || j === null) { j = Array.isArray(tbl) @@ -568,8 +569,7 @@ export const tableApi = new LuaTable({ } return new LuaMultiRes(result); }, - { - kind: "builtin", + documentation: { description: "Returns the table values from index `i` through `j` as separate results.", parameters: [ @@ -582,7 +582,7 @@ export const tableApi = new LuaTable({ { code: 'local second, third = table.unpack({"a", "b", "c"}, 2, 3)' }, ], }, - ), + }), // Non-standard Lua functions /** @@ -592,8 +592,8 @@ export const tableApi = new LuaTable({ * @param fromIndex - The index to start searching from. * @returns Lua multi value of index, value, or nil if no element is found. */ - find: new LuaBuiltinFunction( - async ( + find: new LuaBuiltinFunction({ + callback: async ( sf, tbl: LuaTable | any[], criteriaFn: ILuaFunction, @@ -614,8 +614,7 @@ export const tableApi = new LuaTable({ } return null; }, - { - kind: "builtin", + documentation: { description: "Finds the first array element accepted by a predicate and returns its index and value.", parameters: [ @@ -637,5 +636,5 @@ export const tableApi = new LuaTable({ }, ], }, - ), + }), }); diff --git a/client/space_lua_api.ts b/client/space_lua_api.ts index 72a3b87f..fd83b630 100644 --- a/client/space_lua_api.ts +++ b/client/space_lua_api.ts @@ -42,12 +42,18 @@ export function exposeSyscalls(env: LuaEnv, system: System) { ...syscall.documentation, }; const luaFn = isLuaNativeSyscall - ? new LuaBuiltinFunction((_sf, ...args) => { - return system.localSyscall(syscallName, args); - }, info) - : new LuaNativeJSFunction((...args) => { - return system.localSyscall(syscallName, args); - }, info); + ? new LuaBuiltinFunction({ + callback: (_sf, ...args) => { + return system.localSyscall(syscallName, args); + }, + documentation: info, + }) + : new LuaNativeJSFunction({ + callback: (...args) => { + return system.localSyscall(syscallName, args); + }, + documentation: info, + }); env.get(ns, nativeFs).set(fn, luaFn, nativeFs); } }