Skip to content

Commit 1f3a245

Browse files
committed
feat: export compilation types and classes for advanced users
1 parent 6200dd4 commit 1f3a245

5 files changed

Lines changed: 345 additions & 7 deletions

File tree

CHANGELOG.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,39 @@
3333
- Works with both scalar and collection (vector/array) arguments
3434
- Partial overrides supported (only override some operators)
3535

36+
- **Exported Compilation Interfaces**: Advanced users can now create custom
37+
compilation targets by using the exported `CompileTarget` interface,
38+
`BaseCompiler` class, and `JavaScriptTarget` class.
39+
40+
```javascript
41+
import { BaseCompiler, JavaScriptTarget } from '@cortex-js/compute-engine';
42+
43+
// Create a custom compilation target
44+
const customTarget = {
45+
language: 'my-dsl',
46+
operators: (op) => ({ Add: ['ADD', 11], Multiply: ['MUL', 12] }[op]),
47+
functions: (id) => id.toUpperCase(),
48+
var: (id) => `VAR("${id}")`,
49+
string: (s) => `"${s}"`,
50+
number: (n) => n.toString(),
51+
ws: () => ' ',
52+
preamble: '',
53+
indent: 0,
54+
};
55+
56+
const expr = ce.parse('x + y * 2');
57+
const code = BaseCompiler.compile(expr, customTarget);
58+
// → "ADD(VAR("x"), MUL(VAR("y"), 2))"
59+
```
60+
61+
**Exported types and classes**:
62+
- `CompileTarget` - Interface for defining compilation targets
63+
- `CompiledOperators`, `CompiledFunctions` - Type definitions
64+
- `CompilationOptions`, `CompiledExecutable` - Options and result types
65+
- `LanguageTarget` - Interface for language-specific targets
66+
- `BaseCompiler` - Core compilation logic
67+
- `JavaScriptTarget` - JavaScript compilation target implementation
68+
3669
### Improvements
3770

3871
- **Improved `ask()` Queries**: `ce.ask()` now matches patterns with wildcards

