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 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