Skip to content

Commit 5662a87

Browse files
committed
perf(latex): Optimize LaTeX parsing with trigger-based indexing
test(tests): Add comprehensive tests for Element and verification behavior
1 parent 35d5f1b commit 5662a87

10 files changed

Lines changed: 926 additions & 115 deletions

File tree

SIMPLIFY.md

Lines changed: 81 additions & 78 deletions
Large diffs are not rendered by default.
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
# Function Validation Guidelines
2+
3+
This document provides guidelines for implementing proper validation in function
4+
definitions, especially for functions with custom canonical handlers.
5+
6+
## The Problem
7+
8+
When a function has a custom `canonical` handler, signature validation may not
9+
run automatically. This can lead to missing error expressions for invalid
10+
arguments.
11+
12+
## Best Practices
13+
14+
### 1. Functions with Required Arguments
15+
16+
Always validate that required arguments are present and of the correct type:
17+
18+
```typescript
19+
{
20+
signature: '(value, collection) -> boolean',
21+
canonical: (args, { engine: ce }) => {
22+
// Validate required arguments
23+
if (args.length === 0) {
24+
return ce._fn('MyFunction', [ce.error('missing'), ce.error('missing')]);
25+
}
26+
if (args.length === 1) {
27+
return ce._fn('MyFunction', [args[0].canonical, ce.error('missing')]);
28+
}
29+
30+
// Continue with normal canonicalization...
31+
return ce._fn('MyFunction', [args[0].canonical, args[1].canonical]);
32+
}
33+
}
34+
```
35+
36+
### 2. Functions with Optional Arguments
37+
38+
Validate optional arguments when present:
39+
40+
```typescript
41+
{
42+
signature: '(value, collection, boolean?) -> boolean',
43+
canonical: (args, { engine: ce }) => {
44+
// Handle missing required arguments
45+
if (args.length < 2) {
46+
// ... add errors as shown above
47+
}
48+
49+
const [value, collection, condition] = args;
50+
51+
// Validate optional argument type if present
52+
if (condition && !condition.type.matches('boolean')) {
53+
return ce._fn('MyFunction', [
54+
value.canonical,
55+
collection.canonical,
56+
ce.error(['incompatible-type', "'boolean'", condition.type.toString()])
57+
]);
58+
}
59+
60+
// Continue with normal canonicalization...
61+
}
62+
}
63+
```
64+
65+
### 3. Type Validation
66+
67+
Validate argument types and add appropriate errors:
68+
69+
```typescript
70+
{
71+
canonical: (args, { engine: ce }) => {
72+
// ... handle missing arguments ...
73+
74+
const collection = args[1];
75+
76+
// Validate that collection is the right type
77+
if (!collection.type.matches('collection') && !collection.symbol) {
78+
return ce._fn('MyFunction', [
79+
args[0].canonical,
80+
ce.error(['incompatible-type', "'collection'", collection.type.toString()])
81+
]);
82+
}
83+
84+
// Continue...
85+
}
86+
}
87+
```
88+
89+
## Examples from the Codebase
90+
91+
### Good Example: Element (sets.ts)
92+
93+
```typescript
94+
Element: {
95+
signature: '(value, collection, boolean?) -> boolean',
96+
canonical: (args, { engine: ce }) => {
97+
// Explicit validation for missing required arguments
98+
if (args.length === 0) {
99+
return ce._fn('Element', [ce.error('missing'), ce.error('missing')]);
100+
}
101+
if (args.length === 1) {
102+
return ce._fn('Element', [args[0].canonical, ce.error('missing')]);
103+
}
104+
105+
const [value, collection, condition] = args;
106+
107+
// ... canonicalization logic ...
108+
109+
// Validate optional third argument
110+
if (condition && !condition.type.matches('boolean')) {
111+
return ce._fn('Element', [
112+
value.canonical,
113+
canonicalCollection,
114+
ce.error(['incompatible-type', "'boolean'", collection.type.toString()])
115+
]);
116+
}
117+
118+
// ... continue ...
119+
}
120+
}
121+
```
122+
123+
### Good Example: Rational (arithmetic.ts)
124+
125+
```typescript
126+
Rational: {
127+
signature: '(number, integer?) -> rational',
128+
canonical: (args, { engine }) => {
129+
const ce = engine;
130+
args = flatten(args);
131+
132+
if (args.length === 0) return ce._fn('Rational', [ce.error('missing')]);
133+
134+
if (args.length === 1)
135+
return ce._fn('Rational', [checkType(ce, args[0], 'real')]);
136+
137+
args = checkTypes(ce, args, ['integer', 'integer']);
138+
139+
if (args.length !== 2 || !args[0].isValid || !args[1].isValid)
140+
return ce._fn('Rational', args);
141+
142+
return args[0].div(args[1]);
143+
}
144+
}
145+
```
146+
147+
## Common Pitfalls
148+
149+
### ❌ Bad: No validation
150+
151+
```typescript
152+
canonical: (args, { engine: ce }) => {
153+
if (args.length < 2) return ce._fn('MyFunction', args); // Missing validation!
154+
// ...
155+
}
156+
```
157+
158+
### ✅ Good: Explicit validation
159+
160+
```typescript
161+
canonical: (args, { engine: ce }) => {
162+
if (args.length === 0) {
163+
return ce._fn('MyFunction', [ce.error('missing'), ce.error('missing')]);
164+
}
165+
if (args.length === 1) {
166+
return ce._fn('MyFunction', [args[0].canonical, ce.error('missing')]);
167+
}
168+
// ...
169+
}
170+
```
171+
172+
## Testing Validation
173+
174+
Always add tests for invalid arguments:
175+
176+
```typescript
177+
test('INVALID', () => {
178+
expect(ce.box(['MyFunction']).evaluate()).toMatchInlineSnapshot(
179+
`["MyFunction", ["Error", "'missing'"], ["Error", "'missing'"]]`
180+
);
181+
expect(ce.box(['MyFunction', 2]).evaluate()).toMatchInlineSnapshot(
182+
`["MyFunction", 2, ["Error", "'missing'"]]`
183+
);
184+
expect(ce.box(['MyFunction', 2, 'wrongType']).evaluate()).toMatchInlineSnapshot(
185+
`["MyFunction", 2, ["Error", ["ErrorCode", "incompatible-type", ...]]]`
186+
);
187+
});
188+
```
189+
190+
## Helper Functions
191+
192+
Consider using these helper functions from the library:
193+
194+
- `checkType(ce, arg, expectedType)` - Validates a single argument type
195+
- `checkTypes(ce, args, types)` - Validates multiple argument types
196+
- `ce.error(message)` - Creates an error expression
197+
- `ce.error(['incompatible-type', expected, actual])` - Creates a type error
198+
199+
## Summary
200+
201+
1. **Always validate** required arguments in custom canonical handlers
202+
2. **Check types** for both required and optional arguments
203+
3. **Add error expressions** using `ce.error()` for invalid cases
204+
4. **Test thoroughly** with missing, extra, and wrong-type arguments
205+
5. **Follow the patterns** shown in Element and Rational functions

0 commit comments

Comments
 (0)