COMPILE.md

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -330,15 +330,27 @@ float f(float x) {
330330
- Added comprehensive test coverage including edge cases
331331
- Created working example in `examples/compile-vector-operations.js`
332332

333-
### Medium Priority (Phase 2)
333+
### Medium Priority (Phase 2) ✅ COMPLETED
334334

335-
- [ ] Export `CompileTarget` and related interfaces
336-
- [ ] Export `JavaScriptTarget` and `BaseCompiler`
337-
- [ ] Add advanced examples
338-
- [ ] Document custom target creation
335+
- [x] Export `CompileTarget` and related interfaces
336+
- [x] Export `JavaScriptTarget` and `BaseCompiler`
337+
- [x] Add advanced examples
338+
- [x] Document custom target creation
339339

340-
**Estimated effort**: 1 day **Impact**: Enables advanced users to create custom
341-
targets
340+
**Status**: ✅ **Completed**
341+
**Impact**: Enables advanced users to create custom targets
342+
343+
**Implementation notes**:
344+
- Exported all compilation types from main entry point (`src/compute-engine.ts`)
345+
- Types exported: `CompileTarget`, `CompiledOperators`, `CompiledFunctions`, `CompilationOptions`, `CompiledExecutable`, `LanguageTarget`, `TargetSource`, `CompiledFunction`
346+
- Classes exported: `JavaScriptTarget`, `BaseCompiler`
347+
- Created comprehensive example (`examples/compile-custom-target.js`) showing:
348+
- Extending JavaScriptTarget with custom mappings
349+
- Creating all-function-call targets for legacy systems
350+
- Implementing RPN (Reverse Polish Notation) compilation
351+
- SQL-like expression target
352+
- Pretty-print target with formatting
353+
- All exports are available in the production build
342354

343355
### Lower Priority (Phase 3)
344356

examples/compile-custom-target.js

Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
/**
2+
* Example: Creating a Custom Compilation Target
3+
*
4+
* This example demonstrates how to create a custom compilation target
5+
* using the exported CompileTarget interface and BaseCompiler.
6+
*
7+
* We'll create a simple "Calculator DSL" that compiles to function calls
8+
* with uppercase names, useful for interfacing with legacy systems or
9+
* domain-specific calculators.
10+
*/
11+
12+
import {
13+
ComputeEngine,
14+
BaseCompiler,
15+
JavaScriptTarget,
16+
} from '../dist/compute-engine.esm.js';
17+
18+
const ce = new ComputeEngine();
19+
20+
// ============================================================================
21+
// Example 1: Using JavaScriptTarget as a Base
22+
// ============================================================================
23+
console.log('Example 1: Extending JavaScriptTarget');
24+
console.log('='.repeat(70));
25+
26+
// Create a custom target that extends the JavaScript target
27+
// with custom formatting for constants
28+
const jsTarget = new JavaScriptTarget();
29+
const customTarget = jsTarget.createTarget({
30+
// Override variable formatting
31+
var: (id) => {
32+
// Map common constants to uppercase
33+
const constants = {
34+
Pi: 'PI',
35+
ExponentialE: 'E',
36+
ImaginaryUnit: 'I',
37+
};
38+
if (id in constants) return constants[id];
39+
return `VAR_${id.toUpperCase()}`;
40+
},
41+
// Add custom preamble
42+
preamble: '// Generated by Custom Calculator DSL\n',
43+
});
44+
45+
const expr1 = ce.parse('2\\pi + e');
46+
const code1 = BaseCompiler.compile(expr1, customTarget);
47+
console.log('Expression:', expr1.toString());
48+
console.log('Compiled:', code1);
49+
console.log();
50+
51+
// ============================================================================
52+
// Example 2: Custom Target with Function Calls
53+
// ============================================================================
54+
console.log('Example 2: Custom Target with All Function Calls');
55+
console.log('='.repeat(70));
56+
57+
// Create a target where ALL operations are function calls
58+
// (useful for DSLs that don't support infix operators)
59+
const functionTarget = {
60+
language: 'calculator-dsl',
61+
operators: (op) => {
62+
// Map all operators to uppercase function names
63+
const opMap = {
64+
Add: ['ADD', 11],
65+
Subtract: ['SUB', 11],
66+
Multiply: ['MUL', 12],
67+
Divide: ['DIV', 13],
68+
Power: ['POW', 14],
69+
Negate: ['NEG', 14],
70+
Equal: ['EQ', 8],
71+
Less: ['LT', 9],
72+
Greater: ['GT', 9],
73+
};
74+
return opMap[op];
75+
},
76+
functions: (id) => {
77+
// Map common functions to uppercase
78+
const fnMap = {
79+
Sin: 'SIN',
80+
Cos: 'COS',
81+
Tan: 'TAN',
82+
Sqrt: 'SQRT',
83+
Abs: 'ABS',
84+
Exp: 'EXP',
85+
Log: 'LOG',
86+
Ln: 'LN',
87+
};
88+
return fnMap[id];
89+
},
90+
var: (id) => `VAR("${id}")`,
91+
string: (s) => `"${s}"`,
92+
number: (n) => n.toString(),
93+
ws: () => ' ',
94+
preamble: '',
95+
indent: 0,
96+
};
97+
98+
const expr2 = ce.parse('\\sin(x) + \\cos(y)^2');
99+
const code2 = BaseCompiler.compile(expr2, functionTarget);
100+
console.log('Expression:', expr2.toString());
101+
console.log('Compiled:', code2);
102+
console.log();
103+
104+
// ============================================================================
105+
// Example 3: RPN (Reverse Polish Notation) Target
106+
// ============================================================================
107+
console.log('Example 3: Custom RPN (Reverse Polish Notation) Target');
108+
console.log('='.repeat(70));
109+
110+
// Create a target that outputs RPN (like HP calculators)
111+
// We'll use a custom compilation approach
112+
class RPNTarget {
113+
constructor() {
114+
this.jsTarget = new JavaScriptTarget();
115+
}
116+
117+
compile(expr) {
118+
// For RPN, we need to traverse the expression tree differently
119+
// This is a simplified example
120+
return this.compileToRPN(expr);
121+
}
122+
123+
compileToRPN(expr) {
124+
if (expr.symbol) return expr.symbol;
125+
if (expr.isNumberLiteral) return expr.re.toString();
126+
127+
const op = expr.operator;
128+
const args = expr.ops || [];
129+
130+
// Compile arguments first (postorder traversal)
131+
const compiledArgs = args.map((arg) => this.compileToRPN(arg));
132+
133+
// Map operators to RPN commands
134+
const opMap = {
135+
Add: '+',
136+
Subtract: '-',
137+
Multiply: '*',
138+
Divide: '/',
139+
Power: '^',
140+
Sin: 'SIN',
141+
Cos: 'COS',
142+
Sqrt: 'SQRT',
143+
};
144+
145+
const rpnOp = opMap[op] || op;
146+
147+
// In RPN: operands first, then operator
148+
return `${compiledArgs.join(' ')} ${rpnOp}`;
149+
}
150+
}
151+
152+
const rpnTarget = new RPNTarget();
153+
const expr3 = ce.parse('(3 + 4) * 5');
154+
const rpn = rpnTarget.compile(expr3);
155+
console.log('Expression:', expr3.toString());
156+
console.log('RPN:', rpn);
157+
console.log('(Stack-based evaluation: push 3, push 4, add, push 5, multiply)');
158+
console.log();
159+
160+
// ============================================================================
161+
// Example 4: SQL-like Target
162+
// ============================================================================
163+
console.log('Example 4: SQL-like Expression Target');
164+
console.log('='.repeat(70));
165+
166+
// Create a target for SQL-like expressions
167+
const sqlTarget = {
168+
language: 'sql',
169+
operators: (op) => {
170+
const opMap = {
171+
Add: ['+', 11],
172+
Subtract: ['-', 11],
173+
Multiply: ['*', 12],
174+
Divide: ['/', 13],
175+
Equal: ['=', 8],
176+
NotEqual: ['<>', 8],
177+
Less: ['<', 9],
178+
Greater: ['>', 9],
179+
LessEqual: ['<=', 9],
180+
GreaterEqual: ['>=', 9],
181+
And: ['AND', 4],
182+
Or: ['OR', 3],
183+
Not: ['NOT', 14],
184+
};
185+
return opMap[op];
186+
},
187+
functions: (id) => {
188+
const fnMap = {
189+
Abs: 'ABS',
190+
Sqrt: 'SQRT',
191+
Power: 'POWER',
192+
Ceiling: 'CEILING',
193+
Floor: 'FLOOR',
194+
Round: 'ROUND',
195+
};
196+
return fnMap[id];
197+
},
198+
var: (id) => `"${id}"`, // Quote column names
199+
string: (s) => `'${s.replace(/'/g, "''")}'`, // SQL string escaping
200+
number: (n) => n.toString(),
201+
ws: () => ' ',
202+
preamble: '',
203+
indent: 0,
204+
};
205+
206+
const expr4 = ce.parse('x > 10 \\land y \\leq 20');
207+
const sql = BaseCompiler.compile(expr4, sqlTarget);
208+
console.log('Expression:', expr4.toString());
209+
console.log('SQL WHERE clause:', `WHERE ${sql}`);
210+
console.log();
211+
212+
// ============================================================================
213+
// Example 5: Pretty-Print Target with Indentation
214+
// ============================================================================
215+
console.log('Example 5: Pretty-Print Target with Indentation');
216+
console.log('='.repeat(70));
217+
218+
// Create a target that pretty-prints with indentation
219+
const prettyTarget = {
220+
language: 'pretty',
221+
operators: (op) => {
222+
const opMap = {
223+
Add: ['add', 11],
224+
Multiply: ['multiply', 12],
225+
Power: ['power', 14],
226+
};
227+
return opMap[op];
228+
},
229+
functions: (id) => id.toLowerCase(),
230+
var: (id) => id,
231+
string: (s) => `"${s}"`,
232+
number: (n) => n.toString(),
233+
ws: (s) => {
234+
if (s === '\n') return '\n '; // Add indentation
235+
return s || '';
236+
},
237+
preamble: '',
238+
indent: 0,
239+
};
240+
241+
const expr5 = ce.parse('(a + b) * (c + d)');
242+
const pretty = BaseCompiler.compile(expr5, prettyTarget);
243+
console.log('Expression:', expr5.toString());
244+
console.log('Pretty-printed:');
245+
console.log(pretty);
246+
console.log();
247+
248+
// ============================================================================
249+
// Summary
250+
// ============================================================================
251+
console.log('Summary');
252+
console.log('='.repeat(70));
253+
console.log('✓ Extended JavaScriptTarget with custom variable mapping');
254+
console.log('✓ Created all-function-call target for legacy systems');
255+
console.log('✓ Implemented custom RPN compilation');
256+
console.log('✓ Created SQL-like expression target');
257+
console.log('✓ Built pretty-print target with formatting');
258+
console.log();
259+
console.log('The CompileTarget interface enables:');
260+
console.log(' • Custom operator and function mappings');
261+
console.log(' • Domain-specific language generation');
262+
console.log(' • Code generation for external systems');
263+
console.log(' • Alternative evaluation strategies');

src/compute-engine.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,21 @@ export { ComputeEngine } from './compute-engine/index';
1010

1111
export * from './compute-engine/types';
1212

13+
// Export compilation types and classes for advanced users
14+
export type {
15+
CompileTarget,
16+
CompiledOperators,
17+
CompiledFunctions,
18+
CompilationOptions,
19+
CompiledExecutable,
20+
LanguageTarget,
21+
TargetSource,
22+
CompiledFunction,
23+
} from './compute-engine/compilation/types';
24+
25+
export { JavaScriptTarget } from './compute-engine/compilation/javascript-target';
26+
export { BaseCompiler } from './compute-engine/compilation/base-compiler';
27+
1328
globalThis[Symbol.for('io.cortexjs.compute-engine')] = {
1429
ComputeEngine: ComputeEngine.prototype.constructor,
1530
version: '{{SDK_VERSION}}',

src/compute-engine/index.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,21 @@ export * from './global-types';
157157

158158
export { validatePattern };
159159

160+
// Export compilation types and classes for advanced users
161+
export type {
162+
CompileTarget,
163+
CompiledOperators,
164+
CompiledFunctions,
165+
CompilationOptions,
166+
CompiledExecutable,
167+
LanguageTarget,
168+
TargetSource,
169+
CompiledFunction,
170+
} from './compilation/types';
171+
172+
export { JavaScriptTarget } from './compilation/javascript-target';
173+
export { BaseCompiler } from './compilation/base-compiler';
174+
160175
/**
161176
*
162177
* To use the Compute Engine, create a `ComputeEngine` instance:

0 commit comments

Comments
 (0)