littlewing 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.
- package/LICENSE +21 -0
- package/README.md +414 -0
- package/dist/index.d.ts +360 -0
- package/dist/index.js +673 -0
- package/package.json +81 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 brielov
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
# littlewing
|
|
2
|
+
|
|
3
|
+
A minimal, high-performance arithmetic expression language with a complete lexer, parser, and executor. Optimized for browsers with **zero dependencies** and **type-safe execution**.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🚀 **Minimal & Fast** - O(n) algorithms throughout (lexer, parser, executor)
|
|
8
|
+
- 📦 **Tiny Bundle** - 3.61 KB gzipped, zero dependencies
|
|
9
|
+
- 🌐 **Browser Ready** - 100% ESM, no Node.js APIs
|
|
10
|
+
- 🔒 **Type-Safe** - Strict TypeScript with full type coverage
|
|
11
|
+
- ✅ **Thoroughly Tested** - 71 tests, 97.66% coverage
|
|
12
|
+
- 📐 **Math Expressions** - Numbers, dates, operators, functions, variables
|
|
13
|
+
- 🎯 **Clean API** - Intuitive dual API (class-based + functional)
|
|
14
|
+
- 📝 **Well Documented** - Complete JSDoc and examples
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install littlewing
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
### Basic Usage
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import { execute, defaultContext } from "littlewing";
|
|
28
|
+
|
|
29
|
+
// Simple arithmetic
|
|
30
|
+
execute("2 + 3 * 4"); // → 14
|
|
31
|
+
|
|
32
|
+
// Variables
|
|
33
|
+
execute("x = 5; y = 10; x + y"); // → 15
|
|
34
|
+
|
|
35
|
+
// Functions from context
|
|
36
|
+
execute("abs(-42)", defaultContext); // → 42
|
|
37
|
+
execute("sqrt(16)", defaultContext); // → 4
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### With Custom Context
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
import { execute } from "littlewing";
|
|
44
|
+
|
|
45
|
+
const context = {
|
|
46
|
+
functions: {
|
|
47
|
+
double: (n) => n * 2,
|
|
48
|
+
triple: (n) => n * 3,
|
|
49
|
+
},
|
|
50
|
+
variables: {
|
|
51
|
+
pi: 3.14159,
|
|
52
|
+
maxValue: 100,
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
execute("double(5)", context); // → 10
|
|
57
|
+
execute("pi * 2", context); // → 6.28318
|
|
58
|
+
execute("maxValue - 25", context); // → 75
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Date Support
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
import { execute, defaultContext } from "littlewing";
|
|
65
|
+
|
|
66
|
+
// Dates as first-class citizens
|
|
67
|
+
execute("now()", defaultContext); // → Date object (current time)
|
|
68
|
+
execute("date('2025-10-01')", defaultContext); // → Date object
|
|
69
|
+
execute("now() + minutes(30)", defaultContext); // → Date 30 minutes from now
|
|
70
|
+
|
|
71
|
+
// Time conversion helpers
|
|
72
|
+
execute("seconds(30)", defaultContext); // → 30000 (milliseconds)
|
|
73
|
+
execute("minutes(5)", defaultContext); // → 300000 (milliseconds)
|
|
74
|
+
execute("hours(2)", defaultContext); // → 7200000 (milliseconds)
|
|
75
|
+
execute("days(1)", defaultContext); // → 86400000 (milliseconds)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Manual AST Construction
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
import { ast, Executor } from "littlewing";
|
|
82
|
+
|
|
83
|
+
const expr = ast.add(ast.number(2), ast.multiply(ast.number(3), ast.number(4)));
|
|
84
|
+
|
|
85
|
+
const executor = new Executor();
|
|
86
|
+
executor.execute(expr); // → 14
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Language Syntax
|
|
90
|
+
|
|
91
|
+
### Literals
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
42; // number
|
|
95
|
+
3.14; // floating point
|
|
96
|
+
("hello"); // string (function arguments only)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Variables
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
x = 5;
|
|
103
|
+
y = x + 10;
|
|
104
|
+
z = x * y;
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Operators
|
|
108
|
+
|
|
109
|
+
All standard arithmetic operators with proper precedence:
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
2 + 3; // addition
|
|
113
|
+
10 - 4; // subtraction
|
|
114
|
+
3 * 4; // multiplication
|
|
115
|
+
10 / 2; // division
|
|
116
|
+
10 % 3; // modulo
|
|
117
|
+
2 ^
|
|
118
|
+
(3 - // exponentiation (power)
|
|
119
|
+
5); // unary minus
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Operator Precedence
|
|
123
|
+
|
|
124
|
+
1. Unary minus (`-`) - Highest
|
|
125
|
+
2. Exponentiation (`^`)
|
|
126
|
+
3. Multiplication, division, modulo (`*`, `/`, `%`)
|
|
127
|
+
4. Addition, subtraction (`+`, `-`)
|
|
128
|
+
5. Assignment (`=`) - Lowest
|
|
129
|
+
|
|
130
|
+
Parentheses override precedence:
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
(2 + 3) * 4; // → 20 (not 14)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Functions
|
|
137
|
+
|
|
138
|
+
Functions accept any number of arguments:
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
abs(-5); // → 5
|
|
142
|
+
max(1, 5, 3); // → 5
|
|
143
|
+
date("2025-01-01"); // → Date object
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Comments
|
|
147
|
+
|
|
148
|
+
Single-line comments with `//`:
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
x = 5; // this is a comment
|
|
152
|
+
y = x + 10; // another comment
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Return Value
|
|
156
|
+
|
|
157
|
+
The last expression is always returned:
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
execute("x = 5; x + 10"); // → 15
|
|
161
|
+
execute("42"); // → 42
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## API Reference
|
|
165
|
+
|
|
166
|
+
### Main Functions
|
|
167
|
+
|
|
168
|
+
#### `execute(source: string, context?: ExecutionContext): RuntimeValue`
|
|
169
|
+
|
|
170
|
+
Execute source code with an optional execution context.
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
execute("2 + 2");
|
|
174
|
+
execute("abs(-5)", { functions: { abs: Math.abs } });
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
#### `parseSource(source: string): ASTNode`
|
|
178
|
+
|
|
179
|
+
Parse source code into an Abstract Syntax Tree without executing.
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
const ast = parseSource("2 + 3 * 4");
|
|
183
|
+
// Returns: BinaryOp(+, NumberLiteral(2), BinaryOp(*, ...))
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Classes
|
|
187
|
+
|
|
188
|
+
#### `Lexer`
|
|
189
|
+
|
|
190
|
+
Tokenize source code into a token stream.
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
import { Lexer, TokenType } from "littlewing";
|
|
194
|
+
|
|
195
|
+
const lexer = new Lexer("x = 42");
|
|
196
|
+
const tokens = lexer.tokenize();
|
|
197
|
+
// → [Identifier('x'), Equals, Number(42), EOF]
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
#### `Parser`
|
|
201
|
+
|
|
202
|
+
Parse tokens into an AST.
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
import { Parser } from "littlewing";
|
|
206
|
+
|
|
207
|
+
const parser = new Parser(tokens);
|
|
208
|
+
const ast = parser.parse();
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
#### `Executor`
|
|
212
|
+
|
|
213
|
+
Execute an AST with a given context.
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
import { Executor } from "littlewing";
|
|
217
|
+
|
|
218
|
+
const executor = new Executor(context);
|
|
219
|
+
const result = executor.execute(ast);
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### AST Builders
|
|
223
|
+
|
|
224
|
+
The `ast` namespace provides convenient functions for building AST nodes:
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
import { ast } from "littlewing";
|
|
228
|
+
|
|
229
|
+
ast.number(42);
|
|
230
|
+
ast.identifier("x");
|
|
231
|
+
ast.add(left, right);
|
|
232
|
+
ast.subtract(left, right);
|
|
233
|
+
ast.multiply(left, right);
|
|
234
|
+
ast.divide(left, right);
|
|
235
|
+
ast.modulo(left, right);
|
|
236
|
+
ast.negate(argument);
|
|
237
|
+
ast.assign("x", value);
|
|
238
|
+
ast.functionCall("abs", [ast.number(-5)]);
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Default Context
|
|
242
|
+
|
|
243
|
+
The `defaultContext` provides a comprehensive set of built-in functions:
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
import { defaultContext } from "littlewing";
|
|
247
|
+
|
|
248
|
+
// Math functions
|
|
249
|
+
(abs, ceil, floor, round, sqrt, min, max);
|
|
250
|
+
(sin, cos, tan, log, log10, exp);
|
|
251
|
+
|
|
252
|
+
// Date functions
|
|
253
|
+
(now, date);
|
|
254
|
+
|
|
255
|
+
// Time conversion (returns milliseconds)
|
|
256
|
+
(milliseconds, seconds, minutes, hours, days);
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Type Definitions
|
|
260
|
+
|
|
261
|
+
### ExecutionContext
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
interface ExecutionContext {
|
|
265
|
+
functions?: Record<string, (...args: any[]) => number | Date>;
|
|
266
|
+
variables?: Record<string, number | Date>;
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### RuntimeValue
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
type RuntimeValue = number | Date | unknown;
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### ASTNode
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
type ASTNode =
|
|
280
|
+
| Program
|
|
281
|
+
| NumberLiteral
|
|
282
|
+
| StringLiteral
|
|
283
|
+
| Identifier
|
|
284
|
+
| BinaryOp
|
|
285
|
+
| UnaryOp
|
|
286
|
+
| FunctionCall
|
|
287
|
+
| Assignment;
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
## Examples
|
|
291
|
+
|
|
292
|
+
### Calculator
|
|
293
|
+
|
|
294
|
+
```typescript
|
|
295
|
+
import { execute, defaultContext } from "littlewing";
|
|
296
|
+
|
|
297
|
+
function calculate(expression: string): number {
|
|
298
|
+
return execute(expression, defaultContext) as number;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
calculate("2 + 2 * 3"); // → 8
|
|
302
|
+
calculate("(2 + 2) * 3"); // → 12
|
|
303
|
+
calculate("sqrt(16) + abs(-5)"); // → 9
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### Financial Calculations
|
|
307
|
+
|
|
308
|
+
```typescript
|
|
309
|
+
import { execute } from "littlewing";
|
|
310
|
+
|
|
311
|
+
const context = {
|
|
312
|
+
functions: {},
|
|
313
|
+
variables: {
|
|
314
|
+
principal: 1000,
|
|
315
|
+
rate: 0.05,
|
|
316
|
+
years: 2,
|
|
317
|
+
},
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
const compound = execute("principal * (1 + rate) ^ years", context);
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### Date Arithmetic
|
|
324
|
+
|
|
325
|
+
```typescript
|
|
326
|
+
import { execute, defaultContext } from "littlewing";
|
|
327
|
+
|
|
328
|
+
// 5 days from now
|
|
329
|
+
const deadline = execute("now() + days(5)", defaultContext);
|
|
330
|
+
|
|
331
|
+
// Add 2 hours and 30 minutes
|
|
332
|
+
const futureTime = execute("now() + hours(2) + minutes(30)", defaultContext);
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### Custom Functions
|
|
336
|
+
|
|
337
|
+
```typescript
|
|
338
|
+
import { execute } from "littlewing";
|
|
339
|
+
|
|
340
|
+
const context = {
|
|
341
|
+
functions: {
|
|
342
|
+
fahrenheit: (celsius) => (celsius * 9) / 5 + 32,
|
|
343
|
+
kilometers: (miles) => miles * 1.60934,
|
|
344
|
+
factorial: (n) => (n <= 1 ? 1 : n * factorial(n - 1)),
|
|
345
|
+
},
|
|
346
|
+
variables: {
|
|
347
|
+
roomTemp: 20,
|
|
348
|
+
},
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
execute("fahrenheit(roomTemp)", context); // → 68
|
|
352
|
+
execute("kilometers(5)", context); // → 8.0467
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
## Performance
|
|
356
|
+
|
|
357
|
+
### Algorithms
|
|
358
|
+
|
|
359
|
+
- **Lexer**: O(n) single-pass tokenization
|
|
360
|
+
- **Parser**: Optimal Pratt parsing with O(n) time complexity
|
|
361
|
+
- **Executor**: O(n) tree-walk evaluation
|
|
362
|
+
|
|
363
|
+
### Bundle Size
|
|
364
|
+
|
|
365
|
+
- Raw: 16.70 KB
|
|
366
|
+
- Gzipped: **3.61 KB**
|
|
367
|
+
- Zero dependencies
|
|
368
|
+
|
|
369
|
+
### Test Coverage
|
|
370
|
+
|
|
371
|
+
- 71 comprehensive tests
|
|
372
|
+
- 97.66% code coverage
|
|
373
|
+
- All edge cases handled
|
|
374
|
+
|
|
375
|
+
## Type Safety
|
|
376
|
+
|
|
377
|
+
- Strict TypeScript mode
|
|
378
|
+
- Zero implicit `any` types
|
|
379
|
+
- Complete type annotations
|
|
380
|
+
- Runtime type validation
|
|
381
|
+
- Type guards on all operations
|
|
382
|
+
|
|
383
|
+
## Error Handling
|
|
384
|
+
|
|
385
|
+
Clear, actionable error messages for:
|
|
386
|
+
|
|
387
|
+
- Undefined variables: `"Undefined variable: x"`
|
|
388
|
+
- Undefined functions: `"Undefined function: abs"`
|
|
389
|
+
- Type mismatches: `"Cannot add string and number"`
|
|
390
|
+
- Division by zero: `"Division by zero"`
|
|
391
|
+
- Invalid assignments: `"Cannot assign string to variable"`
|
|
392
|
+
|
|
393
|
+
## Browser Support
|
|
394
|
+
|
|
395
|
+
- ✅ All modern browsers (ES2023+)
|
|
396
|
+
- ✅ No polyfills required
|
|
397
|
+
- ✅ Tree-shakeable for optimal bundle sizes
|
|
398
|
+
- ✅ 100% ESM, no CommonJS
|
|
399
|
+
|
|
400
|
+
## Node.js Support
|
|
401
|
+
|
|
402
|
+
Works with Node.js 18+ via ESM imports.
|
|
403
|
+
|
|
404
|
+
## License
|
|
405
|
+
|
|
406
|
+
MIT
|
|
407
|
+
|
|
408
|
+
## Contributing
|
|
409
|
+
|
|
410
|
+
See [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines.
|
|
411
|
+
|
|
412
|
+
---
|
|
413
|
+
|
|
414
|
+
Made with ❤️ by the littlewing team
|