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.
- package/README.md +43 -30
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +106 -53
- package/dist/analyzer.js.map +1 -1
- package/dist/build-info.d.ts +4 -4
- package/dist/build-info.js +4 -4
- package/dist/build-info.js.map +1 -1
- package/dist/clauses.d.ts +11 -0
- package/dist/clauses.d.ts.map +1 -1
- package/dist/clauses.js +12 -0
- package/dist/clauses.js.map +1 -1
- package/dist/cli.d.ts +5 -7
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +2 -25
- package/dist/cli.js.map +1 -1
- package/dist/index.js +80 -22
- package/dist/index.js.map +1 -1
- package/dist/markdown-generator.d.ts +24 -0
- package/dist/markdown-generator.d.ts.map +1 -0
- package/dist/markdown-generator.js +124 -0
- package/dist/markdown-generator.js.map +1 -0
- package/dist/parser.d.ts +9 -0
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +67 -32
- package/dist/parser.js.map +1 -1
- package/dist/timeline-formatter.d.ts +9 -0
- package/dist/timeline-formatter.d.ts.map +1 -0
- package/dist/timeline-formatter.js +192 -0
- package/dist/timeline-formatter.js.map +1 -0
- package/dist/timeline.d.ts +177 -0
- package/dist/timeline.d.ts.map +1 -0
- package/dist/timeline.js +813 -0
- package/dist/timeline.js.map +1 -0
- package/dist/tree-formatter.d.ts +13 -0
- package/dist/tree-formatter.d.ts.map +1 -0
- package/dist/tree-formatter.js +136 -0
- package/dist/tree-formatter.js.map +1 -0
- package/dist/tree.d.ts +75 -0
- package/dist/tree.d.ts.map +1 -0
- package/dist/tree.js +267 -0
- package/dist/tree.js.map +1 -0
- package/dist/variable-tracker.d.ts +68 -0
- package/dist/variable-tracker.d.ts.map +1 -0
- package/dist/variable-tracker.js +216 -0
- package/dist/variable-tracker.js.map +1 -0
- package/dist/wrapper.d.ts.map +1 -1
- package/dist/wrapper.js +6 -20
- package/dist/wrapper.js.map +1 -1
- package/package.json +1 -1
- 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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"}
|
package/dist/wrapper.d.ts.map
CHANGED
|
@@ -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,
|
|
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(
|
|
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
|
-
|
|
61
|
-
//
|
|
62
|
-
|
|
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);
|
package/dist/wrapper.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wrapper.js","sourceRoot":"","sources":["../src/wrapper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAc9B;;;;;;;;GAQG;AACH,MAAM,UAAU,eAAe,CAAC,MAAqB;IACnD,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"wrapper.js","sourceRoot":"","sources":["../src/wrapper.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
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
|
-
|
|
105
|
+
% Extract clause information with enhanced data
|
|
106
|
+
extract_clause_info_enhanced(Frame, Goal, Port, ClauseInfo),
|
|
66
107
|
|
|
67
|
-
%
|
|
68
|
-
|
|
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
|
-
|
|
183
|
+
write_json_event_or_marker(Stream, Event).
|
|
119
184
|
write_events_list(Stream, [Event|Rest]) :-
|
|
120
185
|
write(Stream, ' '),
|
|
121
|
-
|
|
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
|
-
(
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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
|