233 lines
6.5 KiB
TypeScript
233 lines
6.5 KiB
TypeScript
|
|
/**
|
||
|
|
* Flattened, data-oriented execution IR for first-order logic formulas.
|
||
|
|
*
|
||
|
|
* The hot-path representation stores formulas in parallel typed arrays
|
||
|
|
* indexed by formulaId. No deep nesting exists at runtime; child
|
||
|
|
* relationships are expressed through numeric offsets into the same
|
||
|
|
* flat tables.
|
||
|
|
*
|
||
|
|
* Invariant: every formula referenced by a child pointer is stored in
|
||
|
|
* the same LogicIr instance. The root formula always has id 0.
|
||
|
|
*/
|
||
|
|
|
||
|
|
import type { ImhotepId, SourceOrigin } from './types.js'
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// Formula Kinds (must fit in Uint8)
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
export const FORMULA_KIND_FORALL = 1
|
||
|
|
export const FORMULA_KIND_EXISTS = 2
|
||
|
|
export const FORMULA_KIND_AND = 3
|
||
|
|
export const FORMULA_KIND_OR = 4
|
||
|
|
export const FORMULA_KIND_NOT = 5
|
||
|
|
export const FORMULA_KIND_IMPLIES = 6
|
||
|
|
export const FORMULA_KIND_PREDICATE = 7
|
||
|
|
|
||
|
|
export type FormulaKind =
|
||
|
|
| typeof FORMULA_KIND_FORALL
|
||
|
|
| typeof FORMULA_KIND_EXISTS
|
||
|
|
| typeof FORMULA_KIND_AND
|
||
|
|
| typeof FORMULA_KIND_OR
|
||
|
|
| typeof FORMULA_KIND_NOT
|
||
|
|
| typeof FORMULA_KIND_IMPLIES
|
||
|
|
| typeof FORMULA_KIND_PREDICATE
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// Logic IR Tables
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
export interface LogicIr {
|
||
|
|
/** Total number of formulas in this IR. */
|
||
|
|
formulaCount: number
|
||
|
|
|
||
|
|
// Formula classification (all Uint8Array)
|
||
|
|
formulaKind: Uint8Array
|
||
|
|
|
||
|
|
// Child pointers (Uint32Array, 0xFFFFFFFF means "none")
|
||
|
|
formulaLeft: Uint32Array
|
||
|
|
formulaRight: Uint32Array
|
||
|
|
|
||
|
|
// Binding region (for quantifiers)
|
||
|
|
// Each quantifier formula references a slice [bindingStart, bindingStart + bindingCount)
|
||
|
|
// in the flat binding table below.
|
||
|
|
bindingStart: Uint32Array
|
||
|
|
bindingCount: Uint8Array
|
||
|
|
|
||
|
|
// Predicate call region (for atomic formulas)
|
||
|
|
// Each predicate formula references a slice [argStart, argStart + argCount)
|
||
|
|
predicateId: Uint32Array
|
||
|
|
argStart: Uint32Array
|
||
|
|
argCount: Uint8Array
|
||
|
|
|
||
|
|
// Origin and proof tracking (Uint32Array)
|
||
|
|
originIndex: Uint32Array
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// Flat Binding Table
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
export interface BindingTable {
|
||
|
|
/** Number of bindings. */
|
||
|
|
count: number
|
||
|
|
|
||
|
|
// Variable names (string table index)
|
||
|
|
variableNameId: Uint32Array
|
||
|
|
|
||
|
|
// Domain descriptor index
|
||
|
|
domainId: Uint32Array
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// Flat Argument Table (for predicate calls)
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
export interface ArgTable {
|
||
|
|
/** Number of argument terms. */
|
||
|
|
count: number
|
||
|
|
|
||
|
|
// Term kind: 1 = variable, 2 = accessor, 3 = literal number
|
||
|
|
termKind: Uint8Array
|
||
|
|
|
||
|
|
// For variables: binding table index
|
||
|
|
// For accessors: variable binding index
|
||
|
|
// For literals: 0xFFFFFFFF (value stored in literalValue)
|
||
|
|
termRef: Uint32Array
|
||
|
|
|
||
|
|
// For accessors: string table index of property name
|
||
|
|
// For literals: the literal numeric value (reinterpreted as Uint32)
|
||
|
|
termAux: Uint32Array
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// Predicate Registry Index
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
export interface PredicateIndexEntry {
|
||
|
|
predicateId: number
|
||
|
|
name: string
|
||
|
|
arity: number
|
||
|
|
domainSignature: number[]
|
||
|
|
requiredFacts: string[]
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface PredicateIndex {
|
||
|
|
entries: PredicateIndexEntry[]
|
||
|
|
byName: Map<string, number>
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// String Table (shared across IR tables)
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
export interface IrStringTable {
|
||
|
|
values: string[]
|
||
|
|
byValue: Map<string, number>
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// Evaluation Request
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
export interface LogicEvaluationRequest {
|
||
|
|
logicIr: LogicIr
|
||
|
|
bindings: BindingTable
|
||
|
|
args: ArgTable
|
||
|
|
predicates: PredicateIndex
|
||
|
|
strings: IrStringTable
|
||
|
|
rootFormulaId: number
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// Builder
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
export interface LogicIrBuilder {
|
||
|
|
addFormula(descriptor: LogicFormulaDescriptor): number
|
||
|
|
addBinding(descriptor: LogicBindingDescriptor): number
|
||
|
|
addArg(descriptor: LogicArgDescriptor): number
|
||
|
|
build(): LogicIr
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface LogicFormulaDescriptor {
|
||
|
|
kind: FormulaKind
|
||
|
|
left?: number
|
||
|
|
right?: number
|
||
|
|
bindingStart?: number
|
||
|
|
bindingCount?: number
|
||
|
|
predicateId?: number
|
||
|
|
argStart?: number
|
||
|
|
argCount?: number
|
||
|
|
originIndex?: number
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface LogicBindingDescriptor {
|
||
|
|
variableNameId: number
|
||
|
|
domainId: number
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface LogicArgDescriptor {
|
||
|
|
termKind: number
|
||
|
|
termRef: number
|
||
|
|
termAux: number
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// Helper: Create empty Logic IR
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
export function createEmptyLogicIr(): LogicIr {
|
||
|
|
return {
|
||
|
|
formulaCount: 0,
|
||
|
|
formulaKind: new Uint8Array(0),
|
||
|
|
formulaLeft: new Uint32Array(0),
|
||
|
|
formulaRight: new Uint32Array(0),
|
||
|
|
bindingStart: new Uint32Array(0),
|
||
|
|
bindingCount: new Uint8Array(0),
|
||
|
|
predicateId: new Uint32Array(0),
|
||
|
|
argStart: new Uint32Array(0),
|
||
|
|
argCount: new Uint8Array(0),
|
||
|
|
originIndex: new Uint32Array(0),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
export function createEmptyBindingTable(): BindingTable {
|
||
|
|
return {
|
||
|
|
count: 0,
|
||
|
|
variableNameId: new Uint32Array(0),
|
||
|
|
domainId: new Uint32Array(0),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
export function createEmptyArgTable(): ArgTable {
|
||
|
|
return {
|
||
|
|
count: 0,
|
||
|
|
termKind: new Uint8Array(0),
|
||
|
|
termRef: new Uint32Array(0),
|
||
|
|
termAux: new Uint32Array(0),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
export function createEmptyStringTable(): IrStringTable {
|
||
|
|
return {
|
||
|
|
values: [],
|
||
|
|
byValue: new Map(),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// Helper: Intern a string into the string table
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
export function internString(table: IrStringTable, value: string): number {
|
||
|
|
const existing = table.byValue.get(value)
|
||
|
|
if (existing !== undefined) {
|
||
|
|
return existing
|
||
|
|
}
|
||
|
|
const id = table.values.length
|
||
|
|
table.values.push(value)
|
||
|
|
table.byValue.set(value, id)
|
||
|
|
return id
|
||
|
|
}
|