Files
Imhotep/packages/imhotep-core/src/logic-ast.ts
T
John Dvorak aa69ddc52f fix: replace as any casts in AST walkers with typed guards + add options to PredicateCall
logic-ast.ts: add optional options?: Record<string, unknown> to
  PredicateCall interface. Previously any predicate needing options
  (e.g., space, dimension, tolerance) smuggled them via (node as any)
  .options, bypassing the type system entirely.

extraction.ts: replace all 18 (node as any).body/.left/.right etc.
  casts with proper type guard narrowing (isForAllFormula, isExistsFormula,
  isAndFormula, isOrFormula, isNotFormula, isImpliesFormula, isPredicateCall).
  Affected functions: collectPredicates, formulaNeedsCssLengthMetrics,
  usesLayoutSpace, computeRequiredFacts & nestDomAncestry, getSelectorsFromFormula.

595 SDK + 57 E2E tests pass.
2026-05-22 12:18:22 -07:00

187 lines
4.8 KiB
TypeScript

/**
* Pure first-order logic AST types for Imhotep V1.1.
*
* These nodes represent the deterministic scene logic layer:
* quantifiers, boolean connectives, predicate calls, and terms.
*
* All nodes follow the unist-style shape with source spans.
*/
import type { AstNode } from './ast.js'
import type { Position } from './types.js'
// ---------------------------------------------------------------------------
// Formula Union
// ---------------------------------------------------------------------------
export type FormulaNode =
| ForAllFormula
| ExistsFormula
| AndFormula
| OrFormula
| NotFormula
| ImpliesFormula
| PredicateCall
// ---------------------------------------------------------------------------
// Quantifier Formulas
// ---------------------------------------------------------------------------
export interface ForAllFormula extends AstNode {
type: 'FormulaNode'
kind: 'forall'
bindings: TupleBinding[]
body: FormulaNode
}
export interface ExistsFormula extends AstNode {
type: 'FormulaNode'
kind: 'exists'
bindings: TupleBinding[]
body: FormulaNode
}
// ---------------------------------------------------------------------------
// Boolean Connective Formulas
// ---------------------------------------------------------------------------
export interface AndFormula extends AstNode {
type: 'FormulaNode'
kind: 'and'
left: FormulaNode
right: FormulaNode
}
export interface OrFormula extends AstNode {
type: 'FormulaNode'
kind: 'or'
left: FormulaNode
right: FormulaNode
}
export interface NotFormula extends AstNode {
type: 'FormulaNode'
kind: 'not'
operand: FormulaNode
}
export interface ImpliesFormula extends AstNode {
type: 'FormulaNode'
kind: 'implies'
antecedent: FormulaNode
consequent: FormulaNode
}
// ---------------------------------------------------------------------------
// Atomic Formula: Predicate Call
// ---------------------------------------------------------------------------
export interface PredicateCall extends AstNode {
type: 'FormulaNode'
kind: 'predicate'
predicate: string
args: TermNode[]
options?: Record<string, unknown>
}
// ---------------------------------------------------------------------------
// Terms
// ---------------------------------------------------------------------------
export type TermNode = VariableRef | DomainRef | AccessorTerm
export interface VariableRef extends AstNode {
type: 'VariableRef'
name: string
}
export interface DomainRef extends AstNode {
type: 'DomainRef'
domain: string
selector?: string
parentVar?: string
}
export interface AccessorTerm extends AstNode {
type: 'AccessorTerm'
variable: string
property: string
}
// ---------------------------------------------------------------------------
// Tuple Binding (for multi-variable quantification)
// ---------------------------------------------------------------------------
export interface TupleBinding extends AstNode {
type: 'TupleBinding'
variables: string[]
domain: DomainRef
}
// ---------------------------------------------------------------------------
// Property Run Blocks
// ---------------------------------------------------------------------------
export interface PropertyRunBlock extends AstNode {
type: 'PropertyRunBlock'
mode: 'sampled' | 'enumerated'
inputDomain: unknown
body: AstNode[]
}
export interface SampledRunBlock extends AstNode {
type: 'SampledRunBlock'
arbitrary: unknown
numRuns?: number
seed?: number
body: AstNode[]
}
// ---------------------------------------------------------------------------
// Type Guards
// ---------------------------------------------------------------------------
export function isForAllFormula(node: FormulaNode): node is ForAllFormula {
return node.kind === 'forall'
}
export function isExistsFormula(node: FormulaNode): node is ExistsFormula {
return node.kind === 'exists'
}
export function isAndFormula(node: FormulaNode): node is AndFormula {
return node.kind === 'and'
}
export function isOrFormula(node: FormulaNode): node is OrFormula {
return node.kind === 'or'
}
export function isNotFormula(node: FormulaNode): node is NotFormula {
return node.kind === 'not'
}
export function isImpliesFormula(node: FormulaNode): node is ImpliesFormula {
return node.kind === 'implies'
}
export function isPredicateCall(node: FormulaNode): node is PredicateCall {
return node.kind === 'predicate'
}
export function isVariableRef(node: AstNode): node is VariableRef {
return node.type === 'VariableRef'
}
export function isDomainRef(node: AstNode): node is DomainRef {
return node.type === 'DomainRef'
}
export function isAccessorTerm(node: AstNode): node is AccessorTerm {
return node.type === 'AccessorTerm'
}
export function isTupleBinding(node: AstNode): node is TupleBinding {
return node.type === 'TupleBinding'
}