/** * Timeline Builder - Constructs a nested timeline tree from trace events * * Child calls are embedded inside their parent steps, creating a visual * representation of the call stack depth. */ import { SourceClauseMap } from './clauses.js'; export interface TimelineStep { stepNumber: number; port: 'call' | 'exit' | 'redo' | 'fail' | 'merged'; level: number; goal: string; clause?: { head: string; body: string; line: number; }; unifications: Array<{ variable: string; value: string; }>; subgoals: Array<{ label: string; goal: string; }>; subgoalLabel?: string; subgoalTemplate?: string; subgoalInstantiated?: string; subgoalBindings?: Array<{ variable: string; value: string; fromStep: number; }>; result?: string; exitGoal?: string; resultBindings?: Array<{ variable: string; value: string; }>; queryVarState?: string; isRetry?: boolean; retryOfStep?: number; backtrackFromStep?: number; solutionIndex?: number; retryRejected?: Array<{ variable: string; value: string; }>; children: TimelineStep[]; } /** * Flatten a nested timeline tree into a flat array (depth-first order) * Useful for tests and backward compatibility */ export declare function flattenTimeline(steps: TimelineStep[]): TimelineStep[]; export interface TraceEvent { port: 'call' | 'exit' | 'redo' | 'fail' | 'solution'; level: number; goal: string; predicate: string; arguments?: any[]; clause?: { head: string; body: string; line: number; }; parent_info?: { level: number; goal: string; }; bindings?: Array<{ variable: string; value: string; }>; } /** One enumerated solution: its 1-based index and the query variables' bindings. */ export interface Solution { index: number; bindings: Array<{ variable: string; value: string; }>; } /** * Timeline Builder class - processes trace events into nested timeline steps */ export declare class TimelineBuilder { private events; private sourceClauseMap?; private originalQuery?; private steps; private stepCounter; private callStack; private stepMap; private activeCallStack; private exitedByLevel; private retryOrigin; private lastFailStep; private retryTrigger; private queryConjuncts; private currentSolution; private solutions; private parentOf; private subgoalSlots; constructor(events: TraceEvent[], sourceClauseMap?: SourceClauseMap | undefined, originalQuery?: string | undefined); /** * Check if a predicate is part of tracer infrastructure and should be filtered */ private isTracerPredicate; /** * Build the complete timeline from trace events * Returns root-level steps with children nested inside */ build(): TimelineStep[]; /** * Process a single event and build nested structure */ private processEvent; /** * A solution-boundary marker: record the solution with the current index and * bindings, then advance so subsequent steps belong to the next solution. */ private processSolution; /** The solutions enumerated during build(), in order. */ getSolutions(): Solution[]; /** * Process CALL event - create new step and push to active stack */ private processCall; /** * Process EXIT event - merge with CALL and pop from stack */ private processExit; /** * Process REDO event. Two distinct situations share this port: * * 1. The goal at this level is still running (its body failed), so Prolog is * retrying it with a different clause. The children accumulated so far * belong to the clause attempt that just failed, so discard them: the * surviving children must align with the clause that ultimately succeeds * (backfilled at EXIT). * * 2. The goal at this level already succeeded and Prolog is backtracking into * it for another solution — the usual case for a conjunction, whose goals * all share one trace level. That retry is a real execution step, so emit * it: the EXIT that follows is the *next* solution of the same goal and * must not be attributed to whatever else ran at this level meanwhile. */ private processRedo; /** * Open a step that re-enters `origin`, and make it the active frame for its * level so the EXIT that follows records the new solution against it. */ private pushRetryStep; /** * Forget a choice point that is being re-entered, so it cannot be picked twice. */ private dropExitedStep; /** * Find and consume the choice point a REDO at this level is re-entering. * * Sibling goals of a conjunction share a trace level, so the most recent EXIT * at that level is not necessarily the goal being redone. Prefer the newest * exited step whose goal matches (internal variable names differ between the * CALL and the REDO because backtracking undoes the bindings), and fall back * to the most recent one. * * Entries are never expired: Prolog only emits a REDO for a choice point that * is still live, and a newer entry always wins the search, so a superseded one * can only ever be picked when it is the genuine target. */ private takeExitedStep; /** * Process FAIL event */ private processFail; /** * Renumber steps in depth-first order for consistent display * Also assigns subgoalLabel and subgoalTemplate to children now that clause info is backfilled * Computes subgoalBindings by comparing template to actual goal */ private renumberStepsDepthFirst; /** * Work out what a step actually bound, by comparing the goal as it was CALLed * with the goal as it stood at EXIT: every argument the caller left open and * execution filled in. * * This replaces the old "the result is the last argument" assumption, which * misreads any predicate whose output is not last - `member(X, [a,b,c])` * reported `[a,b,c] = [a,b,c]` rather than `X = a`. * * Names come from the caller's vocabulary: the subgoal template (or, for a * top-level goal, the query conjunct), falling back to the matched clause * head. A position with no meaningful name is left out rather than shown * under an internal name like `_788`. */ private assignResultBindings; /** * Compute bindings that were applied to transform template into actual goal */ private computeSubgoalBindings; /** * Extract variable names from a term (variables start with uppercase or _) */ private extractVariablesFromTerm; /** * Extract the output variable from a goal template (typically the last argument) */ private extractOutputVariable; /** * Get source clause from the source clause map */ private getSourceClause; /** * Extract subgoals from a clause body */ private extractSubgoals; /** * Extract the result value from an EXIT goal. * * - Compound term `f(a, b, R)` -> the last argument is the result. * - `is/2` goal `6 is 16-10` -> the value bound to the LHS ("6"). * - Comparison goals (`<`, `>=`, ...) bind nothing -> empty string, which * suppresses the `=>` result line in the formatter. */ private extractResult; /** * Format subgoal with instantiation */ private formatSubgoalWithInstantiation; /** * Extract pattern match bindings by comparing goal with clause head */ private extractPatternMatchBindings; /** * Recursively extract bindings by comparing pattern term with value term */ private extractBindingsFromTermPair; private isSimpleVariable; private findOperator; private parseListPattern; private splitArguments; } //# sourceMappingURL=timeline.d.ts.map