( ce: ComputeEngine, rule: RubiRule, id: string, priority: number )
| 182 | ) { |
| 183 | if (lhs[1] !== variable) out.add(lhs[1]); |
| 184 | } else if (Array.isArray(lhs)) |
| 185 | for (const a of lhs.slice(1)) lhsNames(a, variable, out); |
| 186 | return out; |
| 187 | } |
| 188 | |
| 189 | export function compileRule( |
| 190 | ce: ComputeEngine, |
| 191 | rule: RubiRule, |
| 192 | id: string, |
| 193 | priority: number |
| 194 | ): { rule: CompiledRule | null; reason?: string } { |
| 195 | const skeleton = skeletonize(rule.lhs, rule.variable); |
| 196 | let boxed: Expression; |
| 197 | try { |
| 198 | boxed = ce.expr(skeleton as any); |
| 199 | } catch (e) { |
| 200 | return { rule: null, reason: `box error: ${e}` }; |
| 201 | } |
| 202 | if (!boxed.isValid) |
| 203 | return { rule: null, reason: 'boxes to invalid expression' }; |
| 204 | |
| 205 | const pat = toPat(ce, toTimesPower(ce, boxed), null, 1); |
| 206 | const expected = lhsNames(rule.lhs, rule.variable); |
| 207 | const got = slotNames(pat); |
| 208 | const missing = [...expected].filter((n) => !got.has(n)); |
| 209 | if (missing.length > 0) |
| 210 | return { |
| 211 | rule: null, |
| 212 | reason: `slots folded away by canonicalization: ${missing.join(',')}`, |
| 213 | }; |
| 214 | |
| 215 | // dispatch pre-screen: a node pattern matches only its own root |
| 216 | // operator — unless it can collapse (all operands but one optional) |
| 217 | let rootOp: string | null = null; |
| 218 | if (pat.kind === 'node') { |
| 219 | const optionals = pat.ops.filter((p) => p.kind === 'optslot').length; |
| 220 | const collapsible = optionals >= 1 && pat.ops.length - optionals === 1; |
| 221 | if (!collapsible) rootOp = pat.op; |
| 222 | } |
| 223 | |
| 224 | return { |
| 225 | rule: { |
| 226 | id, |
| 227 | priority, |
| 228 | variable: rule.variable, |
| 229 | pat, |
| 230 | rootOp, |
| 231 | requiredHeads: requiredHeads(pat), |
| 232 | bindings: rule.bindings, |
| 233 | condition: rule.condition, |
| 234 | innerCondition: rule.innerCondition, |
| 235 | rhs: rule.rhs, |
| 236 | source: rule.source ?? '', // stripped from the shipped bundle |
| 237 | }, |
no test coverage detected