slangmath 1.0.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.
@@ -0,0 +1,454 @@
1
+ /**
2
+ * SLaNg Error Handling System
3
+ * Comprehensive error types and handling utilities
4
+ */
5
+
6
+ // ============================================================================
7
+ // ERROR CLASSES
8
+ // ============================================================================
9
+
10
+ /**
11
+ * Base SLaNg Error class
12
+ */
13
+ class SLaNgError extends Error {
14
+ constructor(message, code, context = {}) {
15
+ super(message);
16
+ this.name = 'SLaNgError';
17
+ this.code = code;
18
+ this.context = context;
19
+ this.timestamp = new Date().toISOString();
20
+ }
21
+
22
+ toJSON() {
23
+ return {
24
+ name: this.name,
25
+ message: this.message,
26
+ code: this.code,
27
+ context: this.context,
28
+ timestamp: this.timestamp,
29
+ stack: this.stack
30
+ };
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Parsing Error for LaTeX/SLaNg conversion issues
36
+ */
37
+ class ParseError extends SLaNgError {
38
+ constructor(message, input, position = null, suggestions = []) {
39
+ super(message, 'PARSE_ERROR', {
40
+ input,
41
+ position,
42
+ suggestions
43
+ });
44
+ this.name = 'ParseError';
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Validation Error for invalid expressions
50
+ */
51
+ class ValidationError extends SLaNgError {
52
+ constructor(message, expression, validationType) {
53
+ super(message, 'VALIDATION_ERROR', {
54
+ expression,
55
+ validationType
56
+ });
57
+ this.name = 'ValidationError';
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Conversion Error for SLaNg ↔ LaTeX conversion failures
63
+ */
64
+ class ConversionError extends SLaNgError {
65
+ constructor(message, fromType, toType, expression) {
66
+ super(message, 'CONVERSION_ERROR', {
67
+ fromType,
68
+ toType,
69
+ expression
70
+ });
71
+ this.name = 'ConversionError';
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Type Error for unsupported expression types
77
+ */
78
+ class TypeError extends SLaNgError {
79
+ constructor(message, expectedType, actualType, expression) {
80
+ super(message, 'TYPE_ERROR', {
81
+ expectedType,
82
+ actualType,
83
+ expression
84
+ });
85
+ this.name = 'TypeError';
86
+ }
87
+ }
88
+
89
+ // ============================================================================
90
+ // ERROR FACTORIES
91
+ // ============================================================================
92
+
93
+ /**
94
+ * Create parsing errors with helpful context
95
+ */
96
+ export function createParseError(message, input, position = null) {
97
+ const suggestions = generateSuggestions(input, position);
98
+ return new ParseError(message, input, position, suggestions);
99
+ }
100
+
101
+ /**
102
+ * Create validation errors
103
+ */
104
+ export function createValidationError(message, expression, type = 'general') {
105
+ return new ValidationError(message, expression, type);
106
+ }
107
+
108
+ /**
109
+ * Create conversion errors
110
+ */
111
+ export function createConversionError(message, fromType, toType, expression) {
112
+ return new ConversionError(message, fromType, toType, expression);
113
+ }
114
+
115
+ /**
116
+ * Create type errors
117
+ */
118
+ export function createTypeError(message, expectedType, actualType, expression) {
119
+ return new TypeError(message, expectedType, actualType, expression);
120
+ }
121
+
122
+ // ============================================================================
123
+ // ERROR HANDLERS
124
+ // ============================================================================
125
+
126
+ /**
127
+ * Enhanced error handler with multiple strategies
128
+ */
129
+ export function handleError(error, options = {}) {
130
+ const {
131
+ logErrors = true,
132
+ throwOnError = false,
133
+ returnNull = false,
134
+ customHandler = null
135
+ } = options;
136
+
137
+ // Log error if requested
138
+ if (logErrors) {
139
+ logError(error);
140
+ }
141
+
142
+ // Custom handler takes precedence
143
+ if (customHandler && typeof customHandler === 'function') {
144
+ return customHandler(error);
145
+ }
146
+
147
+ // Handle based on options
148
+ if (throwOnError) {
149
+ throw error;
150
+ }
151
+
152
+ if (returnNull) {
153
+ return null;
154
+ }
155
+
156
+ // Default: return error object
157
+ return {
158
+ success: false,
159
+ error: error.toJSON(),
160
+ message: error.message
161
+ };
162
+ }
163
+
164
+ /**
165
+ * Batch error handler for multiple operations
166
+ */
167
+ export function handleBatchErrors(errors, options = {}) {
168
+ const {
169
+ groupByType = true,
170
+ includeContext = true,
171
+ summaryOnly = false
172
+ } = options;
173
+
174
+ if (summaryOnly) {
175
+ return createErrorSummary(errors);
176
+ }
177
+
178
+ const processed = errors.map(error => ({
179
+ index: error.index || -1,
180
+ ...error.toJSON()
181
+ }));
182
+
183
+ if (groupByType) {
184
+ return groupErrorsByType(processed);
185
+ }
186
+
187
+ return {
188
+ total: errors.length,
189
+ errors: processed
190
+ };
191
+ }
192
+
193
+ // ============================================================================
194
+ // ERROR UTILITIES
195
+ // ============================================================================
196
+
197
+ /**
198
+ * Generate helpful suggestions based on error context
199
+ */
200
+ function generateSuggestions(input, position) {
201
+ const suggestions = [];
202
+
203
+ if (!input) {
204
+ suggestions.push('Empty input provided');
205
+ return suggestions;
206
+ }
207
+
208
+ // Common LaTeX syntax issues
209
+ if (input.includes('\\frac') && !input.includes('{')) {
210
+ suggestions.push('LaTeX fractions require braces: \\frac{numerator}{denominator}');
211
+ }
212
+
213
+ if (input.includes('^') && !input.includes('{')) {
214
+ suggestions.push('Consider using braces for powers: x^{2} instead of x^2');
215
+ }
216
+
217
+ if (input.includes('{') && !input.includes('}')) {
218
+ suggestions.push('Unclosed braces detected');
219
+ }
220
+
221
+ // Common parsing issues
222
+ if (position !== null && position > 0) {
223
+ const before = input.substring(0, position);
224
+ const after = input.substring(position);
225
+
226
+ if (before.endsWith('^') && after.startsWith('{')) {
227
+ suggestions.push('Power syntax looks correct');
228
+ }
229
+
230
+ if (before.endsWith('\\frac') && !after.startsWith('{')) {
231
+ suggestions.push('Fraction requires opening brace after \\frac');
232
+ }
233
+ }
234
+
235
+ return suggestions;
236
+ }
237
+
238
+ /**
239
+ * Log errors with structured format
240
+ */
241
+ function logError(error) {
242
+ const logEntry = {
243
+ timestamp: error.timestamp,
244
+ level: 'ERROR',
245
+ type: error.name,
246
+ code: error.code,
247
+ message: error.message,
248
+ context: error.context
249
+ };
250
+
251
+ console.error('🚨 SLaNg Error:', JSON.stringify(logEntry, null, 2));
252
+ }
253
+
254
+ /**
255
+ * Create error summary for batch operations
256
+ */
257
+ function createErrorSummary(errors) {
258
+ const summary = {
259
+ total: errors.length,
260
+ byType: {},
261
+ mostCommon: null
262
+ };
263
+
264
+ errors.forEach(error => {
265
+ const type = error.name || 'Unknown';
266
+ summary.byType[type] = (summary.byType[type] || 0) + 1;
267
+ });
268
+
269
+ // Find most common error type
270
+ const types = Object.entries(summary.byType);
271
+ if (types.length > 0) {
272
+ summary.mostCommon = types.reduce((a, b) => a[1] > b[1] ? a : b)[0];
273
+ }
274
+
275
+ return summary;
276
+ }
277
+
278
+ /**
279
+ * Group errors by type
280
+ */
281
+ function groupErrorsByType(errors) {
282
+ const grouped = {};
283
+
284
+ errors.forEach(error => {
285
+ const type = error.name || 'Unknown';
286
+ if (!grouped[type]) {
287
+ grouped[type] = [];
288
+ }
289
+ grouped[type].push(error);
290
+ });
291
+
292
+ return grouped;
293
+ }
294
+
295
+ /**
296
+ * Validate error context
297
+ */
298
+ export function validateErrorContext(context) {
299
+ const required = ['timestamp', 'code'];
300
+ const missing = required.filter(key => !(key in context));
301
+
302
+ if (missing.length > 0) {
303
+ throw new Error(`Missing required error context: ${missing.join(', ')}`);
304
+ }
305
+
306
+ return true;
307
+ }
308
+
309
+ /**
310
+ * Create error recovery suggestions
311
+ */
312
+ export function createRecoveryPlan(error) {
313
+ const plan = {
314
+ error: error.message,
315
+ steps: [],
316
+ alternatives: []
317
+ };
318
+
319
+ switch (error.code) {
320
+ case 'PARSE_ERROR':
321
+ plan.steps.push('Check LaTeX syntax');
322
+ plan.steps.push('Verify all braces are matched');
323
+ plan.steps.push('Ensure fraction format is correct');
324
+ plan.alternatives.push('Try simpler expression');
325
+ break;
326
+
327
+ case 'VALIDATION_ERROR':
328
+ plan.steps.push('Review expression format');
329
+ plan.steps.push('Check for invalid characters');
330
+ plan.alternatives.push('Use validateLatex() first');
331
+ break;
332
+
333
+ case 'CONVERSION_ERROR':
334
+ plan.steps.push('Verify expression type');
335
+ plan.steps.push('Check converter options');
336
+ plan.alternatives.push('Try different conversion method');
337
+ break;
338
+
339
+ default:
340
+ plan.steps.push('Review error context');
341
+ plan.alternatives.push('Contact support');
342
+ }
343
+
344
+ return plan;
345
+ }
346
+
347
+ // ============================================================================
348
+ // ERROR RECOVERY
349
+ // ============================================================================
350
+
351
+ /**
352
+ * Attempt to recover from parsing errors
353
+ */
354
+ export function attemptRecovery(error, originalInput) {
355
+ const recovery = {
356
+ success: false,
357
+ attempts: [],
358
+ result: null
359
+ };
360
+
361
+ // Try common fixes
362
+ const fixes = [
363
+ { name: 'Add missing braces', fix: addMissingBraces },
364
+ { name: 'Fix fraction syntax', fix: fixFractionSyntax },
365
+ { name: 'Normalize whitespace', fix: normalizeWhitespace },
366
+ { name: 'Escape special chars', fix: escapeSpecialChars }
367
+ ];
368
+
369
+ for (const fix of fixes) {
370
+ try {
371
+ const fixed = fix.fix(originalInput);
372
+ recovery.attempts.push({
373
+ name: fix.name,
374
+ input: fixed,
375
+ success: true
376
+ });
377
+
378
+ if (fixed !== originalInput) {
379
+ recovery.success = true;
380
+ recovery.result = fixed;
381
+ break;
382
+ }
383
+ } catch (attemptError) {
384
+ recovery.attempts.push({
385
+ name: fix.name,
386
+ error: attemptError.message,
387
+ success: false
388
+ });
389
+ }
390
+ }
391
+
392
+ return recovery;
393
+ }
394
+
395
+ /**
396
+ * Recovery helper functions
397
+ */
398
+ function addMissingBraces(input) {
399
+ // Add missing braces for fractions and powers
400
+ let fixed = input;
401
+
402
+ // Fix fractions
403
+ fixed = fixed.replace(/\\frac([^{}])/g, '\\frac{$1');
404
+ fixed = fixed.replace(/\\frac{([^{}]+)}([^{}])/g, '\\frac{$1}{$2}');
405
+
406
+ // Fix powers
407
+ fixed = fixed.replace(/\^([^{}])/g, '^{$1}');
408
+
409
+ return fixed;
410
+ }
411
+
412
+ function fixFractionSyntax(input) {
413
+ // Common fraction syntax fixes
414
+ return input
415
+ .replace(/\\frac\s*\{/g, '\\frac{')
416
+ .replace(/\}\s*\{/g, '}{');
417
+ }
418
+
419
+ function normalizeWhitespace(input) {
420
+ // Normalize whitespace around operators
421
+ return input
422
+ .replace(/\s*\+\s*/g, ' + ')
423
+ .replace(/\s*-\s*/g, ' - ')
424
+ .replace(/\s*\*\s*/g, ' * ')
425
+ .trim();
426
+ }
427
+
428
+ function escapeSpecialChars(input) {
429
+ // Escape problematic characters
430
+ return input
431
+ .replace(/&/g, '\\&')
432
+ .replace(/%/g, '\\%')
433
+ .replace(/#/g, '\\#');
434
+ }
435
+
436
+ // ============================================================================
437
+ // EXPORTS
438
+ // ============================================================================
439
+
440
+ export {
441
+ SLaNgError,
442
+ ParseError,
443
+ ValidationError,
444
+ ConversionError,
445
+ TypeError
446
+ };
447
+
448
+ // Error codes for reference
449
+ export const ERROR_CODES = {
450
+ PARSE_ERROR: 'PARSE_ERROR',
451
+ VALIDATION_ERROR: 'VALIDATION_ERROR',
452
+ CONVERSION_ERROR: 'CONVERSION_ERROR',
453
+ TYPE_ERROR: 'TYPE_ERROR'
454
+ };