/** * Macro Parser and Processor * Parses HeroScript-style macros from markdown content */ class MacroParser { /** * Extract macros with improved parsing */ static extractMacros(content) { const macroRegex = /!!([\w.]+)\s*([\s\S]*?)(?=\n!!|\n#|$)/g; const macros = []; let match; while ((match = macroRegex.exec(content)) !== null) { const fullMatch = match[0]; const actionPart = match[1]; const paramsPart = match[2]; const [actor, method] = actionPart.includes('.') ? actionPart.split('.') : ['core', actionPart]; const params = this.parseParams(paramsPart); console.log(`[MacroParser] Extracted: !!${actor}.${method}`, params); macros.push({ fullMatch: fullMatch.trim(), actor, method, params, start: match.index, end: match.index + fullMatch.length }); } return macros; } /** * Parse HeroScript parameters with multiline support * Supports: * key: 'value' * key: '''multiline value''' * key: | * multiline * value */ static parseParams(paramsPart) { const params = {}; if (!paramsPart || !paramsPart.trim()) { return params; } let lines = paramsPart.split('\n'); let i = 0; while (i < lines.length) { const line = lines[i].trim(); if (!line) { i++; continue; } // Check for key: value pattern if (line.includes(':')) { const colonIndex = line.indexOf(':'); const key = line.substring(0, colonIndex).trim(); let value = line.substring(colonIndex + 1).trim(); // Handle triple-quoted multiline if (value.startsWith("'''")) { value = value.substring(3); const valueLines = [value]; i++; while (i < lines.length) { const contentLine = lines[i]; if (contentLine.trim().endsWith("'''")) { valueLines.push(contentLine.trim().slice(0, -3)); break; } valueLines.push(contentLine); i++; } // Remove leading whitespace from multiline const processedValue = this.dedent(valueLines.join('\n')); params[key] = processedValue; } // Handle pipe multiline else if (value === '|') { const valueLines = []; i++; while (i < lines.length && lines[i].startsWith('\t')) { valueLines.push(lines[i].substring(1)); // Remove tab i++; } i--; // Back up one since loop will increment const processedValue = this.dedent(valueLines.join('\n')); params[key] = processedValue; } // Handle quoted value else if (value.startsWith("'") && value.endsWith("'")) { params[key] = value.slice(1, -1); } // Handle unquoted value else { params[key] = value; } } i++; } console.log(`[MacroParser] Parsed parameters:`, params); return params; } /** * Remove common leading whitespace from multiline strings */ static dedent(text) { const lines = text.split('\n'); // Find minimum indentation let minIndent = Infinity; for (const line of lines) { if (line.trim().length === 0) continue; const indent = line.search(/\S/); minIndent = Math.min(minIndent, indent); } if (minIndent === Infinity) minIndent = 0; // Remove common indentation return lines .map(line => line.slice(minIndent)) .join('\n') .trim(); } } window.MacroParser = MacroParser;