@struktoai/mirage-core
Version:
A Unified Virtual Filesystem For AI Agents
77 lines • 3.7 kB
JavaScript
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
import { expandNode } from "../expand/node.js";
import { expandPattern, expandRegex } from "../expand/pattern.js";
import { CondParser, PATTERN_OPS, condTokens } from "./test_tokens.js";
/**
* Build a structured condition tree from a `[[ ... ]]` node.
*
* The parser nests the expression by its own precedence (`=~` looser than
* `||`, `!` tighter than `=~`), so its tokens are read again as bash parses
* them.
*/
export async function expandDoubleBracket(node, context, executeFn, cs, view) {
return condNode(new CondParser(condTokens(node)).parse(), context, executeFn, cs, view);
}
/** Expand one parsed `[[ ]]` tree into a CondNode. */
async function condNode(tree, context, executeFn, cs, view) {
switch (tree.kind) {
case 'and':
case 'or': {
// Expansion is part of evaluating an operand, so skipped branches cannot
// run substitutions or observe state before the left side.
const { left, right } = tree;
const first = {
kind: 'lazy',
expand: () => condNode(left, context, executeFn, cs, view),
};
const second = {
kind: 'lazy',
expand: () => condNode(right, context, executeFn, cs, view),
};
return { kind: tree.kind, left: first, right: second };
}
case 'not':
return { kind: 'not', inner: await condNode(tree.inner, context, executeFn, cs, view) };
case 'group':
return condNode(tree.inner, context, executeFn, cs, view);
case 'unary':
return {
kind: 'unary',
op: tree.op,
operand: await expandNode(tree.operand, context, executeFn, cs, view),
};
case 'binary': {
const leftText = await expandNode(tree.left, context, executeFn, cs, view);
return comparison(tree.op, leftText, tree.right, context, executeFn, cs, view);
}
case 'word':
return { kind: 'word', value: await expandNode(tree.operand, context, executeFn, cs, view) };
}
}
/** A binary test, its right operand expanded as `op` reads it. */
async function comparison(op, leftText, right, context, executeFn, cs, view) {
if (PATTERN_OPS.has(op)) {
// The right side of a match is a pattern word; the shared expander
// renders its quoting into the matcher's dialect per segment (quoted
// literal, unquoted live), so the evaluator matches unconditionally. A
// wholly-literal pattern matches exactly itself.
const expand = op === '=~' ? expandRegex : expandPattern;
const pattern = await expand(right, context, executeFn, cs, view);
return { kind: 'binary', left: leftText, op, right: pattern };
}
const rightText = await expandNode(right, context, executeFn, cs, view);
return { kind: 'binary', left: leftText, op, right: rightText };
}
//# sourceMappingURL=test_expr.js.map