mlld
Version:
mlld: llm scripting language
1,484 lines (1,476 loc) • 14 MB
JavaScript
#!/usr/bin/env node
'use strict';
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
var __esm = (fn, res) => function __init() {
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
};
var __commonJS = (cb, mod) => function __require() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
__defProp(target, "default", { value: mod, enumerable: true }) ,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// services/fs/NodeFileSystem.ts
var fs, path, _NodeFileSystem, NodeFileSystem;
var init_NodeFileSystem = __esm({
"services/fs/NodeFileSystem.ts"() {
fs = __toESM(require("fs/promises"));
path = __toESM(require("path"));
_NodeFileSystem = class _NodeFileSystem {
async readFile(filePath) {
return await fs.readFile(filePath, "utf-8");
}
async writeFile(filePath, content) {
const dir = path.dirname(filePath);
await fs.mkdir(dir, { recursive: true });
await fs.writeFile(filePath, content, "utf-8");
}
async exists(filePath) {
try {
await fs.access(filePath);
return true;
} catch {
return false;
}
}
async mkdir(dirPath, options2) {
await fs.mkdir(dirPath, options2);
}
async readdir(dirPath) {
return await fs.readdir(dirPath);
}
async isDirectory(filePath) {
try {
const stats = await fs.stat(filePath);
return stats.isDirectory();
} catch {
return false;
}
}
async stat(filePath) {
const stats = await fs.stat(filePath);
return {
isDirectory: /* @__PURE__ */ __name(() => stats.isDirectory(), "isDirectory"),
isFile: /* @__PURE__ */ __name(() => stats.isFile(), "isFile")
};
}
};
__name(_NodeFileSystem, "NodeFileSystem");
NodeFileSystem = _NodeFileSystem;
}
});
// services/fs/PathService.ts
var path2, _PathService, PathService;
var init_PathService = __esm({
"services/fs/PathService.ts"() {
path2 = __toESM(require("path"));
_PathService = class _PathService {
// Basic path operations
resolve(...segments) {
return path2.resolve(...segments);
}
relative(from, to) {
return path2.relative(from, to);
}
join(...segments) {
return path2.join(...segments);
}
dirname(filePath) {
return path2.dirname(filePath);
}
basename(filePath, ext) {
return path2.basename(filePath, ext);
}
extname(filePath) {
return path2.extname(filePath);
}
isAbsolute(filePath) {
return path2.isAbsolute(filePath);
}
normalize(filePath) {
return path2.normalize(filePath);
}
// URL support
isURL(path67) {
try {
const url = new URL(path67);
return url.protocol === "http:" || url.protocol === "https:";
} catch {
return false;
}
}
async validateURL(url) {
if (!this.isURL(url)) {
throw new Error(`Invalid URL: ${url}`);
}
return url;
}
async fetchURL(url) {
await this.validateURL(url);
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Failed to fetch URL: ${response.status} ${response.statusText}`);
}
const content = await response.text();
const headers = {};
response.headers.forEach((value, key) => {
headers[key] = value;
});
return { content, headers };
}
};
__name(_PathService, "PathService");
PathService = _PathService;
}
});
// grammar/generated/parser/grammar-core.js
var import_crypto, acorn, NodeType, DirectiveKind, helpers;
var init_grammar_core = __esm({
"grammar/generated/parser/grammar-core.js"() {
import_crypto = require("crypto");
acorn = __toESM(require("acorn"));
NodeType = {
Text: "Text",
Comment: "Comment",
CodeFence: "CodeFence",
MlldRunBlock: "MlldRunBlock",
VariableReference: "VariableReference",
Directive: "Directive",
PathSeparator: "PathSeparator",
DotSeparator: "DotSeparator",
Literal: "Literal",
SectionMarker: "SectionMarker",
Error: "Error",
Newline: "Newline",
StringLiteral: "StringLiteral",
Frontmatter: "Frontmatter",
CommandBase: "CommandBase",
Parameter: "Parameter",
ExecInvocation: "ExecInvocation",
CommandReference: "CommandReference",
FileReference: "FileReference",
BinaryExpression: "BinaryExpression",
TernaryExpression: "TernaryExpression",
UnaryExpression: "UnaryExpression",
WhenExpression: "WhenExpression"
};
DirectiveKind = {
run: "run",
var: "var",
// NEW: Replaces text/data
show: "show",
// NEW: Replaces add
exe: "exe",
// NEW: Replaces exec
for: "for",
// For loops
path: "path",
import: "import",
output: "output",
when: "when"
// NO deprecated entries - clean break!
};
helpers = {
debug(msg, ...args) {
if (process.env.DEBUG_MLLD_GRAMMAR)
console.log("[DEBUG GRAMMAR]", msg, ...args);
},
isExecutableReference(ref) {
if (!ref)
return false;
if (ref.type === "ExecInvocation")
return true;
if (ref.type === "FieldAccessExec")
return true;
if (ref.arguments !== void 0 && ref.arguments !== null)
return true;
if (ref.hasParentheses === true)
return true;
return false;
},
isLogicalLineStart(input, pos) {
if (pos === 0)
return true;
let i = pos - 1;
while (i >= 0 && " \r".includes(input[i]))
i--;
return i < 0 || input[i] === "\n";
},
// Context Detection System - Core Helper Methods
// ---------------------------------------------
/**
* Determines if the current position represents a slash directive context
* A slash directive context requires:
* 1. / symbol at logical line start
* 2. Followed by a valid directive keyword
*/
isSlashDirectiveContext(input, pos) {
if (input[pos] !== "/")
return false;
const isAtLineStart = this.isLogicalLineStart(input, pos);
if (!isAtLineStart)
return false;
const directiveKeywords = [...Object.keys(DirectiveKind), "log"];
const afterSlashPos = pos + 1;
for (const keyword of directiveKeywords) {
if (afterSlashPos + keyword.length > input.length)
continue;
const potentialKeyword = input.substring(afterSlashPos, afterSlashPos + keyword.length);
if (potentialKeyword === keyword) {
if (afterSlashPos + keyword.length === input.length)
return true;
const nextChar = input[afterSlashPos + keyword.length];
if (" \r\n".includes(nextChar))
return true;
}
}
return false;
},
/**
* Determines if the current position represents a variable reference context
* A variable context requires:
* 1. @ symbol NOT at logical line start, or
* 2. @ at line start but NOT followed by directive keyword
*/
isAtVariableContext(input, pos) {
if (input[pos] !== "@")
return false;
if (this.isSlashDirectiveContext(input, pos))
return false;
return true;
},
/**
* DEPRECATED: RHS slashes are no longer supported
* Keeping for reference but this should not be used
* @deprecated
*/
isRHSContext(input, pos) {
return false;
},
/**
* Determines if the current position represents plain text context
* Plain text is any context that isn't a directive, variable, or RHS
*/
isPlainTextContext(input, pos) {
return !this.isSlashDirectiveContext(input, pos) && !this.isAtVariableContext(input, pos);
},
/**
* Determines if the current position is within a run code block context
* This is used to identify language + code block patterns
*/
isInRunCodeBlockContext(input, pos) {
return false;
},
createNode(type, props) {
if (!props.location && process.env.DEBUG_MLLD_GRAMMAR) {
console.warn(`WARNING: Creating ${type} node without location data`);
if (process.env.DEBUG_MLLD_GRAMMAR_TRACE) {
console.trace();
}
}
return Object.freeze({
type,
nodeId: (0, import_crypto.randomUUID)(),
location: props.location,
// No fallback - let it be undefined if not provided
...props
});
},
createDirective(kind, data) {
return this.createNode(NodeType.Directive, { directive: { kind, ...data } });
},
// New method for creating directives with the updated structure
createStructuredDirective(kind, subtype, values, raw, meta, locationData, source = null) {
return this.createNode(NodeType.Directive, {
kind,
subtype,
source,
values,
raw,
meta,
location: locationData
});
},
createVariableReferenceNode(valueType, data, location) {
if (!location) {
throw new Error(`Location is required for createVariableReferenceNode (valueType: ${valueType}, identifier: ${data.identifier || "unknown"})`);
}
return this.createNode(NodeType.VariableReference, { valueType, ...data, location });
},
normalizePathVar(id) {
return id;
},
validateRunContent: /* @__PURE__ */ __name(() => true, "validateRunContent"),
validateDefineContent: /* @__PURE__ */ __name(() => true, "validateDefineContent"),
validatePath(pathParts, directiveKind) {
const raw = this.reconstructRawString(pathParts).trim();
let hasVariables = false;
if (pathParts && pathParts.length > 0) {
for (const node of pathParts) {
if (node.type === NodeType.VariableReference) {
hasVariables = true;
}
}
}
const finalFlags = {
hasVariables
};
const result = {
raw,
values: pathParts,
...finalFlags
};
this.debug("PATH", "validatePath final result:", JSON.stringify(result, null, 2));
return result;
},
getImportSubtype(list) {
if (!list)
return "importAll";
if (list.length === 0)
return "importAll";
if (list.length === 1 && list[0].name === "*")
return "importAll";
return "importSelected";
},
trace(pos, reason) {
},
reconstructRawString(nodes) {
if (!Array.isArray(nodes)) {
if (nodes && typeof nodes === "object") {
if (nodes.type === NodeType.Text)
return nodes.content || "";
if (nodes.type === NodeType.VariableReference) {
const varId = nodes.identifier;
const valueType = nodes.valueType;
const fields = nodes.fields || [];
let fieldPath = "";
for (const field of fields) {
if (field.type === "field" || field.type === "dot") {
fieldPath += `.${field.name || field.value}`;
} else if (field.type === "array") {
fieldPath += `[${field.index}]`;
}
}
if (valueType === "varInterpolation") {
return `{{${varId}${fieldPath}}}`;
} else if (valueType === "varIdentifier") {
return `@${varId}${fieldPath}`;
} else {
return `{{${varId}${fieldPath}}}`;
}
}
}
return String(nodes || "");
}
let raw = "";
for (const node of nodes) {
if (!node)
continue;
if (node.type === NodeType.Text) {
raw += node.content || "";
} else if (node.type === NodeType.VariableReference) {
const varId = node.identifier;
const valueType = node.valueType;
const fields = node.fields || [];
let fieldPath = "";
for (const field of fields) {
if (field.type === "field" || field.type === "dot") {
fieldPath += `.${field.name || field.value}`;
} else if (field.type === "array") {
fieldPath += `[${field.index}]`;
}
}
if (valueType === "varInterpolation") {
raw += `{{${varId}${fieldPath}}}`;
} else if (valueType === "varIdentifier") {
raw += `@${varId}${fieldPath}`;
} else {
raw += `{{${varId}${fieldPath}}}`;
}
} else if (node.type === NodeType.PathSeparator) {
raw += node.value || "";
} else if (node.type === NodeType.SectionMarker) {
raw += node.value || "";
} else if (node.type === NodeType.StringLiteral) {
raw += node.value || "";
} else if (typeof node === "string") {
raw += node;
} else {
raw += node.content || node.value || node.raw || "";
}
}
return raw;
},
createPathMetadata(rawPath, parts) {
return {
hasVariables: parts.some((p) => p && p.type === NodeType.VariableReference),
isAbsolute: rawPath.startsWith("/"),
hasExtension: /\.[a-zA-Z0-9]+$/.test(rawPath),
extension: rawPath.match(/\.([a-zA-Z0-9]+)$/)?.[1] || null
};
},
createCommandMetadata(parts) {
return {
hasVariables: parts.some((p) => p && p.type === NodeType.VariableReference)
};
},
createTemplateMetadata(parts, wrapperType) {
return {
hasVariables: parts.some((p) => p && (p.type === NodeType.VariableReference || p.type === NodeType.ExecInvocation)),
isTemplateContent: wrapperType === "doubleBracket"
};
},
createUrlMetadata(protocol, parts, hasSection = false) {
return {
isUrl: true,
protocol,
hasVariables: parts.some((p) => p && p.type === NodeType.VariableReference),
hasSection
};
},
ttlToSeconds(value, unit) {
const multipliers = {
"milliseconds": 1 / 1e3,
"seconds": 1,
"minutes": 60,
"hours": 3600,
"days": 86400,
"weeks": 604800
};
return value * (multipliers[unit] || 1);
},
detectFormatFromPath(path67) {
const ext = path67.match(/\.([a-zA-Z0-9]+)$/)?.[1]?.toLowerCase();
if (!ext)
return null;
const formatMap = {
"json": "json",
"xml": "xml",
"yaml": "yaml",
"yml": "yaml",
"csv": "csv",
"md": "markdown",
"markdown": "markdown",
"txt": "text",
"text": "text"
};
return formatMap[ext] || null;
},
createSectionMeta(pathParts, sectionParts, hasRename) {
return {
sourceType: "section",
hasVariables: [...pathParts, ...sectionParts].some((part) => part && part.type === "VariableReference"),
hasRename
};
},
reconstructSectionPath(pathParts, sectionParts) {
const pathStr = this.reconstructRawString(pathParts);
const sectionStr = this.reconstructRawString(sectionParts);
return `${pathStr} # ${sectionStr}`;
},
/**
* Checks if we're at a bracket that should end command parsing
* This uses a specific heuristic: ] at end of input OR ] on its own line
*/
isCommandEndingBracket(input, pos) {
if (input[pos] !== "]")
return false;
const nextPos = pos + 1;
if (nextPos >= input.length)
return true;
let i = nextPos;
while (i < input.length && (input[i] === " " || input[i] === " ")) {
i++;
}
return i >= input.length || input[i] === "\n";
},
/**
* Parse command content that may contain variables and text segments
* This is used by the CommandBracketContent rule to handle @var interpolation
*
* @param content - The content to parse
* @param baseLocation - The location of the content in the source
*/
parseCommandContent(content, baseLocation) {
const parts = [];
let i = 0;
let currentText = "";
let textStartOffset = 0;
if (!baseLocation) {
console.warn("parseCommandContent called without baseLocation");
return this.parseCommandContentLegacy(content);
}
let currentOffset = baseLocation.start.offset;
let currentLine = baseLocation.start.line;
let currentColumn = baseLocation.start.column;
while (i < content.length) {
if (content[i] === "@" && i + 1 < content.length) {
if (currentText) {
const textEndOffset = currentOffset;
const textEndLine = currentLine;
const textEndColumn = currentColumn;
parts.push(this.createNode(NodeType.Text, {
content: currentText,
location: {
start: {
offset: baseLocation.start.offset + textStartOffset,
line: baseLocation.start.line,
column: baseLocation.start.column + textStartOffset
},
end: {
offset: textEndOffset,
line: textEndLine,
column: textEndColumn
}
}
}));
currentText = "";
}
const varStartOffset = currentOffset;
const varStartLine = currentLine;
const varStartColumn = currentColumn;
i++;
currentOffset++;
currentColumn++;
let varName = "";
while (i < content.length && /[a-zA-Z0-9_]/.test(content[i])) {
varName += content[i];
i++;
currentOffset++;
currentColumn++;
}
if (varName) {
const varEndOffset = currentOffset;
const varEndLine = currentLine;
const varEndColumn = currentColumn;
parts.push(this.createVariableReferenceNode("varIdentifier", {
identifier: varName
}, {
start: { offset: varStartOffset, line: varStartLine, column: varStartColumn },
end: { offset: varEndOffset, line: varEndLine, column: varEndColumn }
}));
textStartOffset = i;
} else {
currentText += "@";
}
} else {
if (currentText === "") {
textStartOffset = i;
}
currentText += content[i];
if (content[i] === "\n") {
currentLine++;
currentColumn = 1;
} else {
currentColumn++;
}
currentOffset++;
i++;
}
}
if (currentText) {
parts.push(this.createNode(NodeType.Text, {
content: currentText,
location: {
start: {
offset: baseLocation.start.offset + textStartOffset,
line: baseLocation.start.line,
column: baseLocation.start.column + textStartOffset
},
end: {
offset: currentOffset,
line: currentLine,
column: currentColumn
}
}
}));
}
return parts;
},
/**
* Legacy version of parseCommandContent for backward compatibility
* Creates nodes without proper location data
*/
parseCommandContentLegacy(content) {
const parts = [];
let i = 0;
let currentText = "";
while (i < content.length) {
if (content[i] === "@" && i + 1 < content.length) {
if (currentText) {
parts.push(this.createNode(NodeType.Text, {
content: currentText
}));
currentText = "";
}
i++;
let varName = "";
while (i < content.length && /[a-zA-Z0-9_]/.test(content[i])) {
varName += content[i];
i++;
}
if (varName) {
parts.push(this.createNode(NodeType.Text, {
content: "@" + varName
}));
} else {
currentText += "@";
}
} else {
currentText += content[i];
i++;
}
}
if (currentText) {
parts.push(this.createNode(NodeType.Text, {
content: currentText
}));
}
return parts;
},
/**
* Create an ExecInvocation node
*/
createExecInvocation(commandRef, withClause, location) {
return this.createNode("ExecInvocation", {
commandRef,
withClause: withClause || null,
location
});
},
/**
* Get the command name from an ExecInvocation node
*/
getExecInvocationName(node) {
if (!node || node.type !== "ExecInvocation")
return null;
return node.commandRef?.identifier || node.commandRef?.name;
},
/**
* Check if a node is an ExecInvocation
*/
isExecInvocationNode(node) {
return node?.type === "ExecInvocation";
},
/**
* Parse a JavaScript code block using acorn to find the complete block
* This handles nested braces, strings, template literals, etc. properly
*
* @param input - The full input string
* @param startPos - Position after the opening brace
* @returns The parsed code content and end position, or null if invalid
*/
parseJavaScriptBlock(input, startPos) {
const potentialCode = input.substring(startPos);
let lastValidEnd = -1;
let lastValidCode = "";
for (let i = 0; i < potentialCode.length; i++) {
if (potentialCode[i] !== "}")
continue;
const testCode = potentialCode.substring(0, i);
try {
acorn.parse(`(${testCode})`, {
ecmaVersion: "latest",
allowReturnOutsideFunction: true
});
lastValidEnd = i;
lastValidCode = testCode;
} catch (e) {
try {
acorn.parse(testCode, {
ecmaVersion: "latest",
allowReturnOutsideFunction: true,
sourceType: "module"
});
lastValidEnd = i;
lastValidCode = testCode;
} catch (e2) {
}
}
}
if (lastValidEnd >= 0) {
return {
content: lastValidCode.trim(),
endPos: startPos + lastValidEnd
};
}
return null;
},
// Array vs Path disambiguation helpers for /var directive
createEmptyArray(location) {
return {
type: "array",
items: [],
location
};
},
createArrayFromContent(content, location) {
return {
type: "array",
items: content,
location
};
},
createSectionExtraction(content, location) {
return {
type: "section",
path: content.path,
section: content.section,
location
};
},
createPathDereference(content, location) {
return {
type: "path",
segments: content,
location
};
},
createObjectFromProperties(properties, location) {
return {
type: "object",
properties: properties || {},
location
};
},
// Error Recovery Helper Functions
// --------------------------------
/**
* Checks if an array is unclosed by scanning ahead
* Returns true if we hit a newline before finding the closing bracket
*/
isUnclosedArray(input, pos) {
let depth = 1;
let i = pos;
let hasHash = false;
this.debug("isUnclosedArray starting at pos", pos, "first 50 chars:", input.substring(pos, pos + 50));
while (i < input.length && depth > 0) {
const char = input[i];
if (char === "[") {
depth++;
this.debug("Found [ at", i, "depth now", depth);
} else if (char === "]") {
depth--;
this.debug("Found ] at", i, "depth now", depth);
} else if (char === "#" && depth === 1) {
hasHash = true;
this.debug("Found # at", i, "in brackets - this is section syntax");
} else if (char === "\n" && depth > 0) {
if (!hasHash) {
this.debug("Found newline at", i, "without # - unclosed array");
return true;
}
this.debug("Found newline at", i, "but has # - continuing scan");
}
i++;
}
const result = depth > 0;
this.debug("isUnclosedArray finished: result=", result, "hasHash=", hasHash, "depth=", depth, "scanned to pos", i);
return result;
},
/**
* Checks if an object is unclosed by scanning ahead
* Returns true if we hit a newline before finding the closing brace
*/
isUnclosedObject(input, pos) {
let depth = 1;
let i = pos;
let inString = false;
let stringChar = null;
while (i < input.length && depth > 0) {
const char = input[i];
if ((char === '"' || char === "'") && (i === 0 || input[i - 1] !== "\\")) {
if (!inString) {
inString = true;
stringChar = char;
} else if (char === stringChar) {
inString = false;
stringChar = null;
}
}
if (!inString) {
if (char === "{")
depth++;
else if (char === "}")
depth--;
else if (char === "\n" && depth > 0)
return true;
}
i++;
}
return depth > 0;
},
/**
* Checks if a string quote is unclosed
* Returns true if we hit a newline or end of input before finding the closing quote
*/
detectMissingQuoteClose(input, pos, quoteChar) {
let i = pos;
while (i < input.length) {
if (input[i] === quoteChar && input[i - 1] !== "\\")
return false;
if (input[i] === "\n")
return true;
i++;
}
return true;
},
/**
* Checks if a template delimiter (::) is unclosed
*/
isUnclosedTemplate(input, pos) {
let i = pos;
while (i < input.length - 1) {
if (input[i] === ":" && input[i + 1] === ":")
return false;
i++;
}
return true;
},
/**
* Checks if we're at the start of what looks like a multiline array
* (array with newline after opening bracket)
*/
isMultilineArrayStart(input, pos) {
let i = pos;
while (i < input.length && (input[i] === " " || input[i] === " ")) {
i++;
}
return i < input.length && input[i] === "\n";
},
/**
* Scans ahead to check if this looks like a valid language identifier for /run
*/
isValidLanguageKeyword(input, pos, lang) {
const validLanguages = ["js", "javascript", "node", "python", "py", "bash", "sh"];
return validLanguages.includes(lang.toLowerCase());
},
/**
* Checks if we're missing a 'from' keyword in an import statement
*/
isMissingFromKeyword(input, pos) {
let i = pos;
while (i < input.length && (input[i] === " " || input[i] === " ")) {
i++;
}
if (i < input.length) {
const char = input[i];
return char === '"' || char === "'" || char === "[" || char === "@";
}
return false;
},
/**
* Create an error with enhanced location tracking
* Since we can't access parser internals from here, we'll just throw
* a regular error and let the parser enhance it
*/
mlldError(message, expectedToken, loc) {
const error = new Error(message);
error.isMlldError = true;
error.expectedToken = expectedToken;
error.mlldErrorLocation = loc;
throw error;
},
// Parser State Management for Code Blocks
// ----------------------------------------
// These functions help prevent state corruption when parsing multiple
// complex functions in mlld-run blocks
/**
* Parser state tracking object
* Used to detect and prevent state corruption issues
*/
parserState: {
codeBlockDepth: 0,
braceDepth: 0,
inString: false,
stringChar: null,
lastDirectiveEndPos: -1,
functionCount: 0,
maxNestingDepth: 20
},
/**
* Reset parser state between functions
* This prevents state corruption when parsing multiple complex functions
*/
resetCodeParsingState() {
this.parserState.braceDepth = 0;
this.parserState.inString = false;
this.parserState.stringChar = null;
this.parserState.functionCount++;
this.debug("Parser state reset", {
functionCount: this.parserState.functionCount,
lastEndPos: this.parserState.lastDirectiveEndPos
});
},
/**
* Get current brace depth for debugging and limits
*/
getBraceDepth() {
return this.parserState.braceDepth;
},
/**
* Increment brace depth with overflow checking
*/
incrementBraceDepth() {
this.parserState.braceDepth++;
if (this.parserState.braceDepth > this.parserState.maxNestingDepth) {
this.mlldError(`Code block nesting too deep (${this.parserState.braceDepth} levels). Consider simplifying your function or splitting it into smaller functions.`);
}
},
/**
* Decrement brace depth with underflow checking
*/
decrementBraceDepth() {
this.parserState.braceDepth--;
if (this.parserState.braceDepth < 0) {
this.debug("WARNING: Brace depth underflow detected", {
depth: this.parserState.braceDepth,
functionCount: this.parserState.functionCount
});
this.parserState.braceDepth = 0;
}
},
/**
* Validate parser state consistency
* Returns true if state is valid, false if corrupted
*/
validateParserState() {
const isValid = this.parserState.braceDepth >= 0 && this.parserState.braceDepth <= this.parserState.maxNestingDepth;
if (!isValid) {
this.debug("Parser state validation failed", {
braceDepth: this.parserState.braceDepth,
inString: this.parserState.inString,
functionCount: this.parserState.functionCount
});
}
return isValid;
},
/**
* Mark the end of a directive for state tracking
*/
markDirectiveEnd(pos) {
this.parserState.lastDirectiveEndPos = pos;
},
// File Reference Helper Functions
// --------------------------------
/**
* Checks if content inside <...> represents a file reference
* File references are detected by presence of: . * @
* Note: We don't include / since we don't support directories
* Files without extensions can be used outside interpolation contexts
*/
isFileReferenceContent(content) {
if (content.trim().startsWith("!")) {
return false;
}
return /[.*@]/.test(content);
},
/**
* Creates a FileReference AST node
*/
createFileReferenceNode(source, fields, pipes, location) {
return {
type: "FileReference",
nodeId: (0, import_crypto.randomUUID)(),
source,
fields: fields || [],
pipes: pipes || [],
location,
meta: {
isFileReference: true,
hasGlob: typeof source === "object" && source.raw && source.raw.includes("*"),
isPlaceholder: source && source.type === "placeholder"
}
};
},
// Binary expression builder with left-to-right associativity
createBinaryExpression(first, rest, location) {
if (!rest || rest.length === 0)
return first;
return rest.reduce((left, { op, right }) => this.createNode("BinaryExpression", {
operator: op,
left,
right,
location
}), first);
},
// Check if nodes contain newlines
containsNewline(nodes) {
if (!Array.isArray(nodes))
nodes = [nodes];
return nodes.some((n) => n.type === "Newline" || n.content && n.content.includes("\n") || n.raw && n.raw.includes("\n"));
},
/**
* Creates a WhenExpression node for when expressions (used in /var assignments)
*/
createWhenExpression(conditions, withClause, location, modifier = null) {
return this.createNode(NodeType.WhenExpression, {
conditions,
withClause: withClause || null,
meta: {
conditionCount: conditions.length,
isValueReturning: true,
evaluationType: "expression",
hasTailModifiers: !!withClause,
modifier
},
location
});
},
/**
* Creates a ForExpression node for for...in expressions in /var assignments
*/
createForExpression(variable, source, expression, location, opts, batchPipeline) {
const meta = {
isForExpression: true
};
if (opts) {
meta.forOptions = opts;
}
if (batchPipeline) {
meta.batchPipeline = batchPipeline;
}
return {
type: "ForExpression",
nodeId: (0, import_crypto.randomUUID)(),
variable,
source,
expression: Array.isArray(expression) ? expression : [expression],
location,
meta
};
},
/**
* Creates an action node for /for directive actions
*/
createForActionNode(directive, content, location, endingTail) {
const kind = directive;
if (kind === "show" && content) {
if (content && typeof content === "object" && "content" in content && "wrapperType" in content) {
const values2 = { content: content.content };
if (endingTail && endingTail.pipeline) {
values2.pipeline = endingTail.pipeline;
}
return [this.createNode(NodeType.Directive, {
kind,
subtype: "showTemplate",
values: values2,
raw: { content: this.reconstructRawString(content.content) },
meta: { implicit: false, isTemplateContent: true },
location
})];
}
const isExec = content && typeof content === "object" && content.type === "ExecInvocation";
const values = { invocation: content };
if (endingTail && endingTail.pipeline) {
values.withClause = { pipeline: endingTail.pipeline };
}
return [this.createNode(NodeType.Directive, {
kind,
subtype: isExec ? "showInvocation" : "showVariable",
values,
raw: { content: this.reconstructRawString(content) },
meta: { implicit: false },
location
})];
}
return [this.createNode(NodeType.Directive, {
kind,
subtype: kind,
values: { content: Array.isArray(content) ? content : [content] },
raw: { content: this.reconstructRawString(content) },
meta: { implicit: false },
location
})];
},
/**
* Helper functions for expression context detection
*/
isSimpleCondition(expr) {
return expr.type === "VariableReference" || expr.type === "Literal" || expr.type === "UnaryExpression" && expr.operator === "!";
},
extractConditionVariables(expr) {
const variables = [];
function traverse(node) {
if (node.type === "VariableReference") {
variables.push(node.name || node.identifier);
} else if (node.left)
traverse(node.left);
if (node.right)
traverse(node.right);
if (node.operand)
traverse(node.operand);
}
__name(traverse, "traverse");
traverse(expr);
return [...new Set(variables)];
},
/**
* Unified pipeline processing helper
* Consolidates pipeline handling across directive contexts
*/
processPipelineEnding(values, raw, meta, ending) {
if (ending.tail) {
const pipeline = ending.tail.pipeline;
raw.pipeline = pipeline.map((cmd) => `@${cmd.rawIdentifier || cmd.name || cmd}`).join(" | ");
meta.hasPipeline = true;
if (ending.parallel) {
values.withClause = { pipeline, ...ending.parallel };
meta.withClause = { ...meta.withClause || {}, ...ending.parallel };
} else {
values.pipeline = pipeline;
}
}
if (ending.comment) {
meta.comment = ending.comment;
}
}
};
}
});
// grammar/generated/parser/deps/node-type.js
var node_type_default;
var init_node_type = __esm({
"grammar/generated/parser/deps/node-type.js"() {
init_grammar_core();
node_type_default = NodeType;
}
});
// grammar/generated/parser/deps/directive-kind.js
var directive_kind_default;
var init_directive_kind = __esm({
"grammar/generated/parser/deps/directive-kind.js"() {
init_grammar_core();
directive_kind_default = DirectiveKind;
}
});
// grammar/generated/parser/deps/helpers.js
var helpers_default;
var init_helpers = __esm({
"grammar/generated/parser/deps/helpers.js"() {
init_grammar_core();
helpers_default = helpers;
}
});
// grammar/generated/parser/parser.js
function peg$subclass(child, parent) {
function C() {
this.constructor = child;
}
__name(C, "C");
C.prototype = parent.prototype;
child.prototype = new C();
}
function peg$SyntaxError(message, expected, found, location) {
var self = Error.call(this, message);
if (Object.setPrototypeOf) {
Object.setPrototypeOf(self, peg$SyntaxError.prototype);
}
self.expected = expected;
self.found = found;
self.location = location;
self.name = "SyntaxError";
return self;
}
function peg$padEnd(str, targetLength, padString) {
padString = padString || " ";
if (str.length > targetLength) {
return str;
}
targetLength -= str.length;
padString += padString.repeat(targetLength);
return str + padString.slice(0, targetLength);
}
function peg$parse(input, options2) {
options2 = options2 !== void 0 ? options2 : {};
var peg$FAILED = {};
var peg$source = options2.grammarSource;
var peg$startRuleFunctions = { Start: peg$parseStart };
var peg$startRuleFunction = peg$parseStart;
var peg$c0 = ">>";
var peg$c1 = "<<";
var peg$c2 = "\n";
var peg$c3 = "::";
var peg$c4 = "```";
var peg$c5 = "mlld-run";
var peg$c6 = "---";
var peg$c7 = "'";
var peg$c8 = '"';
var peg$c9 = "-";
var peg$c10 = ".";
var peg$c11 = "true";
var peg$c12 = "false";
var peg$c13 = "null";
var peg$c14 = "*";
var peg$c15 = "none";
var peg$c16 = "retry";
var peg$c17 = "ms";
var peg$c18 = "s";
var peg$c19 = "m";
var peg$c20 = "h";
var peg$c21 = "d";
var peg$c22 = "w";
var peg$c23 = "y";
var peg$c24 = "[[";
var peg$c25 = "]]";
var peg$c26 = "\\";
var peg$c27 = "{{";
var peg$c28 = "}}";
var peg$c29 = "<";
var peg$c30 = "`";
var peg$c31 = "/";
var peg$c32 = "#";
var peg$c33 = "/var";
var peg$c34 = "/show";
var peg$c35 = "/run";
var peg$c36 = "/exe";
var peg$c37 = "/path";
var peg$c38 = "/import";
var peg$c39 = "/when";
var peg$c40 = "/output";
var peg$c41 = "?";
var peg$c42 = ":";
var peg$c43 = "||";
var peg$c44 = "&&";
var peg$c45 = "==";
var peg$c46 = "!=";
var peg$c47 = "~=";
var peg$c48 = "<=";
var peg$c49 = ">=";
var peg$c50 = "=";
var peg$c51 = ">";
var peg$c52 = "(";
var peg$c53 = ")";
var peg$c54 = "!";
var peg$c55 = "@";
var peg$c56 = "[?";
var peg$c57 = "]";
var peg$c58 = "[";
var peg$c59 = "/for";
var peg$c60 = "/end";
var peg$c61 = ":::";
var peg$c62 = "now";
var peg$c63 = "base";
var peg$c64 = "input";
var peg$c65 = "debug";
var peg$c66 = "pipeline";
var peg$c67 = "frontmatter";
var peg$c68 = "fm";
var peg$c69 = "\\\\";
var peg$c70 = "\r\n";
var peg$c71 = "{";
var peg$c72 = ",";
var peg$c73 = "}";
var peg$c74 = "https";
var peg$c75 = "http";
var peg$c76 = "://";
var peg$c77 = " as ";
var peg$c78 = "as";
var peg$c79 = " as";
var peg$c80 = "<>";
var peg$c81 = "run";
var peg$c82 = "|";
var peg$c83 = "template";
var peg$c84 = "when";
var peg$c85 = "first";
var peg$c86 = "=>";
var peg$c87 = "for";
var peg$c88 = "in";
var peg$c89 = "each";
var peg$c90 = "~";
var peg$c91 = "foreach";
var peg$c92 = "with";
var peg$c93 = "separator";
var peg$c94 = "parallel";
var peg$c95 = "output";
var peg$c96 = "to";
var peg$c97 = "log";
var peg$c98 = "show";
var peg$c99 = "var";
var peg$c100 = ";";
var peg$c101 = "trust";
var peg$c102 = "needs";
var peg$c103 = "@run";
var peg$c104 = "stdout";
var peg$c105 = "stderr";
var peg$c106 = "env";
var peg$c107 = "file";
var peg$c108 = "//";
var peg$c109 = "\\@";
var peg$c110 = "[(";
var peg$c111 = ")]";
var peg$c112 = "/*";
var peg$c113 = "*/";
var peg$c114 = "js";
var peg$c115 = "javascript";
var peg$c116 = "node";
var peg$c117 = "python";
var peg$c118 = "bash";
var peg$c119 = "sh";
var peg$c120 = "skip";
var peg$c121 = "stdin";
var peg$c122 = "format";
var peg$c123 = "asSection";
var peg$c124 = "delay";
var peg$c125 = "from";
var peg$c126 = "nodejs";
var peg$c127 = "py";
var peg$c128 = "risk.high";
var peg$c129 = "risk.med";
var peg$c130 = "risk.low";
var peg$c131 = "risk";
var peg$c132 = "about";
var peg$c133 = "meta";
var peg$c134 = "/export";
var peg$c135 = "@item";
var peg$c136 = "module";
var peg$c137 = "static";
var peg$c138 = "live";
var peg$c139 = "local";
var peg$c140 = "cached";
var peg$c141 = "@input";
var peg$c142 = "@now";
var peg$c143 = "@time";
var peg$c144 = "@stdin";
var peg$c145 = "mld";
var peg$c146 = "mlld";
var peg$c147 = "md";
var peg$c148 = ".md";
var peg$c149 = "env:";
var peg$c150 = "/log";
var peg$c151 = "PROJECTPATH";
var peg$c152 = "under";
var peg$c153 = "any";
var peg$c154 = "all";
var peg$r0 = /^[ \t\r]/;
var peg$r1 = /^[^\n]/;
var peg$r2 = /^[ \t]/;
var peg$r3 = /^[^`\r\n]/;
var peg$r4 = /^[0-9]/;
var peg$r5 = /^[a-zA-Z0-9_]/;
var peg$r6 = /^["$'@[-\]`{}]/;
var peg$r7 = /^["'.0\\nrt]/;
var peg$r8 = /^[ \t\r\n\/\]@${{}"'`]/;
var peg$r9 = /^[\/[\]@${}]/;
var peg$r10 = /^[\]\/[\]@${}]/;
var peg$r11 = /^[\]]/;
var peg$r12 = /^["@\n\r]/;
var peg$r13 = /^[a-zA-Z_]/;
var peg$r14 = /^[.~]/;
var peg$r15 = /^[<@`]/;
var peg$r16 = /^[<@]/;
var peg$r17 = /^[ \t\r\n\u200B\u200C\u200D]/;
var peg$r18 = /^[ \t\r\n]/;
var peg$r19 = /^[ \t\r\u200B\u200C\u200D]/;
var peg$r20 = /^[\r\u2028-\u2029]/;
var peg$r21 = /^[^\r\n]/;
var peg$r22 = /^[<"]/;
var peg$r23 = /^[`<@]/;
var peg$r24 = /^[a-zA-Z0-9.\-]/;
var peg$r25 = /^[^> ]/;
var peg$r26 = /^["'),`]/;
var peg$r27 = /^["'),\\`]/;
var peg$r28 = /^[`@<]/;
var peg$r29 = /^[ \t\r\n\/\]{}]/;
var peg$r30 = /^[^[\n]/;
var peg$r31 = /^[^\]]/;
var peg$r32 = /^[a-zA-Z0-9_@\-]/;
var peg$r33 = /^[a-zA-Z0-9_\/@\-]/;
var peg$r34 = /^[^ \t\n\r]/;
var peg$r35 = /^[^ \t\n\r"'[\]]/;
var peg$r36 = /^[^\/s]/;
var peg$r37 = /^[^"]/;
var peg$r38 = /^[^']/;
var peg$r39 = /^[^@\\s\n]/;
var peg$r40 = /^[\]"'\r\n]/;
var peg$r41 = /^[^}]/;
var peg$r42 = /^[ \t\n\r]/;
var peg$r43 = /^[\n\r]/;
var peg$r44 = /^[a-zA-Z@]/;
var peg$r45 = /^[^=]/;
var peg$r46 = /^[A-Za-z0-9_]/;
var peg$r47 = /^[^#\]]/;
var peg$r48 = /^[^\\s\\n]/;
var peg$r49 = /^[^)]/;
var peg$r50 = /^[a-zA-Z0-9_\-]/;
var peg$r51 = /^[a-f0-9]/;
var peg$e0 = peg$classExpectation([" ", " ", "\r"], false, false);
var peg$e1 = peg$literalExpectation(">>", false);
var peg$e2 = peg$literalExpectation("<<", false);
var peg$e3 = peg$classExpectation(["\n"], true, false);
var peg$e4 = peg$literalExpectation("\n", false);
var peg$e5 = peg$literalExpectation("::", false);
var peg$e6 = peg$anyExpectation();
var peg$e7 = peg$classExpectation([" ", " "], false, false);
var peg$e8 = peg$literalExpectation("```", false);
var peg$e9 = peg$literalExpectation("mlld-run", false);
var peg$e10 = peg$classExpectation(["`", "\r", "\n"], true, false);
var peg$e11 = peg$otherExpectation("Top-level directive context");
var peg$e12 = peg$otherExpectation("Variable reference context");
var peg$e13 = peg$otherExpectation("Right-hand side context");
var peg$e14 = peg$otherExpectation("Plain text context");
var peg$e15 = peg$otherExpectation("Run-style code block context");
var peg$e16 = peg$otherExpectation("Exec /run right-hand side context");
var peg$e17 = peg$otherExpectation("Path starting with @variable context");
var peg$e18 = peg$otherExpectation("Directive boundary");
var peg$e19 = peg$otherExpectation("YAML frontmatter");
var peg$e20 = peg$literalExpectation("---", false);
var peg$e21 = peg$otherExpectation("String Literal");
var peg$e22 = peg$literalExpectation("'", false);
var peg$e23 = peg$literalExpectation('"', false);
var peg$e24 = peg$otherExpectation("Number Literal");
var peg$e25 = peg$literalExpectation("-", false);
var peg$e26 = peg$classExpectation([["0", "9"]], false, false);
var peg$e27 = peg$literalExpectation(".", false);
var peg$e28 = peg$otherExpectation("Boolean Literal");
var peg$e29 = peg$literalExpectation("true", false);
var peg$e30 = peg$literalExpectation("false", false);
var peg$e31 = peg$otherExpectation("Null Literal");
var peg$e32 = peg$literalExpectation("null", false);
var peg$e33 = peg$otherExpectation("Wildcard Literal");
var peg$e34 = peg$literalExpectation("*", false);
var peg$e35 = peg$otherExpectation("none literal");
var peg$e36 = peg$literalExpectation("none", false);
var peg$e37 = peg$classExpectation([["a", "z"], ["A", "Z"], ["0", "9"], "_"], false, false);
var peg$e38 = peg$otherExpectation("Retry Literal");
var peg$e39 = peg$literalExpectation("retry", false);
var peg$e40 = peg$otherExpectation("Time Duration Literal");
var peg$e41 = peg$otherExpectation("Time Unit");
var peg$e42 = peg$literalExpectation("ms", false);
var peg$e43 = peg$literalExpectation("s", false);
var peg$e44 = peg$literalExpectation("m", false);
var peg$e45 = peg$literalExpectation("h", false);
var peg$e46 = peg$literalExpectation("d", false);
var peg$e47 = peg$literalExpectation("w", false);
var peg$e48 = peg$literalExpectation("y", false);
var peg$e49 = peg$otherExpectation("Multi-line Template Literal");
var peg$e50 = peg$literalExpectation("[[", false);
var peg$e51 = peg$literalExpectation("]]", false);
var peg$e52 = peg$otherExpectation("Escape sequence");
var peg$e53 = peg$literalExpectation("\\", false);
var peg$e54 = peg$classExpectation(['"', "$", "'", "@", ["[", "]"], "`", "{", "}"], false, false);
va