verifiable-thinking-mcp 0.4.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/LICENSE +21 -0
- package/README.md +339 -0
- package/package.json +75 -0
- package/src/index.ts +38 -0
- package/src/lib/cache.ts +246 -0
- package/src/lib/compression.ts +804 -0
- package/src/lib/compute/cache.ts +86 -0
- package/src/lib/compute/classifier.ts +555 -0
- package/src/lib/compute/confidence.ts +79 -0
- package/src/lib/compute/context.ts +154 -0
- package/src/lib/compute/extract.ts +200 -0
- package/src/lib/compute/filter.ts +224 -0
- package/src/lib/compute/index.ts +171 -0
- package/src/lib/compute/math.ts +247 -0
- package/src/lib/compute/patterns.ts +564 -0
- package/src/lib/compute/registry.ts +145 -0
- package/src/lib/compute/solvers/arithmetic.ts +65 -0
- package/src/lib/compute/solvers/calculus.ts +249 -0
- package/src/lib/compute/solvers/derivation-core.ts +371 -0
- package/src/lib/compute/solvers/derivation-latex.ts +160 -0
- package/src/lib/compute/solvers/derivation-mistakes.ts +1046 -0
- package/src/lib/compute/solvers/derivation-simplify.ts +451 -0
- package/src/lib/compute/solvers/derivation-transform.ts +620 -0
- package/src/lib/compute/solvers/derivation.ts +67 -0
- package/src/lib/compute/solvers/facts.ts +120 -0
- package/src/lib/compute/solvers/formula.ts +728 -0
- package/src/lib/compute/solvers/index.ts +36 -0
- package/src/lib/compute/solvers/logic.ts +422 -0
- package/src/lib/compute/solvers/probability.ts +307 -0
- package/src/lib/compute/solvers/statistics.ts +262 -0
- package/src/lib/compute/solvers/word-problems.ts +408 -0
- package/src/lib/compute/types.ts +107 -0
- package/src/lib/concepts.ts +111 -0
- package/src/lib/domain.ts +731 -0
- package/src/lib/extraction.ts +912 -0
- package/src/lib/index.ts +122 -0
- package/src/lib/judge.ts +260 -0
- package/src/lib/math/ast.ts +842 -0
- package/src/lib/math/index.ts +8 -0
- package/src/lib/math/operators.ts +171 -0
- package/src/lib/math/tokenizer.ts +477 -0
- package/src/lib/patterns.ts +200 -0
- package/src/lib/session.ts +825 -0
- package/src/lib/think/challenge.ts +323 -0
- package/src/lib/think/complexity.ts +504 -0
- package/src/lib/think/confidence-drift.ts +507 -0
- package/src/lib/think/consistency.ts +347 -0
- package/src/lib/think/guidance.ts +188 -0
- package/src/lib/think/helpers.ts +568 -0
- package/src/lib/think/hypothesis.ts +216 -0
- package/src/lib/think/index.ts +127 -0
- package/src/lib/think/prompts.ts +262 -0
- package/src/lib/think/route.ts +358 -0
- package/src/lib/think/schema.ts +98 -0
- package/src/lib/think/scratchpad-schema.ts +662 -0
- package/src/lib/think/spot-check.ts +961 -0
- package/src/lib/think/types.ts +93 -0
- package/src/lib/think/verification.ts +260 -0
- package/src/lib/tokens.ts +177 -0
- package/src/lib/verification.ts +620 -0
- package/src/prompts/index.ts +10 -0
- package/src/prompts/templates.ts +336 -0
- package/src/resources/index.ts +8 -0
- package/src/resources/sessions.ts +196 -0
- package/src/tools/compress.ts +138 -0
- package/src/tools/index.ts +5 -0
- package/src/tools/scratchpad.ts +2659 -0
- package/src/tools/sessions.ts +144 -0
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Derivation Simplification - simplification path and next step suggestions
|
|
3
|
+
*
|
|
4
|
+
* Provides tools for simplifying derivations, suggesting next steps,
|
|
5
|
+
* and computing full simplification paths.
|
|
6
|
+
*
|
|
7
|
+
* @module derivation-simplify
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { ASTNode } from "../../math/ast.ts";
|
|
11
|
+
import {
|
|
12
|
+
buildAST,
|
|
13
|
+
compareExpressions,
|
|
14
|
+
formatAST,
|
|
15
|
+
simplifyAST,
|
|
16
|
+
tokenizeMathExpression,
|
|
17
|
+
} from "../../verification.ts";
|
|
18
|
+
import { extractDerivationSteps } from "./derivation-core.ts";
|
|
19
|
+
import { applyTransformation, TRANSFORM_PATTERNS } from "./derivation-transform.ts";
|
|
20
|
+
|
|
21
|
+
/** A single step in a simplified derivation */
|
|
22
|
+
export interface SimplifiedStep {
|
|
23
|
+
/** Original LHS expression */
|
|
24
|
+
originalLhs: string;
|
|
25
|
+
/** Original RHS expression */
|
|
26
|
+
originalRhs: string;
|
|
27
|
+
/** Simplified LHS (after algebraic simplification) */
|
|
28
|
+
simplifiedLhs: string;
|
|
29
|
+
/** Simplified RHS (after algebraic simplification) */
|
|
30
|
+
simplifiedRhs: string;
|
|
31
|
+
/** Whether this step was simplified (changed from original) */
|
|
32
|
+
wasSimplified: boolean;
|
|
33
|
+
/** Suggestion for improvement, if any */
|
|
34
|
+
suggestion?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Result of simplifying a derivation */
|
|
38
|
+
export interface SimplifyDerivationResult {
|
|
39
|
+
/** Original steps */
|
|
40
|
+
original: Array<{ lhs: string; rhs: string }>;
|
|
41
|
+
/** Simplified steps with suggestions */
|
|
42
|
+
simplified: SimplifiedStep[];
|
|
43
|
+
/** Cleaned derivation chain with redundant steps removed */
|
|
44
|
+
cleaned: Array<{ lhs: string; rhs: string }>;
|
|
45
|
+
/** Number of steps removed as redundant */
|
|
46
|
+
stepsRemoved: number;
|
|
47
|
+
/** Summary of simplifications applied */
|
|
48
|
+
summary: string[];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Parse an expression string to AST, returning null on failure
|
|
53
|
+
*/
|
|
54
|
+
export function parseToAST(expr: string): ASTNode | null {
|
|
55
|
+
const { tokens, errors } = tokenizeMathExpression(expr);
|
|
56
|
+
if (errors.length > 0) return null;
|
|
57
|
+
const { ast } = buildAST(tokens);
|
|
58
|
+
return ast;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Simplify an expression string using AST simplification
|
|
63
|
+
* Returns the simplified string, or original if parsing fails
|
|
64
|
+
*/
|
|
65
|
+
function simplifyExprString(expr: string): { result: string; changed: boolean } {
|
|
66
|
+
const ast = parseToAST(expr);
|
|
67
|
+
if (!ast) {
|
|
68
|
+
return { result: expr, changed: false };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const simplified = simplifyAST(ast);
|
|
72
|
+
const result = formatAST(simplified, { spaces: true, minimalParens: true });
|
|
73
|
+
|
|
74
|
+
// Check if simplification changed anything
|
|
75
|
+
const changed = result !== formatAST(ast, { spaces: true, minimalParens: true });
|
|
76
|
+
return { result, changed };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Check if two expression strings are equivalent
|
|
81
|
+
*/
|
|
82
|
+
function areEquivalent(a: string, b: string): boolean {
|
|
83
|
+
return compareExpressions(a, b);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Simplify a derivation by applying algebraic simplification to each step
|
|
88
|
+
* and removing redundant steps.
|
|
89
|
+
*
|
|
90
|
+
* A step is considered redundant if:
|
|
91
|
+
* 1. Its simplified LHS equals its simplified RHS (identity step like "x = x")
|
|
92
|
+
* 2. It's equivalent to the previous step after simplification
|
|
93
|
+
*
|
|
94
|
+
* @param steps Array of {lhs, rhs} pairs representing the derivation
|
|
95
|
+
* @returns SimplifyDerivationResult with simplified and cleaned derivation
|
|
96
|
+
*/
|
|
97
|
+
export function simplifyDerivation(
|
|
98
|
+
steps: Array<{ lhs: string; rhs: string }>,
|
|
99
|
+
): SimplifyDerivationResult {
|
|
100
|
+
if (steps.length === 0) {
|
|
101
|
+
return {
|
|
102
|
+
original: [],
|
|
103
|
+
simplified: [],
|
|
104
|
+
cleaned: [],
|
|
105
|
+
stepsRemoved: 0,
|
|
106
|
+
summary: ["No steps to simplify"],
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const simplified: SimplifiedStep[] = [];
|
|
111
|
+
const summary: string[] = [];
|
|
112
|
+
|
|
113
|
+
// Phase 1: Simplify each step
|
|
114
|
+
for (let i = 0; i < steps.length; i++) {
|
|
115
|
+
const step = steps[i];
|
|
116
|
+
if (!step) continue;
|
|
117
|
+
|
|
118
|
+
const { lhs, rhs } = step;
|
|
119
|
+
const simplifiedLhs = simplifyExprString(lhs);
|
|
120
|
+
const simplifiedRhs = simplifyExprString(rhs);
|
|
121
|
+
|
|
122
|
+
const wasSimplified = simplifiedLhs.changed || simplifiedRhs.changed;
|
|
123
|
+
|
|
124
|
+
let suggestion: string | undefined;
|
|
125
|
+
|
|
126
|
+
// Generate suggestions
|
|
127
|
+
if (simplifiedLhs.changed && simplifiedRhs.changed) {
|
|
128
|
+
suggestion = `Step ${i + 1}: Both sides simplify (${lhs} → ${simplifiedLhs.result}, ${rhs} → ${simplifiedRhs.result})`;
|
|
129
|
+
} else if (simplifiedLhs.changed) {
|
|
130
|
+
suggestion = `Step ${i + 1}: LHS simplifies: ${lhs} → ${simplifiedLhs.result}`;
|
|
131
|
+
} else if (simplifiedRhs.changed) {
|
|
132
|
+
suggestion = `Step ${i + 1}: RHS simplifies: ${rhs} → ${simplifiedRhs.result}`;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Check for identity steps (x = x after simplification)
|
|
136
|
+
if (areEquivalent(simplifiedLhs.result, simplifiedRhs.result)) {
|
|
137
|
+
if (simplifiedLhs.result === simplifiedRhs.result) {
|
|
138
|
+
suggestion = suggestion
|
|
139
|
+
? `${suggestion}; this is an identity step (${simplifiedLhs.result} = ${simplifiedRhs.result})`
|
|
140
|
+
: `Step ${i + 1}: Identity step (${simplifiedLhs.result} = ${simplifiedRhs.result})`;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
simplified.push({
|
|
145
|
+
originalLhs: lhs,
|
|
146
|
+
originalRhs: rhs,
|
|
147
|
+
simplifiedLhs: simplifiedLhs.result,
|
|
148
|
+
simplifiedRhs: simplifiedRhs.result,
|
|
149
|
+
wasSimplified,
|
|
150
|
+
suggestion,
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
if (suggestion) {
|
|
154
|
+
summary.push(suggestion);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Phase 2: Remove redundant steps
|
|
159
|
+
// A step is redundant if its simplified form is equivalent to the previous step's result
|
|
160
|
+
const cleaned: Array<{ lhs: string; rhs: string }> = [];
|
|
161
|
+
let lastRhs: string | null = null;
|
|
162
|
+
|
|
163
|
+
for (let i = 0; i < simplified.length; i++) {
|
|
164
|
+
const step = simplified[i];
|
|
165
|
+
if (!step) continue;
|
|
166
|
+
|
|
167
|
+
const { simplifiedLhs, simplifiedRhs } = step;
|
|
168
|
+
|
|
169
|
+
// First step is never redundant
|
|
170
|
+
if (i === 0) {
|
|
171
|
+
cleaned.push({ lhs: simplifiedLhs, rhs: simplifiedRhs });
|
|
172
|
+
lastRhs = simplifiedRhs;
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Check if this step is redundant
|
|
177
|
+
// Redundant if: lastRhs ≈ simplifiedLhs ≈ simplifiedRhs (no actual progress)
|
|
178
|
+
const lhsMatchesLast = lastRhs && areEquivalent(lastRhs, simplifiedLhs);
|
|
179
|
+
const isIdentityStep = areEquivalent(simplifiedLhs, simplifiedRhs);
|
|
180
|
+
const rhsMatchesLast = lastRhs && areEquivalent(lastRhs, simplifiedRhs);
|
|
181
|
+
|
|
182
|
+
// Skip if this step makes no progress (both sides equivalent to where we were)
|
|
183
|
+
// or if it's a pure identity step that doesn't advance the derivation
|
|
184
|
+
if (lhsMatchesLast && rhsMatchesLast) {
|
|
185
|
+
summary.push(`Step ${i + 1}: Removed as redundant (no progress from ${lastRhs})`);
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Also skip pure identity steps in the middle that don't add information
|
|
190
|
+
if (isIdentityStep && lhsMatchesLast) {
|
|
191
|
+
summary.push(`Step ${i + 1}: Removed identity step (${simplifiedLhs} = ${simplifiedRhs})`);
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Keep the step
|
|
196
|
+
cleaned.push({ lhs: simplifiedLhs, rhs: simplifiedRhs });
|
|
197
|
+
lastRhs = simplifiedRhs;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const stepsRemoved = steps.length - cleaned.length;
|
|
201
|
+
|
|
202
|
+
if (stepsRemoved > 0) {
|
|
203
|
+
summary.push(`Removed ${stepsRemoved} redundant step${stepsRemoved > 1 ? "s" : ""}`);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (summary.length === 0) {
|
|
207
|
+
summary.push("Derivation is already in simplified form");
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return {
|
|
211
|
+
original: steps,
|
|
212
|
+
simplified,
|
|
213
|
+
cleaned,
|
|
214
|
+
stepsRemoved,
|
|
215
|
+
summary,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Simplify a derivation from text and return the cleaned version
|
|
221
|
+
*
|
|
222
|
+
* @param text Text containing a derivation (e.g., "x + 0 = x = x * 1 = x")
|
|
223
|
+
* @returns SimplifyDerivationResult or null if no derivation found
|
|
224
|
+
*/
|
|
225
|
+
export function simplifyDerivationText(text: string): SimplifyDerivationResult | null {
|
|
226
|
+
const steps = extractDerivationSteps(text);
|
|
227
|
+
if (steps.length === 0) {
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
return simplifyDerivation(steps);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/** Result of suggesting the next step */
|
|
234
|
+
export interface NextStepSuggestion {
|
|
235
|
+
/** Whether a suggestion was found */
|
|
236
|
+
hasSuggestion: boolean;
|
|
237
|
+
/** The suggested transformation */
|
|
238
|
+
transformation?: string;
|
|
239
|
+
/** Human-readable description */
|
|
240
|
+
description?: string;
|
|
241
|
+
/** The current expression (last RHS) */
|
|
242
|
+
currentExpression?: string;
|
|
243
|
+
/** All applicable transformations in priority order */
|
|
244
|
+
allApplicable: Array<{ name: string; description: string }>;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Suggest the next logical simplification step for a derivation
|
|
249
|
+
*
|
|
250
|
+
* Analyzes the last expression in the derivation chain and identifies
|
|
251
|
+
* applicable algebraic transformations, returning the highest-priority one.
|
|
252
|
+
*
|
|
253
|
+
* @param steps Array of {lhs, rhs} pairs representing the derivation so far
|
|
254
|
+
* @returns NextStepSuggestion with the recommended transformation
|
|
255
|
+
*/
|
|
256
|
+
export function suggestNextStep(steps: Array<{ lhs: string; rhs: string }>): NextStepSuggestion {
|
|
257
|
+
if (steps.length === 0) {
|
|
258
|
+
return { hasSuggestion: false, allApplicable: [] };
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Get the last expression (RHS of last step)
|
|
262
|
+
const lastStep = steps[steps.length - 1];
|
|
263
|
+
if (!lastStep) {
|
|
264
|
+
return { hasSuggestion: false, allApplicable: [] };
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const currentExpr = lastStep.rhs;
|
|
268
|
+
|
|
269
|
+
// Parse to AST
|
|
270
|
+
const ast = parseToAST(currentExpr);
|
|
271
|
+
if (!ast) {
|
|
272
|
+
return { hasSuggestion: false, allApplicable: [], currentExpression: currentExpr };
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Find all applicable transformations
|
|
276
|
+
const applicable: Array<{ name: string; description: string; priority: number }> = [];
|
|
277
|
+
|
|
278
|
+
for (const pattern of TRANSFORM_PATTERNS) {
|
|
279
|
+
if (pattern.applies(ast)) {
|
|
280
|
+
applicable.push({
|
|
281
|
+
name: pattern.name,
|
|
282
|
+
description: pattern.description,
|
|
283
|
+
priority: pattern.priority,
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Sort by priority (highest first)
|
|
289
|
+
applicable.sort((a, b) => b.priority - a.priority);
|
|
290
|
+
|
|
291
|
+
if (applicable.length === 0) {
|
|
292
|
+
return {
|
|
293
|
+
hasSuggestion: false,
|
|
294
|
+
currentExpression: currentExpr,
|
|
295
|
+
allApplicable: [],
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const best = applicable[0]!;
|
|
300
|
+
|
|
301
|
+
return {
|
|
302
|
+
hasSuggestion: true,
|
|
303
|
+
transformation: best.name,
|
|
304
|
+
description: best.description,
|
|
305
|
+
currentExpression: currentExpr,
|
|
306
|
+
allApplicable: applicable.map(({ name, description }) => ({ name, description })),
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Suggest next step from text containing a derivation
|
|
312
|
+
*
|
|
313
|
+
* @param text Text containing a derivation
|
|
314
|
+
* @returns NextStepSuggestion or null if no derivation found
|
|
315
|
+
*/
|
|
316
|
+
export function suggestNextStepFromText(text: string): NextStepSuggestion | null {
|
|
317
|
+
const steps = extractDerivationSteps(text);
|
|
318
|
+
if (steps.length === 0) {
|
|
319
|
+
return null;
|
|
320
|
+
}
|
|
321
|
+
return suggestNextStep(steps);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/** A single step in a simplification path */
|
|
325
|
+
export interface SimplificationStep {
|
|
326
|
+
/** Step number (1-indexed) */
|
|
327
|
+
step: number;
|
|
328
|
+
/** The transformation applied */
|
|
329
|
+
transformation: string;
|
|
330
|
+
/** Human-readable description */
|
|
331
|
+
description: string;
|
|
332
|
+
/** Expression before this step */
|
|
333
|
+
before: string;
|
|
334
|
+
/** Expression after this step */
|
|
335
|
+
after: string;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/** Result of computing a full simplification path */
|
|
339
|
+
export interface SimplificationPath {
|
|
340
|
+
/** Whether simplification was possible */
|
|
341
|
+
success: boolean;
|
|
342
|
+
/** Original expression */
|
|
343
|
+
original: string;
|
|
344
|
+
/** Final simplified expression */
|
|
345
|
+
simplified: string;
|
|
346
|
+
/** Sequence of transformation steps */
|
|
347
|
+
steps: SimplificationStep[];
|
|
348
|
+
/** Whether the expression is fully simplified */
|
|
349
|
+
isFullySimplified: boolean;
|
|
350
|
+
/** Total number of transformations applied */
|
|
351
|
+
transformationCount: number;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Compute a complete simplification path for an expression
|
|
356
|
+
*
|
|
357
|
+
* Iteratively applies transformations until the expression cannot be
|
|
358
|
+
* simplified further, recording each step along the way.
|
|
359
|
+
*
|
|
360
|
+
* @param expression The expression to simplify
|
|
361
|
+
* @param maxSteps Maximum number of simplification steps (default: 50)
|
|
362
|
+
* @returns SimplificationPath with the complete sequence of transformations
|
|
363
|
+
*/
|
|
364
|
+
export function suggestSimplificationPath(expression: string, maxSteps = 50): SimplificationPath {
|
|
365
|
+
const ast = parseToAST(expression);
|
|
366
|
+
|
|
367
|
+
if (!ast) {
|
|
368
|
+
return {
|
|
369
|
+
success: false,
|
|
370
|
+
original: expression,
|
|
371
|
+
simplified: expression,
|
|
372
|
+
steps: [],
|
|
373
|
+
isFullySimplified: false,
|
|
374
|
+
transformationCount: 0,
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
const steps: SimplificationStep[] = [];
|
|
379
|
+
let currentAst = ast;
|
|
380
|
+
let currentExpr = expression;
|
|
381
|
+
let stepCount = 0;
|
|
382
|
+
|
|
383
|
+
while (stepCount < maxSteps) {
|
|
384
|
+
// Find applicable transformations for current AST
|
|
385
|
+
const applicable: Array<{ name: string; description: string; priority: number }> = [];
|
|
386
|
+
|
|
387
|
+
for (const pattern of TRANSFORM_PATTERNS) {
|
|
388
|
+
if (pattern.applies(currentAst)) {
|
|
389
|
+
applicable.push({
|
|
390
|
+
name: pattern.name,
|
|
391
|
+
description: pattern.description,
|
|
392
|
+
priority: pattern.priority,
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
if (applicable.length === 0) {
|
|
398
|
+
// No more transformations possible
|
|
399
|
+
break;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// Sort by priority and try to apply the highest-priority transformation
|
|
403
|
+
applicable.sort((a, b) => b.priority - a.priority);
|
|
404
|
+
|
|
405
|
+
let appliedAny = false;
|
|
406
|
+
|
|
407
|
+
for (const transform of applicable) {
|
|
408
|
+
const { transformed, applied } = applyTransformation(currentAst, transform.name);
|
|
409
|
+
|
|
410
|
+
if (applied) {
|
|
411
|
+
const beforeExpr = currentExpr;
|
|
412
|
+
currentAst = transformed;
|
|
413
|
+
currentExpr = formatAST(transformed, { spaces: true, minimalParens: true });
|
|
414
|
+
stepCount++;
|
|
415
|
+
|
|
416
|
+
steps.push({
|
|
417
|
+
step: stepCount,
|
|
418
|
+
transformation: transform.name,
|
|
419
|
+
description: transform.description,
|
|
420
|
+
before: beforeExpr,
|
|
421
|
+
after: currentExpr,
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
appliedAny = true;
|
|
425
|
+
break; // Apply one transformation at a time
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
if (!appliedAny) {
|
|
430
|
+
// Transformations detected but none could be applied (edge case)
|
|
431
|
+
break;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// Check if fully simplified (no more applicable transformations)
|
|
436
|
+
const remainingTransforms: Array<{ name: string }> = [];
|
|
437
|
+
for (const pattern of TRANSFORM_PATTERNS) {
|
|
438
|
+
if (pattern.applies(currentAst)) {
|
|
439
|
+
remainingTransforms.push({ name: pattern.name });
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
return {
|
|
444
|
+
success: true,
|
|
445
|
+
original: expression,
|
|
446
|
+
simplified: currentExpr,
|
|
447
|
+
steps,
|
|
448
|
+
isFullySimplified: remainingTransforms.length === 0,
|
|
449
|
+
transformationCount: steps.length,
|
|
450
|
+
};
|
|
451
|
+
}
|