| 206 | |
| 207 | // Orderless + flat matching for Add/Multiply. |
| 208 | // |
| 209 | // Pattern operands are ordered deterministic-first (var, const, node, |
| 210 | // slots last) and assigned to expression operands by backtracking. |
| 211 | // Optional slots may bind their default instead of consuming an operand. |
| 212 | // The FINAL pattern operand, when it is a slot, absorbs all remaining |
| 213 | // expression operands ("u_. absorbs the rest of the product"). |
| 214 | function mAC( |
| 215 | pat: { op: string; ops: Pat[] }, |
| 216 | expr: Expression, |
| 217 | x: Expression, |
| 218 | env: Env, |
| 219 | k: () => boolean |
| 220 | ): boolean { |
| 221 | const exprOps = expr.ops!; |
| 222 | const rank = (p: Pat): number => |
| 223 | p.kind === 'var' |
| 224 | ? 0 |
| 225 | : p.kind === 'const' |
| 226 | ? 1 |
| 227 | : p.kind === 'node' |
| 228 | ? 2 |
| 229 | : p.kind === 'slot' |
| 230 | ? 3 |
| 231 | : 4; |
| 232 | const ordered = [...pat.ops].sort((a, b) => rank(a) - rank(b)); |
| 233 | |
| 234 | const used = new Array<boolean>(exprOps.length).fill(false); |
| 235 | const ce = expr.engine; |
| 236 | |
| 237 | const assign = (i: number): boolean => { |
| 238 | if (i === ordered.length) return used.every(Boolean) && k(); |
| 239 | const p = ordered[i]; |