prolog-trace-viz 1.1.3 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +43 -30
  2. package/dist/analyzer.d.ts.map +1 -1
  3. package/dist/analyzer.js +106 -53
  4. package/dist/analyzer.js.map +1 -1
  5. package/dist/build-info.d.ts +4 -4
  6. package/dist/build-info.js +4 -4
  7. package/dist/build-info.js.map +1 -1
  8. package/dist/clauses.d.ts +11 -0
  9. package/dist/clauses.d.ts.map +1 -1
  10. package/dist/clauses.js +12 -0
  11. package/dist/clauses.js.map +1 -1
  12. package/dist/cli.d.ts +5 -7
  13. package/dist/cli.d.ts.map +1 -1
  14. package/dist/cli.js +2 -25
  15. package/dist/cli.js.map +1 -1
  16. package/dist/index.js +80 -22
  17. package/dist/index.js.map +1 -1
  18. package/dist/markdown-generator.d.ts +24 -0
  19. package/dist/markdown-generator.d.ts.map +1 -0
  20. package/dist/markdown-generator.js +124 -0
  21. package/dist/markdown-generator.js.map +1 -0
  22. package/dist/parser.d.ts +9 -0
  23. package/dist/parser.d.ts.map +1 -1
  24. package/dist/parser.js +67 -32
  25. package/dist/parser.js.map +1 -1
  26. package/dist/timeline-formatter.d.ts +9 -0
  27. package/dist/timeline-formatter.d.ts.map +1 -0
  28. package/dist/timeline-formatter.js +192 -0
  29. package/dist/timeline-formatter.js.map +1 -0
  30. package/dist/timeline.d.ts +177 -0
  31. package/dist/timeline.d.ts.map +1 -0
  32. package/dist/timeline.js +813 -0
  33. package/dist/timeline.js.map +1 -0
  34. package/dist/tree-formatter.d.ts +13 -0
  35. package/dist/tree-formatter.d.ts.map +1 -0
  36. package/dist/tree-formatter.js +136 -0
  37. package/dist/tree-formatter.js.map +1 -0
  38. package/dist/tree.d.ts +75 -0
  39. package/dist/tree.d.ts.map +1 -0
  40. package/dist/tree.js +267 -0
  41. package/dist/tree.js.map +1 -0
  42. package/dist/variable-tracker.d.ts +68 -0
  43. package/dist/variable-tracker.d.ts.map +1 -0
  44. package/dist/variable-tracker.js +216 -0
  45. package/dist/variable-tracker.js.map +1 -0
  46. package/dist/wrapper.d.ts.map +1 -1
  47. package/dist/wrapper.js +6 -20
  48. package/dist/wrapper.js.map +1 -1
  49. package/package.json +1 -1
  50. package/tracer.pl +127 -16
