| import { MacroParser } from './MacroParser.js'; |
| import { MacroCstWalker } from './MacroCstWalker.js'; |
| import { MacroRegistry } from './MacroRegistry.js'; |
| import { logMacroGeneralError, logMacroInternalError, logMacroRuntimeWarning, logMacroSyntaxWarning } from './MacroDiagnostics.js'; |
|
|
| |
| |
| |
|
|
| |
| |
| |
| |
| |
| let instance; |
| export { instance as MacroEngine }; |
|
|
| class MacroEngine { |
| static #instance; |
| static get instance() { return MacroEngine.#instance ?? (MacroEngine.#instance = new MacroEngine()); } |
|
|
| constructor() { } |
|
|
| |
| |
| |
| |
| |
| |
| |
| evaluate(input, env) { |
| if (!input) { |
| return ''; |
| } |
| const safeEnv = Object.freeze({ ...env }); |
|
|
| const preProcessed = this.#runPreProcessors(input, safeEnv); |
|
|
| const { cst, lexingErrors, parserErrors } = MacroParser.parseDocument(preProcessed); |
|
|
| |
| if (lexingErrors && lexingErrors.length > 0) { |
| logMacroSyntaxWarning({ phase: 'lexing', input, errors: lexingErrors }); |
| } |
| if (parserErrors && parserErrors.length > 0) { |
| logMacroSyntaxWarning({ phase: 'parsing', input, errors: parserErrors }); |
| } |
|
|
| |
| if (!cst || typeof cst !== 'object' || !cst.children) { |
| logMacroGeneralError({ message: 'Macro parser produced an invalid CST. Returning original input.', error: { input, lexingErrors, parserErrors } }); |
| return input; |
| } |
|
|
| let evaluated; |
| try { |
| evaluated = MacroCstWalker.evaluateDocument({ |
| text: preProcessed, |
| cst, |
| env: safeEnv, |
| resolveMacro: this.#resolveMacro.bind(this), |
| }); |
| } catch (error) { |
| logMacroGeneralError({ message: 'Macro evaluation failed. Returning original input.', error: { input, error } }); |
| return input; |
| } |
|
|
| const result = this.#runPostProcessors(evaluated, safeEnv); |
|
|
| return result; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| #resolveMacro(call) { |
| const { name, env } = call; |
|
|
| const raw = `{{${call.rawInner}}}`; |
| if (!name) return raw; |
|
|
| |
| |
| let defOverride = null; |
| if (Object.hasOwn(env.dynamicMacros, name)) { |
| const impl = env.dynamicMacros[name]; |
| defOverride = { |
| name, |
| aliases: [], |
| category: 'dynamic', |
| description: 'Dynamic macro', |
| minArgs: 0, |
| maxArgs: 0, |
| unnamedArgDefs: [], |
| list: null, |
| strictArgs: true, |
| returns: null, |
| returnType: 'string', |
| displayOverride: null, |
| exampleUsage: [], |
| source: { name: 'dynamic', isExtension: false, isThirdParty: false }, |
| aliasOf: null, |
| aliasVisible: null, |
| handler: typeof impl === 'function' ? impl : () => impl, |
| }; |
| } |
|
|
| |
| if (!defOverride && !MacroRegistry.hasMacro(name)) { |
| return raw; |
| } |
|
|
| try { |
| const result = MacroRegistry.executeMacro(call, { defOverride }); |
|
|
| try { |
| return call.env.functions.postProcess(result); |
| } catch (error) { |
| logMacroInternalError({ message: `Macro "${name}" postProcess function failed.`, call, error }); |
| return result; |
| } |
| } catch (error) { |
| const isRuntimeError = !!(error && (error.name === 'MacroRuntimeError' || error.isMacroRuntimeError)); |
| if (isRuntimeError) { |
| logMacroRuntimeWarning({ message: (error.message || `Macro "${name}" execution failed.`), call, error }); |
| } else { |
| logMacroInternalError({ message: `Macro "${name}" internal execution error.`, call, error }); |
| } |
| return raw; |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| #runPreProcessors(text, env) { |
| let result = text; |
|
|
| |
| |
| result = result.replace(/{{time_(UTC[+-]\d+)}}/gi, (_match, utcOffset) => { |
| return `{{time::${utcOffset}}}`; |
| }); |
|
|
| |
| |
| result = result.replace(/<USER>/gi, '{{user}}'); |
| result = result.replace(/<BOT>/gi, '{{char}}'); |
| result = result.replace(/<CHAR>/gi, '{{char}}'); |
| result = result.replace(/<GROUP>/gi, '{{group}}'); |
| result = result.replace(/<CHARIFNOTGROUP>/gi, '{{charIfNotGroup}}'); |
|
|
| return result; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| #runPostProcessors(text, env) { |
| let result = text; |
|
|
| |
| |
| |
| result = result.replace(/\\([{}])/g, '$1'); |
|
|
| |
| |
| |
| result = result.replace(/(?:\r?\n)*{{trim}}(?:\r?\n)*/gi, ''); |
|
|
| return result; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| normalizeMacroResult(value) { |
| if (value === null || value === undefined) { |
| return ''; |
| } |
| if (value instanceof Date) { |
| return value.toISOString(); |
| } |
| if (typeof value === 'object' || Array.isArray(value)) { |
| try { |
| return JSON.stringify(value); |
| } catch (_error) { |
| return String(value); |
| } |
| } |
|
|
| return String(value); |
| } |
| } |
|
|
| instance = MacroEngine.instance; |
|
|