161 lines
4.6 KiB
TypeScript
161 lines
4.6 KiB
TypeScript
import {
|
|
LuaBuiltinFunction,
|
|
luaCall,
|
|
LuaMultiRes,
|
|
LuaTable,
|
|
luaToString,
|
|
} from "$common/space_lua/runtime.ts";
|
|
|
|
export const stringApi = new LuaTable({
|
|
byte: new LuaBuiltinFunction((_sf, s: string, i?: number, j?: number) => {
|
|
i = i ?? 1;
|
|
j = j ?? i;
|
|
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);
|
|
}),
|
|
find: new LuaBuiltinFunction(
|
|
(_sf, s: string, pattern: string, init?: number, plain?: boolean) => {
|
|
init = init ?? 1;
|
|
plain = plain ?? false;
|
|
const result = s.slice(init - 1).match(pattern);
|
|
if (!result) {
|
|
return new LuaMultiRes([]);
|
|
}
|
|
return new LuaMultiRes([
|
|
result.index! + 1,
|
|
result.index! + result[0].length,
|
|
]);
|
|
},
|
|
),
|
|
gmatch: new LuaBuiltinFunction((_sf, s: string, pattern: string) => {
|
|
const regex = new RegExp(pattern, "g");
|
|
return () => {
|
|
const result = regex.exec(s);
|
|
if (!result) {
|
|
return;
|
|
}
|
|
return new LuaMultiRes(result.slice(1));
|
|
};
|
|
}),
|
|
gsub: new LuaBuiltinFunction(
|
|
async (
|
|
sf,
|
|
s: string,
|
|
pattern: string,
|
|
repl: any, // string or LuaFunction
|
|
n?: number,
|
|
) => {
|
|
n = n ?? Infinity;
|
|
|
|
// Convert Lua patterns to JavaScript regex
|
|
// This handles:
|
|
// - %.: Match literal dot
|
|
// - %%: Match literal %
|
|
// - %d: Match digit
|
|
// - %s: Match whitespace
|
|
// - %w: Match word character
|
|
const jsPattern = pattern
|
|
.replace(/%(.)/g, (_, char) => {
|
|
switch (char) {
|
|
case ".":
|
|
return "[.]"; // Match literal dot using character class
|
|
case "%":
|
|
return "%"; // Match literal %
|
|
case "d":
|
|
return "\\d"; // Match digit
|
|
case "s":
|
|
return "\\s"; // Match whitespace
|
|
case "w":
|
|
return "\\w"; // Match word character
|
|
default:
|
|
return char; // Match literal character
|
|
}
|
|
});
|
|
|
|
const regex = new RegExp(jsPattern, "g");
|
|
let result = s;
|
|
let count = 0;
|
|
|
|
// Collect all matches first to handle replacements properly
|
|
const positions: Array<[number, number, string, string[]]> = [];
|
|
let match: RegExpExecArray | null;
|
|
let lastIndex = 0;
|
|
|
|
while ((match = regex.exec(result)) !== null && count < n) {
|
|
if (match.index >= lastIndex) {
|
|
positions.push([
|
|
match.index,
|
|
match[0].length,
|
|
match[0],
|
|
match.slice(1),
|
|
]);
|
|
count++;
|
|
lastIndex = match.index + 1;
|
|
}
|
|
regex.lastIndex = match.index + 1;
|
|
}
|
|
|
|
// Process replacements in reverse order to maintain string indices
|
|
for (let i = positions.length - 1; i >= 0; i--) {
|
|
const [index, length, fullMatch, captures] = positions[i];
|
|
|
|
let replacement: any;
|
|
if (repl.call) {
|
|
const args = captures.length > 0 ? captures : [fullMatch];
|
|
replacement = await luaCall(repl, args, sf.astCtx!, sf);
|
|
replacement = (replacement === null || replacement === undefined)
|
|
? fullMatch
|
|
: replacement;
|
|
} else {
|
|
replacement = repl;
|
|
}
|
|
|
|
result = result.slice(0, index) +
|
|
replacement +
|
|
result.slice(index + length);
|
|
}
|
|
|
|
return new LuaMultiRes([result, count]);
|
|
},
|
|
),
|
|
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?: number) => {
|
|
init = init ?? 1;
|
|
const result = s.slice(init - 1).match(pattern);
|
|
if (!result) {
|
|
return new LuaMultiRes([]);
|
|
}
|
|
return new LuaMultiRes(result.slice(1));
|
|
},
|
|
),
|
|
rep: new LuaBuiltinFunction((_sf, s: string, n: number, sep?: string) => {
|
|
sep = sep ?? "";
|
|
return s.repeat(n) + sep;
|
|
}),
|
|
reverse: new LuaBuiltinFunction((_sf, s: string) => {
|
|
return s.split("").reverse().join("");
|
|
}),
|
|
sub: new LuaBuiltinFunction((_sf, s: string, i: number, j?: number) => {
|
|
j = j ?? s.length;
|
|
return s.slice(i - 1, j);
|
|
}),
|
|
split: new LuaBuiltinFunction((_sf, s: string, sep: string) => {
|
|
return s.split(sep);
|
|
}),
|
|
});
|