gradient-script 0.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 (52) hide show
  1. package/README.md +515 -0
  2. package/dist/cli.d.ts +2 -0
  3. package/dist/cli.js +136 -0
  4. package/dist/dsl/AST.d.ts +123 -0
  5. package/dist/dsl/AST.js +23 -0
  6. package/dist/dsl/BuiltIns.d.ts +58 -0
  7. package/dist/dsl/BuiltIns.js +181 -0
  8. package/dist/dsl/CSE.d.ts +21 -0
  9. package/dist/dsl/CSE.js +194 -0
  10. package/dist/dsl/CodeGen.d.ts +60 -0
  11. package/dist/dsl/CodeGen.js +474 -0
  12. package/dist/dsl/Differentiation.d.ts +45 -0
  13. package/dist/dsl/Differentiation.js +421 -0
  14. package/dist/dsl/DiscontinuityAnalyzer.d.ts +18 -0
  15. package/dist/dsl/DiscontinuityAnalyzer.js +75 -0
  16. package/dist/dsl/Errors.d.ts +22 -0
  17. package/dist/dsl/Errors.js +49 -0
  18. package/dist/dsl/Expander.d.ts +13 -0
  19. package/dist/dsl/Expander.js +220 -0
  20. package/dist/dsl/ExpressionTransformer.d.ts +54 -0
  21. package/dist/dsl/ExpressionTransformer.js +102 -0
  22. package/dist/dsl/ExpressionUtils.d.ts +55 -0
  23. package/dist/dsl/ExpressionUtils.js +175 -0
  24. package/dist/dsl/GradientChecker.d.ts +71 -0
  25. package/dist/dsl/GradientChecker.js +258 -0
  26. package/dist/dsl/Guards.d.ts +27 -0
  27. package/dist/dsl/Guards.js +206 -0
  28. package/dist/dsl/Inliner.d.ts +10 -0
  29. package/dist/dsl/Inliner.js +40 -0
  30. package/dist/dsl/Lexer.d.ts +63 -0
  31. package/dist/dsl/Lexer.js +243 -0
  32. package/dist/dsl/Parser.d.ts +92 -0
  33. package/dist/dsl/Parser.js +328 -0
  34. package/dist/dsl/Simplify.d.ts +17 -0
  35. package/dist/dsl/Simplify.js +276 -0
  36. package/dist/dsl/TypeInference.d.ts +39 -0
  37. package/dist/dsl/TypeInference.js +147 -0
  38. package/dist/dsl/Types.d.ts +58 -0
  39. package/dist/dsl/Types.js +114 -0
  40. package/dist/index.d.ts +13 -0
  41. package/dist/index.js +11 -0
  42. package/dist/symbolic/AST.d.ts +113 -0
  43. package/dist/symbolic/AST.js +128 -0
  44. package/dist/symbolic/CodeGen.d.ts +35 -0
  45. package/dist/symbolic/CodeGen.js +280 -0
  46. package/dist/symbolic/Parser.d.ts +64 -0
  47. package/dist/symbolic/Parser.js +329 -0
  48. package/dist/symbolic/Simplify.d.ts +10 -0
  49. package/dist/symbolic/Simplify.js +244 -0
  50. package/dist/symbolic/SymbolicDiff.d.ts +35 -0
  51. package/dist/symbolic/SymbolicDiff.js +339 -0
  52. package/package.json +56 -0
