( expr: Expression, f: (x: Expression) => Promise<Expression | null> )
| 46 | if (y !== null) result.push(y); |
| 47 | } |
| 48 | } |
| 49 | } |
| 50 | return def?.associative && !hasContinuation |
| 51 | ? flatten(result, expr.operator, false) |
| 52 | : result; |
| 53 | } |
| 54 | |
| 55 | export async function holdMapAsync( |
| 56 | expr: Expression, |
| 57 | f: (x: Expression) => Promise<Expression | null> |
| 58 | ): Promise<ReadonlyArray<Expression>> { |
| 59 | if (!isFunction(expr)) return []; |
| 60 | |
| 61 | let xs = expr.ops; |
| 62 | |
| 63 | const def = expr.operatorDefinition; |
| 64 | |
| 65 | if (!def || xs.length === 0) return xs; |
| 66 | |
| 67 | // f(a, f(b, c), d) -> f(a, b, c, d) |
| 68 | // Ellipsis fold barrier (see `holdMap`): a `ContinuationPlaceholder` operand |
| 69 | // marks a notational sum/product; keep nested associative anchors intact. |
| 70 | const hasContinuation = xs.some((x) => isContinuationOperand(x)); |
| 71 | if (def?.associative && !hasContinuation) |
| 72 | xs = flatten(xs, expr.operator, false); |
| 73 | |
| 74 | // |
| 75 | // Apply the hold as necessary |
| 76 | // |
| 77 | if (def.lazy) return xs; |
| 78 | |
| 79 | const result: Expression[] = []; |
| 80 | for (const x of xs) { |
| 81 | const h = x.operator; |
| 82 | if (h === 'Hold') result.push(x); |
| 83 | else { |
| 84 | const op = h === 'ReleaseHold' && isFunction(x) ? x.op1 : x; |
no test coverage detected