littlewing 0.8.2 → 0.9.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/README.md +46 -4
- package/dist/index.d.ts +9 -6
- package/dist/index.js +11 -19
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -156,22 +156,38 @@ For complete language documentation including all operators, functions, and exam
|
|
|
156
156
|
|
|
157
157
|
### Main Functions
|
|
158
158
|
|
|
159
|
-
#### `execute(
|
|
159
|
+
#### `execute(input: string | ASTNode, context?: ExecutionContext): number`
|
|
160
160
|
|
|
161
|
-
Execute an expression and return the result.
|
|
161
|
+
Execute an expression or AST and return the result. Accepts either a source string or a pre-parsed AST node.
|
|
162
162
|
|
|
163
163
|
```typescript
|
|
164
|
+
// Execute source string directly
|
|
164
165
|
execute("2 + 2"); // → 4
|
|
165
166
|
execute("ABS(-5)", { functions: { ABS: Math.abs } }); // → 5
|
|
167
|
+
|
|
168
|
+
// Execute pre-parsed AST (useful for parse-once, execute-many scenarios)
|
|
169
|
+
const ast = parseSource("2 + 2");
|
|
170
|
+
execute(ast); // → 4
|
|
171
|
+
execute(ast); // → 4 (no re-parsing)
|
|
166
172
|
```
|
|
167
173
|
|
|
168
174
|
#### `parseSource(source: string): ASTNode`
|
|
169
175
|
|
|
170
|
-
Parse source into an Abstract Syntax Tree without executing.
|
|
176
|
+
Parse source into an Abstract Syntax Tree without executing. Useful for parse-once, execute-many scenarios.
|
|
171
177
|
|
|
172
178
|
```typescript
|
|
173
179
|
const ast = parseSource("2 + 3 * 4");
|
|
174
|
-
|
|
180
|
+
|
|
181
|
+
// Execute multiple times with different contexts (no re-parsing)
|
|
182
|
+
execute(ast); // → 14
|
|
183
|
+
execute(ast, {
|
|
184
|
+
variables: {
|
|
185
|
+
/* ... */
|
|
186
|
+
},
|
|
187
|
+
}); // → 14 (with context)
|
|
188
|
+
|
|
189
|
+
// Or use with Executor class or optimize() function
|
|
190
|
+
const optimized = optimize(ast);
|
|
175
191
|
```
|
|
176
192
|
|
|
177
193
|
#### `optimize(node: ASTNode): ASTNode`
|
|
@@ -225,6 +241,32 @@ The `defaultContext` includes these built-in functions:
|
|
|
225
241
|
|
|
226
242
|
**Date comparisons:** `IS_SAME_DAY`, `IS_WEEKEND`, `IS_LEAP_YEAR` (use `<`, `>`, `<=`, `>=` operators for before/after comparisons)
|
|
227
243
|
|
|
244
|
+
## Performance Optimization
|
|
245
|
+
|
|
246
|
+
For expressions that are executed multiple times with different contexts, parse once and reuse the AST:
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
import { execute, parseSource } from "littlewing";
|
|
250
|
+
|
|
251
|
+
// Parse once
|
|
252
|
+
const formula = parseSource(
|
|
253
|
+
"price * quantity * (1 - discount) * (1 + taxRate)",
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
// Execute many times with different values (no re-parsing)
|
|
257
|
+
execute(formula, {
|
|
258
|
+
variables: { price: 10, quantity: 5, discount: 0.1, taxRate: 0.08 },
|
|
259
|
+
});
|
|
260
|
+
execute(formula, {
|
|
261
|
+
variables: { price: 20, quantity: 3, discount: 0.15, taxRate: 0.08 },
|
|
262
|
+
});
|
|
263
|
+
execute(formula, {
|
|
264
|
+
variables: { price: 15, quantity: 10, discount: 0.2, taxRate: 0.08 },
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
// This avoids lexing and parsing overhead on every execution
|
|
268
|
+
```
|
|
269
|
+
|
|
228
270
|
## Use Cases
|
|
229
271
|
|
|
230
272
|
- **User-defined formulas** - Let users write safe arithmetic expressions
|
package/dist/index.d.ts
CHANGED
|
@@ -585,9 +585,11 @@ declare class Executor {
|
|
|
585
585
|
private executeConditionalExpression;
|
|
586
586
|
}
|
|
587
587
|
/**
|
|
588
|
-
* Execute source code with given context
|
|
588
|
+
* Execute source code or AST with given context
|
|
589
|
+
* @param input - Either a source code string or an AST node
|
|
590
|
+
* @param context - Optional execution context with variables and functions
|
|
589
591
|
*/
|
|
590
|
-
declare function execute(
|
|
592
|
+
declare function execute(input: string | ASTNode, context?: ExecutionContext): RuntimeValue;
|
|
591
593
|
/**
|
|
592
594
|
* Lexer - converts source code into tokens
|
|
593
595
|
* Implements a single-pass O(n) tokenization algorithm
|
|
@@ -659,11 +661,12 @@ declare function optimize(node: ASTNode): ASTNode;
|
|
|
659
661
|
/**
|
|
660
662
|
* Parser using Pratt parsing (top-down operator precedence)
|
|
661
663
|
* Implements an efficient O(n) parsing algorithm
|
|
664
|
+
* Uses lazy lexing - calls lexer on-demand instead of receiving all tokens upfront
|
|
662
665
|
*/
|
|
663
666
|
declare class Parser {
|
|
664
|
-
private
|
|
665
|
-
private
|
|
666
|
-
constructor(
|
|
667
|
+
private lexer;
|
|
668
|
+
private currentToken;
|
|
669
|
+
constructor(lexer: Lexer);
|
|
667
670
|
/**
|
|
668
671
|
* Parse tokens into an AST
|
|
669
672
|
* Supports multiple statements separated by semicolons or newlines
|
|
@@ -698,7 +701,7 @@ declare class Parser {
|
|
|
698
701
|
*/
|
|
699
702
|
private peek;
|
|
700
703
|
/**
|
|
701
|
-
* Advance to next token
|
|
704
|
+
* Advance to next token by calling lexer
|
|
702
705
|
*/
|
|
703
706
|
private advance;
|
|
704
707
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { createRequire } from "node:module";
|
|
2
1
|
var __defProp = Object.defineProperty;
|
|
3
2
|
var __export = (target, all) => {
|
|
4
3
|
for (var name in all)
|
|
@@ -807,10 +806,11 @@ var BINARY_OPERATOR_TOKENS = new Set([
|
|
|
807
806
|
]);
|
|
808
807
|
|
|
809
808
|
class Parser {
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
constructor(
|
|
813
|
-
this.
|
|
809
|
+
lexer;
|
|
810
|
+
currentToken;
|
|
811
|
+
constructor(lexer) {
|
|
812
|
+
this.lexer = lexer;
|
|
813
|
+
this.currentToken = lexer.nextToken();
|
|
814
814
|
}
|
|
815
815
|
parse() {
|
|
816
816
|
const statements = [];
|
|
@@ -920,23 +920,15 @@ class Parser {
|
|
|
920
920
|
return BINARY_OPERATOR_TOKENS.has(type);
|
|
921
921
|
}
|
|
922
922
|
peek() {
|
|
923
|
-
|
|
924
|
-
return { type: "EOF" /* EOF */, value: "", position: -1 };
|
|
925
|
-
}
|
|
926
|
-
const token = this.tokens[this.current];
|
|
927
|
-
if (token === undefined) {
|
|
928
|
-
return { type: "EOF" /* EOF */, value: "", position: -1 };
|
|
929
|
-
}
|
|
930
|
-
return token;
|
|
923
|
+
return this.currentToken;
|
|
931
924
|
}
|
|
932
925
|
advance() {
|
|
933
|
-
this.
|
|
926
|
+
this.currentToken = this.lexer.nextToken();
|
|
934
927
|
}
|
|
935
928
|
}
|
|
936
929
|
function parseSource(source) {
|
|
937
930
|
const lexer = new Lexer(source);
|
|
938
|
-
const
|
|
939
|
-
const parser = new Parser(tokens);
|
|
931
|
+
const parser = new Parser(lexer);
|
|
940
932
|
return parser.parse();
|
|
941
933
|
}
|
|
942
934
|
|
|
@@ -1025,10 +1017,10 @@ class Executor {
|
|
|
1025
1017
|
return condition !== 0 ? this.execute(node.consequent) : this.execute(node.alternate);
|
|
1026
1018
|
}
|
|
1027
1019
|
}
|
|
1028
|
-
function execute(
|
|
1029
|
-
const
|
|
1020
|
+
function execute(input, context) {
|
|
1021
|
+
const node = typeof input === "string" ? parseSource(input) : input;
|
|
1030
1022
|
const executor = new Executor(context);
|
|
1031
|
-
return executor.execute(
|
|
1023
|
+
return executor.execute(node);
|
|
1032
1024
|
}
|
|
1033
1025
|
// src/optimizer.ts
|
|
1034
1026
|
function optimize(node) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "littlewing",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "A minimal, high-performance arithmetic expression language with lexer, parser, and executor. Optimized for browsers with zero dependencies and type-safe execution.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"arithmetic",
|