@@ -0,0 +1,258 @@
1
+ /**
2
+ * Numerical gradient checking for GradientScript DSL
3
+ * Validates symbolic gradients against finite difference approximations
4
+ */
5
+ import { Types } from './Types.js';
6
+ import { expandBuiltIn, shouldExpand } from './Expander.js';
7
+ /**
8
+ * Gradient checker
9
+ */
10
+ export class GradientChecker {
11
+ epsilon;
12
+ tolerance;
13
+ constructor(epsilon = 1e-5, tolerance = 1e-4) {
14
+ this.epsilon = epsilon;
15
+ this.tolerance = tolerance;
16
+ }
17
+ /**
18
+ * Check gradients for a function
19
+ */
20
+ check(func, gradients, env, testPoint) {
21
+ const errors = [];
22
+ // For each parameter that has gradients
23
+ for (const [paramName, gradient] of gradients.gradients.entries()) {
24
+ const paramType = env.getOrThrow(paramName);
25
+ const paramValue = testPoint.get(paramName);
26
+ if (!paramValue) {
27
+ throw new Error(`Test point missing value for parameter: ${paramName}`);
28
+ }
29
+ if (Types.isScalar(paramType)) {
30
+ // Scalar parameter
31
+ if (typeof paramValue !== 'number') {
32
+ throw new Error(`Expected scalar value for ${paramName}`);
33
+ }
34
+ const analytical = this.evaluateExpression(gradient, testPoint);
35
+ const numerical = this.numericalGradientScalar(func, testPoint, paramName);
36
+ const error = Math.abs(analytical - numerical);
37
+ const relativeError = Math.abs(error / (numerical + 1e-10));
38
+ if (error > this.tolerance && relativeError > this.tolerance) {
39
+ errors.push({
40
+ parameter: paramName,
41
+ analytical,
42
+ numerical,
43
+ error,
44
+ relativeError
45
+ });
46
+ }
47
+ }
48
+ else {
49
+ // Structured parameter
50
+ if (typeof paramValue === 'number') {
51
+ throw new Error(`Expected structured value for ${paramName}`);
52
+ }
53
+ const structGrad = gradient;
54
+ for (const [comp, expr] of structGrad.components.entries()) {
55
+ const analytical = this.evaluateExpression(expr, testPoint);
56
+ const numerical = this.numericalGradientComponent(func, testPoint, paramName, comp);
57
+ const error = Math.abs(analytical - numerical);
58
+ const relativeError = Math.abs(error / (numerical + 1e-10));
59
+ if (error > this.tolerance && relativeError > this.tolerance) {
60
+ errors.push({
61
+ parameter: paramName,
62
+ component: comp,
63
+ analytical,
64
+ numerical,
65
+ error,
66
+ relativeError
67
+ });
68
+ }
69
+ }
70
+ }
71
+ }
72
+ const maxError = errors.length > 0 ? Math.max(...errors.map(e => e.error)) : 0;
73
+ const meanError = errors.length > 0
74
+ ? errors.reduce((sum, e) => sum + e.error, 0) / errors.length
75
+ : 0;
76
+ return {
77
+ passed: errors.length === 0,
78
+ errors,
79
+ maxError,
80
+ meanError
81
+ };
82
+ }
83
+ /**
84
+ * Compute numerical gradient for scalar parameter using finite differences
85
+ */
86
+ numericalGradientScalar(func, testPoint, paramName) {
87
+ const originalValue = testPoint.get(paramName);
88
+ // f(x + h)
89
+ testPoint.set(paramName, originalValue + this.epsilon);
90
+ const fPlus = this.evaluateFunction(func, testPoint);
91
+ // f(x - h)
92
+ testPoint.set(paramName, originalValue - this.epsilon);
93
+ const fMinus = this.evaluateFunction(func, testPoint);
94
+ // Restore original value
95
+ testPoint.set(paramName, originalValue);
96
+ // Central difference: (f(x+h) - f(x-h)) / (2h)
97
+ return (fPlus - fMinus) / (2 * this.epsilon);
98
+ }
99
+ /**
100
+ * Compute numerical gradient for structured parameter component
101
+ */
102
+ numericalGradientComponent(func, testPoint, paramName, component) {
103
+ const originalValue = testPoint.get(paramName);
104
+ const originalComp = originalValue[component];
105
+ // f(x + h)
106
+ originalValue[component] = originalComp + this.epsilon;
107
+ const fPlus = this.evaluateFunction(func, testPoint);
108
+ // f(x - h)
109
+ originalValue[component] = originalComp - this.epsilon;
110
+ const fMinus = this.evaluateFunction(func, testPoint);
111
+ // Restore original value
112
+ originalValue[component] = originalComp;
113
+ // Central difference
114
+ return (fPlus - fMinus) / (2 * this.epsilon);
115
+ }
116
+ /**
117
+ * Evaluate function at a test point
118
+ */
119
+ evaluateFunction(func, testPoint) {
120
+ // Inline and evaluate the return expression
121
+ const inlined = this.inlineWithValues(func, testPoint);
122
+ return this.evaluateExpression(inlined, testPoint);
123
+ }
124
+ /**
125
+ * Inline function body with test point values
126
+ */
127
+ inlineWithValues(func, testPoint) {
128
+ const substitutions = new Map();
129
+ // Add intermediate variable assignments
130
+ for (const stmt of func.body) {
131
+ if (stmt.kind === 'assignment') {
132
+ substitutions.set(stmt.variable, stmt.expression);
133
+ }
134
+ }
135
+ return this.substituteExpression(func.returnExpr, substitutions, testPoint);
136
+ }
137
+ /**
138
+ * Substitute variables in expression
139
+ */
140
+ substituteExpression(expr, subs, testPoint) {
141
+ switch (expr.kind) {
142
+ case 'number':
143
+ return expr;
144
+ case 'variable':
145
+ if (subs.has(expr.name)) {
146
+ return this.substituteExpression(subs.get(expr.name), subs, testPoint);
147
+ }
148
+ return expr;
149
+ case 'binary':
150
+ return {
151
+ kind: 'binary',
152
+ operator: expr.operator,
153
+ left: this.substituteExpression(expr.left, subs, testPoint),
154
+ right: this.substituteExpression(expr.right, subs, testPoint)
155
+ };
156
+ case 'unary':
157
+ return {
158
+ kind: 'unary',
159
+ operator: expr.operator,
160
+ operand: this.substituteExpression(expr.operand, subs, testPoint)
161
+ };
162
+ case 'call':
163
+ // Expand built-ins before evaluation
164
+ if (shouldExpand(expr.name)) {
165
+ const expanded = expandBuiltIn({
166
+ ...expr,
167
+ args: expr.args.map(arg => this.substituteExpression(arg, subs, testPoint))
168
+ });
169
+ return expanded;
170
+ }
171
+ return {
172
+ kind: 'call',
173
+ name: expr.name,
174
+ args: expr.args.map(arg => this.substituteExpression(arg, subs, testPoint))
175
+ };
176
+ case 'component':
177
+ return {
178
+ kind: 'component',
179
+ object: this.substituteExpression(expr.object, subs, testPoint),
180
+ component: expr.component
181
+ };
182
+ }
183
+ }
184
+ /**
185
+ * Evaluate an expression numerically
186
+ */
187
+ evaluateExpression(expr, testPoint) {
188
+ switch (expr.kind) {
189
+ case 'number':
190
+ return expr.value;
191
+ case 'variable':
192
+ const value = testPoint.get(expr.name);
193
+ if (typeof value === 'number') {
194
+ return value;
195
+ }
196
+ throw new Error(`Cannot evaluate variable ${expr.name}: not a scalar`);
197
+ case 'binary':
198
+ const left = this.evaluateExpression(expr.left, testPoint);
199
+ const right = this.evaluateExpression(expr.right, testPoint);
200
+ switch (expr.operator) {
201
+ case '+': return left + right;
202
+ case '-': return left - right;
203
+ case '*': return left * right;
204
+ case '/': return left / right;
205
+ case '^':
206
+ case '**': return Math.pow(left, right);
207
+ }
208
+ break;
209
+ case 'unary':
210
+ const operand = this.evaluateExpression(expr.operand, testPoint);
211
+ if (expr.operator === '-')
212
+ return -operand;
213
+ if (expr.operator === '+')
214
+ return operand;
215
+ break;
216
+ case 'call':
217
+ // Expand built-ins before evaluation
218
+ if (shouldExpand(expr.name)) {
219
+ const expanded = expandBuiltIn(expr);
220
+ return this.evaluateExpression(expanded, testPoint);
221
+ }
222
+ const args = expr.args.map(arg => this.evaluateExpression(arg, testPoint));
223
+ return this.evaluateMathFunction(expr.name, args);
224
+ case 'component':
225
+ if (expr.object.kind === 'variable') {
226
+ const objValue = testPoint.get(expr.object.name);
227
+ if (typeof objValue === 'object' && objValue !== null) {
228
+ return objValue[expr.component];
229
+ }
230
+ }
231
+ throw new Error(`Cannot evaluate component access: ${JSON.stringify(expr)}`);
232
+ }
233
+ throw new Error(`Cannot evaluate expression: ${JSON.stringify(expr)}`);
234
+ }
235
+ /**
236
+ * Evaluate math function
237
+ */
238
+ evaluateMathFunction(name, args) {
239
+ switch (name) {
240
+ case 'sin': return Math.sin(args[0]);
241
+ case 'cos': return Math.cos(args[0]);
242
+ case 'tan': return Math.tan(args[0]);
243
+ case 'asin': return Math.asin(args[0]);
244
+ case 'acos': return Math.acos(args[0]);
245
+ case 'atan': return Math.atan(args[0]);
246
+ case 'atan2': return Math.atan2(args[0], args[1]);
247
+ case 'exp': return Math.exp(args[0]);
248
+ case 'log': return Math.log(args[0]);
249
+ case 'sqrt': return Math.sqrt(args[0]);
250
+ case 'abs': return Math.abs(args[0]);
251
+ case 'pow': return Math.pow(args[0], args[1]);
252
+ case 'min': return Math.min(args[0], args[1]);
253
+ case 'max': return Math.max(args[0], args[1]);
254
+ default:
255
+ throw new Error(`Unknown math function: ${name}`);
256
+ }
257
+ }
258
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Edge case guards for generated gradient code
3
+ * Detects potential issues like division by zero, sqrt of negative, etc.
4
+ */
5
+ import { Expression, FunctionDef } from './AST.js';
6
+ export interface Guard {
7
+ type: 'division_by_zero' | 'sqrt_negative' | 'normalize_zero' | 'atan2_zero';
8
+ expression: Expression;
9
+ description: string;
10
+ suggestion: string;
11
+ }
12
+ export interface GuardAnalysisResult {
13
+ guards: Guard[];
14
+ hasIssues: boolean;
15
+ }
16
+ /**
17
+ * Analyze function for potential edge cases
18
+ */
19
+ export declare function analyzeGuards(func: FunctionDef): GuardAnalysisResult;
20
+ /**
21
+ * Format guard analysis for display
22
+ */
23
+ export declare function formatGuardWarnings(result: GuardAnalysisResult): string;
24
+ /**
25
+ * Generate guard code snippets for common cases
26
+ */
27
+ export declare function generateGuardCode(guards: Guard[], format?: 'typescript' | 'javascript' | 'python'): string[];
@@ -0,0 +1,206 @@
1
+ /**
2
+ * Edge case guards for generated gradient code
3
+ * Detects potential issues like division by zero, sqrt of negative, etc.
4
+ */
5
+ /**
6
+ * Analyze function for potential edge cases
7
+ */
8
+ export function analyzeGuards(func) {
9
+ const guards = [];
10
+ // Analyze return expression
11
+ collectGuards(func.returnExpr, guards);
12
+ // Analyze intermediate expressions
13
+ for (const stmt of func.body) {
14
+ if (stmt.kind === 'assignment') {
15
+ collectGuards(stmt.expression, guards);
16
+ }
17
+ }
18
+ return {
19
+ guards,
20
+ hasIssues: guards.length > 0
21
+ };
22
+ }
23
+ /**
24
+ * Collect potential guards from an expression
25
+ */
26
+ function collectGuards(expr, guards) {
27
+ switch (expr.kind) {
28
+ case 'binary':
29
+ if (expr.operator === '/') {
30
+ guards.push({
31
+ type: 'division_by_zero',
32
+ expression: expr.right,
33
+ description: `Division by zero if denominator becomes zero`,
34
+ suggestion: `Add check: if (denominator === 0) return { value: 0, gradients: {...} };`
35
+ });
36
+ }
37
+ collectGuards(expr.left, guards);
38
+ collectGuards(expr.right, guards);
39
+ break;
40
+ case 'unary':
41
+ collectGuards(expr.operand, guards);
42
+ break;
43
+ case 'call':
44
+ analyzeCallGuards(expr, guards);
45
+ for (const arg of expr.args) {
46
+ collectGuards(arg, guards);
47
+ }
48
+ break;
49
+ case 'component':
50
+ collectGuards(expr.object, guards);
51
+ break;
52
+ }
53
+ }
54
+ /**
55
+ * Analyze function calls for specific edge cases
56
+ */
57
+ function analyzeCallGuards(expr, guards) {
58
+ switch (expr.name) {
59
+ case 'sqrt':
60
+ guards.push({
61
+ type: 'sqrt_negative',
62
+ expression: expr.args[0],
63
+ description: `sqrt of negative number produces NaN`,
64
+ suggestion: `Add check: Math.max(0, value) or abs(value)`
65
+ });
66
+ break;
67
+ case 'magnitude2d':
68
+ case 'magnitude3d':
69
+ // magnitude uses sqrt internally
70
+ guards.push({
71
+ type: 'sqrt_negative',
72
+ expression: expr,
73
+ description: `magnitude of vector (uses sqrt internally)`,
74
+ suggestion: `Ensure vector components are valid`
75
+ });
76
+ break;
77
+ case 'normalize2d':
78
+ case 'normalize3d':
79
+ guards.push({
80
+ type: 'normalize_zero',
81
+ expression: expr,
82
+ description: `Normalizing zero vector causes division by zero`,
83
+ suggestion: `Check if magnitude > epsilon before normalizing`
84
+ });
85
+ break;
86
+ case 'atan2':
87
+ guards.push({
88
+ type: 'atan2_zero',
89
+ expression: expr,
90
+ description: `atan2(0, 0) is undefined`,
91
+ suggestion: `Check if both arguments are zero: if (y === 0 && x === 0) return 0;`
92
+ });
93
+ break;
94
+ case 'log':
95
+ guards.push({
96
+ type: 'division_by_zero',
97
+ expression: expr.args[0],
98
+ description: `log(0) is -Infinity, log(negative) is NaN`,
99
+ suggestion: `Add check: Math.max(epsilon, value)`
100
+ });
101
+ break;
102
+ case 'asin':
103
+ case 'acos':
104
+ guards.push({
105
+ type: 'division_by_zero',
106
+ expression: expr.args[0],
107
+ description: `${expr.name} requires argument in [-1, 1]`,
108
+ suggestion: `Clamp value: Math.max(-1, Math.min(1, value))`
109
+ });
110
+ break;
111
+ }
112
+ }
113
+ /**
114
+ * Format guard analysis for display
115
+ */
116
+ export function formatGuardWarnings(result) {
117
+ if (!result.hasIssues) {
118
+ return '';
119
+ }
120
+ const lines = [];
121
+ lines.push('⚠️ EDGE CASE WARNINGS:');
122
+ lines.push('');
123
+ lines.push('The generated code may encounter edge cases that produce');
124
+ lines.push('NaN, Infinity, or incorrect results:');
125
+ lines.push('');
126
+ // Group by type
127
+ const byType = new Map();
128
+ for (const guard of result.guards) {
129
+ const existing = byType.get(guard.type) || [];
130
+ existing.push(guard);
131
+ byType.set(guard.type, existing);
132
+ }
133
+ for (const [type, guards] of byType.entries()) {
134
+ const typeLabel = formatGuardType(type);
135
+ lines.push(` • ${typeLabel} (${guards.length} occurrence${guards.length > 1 ? 's' : ''})`);
136
+ // Show first occurrence
137
+ const first = guards[0];
138
+ lines.push(` ${first.description}`);
139
+ lines.push(` 💡 ${first.suggestion}`);
140
+ lines.push('');
141
+ }
142
+ lines.push('Consider adding runtime checks or ensuring inputs are within valid ranges.');
143
+ lines.push('');
144
+ return lines.join('\n');
145
+ }
146
+ function formatGuardType(type) {
147
+ switch (type) {
148
+ case 'division_by_zero':
149
+ return 'Division by zero';
150
+ case 'sqrt_negative':
151
+ return 'Square root of negative';
152
+ case 'normalize_zero':
153
+ return 'Normalizing zero vector';
154
+ case 'atan2_zero':
155
+ return 'atan2(0, 0) undefined';
156
+ default:
157
+ return type;
158
+ }
159
+ }
160
+ /**
161
+ * Generate guard code snippets for common cases
162
+ */
163
+ export function generateGuardCode(guards, format = 'typescript') {
164
+ const snippets = [];
165
+ // Group by type and generate appropriate guards
166
+ const hasNormalize = guards.some(g => g.type === 'normalize_zero');
167
+ const hasDivision = guards.some(g => g.type === 'division_by_zero');
168
+ const hasAtan2 = guards.some(g => g.type === 'atan2_zero');
169
+ if (format === 'typescript' || format === 'javascript') {
170
+ if (hasNormalize) {
171
+ snippets.push('const EPSILON = 1e-10;');
172
+ snippets.push('if (magnitude < EPSILON) {');
173
+ snippets.push(' // Return zero gradient for zero vector');
174
+ snippets.push(' return { value: 0, grad_v: { x: 0, y: 0 } };');
175
+ snippets.push('}');
176
+ }
177
+ if (hasDivision) {
178
+ snippets.push('// Guard against division by zero');
179
+ snippets.push('if (Math.abs(denominator) < EPSILON) {');
180
+ snippets.push(' // Handle edge case appropriately');
181
+ snippets.push('}');
182
+ }
183
+ if (hasAtan2) {
184
+ snippets.push('// Guard against atan2(0, 0)');
185
+ snippets.push('if (y === 0 && x === 0) {');
186
+ snippets.push(' return { value: 0, gradients: {...} };');
187
+ snippets.push('}');
188
+ }
189
+ }
190
+ else {
191
+ // Python
192
+ if (hasNormalize) {
193
+ snippets.push('EPSILON = 1e-10');
194
+ snippets.push('if magnitude < EPSILON:');
195
+ snippets.push(' # Return zero gradient for zero vector');
196
+ snippets.push(' return {"value": 0, "grad_v": {"x": 0, "y": 0}}');
197
+ }
198
+ if (hasDivision) {
199
+ snippets.push('# Guard against division by zero');
200
+ snippets.push('if abs(denominator) < EPSILON:');
201
+ snippets.push(' # Handle edge case appropriately');
202
+ snippets.push(' pass');
203
+ }
204
+ }
205
+ return snippets;
206
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Inliner for GradientScript DSL
3
+ * Inlines intermediate variables before differentiation
4
+ */
5
+ import { Expression, FunctionDef } from './AST.js';
6
+ /**
7
+ * Inline all intermediate variables in a function
8
+ * Returns a new expression with all intermediate variables substituted
9
+ */
10
+ export declare function inlineIntermediateVariables(func: FunctionDef): Expression;
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Inliner for GradientScript DSL
3
+ * Inlines intermediate variables before differentiation
4
+ */
5
+ import { ExpressionTransformer } from './ExpressionTransformer.js';
6
+ /**
7
+ * Expression transformer that substitutes variables from a substitution map
8
+ * Handles recursive inlining by reprocessing substituted expressions
9
+ */
10
+ class SubstitutionTransformer extends ExpressionTransformer {
11
+ substitutions;
12
+ constructor(substitutions) {
13
+ super();
14
+ this.substitutions = substitutions;
15
+ }
16
+ visitVariable(node) {
17
+ const replacement = this.substitutions.get(node.name);
18
+ if (replacement) {
19
+ // Recursively inline the replacement
20
+ return this.transform(replacement);
21
+ }
22
+ return node;
23
+ }
24
+ }
25
+ /**
26
+ * Inline all intermediate variables in a function
27
+ * Returns a new expression with all intermediate variables substituted
28
+ */
29
+ export function inlineIntermediateVariables(func) {
30
+ // Build substitution map from assignments
31
+ const substitutions = new Map();
32
+ for (const stmt of func.body) {
33
+ if (stmt.kind === 'assignment') {
34
+ substitutions.set(stmt.variable, stmt.expression);
35
+ }
36
+ }
37
+ // Use transformer to inline all variables
38
+ const transformer = new SubstitutionTransformer(substitutions);
39
+ return transformer.transform(func.returnExpr);
40
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Lexer for GradientScript DSL
3
+ * Tokenizes input with support for ∇, ^, **, and structured types
4
+ */
5
+ export declare enum TokenType {
6
+ NUMBER = "NUMBER",
7
+ IDENTIFIER = "IDENTIFIER",
8
+ FUNCTION = "FUNCTION",
9
+ RETURN = "RETURN",
10
+ PLUS = "PLUS",// +
11
+ MINUS = "MINUS",// -
12
+ MULTIPLY = "MULTIPLY",// *
13
+ DIVIDE = "DIVIDE",// /
14
+ POWER = "POWER",// ^
15
+ POWER_ALT = "POWER_ALT",// **
16
+ NABLA = "NABLA",// ∇
17
+ LPAREN = "LPAREN",// (
18
+ RPAREN = "RPAREN",// )
19
+ LBRACE = "LBRACE",// {
20
+ RBRACE = "RBRACE",// }
21
+ COMMA = "COMMA",// ,
22
+ COLON = "COLON",// :
23
+ DOT = "DOT",// .
24
+ EQUALS = "EQUALS",// =
25
+ NEWLINE = "NEWLINE",
26
+ EOF = "EOF"
27
+ }
28
+ export interface Token {
29
+ type: TokenType;
30
+ value: string;
31
+ line: number;
32
+ column: number;
33
+ }
34
+ export declare class Lexer {
35
+ private input;
36
+ private position;
37
+ private line;
38
+ private column;
39
+ constructor(input: string);
40
+ /**
41
+ * Get all tokens
42
+ */
43
+ tokenize(): Token[];
44
+ /**
45
+ * Get next token
46
+ */
47
+ nextToken(): Token;
48
+ private number;
49
+ private identifier;
50
+ private skipWhitespace;
51
+ private peek;
52
+ private peekNext;
53
+ private advance;
54
+ private isAtEnd;
55
+ private isDigit;
56
+ private isAlpha;
57
+ private isAlphaNumeric;
58
+ private makeToken;
59
+ }
60
+ /**
61
+ * Convenience function to tokenize input
62
+ */
63
+ export declare function tokenize(input: string): Token[];