refactor: eliminate remaining hardcoded predicate-name dispatch

Extraction.ts (3 fixes):
  - Replace 2 'inStackingContext' string checks with isVariableArityPredicate()
  - Replace 7-name diagnostic formatting if/else with spec-driven
    getPredicateSpec() checks (isDirectional → gap message,
    isSize → threshold hint, else generic)

Grammar.ts: Replace 8 hardcoded parser routing checks
  (atLeast/atMost/aspectRatio/between/clippedBy/attachedToScrollContainer/
  escapeClippingChainOf/inStackingContext) with SIZE_PREDICATE_NAMES and
  TOPOLOGY_PREDICATE_NAMES Sets derived from spec table.

Pipeline.ts: Replace 15-entry CODE_TO_CLAUSE_KIND map with runtime
  generation from PREDICATE_SPECS. Prefix derived from spec.isSize
  ('size.*') / validOptions.includes('axis') ('alignment.*') /
  else ('relation.*'). Manual override for aspectRatio code 15.

Proofs.ts: Replace 11-case switch(kind) with 5 spec-driven if/else
  branches categorized by validOptions presence (hasGap→directional,
  hasAxis→alignment) + 2 specific name checks (inside overflow,
  aspectRatio ratio). 11 predicate names → 0 hardcoded.

Lexer.ts: Export KEYWORDS map for conformance testing.

Conformance tests:
  - Solver: every BUILTIN_PREDICATES entry matches its PREDICATE_SPECS
    counterpart; every spec name (incl. aliases) has a registered
    evaluator with matching descriptor (2 tests)
  - DSL: every predicate name from collectAllPredicateNames() appears
    in the lexer KEYWORDS table (1 test)

598 SDK + 3 conformance + 57 E2E = 658 tests pass.
This commit is contained in:
John Dvorak
2026-05-22 13:15:35 -07:00
parent 283ab1b39f
commit 9df295b915
7 changed files with 238 additions and 165 deletions
+17 -18
View File
@@ -106,6 +106,7 @@ import type {
} from './diagnostics.js'
import { createDiagnostic } from './diagnostics.js'
import { PREDICATE_SPECS } from './predicate-specs.js'
// Adapter interfaces - implementations injected at runtime
interface ExtractorRequest {
@@ -433,25 +434,23 @@ export interface PipelineResult {
/**
* Reverse mapping from execution IR clause type codes to solver clause kinds.
* Mirrors the relationCodes table in imhotep-dsl/compiler.ts.
* Built from the predicate spec table — any predicate with a relationCode
* automatically gets a clause kind entry.
*/
const CODE_TO_CLAUSE_KIND: Record<number, string> = {
1: 'relation.leftOf',
2: 'relation.rightOf',
3: 'relation.above',
4: 'relation.below',
5: 'alignment.alignedWith',
6: 'alignment.leftAlignedWith',
7: 'alignment.rightAlignedWith',
8: 'alignment.topAlignedWith',
9: 'alignment.bottomAlignedWith',
10: 'alignment.centeredWithin',
11: 'relation.inside',
12: 'relation.contains',
13: 'relation.overlaps',
14: 'relation.separatedFrom',
15: 'size.aspectRatio',
}
const CODE_TO_CLAUSE_KIND: Record<number, string> = (() => {
const map: Record<number, string> = {}
for (const spec of PREDICATE_SPECS) {
if (!spec.relationCode) continue
let prefix = 'relation'
if (spec.isSize) prefix = 'size'
else if (spec.validOptions.includes('axis')) prefix = 'alignment'
map[spec.relationCode] = `${prefix}.${spec.name}`
}
return map
})()
// DSL keyword 'aspectRatioBetween' uses code 15; maps to the canonical
// 'aspectRatio' predicate which has no separate relationCode.
CODE_TO_CLAUSE_KIND[15] = 'size.aspectRatio'
// ---------------------------------------------------------------------------
// Main Pipeline Orchestrator