| 183 | ) |
| 184 | return false; |
| 185 | |
| 186 | const defaults = (i: number, kk: () => boolean): boolean => { |
| 187 | if (i === optionals.length) return kk(); |
| 188 | const o = optionals[i] as { name: string; default: Expression }; |
| 189 | return bindIn(env, o.name, o.default, () => defaults(i + 1, kk)); |
| 190 | }; |
| 191 | return defaults(0, () => m(others[0], expr, x, env, k)); |
| 192 | } |
| 193 | |
| 194 | function mSeq( |
| 195 | pats: Pat[], |
| 196 | exprs: ReadonlyArray<Expression>, |
| 197 | x: Expression, |
| 198 | env: Env, |
| 199 | k: () => boolean |
| 200 | ): boolean { |
| 201 | if (pats.length === 0) return k(); |
| 202 | return m(pats[0], exprs[0], x, env, () => |
| 203 | mSeq(pats.slice(1), exprs.slice(1), x, env, k) |
| 204 | ); |
| 205 | } |
| 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]; |
| 240 | const isLast = i === ordered.length - 1; |
| 241 | |
| 242 | // Final slot absorbs the rest (flat matching) |