@@ -0,0 +1,216 @@
1
+ /**
2
+ * Variable Binding Tracker
3
+ *
4
+ * Tracks variable bindings through recursive Prolog execution by processing
5
+ * trace events in order and maintaining accumulated state.
6
+ */
7
+ export class VariableBindingTracker {
8
+ bindings = new Map();
9
+ topLevelQuery;
10
+ queryVarName;
11
+ constructor(originalQuery) {
12
+ this.topLevelQuery = originalQuery;
13
+ // Extract the query variable name (e.g., "X" from "append([1,2], [3,4], X)")
14
+ this.queryVarName = this.extractQueryVarName(originalQuery);
15
+ }
16
+ /**
17
+ * Extract the query variable name from the original query
18
+ * e.g., "append([1,2], [3,4], X)" -> "X"
19
+ * e.g., "factorial(3, Result)" -> "Result"
20
+ */
21
+ extractQueryVarName(query) {
22
+ const match = query.match(/^([^(]+)\((.*)\)$/);
23
+ if (!match)
24
+ return 'X'; // fallback
25
+ const args = this.splitArguments(match[2]);
26
+ const lastArg = args[args.length - 1]?.trim();
27
+ // Check if last argument is a variable (starts with uppercase or underscore)
28
+ if (lastArg && /^[A-Z_]/.test(lastArg)) {
29
+ return lastArg;
30
+ }
31
+ return 'X'; // fallback
32
+ }
33
+ /**
34
+ * Process a trace event and update bindings
35
+ */
36
+ processEvent(event) {
37
+ if (event.port === 'call') {
38
+ this.processCall(event);
39
+ }
40
+ else if (event.port === 'exit') {
41
+ this.processExit(event);
42
+ }
43
+ }
44
+ /**
45
+ * Process CALL event - extract variable relationships from parent_info
46
+ *
47
+ * Key insight: parent_info.goal shows how the PARENT sees its own result variable
48
+ * at the moment it's calling this child.
49
+ */
50
+ processCall(event) {
51
+ // Extract the result variable from this call's goal
52
+ const resultVar = this.extractResultVariable(event.goal);
53
+ if (!resultVar)
54
+ return;
55
+ // Initialize binding for this level
56
+ this.bindings.set(event.level, {
57
+ level: event.level,
58
+ resultVar,
59
+ resultPattern: resultVar, // Initially just the variable name
60
+ isResolved: false,
61
+ });
62
+ // If there's parent_info, it tells us how the parent sees ITS OWN result
63
+ if (event.parent_info && event.parent_info.goal) {
64
+ const parentGoal = event.parent_info.goal;
65
+ // Skip meta-calls and test predicates
66
+ if (parentGoal.includes('<meta-call>') || parentGoal.includes('test_')) {
67
+ return;
68
+ }
69
+ // Extract what the parent's result looks like from parent's perspective
70
+ const parentResultPattern = this.extractResultVariable(parentGoal);
71
+ if (!parentResultPattern)
72
+ return;
73
+ const parentLevel = event.parent_info.level;
74
+ const parentBinding = this.bindings.get(parentLevel);
75
+ if (parentBinding) {
76
+ // Save the parent's OLD result variable before updating
77
+ const parentOldResultVar = parentBinding.resultVar;
78
+ // Update parent's pattern - this is how parent sees its result NOW
79
+ parentBinding.resultPattern = parentResultPattern;
80
+ // Propagate: substitute the parent's OLD result variable with its NEW pattern in all ancestors
81
+ // e.g., if parent was "_79854" and now has pattern "[2|_79774]",
82
+ // then ancestors with "[1|_79854]" should become "[1|[2|_79774]]"
83
+ if (parentOldResultVar !== parentResultPattern) {
84
+ this.propagateBinding(parentLevel, parentOldResultVar, parentResultPattern);
85
+ }
86
+ }
87
+ }
88
+ }
89
+ /**
90
+ * Process EXIT event - we now know the final value
91
+ */
92
+ processExit(event) {
93
+ const binding = this.bindings.get(event.level);
94
+ if (!binding)
95
+ return;
96
+ // Extract the resolved value from EXIT goal
97
+ const resolvedValue = this.extractResultVariable(event.goal);
98
+ if (!resolvedValue)
99
+ return;
100
+ // Mark as resolved and update pattern
101
+ binding.isResolved = true;
102
+ binding.resultPattern = resolvedValue;
103
+ // Propagate this binding up to all parents that reference this variable
104
+ this.propagateBinding(event.level, binding.resultVar, resolvedValue);
105
+ }
106
+ /**
107
+ * Propagate a binding up through parent levels
108
+ */
109
+ propagateBinding(fromLevel, varName, value) {
110
+ // Find all levels that reference this variable and substitute
111
+ for (const [level, binding] of this.bindings.entries()) {
112
+ if (level < fromLevel && binding.resultPattern.includes(varName)) {
113
+ binding.resultPattern = this.substitute(binding.resultPattern, varName, value);
114
+ }
115
+ }
116
+ }
117
+ /**
118
+ * Get the accumulated query variable state for a given level
119
+ * This shows what the top-level query variable looks like at this point
120
+ */
121
+ getQueryVarState(level) {
122
+ // Find the top-level binding (lowest level number)
123
+ let topLevel = Infinity;
124
+ for (const lvl of this.bindings.keys()) {
125
+ if (lvl < topLevel) {
126
+ topLevel = lvl;
127
+ }
128
+ }
129
+ if (topLevel === Infinity)
130
+ return null;
131
+ const topBinding = this.bindings.get(topLevel);
132
+ if (!topBinding)
133
+ return null;
134
+ // Clean up the pattern to show holes
135
+ const cleaned = this.cleanupPattern(topBinding.resultPattern);
136
+ // Always show the state (whether partial or complete)
137
+ return `${this.queryVarName} = ${cleaned}`;
138
+ }
139
+ /**
140
+ * Extract the result variable/pattern from a goal
141
+ * For "append([1,2],[3,4],_79984)", returns "_79984"
142
+ * For "append([1,2],[3,4],[1|_79854])", returns "[1|_79854]"
143
+ */
144
+ extractResultVariable(goal) {
145
+ const match = goal.match(/^([^(]+)\((.*)\)$/);
146
+ if (!match)
147
+ return null;
148
+ const args = this.splitArguments(match[2]);
149
+ // Return the last argument (typically the result in Prolog predicates)
150
+ return args[args.length - 1]?.trim() || null;
151
+ }
152
+ /**
153
+ * Substitute a variable with a value in a pattern
154
+ * e.g., substitute("[1|_79854]", "_79854", "[2|_79774]") -> "[1|[2|_79774]]"
155
+ */
156
+ substitute(pattern, varName, value) {
157
+ // Simple string replacement for now
158
+ // TODO: Handle nested structures more carefully
159
+ return pattern.replace(new RegExp(`\\b${this.escapeRegex(varName)}\\b`, 'g'), value);
160
+ }
161
+ /**
162
+ * Clean up a pattern to show holes
163
+ * Replace unbound variables with ? and simplify nested lists
164
+ */
165
+ cleanupPattern(pattern) {
166
+ // Replace internal variables (_NNNN) with ?
167
+ let cleaned = pattern.replace(/_\d+/g, '?');
168
+ // Replace remaining unbound variables (uppercase identifiers)
169
+ cleaned = cleaned.replace(/\b[A-Z][A-Za-z0-9_]*\b/g, '?');
170
+ // Simplify nested list structures: [1|[2|[3,4]]] -> [1,2,3,4]
171
+ // Keep doing this until no more simplifications possible
172
+ let prev = '';
173
+ while (prev !== cleaned) {
174
+ prev = cleaned;
175
+ // Pattern: [X|[Y,...]] -> [X,Y,...]
176
+ cleaned = cleaned.replace(/\[([^\[\]|]+)\|\[([^\[\]]+)\]\]/g, '[$1,$2]');
177
+ }
178
+ return cleaned;
179
+ }
180
+ /**
181
+ * Split arguments respecting parentheses and brackets
182
+ */
183
+ splitArguments(argsStr) {
184
+ const args = [];
185
+ let current = '';
186
+ let depth = 0;
187
+ for (const char of argsStr) {
188
+ if (char === '(' || char === '[') {
189
+ depth++;
190
+ current += char;
191
+ }
192
+ else if (char === ')' || char === ']') {
193
+ depth--;
194
+ current += char;
195
+ }
196
+ else if (char === ',' && depth === 0) {
197
+ args.push(current.trim());
198
+ current = '';
199
+ }
200
+ else {
201
+ current += char;
202
+ }
203
+ }
204
+ if (current.trim()) {
205
+ args.push(current.trim());
206
+ }
207
+ return args;
208
+ }
209
+ /**
210
+ * Escape special regex characters
211
+ */
212
+ escapeRegex(str) {
213
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
214
+ }
215
+ }
216
+ //# sourceMappingURL=variable-tracker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"variable-tracker.js","sourceRoot":"","sources":["../src/variable-tracker.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"wrapper.d.ts","sourceRoot":"","sources":["../src/wrapper.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,aAAa;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CA+B7D;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CASjE;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,CAwBzF;AACD,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,QAAQ,CAAC,CAiBhF;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CA2BlE"}
1
+ {"version":3,"file":"wrapper.d.ts","sourceRoot":"","sources":["../src/wrapper.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,aAAa;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAgC7D;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CASjE;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,CAMzF;AACD,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,QAAQ,CAAC,CAiBhF;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CA2BlE"}
package/dist/wrapper.js CHANGED
@@ -11,7 +11,8 @@ import * as os from 'node:os';
11
11
  * - Tracer cleanup
12
12
  */
13
13
  export function generateWrapper(config) {
14
- const { prologContent, query, tracerPath } = config;
14
+ const { prologContent, query, depth, tracerPath } = config;
15
+ const maxDepth = depth || 100; // Default to 100 if not specified
15
16
  const lines = [
16
17
  `% Load custom tracer`,
17
18
  `:- ['${tracerPath}'].`,
@@ -25,7 +26,7 @@ export function generateWrapper(config) {
25
26
  lines.push('');
26
27
  lines.push('% Run trace with error handling');
27
28
  lines.push('run_trace :-');
28
- lines.push(' install_tracer,');
29
+ lines.push(` install_tracer(${maxDepth}),`);
29
30
  lines.push(' catch(');
30
31
  lines.push(` (${query.trim()}, export_trace_json('trace.json')),`);
31
32
  lines.push(' Error,');
@@ -57,24 +58,9 @@ export function calculateLineOffset(prologContent) {
57
58
  */
58
59
  export function mapWrapperLineToSource(wrapperLine, prologContent) {
59
60
  const offset = calculateLineOffset(prologContent);
60
- const sourceLineInWrapper = wrapperLine - offset;
61
- // Now we need to find which actual source line this corresponds to
62
- // by accounting for empty lines and comments that were preserved
63
- const sourceLines = prologContent.split('\n');
64
- let actualSourceLine = 0;
65
- let nonEmptyLineCount = 0;
66
- for (let i = 0; i < sourceLines.length; i++) {
67
- actualSourceLine = i + 1; // 1-based line numbers
68
- const line = sourceLines[i].trim();
69
- // Skip empty lines and comments when counting
70
- if (line.length > 0 && !line.startsWith('%') && !line.startsWith('/*')) {
71
- nonEmptyLineCount++;
72
- if (nonEmptyLineCount === sourceLineInWrapper) {
73
- return actualSourceLine;
74
- }
75
- }
76
- }
77
- return actualSourceLine; // Fallback to last line
61
+ // Simple mapping: wrapper line - offset = source line
62
+ // The wrapper includes all source lines as-is, so no need to skip empty lines or comments
63
+ return wrapperLine - offset;
78
64
  }
79
65
  export async function createTempWrapper(config) {
80
66
  const content = generateWrapper(config);
@@ -1 +1 @@
1
- {"version":3,"file":"wrapper.js","sourceRoot":"","sources":["../src/wrapper.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"wrapper.js","sourceRoot":"","sources":["../src/wrapper.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prolog-trace-viz",
3
- "version": "1.1.3",
3
+ "version": "2.1.0",
4
4
  "description": "Generate enhanced visual trace diagrams for Prolog query execution",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/tracer.pl CHANGED
@@ -3,10 +3,17 @@
3
3
 
4
4
  :- dynamic trace_event/1.
5
5
  :- dynamic trace_active/0.
6
+ :- dynamic max_trace_depth/1.
6
7
 
7
8
  %% install_tracer/0
8
- % Install the trace interception hook
9
+ % Install the trace interception hook with default depth
9
10
  install_tracer :-
11
+ install_tracer(100).
12
+
13
+ %% install_tracer/1
14
+ % Install the trace interception hook with specified max depth
15
+ install_tracer(MaxDepth) :-
16
+ asserta(max_trace_depth(MaxDepth)),
10
17
  asserta(trace_active),
11
18
  trace.
12
19
 
@@ -15,6 +22,7 @@ install_tracer :-
15
22
  remove_tracer :-
16
23
  notrace,
17
24
  retractall(trace_active),
25
+ retractall(max_trace_depth(_)),
18
26
  clear_trace.
19
27
 
20
28
  %% clear_trace/0
@@ -26,14 +34,47 @@ clear_trace :-
26
34
  % Hook predicate that intercepts trace events
27
35
  user:prolog_trace_interception(Port, Frame, _Choice, continue) :-
28
36
  trace_active,
37
+ % Check depth limit
38
+ prolog_frame_attribute(Frame, level, Level),
39
+ max_trace_depth(MaxDepth),
40
+ Level =< MaxDepth,
41
+ % Don't trace our own operations
42
+ prolog_frame_attribute(Frame, goal, Goal),
43
+ \+ is_tracer_goal(Goal),
29
44
  !,
30
45
  catch(
31
46
  capture_trace_event(Port, Frame),
32
47
  Error,
33
48
  handle_trace_error(Error, Port, Frame)
34
49
  ).
50
+ user:prolog_trace_interception(Port, Frame, _Choice, continue) :-
51
+ trace_active,
52
+ % Exceeded depth limit - record truncation marker once
53
+ prolog_frame_attribute(Frame, level, Level),
54
+ max_trace_depth(MaxDepth),
55
+ Level > MaxDepth,
56
+ \+ trace_event(truncated(MaxDepth)),
57
+ !,
58
+ assertz(trace_event(truncated(MaxDepth))).
35
59
  user:prolog_trace_interception(_, _, _, continue).
36
60
 
61
+ %% is_tracer_goal(+Goal)
62
+ % Check if a goal is part of the tracer infrastructure
63
+ is_tracer_goal(Goal) :-
64
+ functor(Goal, Functor, _),
65
+ tracer_predicate(Functor).
66
+
67
+ tracer_predicate(trace_event).
68
+ tracer_predicate(call_goal).
69
+ tracer_predicate(assertz).
70
+ tracer_predicate(retract).
71
+ tracer_predicate(retractall).
72
+ tracer_predicate(findall).
73
+ tracer_predicate(format).
74
+ tracer_predicate(write).
75
+ tracer_predicate(open).
76
+ tracer_predicate(close).
77
+
37
78
  %% handle_trace_error(+Error, +Port, +Frame)
38
79
  % Handle errors during trace event capture
39
80
  handle_trace_error(Error, Port, Frame) :-
@@ -61,11 +102,14 @@ capture_trace_event(Port, Frame) :-
61
102
  ; Arguments = []
62
103
  ),
63
104
 
64
- % Extract clause information
65
- extract_clause_info(Frame, Goal, ClauseInfo),
105
+ % Extract clause information with enhanced data
106
+ extract_clause_info_enhanced(Frame, Goal, Port, ClauseInfo),
66
107
 
67
- % Record the event
68
- assertz(trace_event(event(Port, Level, Goal, Arguments, ClauseInfo, Predicate))).
108
+ % Extract parent information
109
+ extract_parent_info(Frame, ParentInfo),
110
+
111
+ % Record the event with enhanced information
112
+ assertz(trace_event(event(Port, Level, Goal, Arguments, ClauseInfo, Predicate, ParentInfo))).
69
113
 
70
114
  %% extract_frame_arguments(+Frame, +Arity, -Arguments)
71
115
  % Extract actual argument values from a frame
@@ -94,6 +138,27 @@ extract_clause_info(Frame, Goal, clause(Head, Body, Line)) :-
94
138
  !.
95
139
  extract_clause_info(_, _, no_clause).
96
140
 
141
+ %% extract_clause_info_enhanced(+Frame, +Goal, +Port, -ClauseInfo)
142
+ % Extract clause information - keep it simple, TypeScript will handle variable names
143
+ extract_clause_info_enhanced(Frame, Goal, _, ClauseInfo) :-
144
+ extract_clause_info(Frame, Goal, ClauseInfo).
145
+
146
+ %% extract_parent_info(+Frame, -ParentInfo)
147
+ % Extract information about the parent frame
148
+ extract_parent_info(Frame, parent(ParentLevel, ParentGoal)) :-
149
+ catch(
150
+ (
151
+ prolog_frame_attribute(Frame, parent, ParentFrame),
152
+ ParentFrame \= 0,
153
+ prolog_frame_attribute(ParentFrame, level, ParentLevel),
154
+ prolog_frame_attribute(ParentFrame, goal, ParentGoal)
155
+ ),
156
+ _,
157
+ fail
158
+ ),
159
+ !.
160
+ extract_parent_info(_, no_parent).
161
+
97
162
  %% export_trace_json(+File)
98
163
  % Export all trace events as JSON
99
164
  export_trace_json(File) :-
@@ -115,16 +180,24 @@ write_events_list(_, []).
115
180
  write_events_list(Stream, [Event]) :-
116
181
  !,
117
182
  write(Stream, ' '),
118
- write_json_event(Stream, Event).
183
+ write_json_event_or_marker(Stream, Event).
119
184
  write_events_list(Stream, [Event|Rest]) :-
120
185
  write(Stream, ' '),
121
- write_json_event(Stream, Event),
186
+ write_json_event_or_marker(Stream, Event),
122
187
  write(Stream, ',\n'),
123
188
  write_events_list(Stream, Rest).
124
189
 
190
+ %% write_json_event_or_marker(+Stream, +EventOrMarker)
191
+ % Write either a regular event or a truncation marker
192
+ write_json_event_or_marker(Stream, truncated(MaxDepth)) :-
193
+ !,
194
+ format(Stream, '{"truncated": true, "max_depth": ~w}', [MaxDepth]).
195
+ write_json_event_or_marker(Stream, Event) :-
196
+ write_json_event(Stream, Event).
197
+
125
198
  %% write_json_event(+Stream, +Event)
126
199
  % Write a single event as JSON object
127
- write_json_event(Stream, event(Port, Level, Goal, Arguments, ClauseInfo, Predicate)) :-
200
+ write_json_event(Stream, event(Port, Level, Goal, Arguments, ClauseInfo, Predicate, ParentInfo)) :-
128
201
  format(Stream, '{', []),
129
202
  format(Stream, '"port": "~w"', [Port]),
130
203
  format(Stream, ', "level": ~w', [Level]),
@@ -140,18 +213,56 @@ write_json_event(Stream, event(Port, Level, Goal, Arguments, ClauseInfo, Predica
140
213
  ),
141
214
 
142
215
  % Write clause info if present
143
- ( ClauseInfo = clause(Head, Body, Line)
144
- -> format(Stream, ', "clause": {', []),
145
- format(Stream, '"head": ', []),
146
- write_json_term(Stream, Head),
147
- format(Stream, ', "body": ', []),
148
- write_json_term(Stream, Body),
149
- format(Stream, ', "line": ~w', [Line]),
150
- format(Stream, '}', [])
216
+ write_clause_info_json(Stream, ClauseInfo),
217
+
218
+ % Write parent info if present
219
+ write_parent_info_json(Stream, ParentInfo),
220
+
221
+ format(Stream, '}', []).
222
+ % Backward compatibility: handle old event format without ParentInfo
223
+ write_json_event(Stream, event(Port, Level, Goal, Arguments, ClauseInfo, Predicate)) :-
224
+ format(Stream, '{', []),
225
+ format(Stream, '"port": "~w"', [Port]),
226
+ format(Stream, ', "level": ~w', [Level]),
227
+ format(Stream, ', "goal": ', []),
228
+ write_json_term(Stream, Goal),
229
+ format(Stream, ', "predicate": "~w"', [Predicate]),
230
+
231
+ % Write arguments if present (exit port)
232
+ ( Arguments \= []
233
+ -> format(Stream, ', "arguments": ', []),
234
+ write_json_list(Stream, Arguments)
151
235
  ; true
152
236
  ),
153
237
 
238
+ % Write clause info if present
239
+ write_clause_info_json(Stream, ClauseInfo),
240
+
241
+ format(Stream, '}', []).
242
+
243
+ %% write_clause_info_json(+Stream, +ClauseInfo)
244
+ % Write clause information as JSON
245
+ write_clause_info_json(Stream, clause(Head, Body, Line)) :-
246
+ !,
247
+ format(Stream, ', "clause": {', []),
248
+ format(Stream, '"head": ', []),
249
+ write_json_term(Stream, Head),
250
+ format(Stream, ', "body": ', []),
251
+ write_json_term(Stream, Body),
252
+ format(Stream, ', "line": ~w', [Line]),
253
+ format(Stream, '}', []).
254
+ write_clause_info_json(_, no_clause).
255
+
256
+ %% write_parent_info_json(+Stream, +ParentInfo)
257
+ % Write parent information as JSON
258
+ write_parent_info_json(Stream, parent(ParentLevel, ParentGoal)) :-
259
+ !,
260
+ format(Stream, ', "parent_info": {', []),
261
+ format(Stream, '"level": ~w', [ParentLevel]),
262
+ format(Stream, ', "goal": ', []),
263
+ write_json_term(Stream, ParentGoal),
154
264
  format(Stream, '}', []).
265
+ write_parent_info_json(_, no_parent).
155
266
 
156
267
  %% write_json_term(+Stream, +Term)
157
268
  % Write a Prolog term as JSON string