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 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(source: string, context?: ExecutionContext): number`
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
- // Use with Executor class or optimize() function
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(source: string, context?: ExecutionContext): RuntimeValue;
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 tokens;
665
- private current;
666
- constructor(tokens: Token[]);
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
- tokens;
811
- current = 0;
812
- constructor(tokens) {
813
- this.tokens = tokens;
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
- if (this.current >= this.tokens.length) {
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.current++;
926
+ this.currentToken = this.lexer.nextToken();
934
927
  }
935
928
  }
936
929
  function parseSource(source) {
937
930
  const lexer = new Lexer(source);
938
- const tokens = lexer.tokenize();
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(source, context) {
1029
- const ast = parseSource(source);
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(ast);
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.8.2",
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",