littlewing 0.5.1 → 0.5.3

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/dist/index.d.ts CHANGED
@@ -306,11 +306,6 @@ declare class CodeGenerator {
306
306
  * - For left-associative operators: parens if lower or equal precedence
307
307
  */
308
308
  private needsParensRight;
309
- /**
310
- * Get precedence of an operator (higher number = higher precedence)
311
- * Must match the precedence in parser.ts
312
- */
313
- private getPrecedence;
314
309
  }
315
310
  /**
316
311
  * Generate source code from an AST node
@@ -416,7 +411,7 @@ declare class Lexer {
416
411
  private skipWhitespaceAndComments;
417
412
  /**
418
413
  * Read a number token
419
- * Supports: integers (42), decimals (3.14), and scientific notation (1.5e6, 2e-3)
414
+ * Supports: integers (42), decimals (3.14), decimal shorthand (.2), and scientific notation (1.5e6, 2e-3, .5e2)
420
415
  */
421
416
  private readNumber;
422
417
  /**
@@ -498,20 +493,6 @@ declare class Parser {
498
493
  */
499
494
  private parseFunctionArguments;
500
495
  /**
501
- * Get operator precedence
502
- * Precedence hierarchy:
503
- * 0: None
504
- * 1: Assignment (=, ??=)
505
- * 2: Ternary conditional (? :)
506
- * 3: Logical OR (||)
507
- * 4: Logical AND (&&)
508
- * 5: Comparison (==, !=, <, >, <=, >=)
509
- * 6: Addition/Subtraction (+, -)
510
- * 7: Multiplication/Division/Modulo (*, /, %)
511
- * 8: Exponentiation (^)
512
- */
513
- private getPrecedence;
514
- /**
515
496
  * Get unary operator precedence
516
497
  * Returns 6 which is higher than add/sub (6) but lower than exponentiation (8)
517
498
  * This means: -2^2 parses as -(2^2) = -4, not (-2)^2 = 4
package/dist/index.js CHANGED
@@ -194,6 +194,105 @@ function isNullishAssignment(node) {
194
194
  return node.type === "NullishAssignment";
195
195
  }
196
196
 
197
+ // src/utils.ts
198
+ function evaluateBinaryOperation(operator, left, right) {
199
+ switch (operator) {
200
+ case "+":
201
+ return left + right;
202
+ case "-":
203
+ return left - right;
204
+ case "*":
205
+ return left * right;
206
+ case "/":
207
+ if (right === 0) {
208
+ throw new Error("Division by zero");
209
+ }
210
+ return left / right;
211
+ case "%":
212
+ if (right === 0) {
213
+ throw new Error("Modulo by zero");
214
+ }
215
+ return left % right;
216
+ case "^":
217
+ return left ** right;
218
+ case "==":
219
+ return left === right ? 1 : 0;
220
+ case "!=":
221
+ return left !== right ? 1 : 0;
222
+ case "<":
223
+ return left < right ? 1 : 0;
224
+ case ">":
225
+ return left > right ? 1 : 0;
226
+ case "<=":
227
+ return left <= right ? 1 : 0;
228
+ case ">=":
229
+ return left >= right ? 1 : 0;
230
+ case "&&":
231
+ return left !== 0 && right !== 0 ? 1 : 0;
232
+ case "||":
233
+ return left !== 0 || right !== 0 ? 1 : 0;
234
+ default:
235
+ throw new Error(`Unknown operator: ${operator}`);
236
+ }
237
+ }
238
+ function getOperatorPrecedence(operator) {
239
+ switch (operator) {
240
+ case "^":
241
+ return 8;
242
+ case "*":
243
+ case "/":
244
+ case "%":
245
+ return 7;
246
+ case "+":
247
+ case "-":
248
+ return 6;
249
+ case "==":
250
+ case "!=":
251
+ case "<":
252
+ case ">":
253
+ case "<=":
254
+ case ">=":
255
+ return 5;
256
+ case "&&":
257
+ return 4;
258
+ case "||":
259
+ return 3;
260
+ default:
261
+ return 0;
262
+ }
263
+ }
264
+ function getTokenPrecedence(type) {
265
+ switch (type) {
266
+ case "EQUALS" /* EQUALS */:
267
+ case "NULLISH_ASSIGN" /* NULLISH_ASSIGN */:
268
+ return 1;
269
+ case "QUESTION" /* QUESTION */:
270
+ return 2;
271
+ case "LOGICAL_OR" /* LOGICAL_OR */:
272
+ return 3;
273
+ case "LOGICAL_AND" /* LOGICAL_AND */:
274
+ return 4;
275
+ case "DOUBLE_EQUALS" /* DOUBLE_EQUALS */:
276
+ case "NOT_EQUALS" /* NOT_EQUALS */:
277
+ case "LESS_THAN" /* LESS_THAN */:
278
+ case "GREATER_THAN" /* GREATER_THAN */:
279
+ case "LESS_EQUAL" /* LESS_EQUAL */:
280
+ case "GREATER_EQUAL" /* GREATER_EQUAL */:
281
+ return 5;
282
+ case "PLUS" /* PLUS */:
283
+ case "MINUS" /* MINUS */:
284
+ return 6;
285
+ case "STAR" /* STAR */:
286
+ case "SLASH" /* SLASH */:
287
+ case "PERCENT" /* PERCENT */:
288
+ return 7;
289
+ case "CARET" /* CARET */:
290
+ return 8;
291
+ default:
292
+ return 0;
293
+ }
294
+ }
295
+
197
296
  // src/codegen.ts
198
297
  class CodeGenerator {
199
298
  generate(node) {
@@ -257,15 +356,15 @@ class CodeGenerator {
257
356
  const condition = this.generate(node.condition);
258
357
  const consequent = this.generate(node.consequent);
259
358
  const alternate = this.generate(node.alternate);
260
- const conditionNeedsParens = isAssignment(node.condition) || isBinaryOp(node.condition) && this.getPrecedence(node.condition.operator) <= 2;
359
+ const conditionNeedsParens = isAssignment(node.condition) || isBinaryOp(node.condition) && getOperatorPrecedence(node.condition.operator) <= 2;
261
360
  const conditionCode = conditionNeedsParens ? `(${condition})` : condition;
262
361
  return `${conditionCode} ? ${consequent} : ${alternate}`;
263
362
  }
264
363
  needsParensLeft(node, operator) {
265
364
  if (!isBinaryOp(node))
266
365
  return false;
267
- const nodePrecedence = this.getPrecedence(node.operator);
268
- const operatorPrecedence = this.getPrecedence(operator);
366
+ const nodePrecedence = getOperatorPrecedence(node.operator);
367
+ const operatorPrecedence = getOperatorPrecedence(operator);
269
368
  if (operator === "^") {
270
369
  return nodePrecedence <= operatorPrecedence;
271
370
  }
@@ -274,39 +373,13 @@ class CodeGenerator {
274
373
  needsParensRight(node, operator) {
275
374
  if (!isBinaryOp(node))
276
375
  return false;
277
- const nodePrecedence = this.getPrecedence(node.operator);
278
- const operatorPrecedence = this.getPrecedence(operator);
376
+ const nodePrecedence = getOperatorPrecedence(node.operator);
377
+ const operatorPrecedence = getOperatorPrecedence(operator);
279
378
  if (operator === "^") {
280
379
  return nodePrecedence < operatorPrecedence;
281
380
  }
282
381
  return nodePrecedence <= operatorPrecedence;
283
382
  }
284
- getPrecedence(operator) {
285
- switch (operator) {
286
- case "^":
287
- return 8;
288
- case "*":
289
- case "/":
290
- case "%":
291
- return 7;
292
- case "+":
293
- case "-":
294
- return 6;
295
- case "==":
296
- case "!=":
297
- case "<":
298
- case ">":
299
- case "<=":
300
- case ">=":
301
- return 5;
302
- case "&&":
303
- return 4;
304
- case "||":
305
- return 3;
306
- default:
307
- return 0;
308
- }
309
- }
310
383
  }
311
384
  function generate(node) {
312
385
  const generator = new CodeGenerator;
@@ -373,6 +446,9 @@ class Lexer {
373
446
  if (this.isDigit(char)) {
374
447
  return this.readNumber();
375
448
  }
449
+ if (char === "." && this.isDigit(this.peek())) {
450
+ return this.readNumber();
451
+ }
376
452
  if (this.isLetter(char) || char === "_") {
377
453
  return this.readIdentifier();
378
454
  }
@@ -485,6 +561,10 @@ class Lexer {
485
561
  const start = this.position;
486
562
  let hasDecimal = false;
487
563
  let hasExponent = false;
564
+ if (this.getCharAt(this.position) === ".") {
565
+ hasDecimal = true;
566
+ this.position++;
567
+ }
488
568
  while (this.position < this.source.length) {
489
569
  const char = this.getCharAt(this.position);
490
570
  if (this.isDigit(char)) {
@@ -548,6 +628,23 @@ class Lexer {
548
628
  }
549
629
 
550
630
  // src/parser.ts
631
+ var BINARY_OPERATOR_TOKENS = new Set([
632
+ "PLUS" /* PLUS */,
633
+ "MINUS" /* MINUS */,
634
+ "STAR" /* STAR */,
635
+ "SLASH" /* SLASH */,
636
+ "PERCENT" /* PERCENT */,
637
+ "CARET" /* CARET */,
638
+ "DOUBLE_EQUALS" /* DOUBLE_EQUALS */,
639
+ "NOT_EQUALS" /* NOT_EQUALS */,
640
+ "LESS_THAN" /* LESS_THAN */,
641
+ "GREATER_THAN" /* GREATER_THAN */,
642
+ "LESS_EQUAL" /* LESS_EQUAL */,
643
+ "GREATER_EQUAL" /* GREATER_EQUAL */,
644
+ "LOGICAL_AND" /* LOGICAL_AND */,
645
+ "LOGICAL_OR" /* LOGICAL_OR */
646
+ ]);
647
+
551
648
  class Parser {
552
649
  tokens;
553
650
  current = 0;
@@ -578,7 +675,7 @@ class Parser {
578
675
  let left = this.parsePrefix();
579
676
  while (true) {
580
677
  const token = this.peek();
581
- const precedence = this.getPrecedence(token.type);
678
+ const precedence = getTokenPrecedence(token.type);
582
679
  if (precedence < minPrecedence) {
583
680
  break;
584
681
  }
@@ -698,42 +795,11 @@ class Parser {
698
795
  }
699
796
  return args;
700
797
  }
701
- getPrecedence(type) {
702
- switch (type) {
703
- case "EQUALS" /* EQUALS */:
704
- case "NULLISH_ASSIGN" /* NULLISH_ASSIGN */:
705
- return 1;
706
- case "QUESTION" /* QUESTION */:
707
- return 2;
708
- case "LOGICAL_OR" /* LOGICAL_OR */:
709
- return 3;
710
- case "LOGICAL_AND" /* LOGICAL_AND */:
711
- return 4;
712
- case "DOUBLE_EQUALS" /* DOUBLE_EQUALS */:
713
- case "NOT_EQUALS" /* NOT_EQUALS */:
714
- case "LESS_THAN" /* LESS_THAN */:
715
- case "GREATER_THAN" /* GREATER_THAN */:
716
- case "LESS_EQUAL" /* LESS_EQUAL */:
717
- case "GREATER_EQUAL" /* GREATER_EQUAL */:
718
- return 5;
719
- case "PLUS" /* PLUS */:
720
- case "MINUS" /* MINUS */:
721
- return 6;
722
- case "STAR" /* STAR */:
723
- case "SLASH" /* SLASH */:
724
- case "PERCENT" /* PERCENT */:
725
- return 7;
726
- case "CARET" /* CARET */:
727
- return 8;
728
- default:
729
- return 0;
730
- }
731
- }
732
798
  getUnaryPrecedence() {
733
799
  return 6;
734
800
  }
735
801
  isBinaryOperator(type) {
736
- return type === "PLUS" /* PLUS */ || type === "MINUS" /* MINUS */ || type === "STAR" /* STAR */ || type === "SLASH" /* SLASH */ || type === "PERCENT" /* PERCENT */ || type === "CARET" /* CARET */ || type === "DOUBLE_EQUALS" /* DOUBLE_EQUALS */ || type === "NOT_EQUALS" /* NOT_EQUALS */ || type === "LESS_THAN" /* LESS_THAN */ || type === "GREATER_THAN" /* GREATER_THAN */ || type === "LESS_EQUAL" /* LESS_EQUAL */ || type === "GREATER_EQUAL" /* GREATER_EQUAL */ || type === "LOGICAL_AND" /* LOGICAL_AND */ || type === "LOGICAL_OR" /* LOGICAL_OR */;
802
+ return BINARY_OPERATOR_TOKENS.has(type);
737
803
  }
738
804
  peek() {
739
805
  if (this.current >= this.tokens.length) {
@@ -805,44 +871,7 @@ class Executor {
805
871
  executeBinaryOp(node) {
806
872
  const left = this.execute(node.left);
807
873
  const right = this.execute(node.right);
808
- switch (node.operator) {
809
- case "+":
810
- return left + right;
811
- case "-":
812
- return left - right;
813
- case "*":
814
- return left * right;
815
- case "/":
816
- if (right === 0) {
817
- throw new Error("Division by zero");
818
- }
819
- return left / right;
820
- case "%":
821
- if (right === 0) {
822
- throw new Error("Modulo by zero");
823
- }
824
- return left % right;
825
- case "^":
826
- return left ** right;
827
- case "==":
828
- return left === right ? 1 : 0;
829
- case "!=":
830
- return left !== right ? 1 : 0;
831
- case "<":
832
- return left < right ? 1 : 0;
833
- case ">":
834
- return left > right ? 1 : 0;
835
- case "<=":
836
- return left <= right ? 1 : 0;
837
- case ">=":
838
- return left >= right ? 1 : 0;
839
- case "&&":
840
- return left !== 0 && right !== 0 ? 1 : 0;
841
- case "||":
842
- return left !== 0 || right !== 0 ? 1 : 0;
843
- default:
844
- throw new Error(`Unknown operator: ${node.operator}`);
845
- }
874
+ return evaluateBinaryOperation(node.operator, left, right);
846
875
  }
847
876
  executeUnaryOp(node) {
848
877
  const arg = this.execute(node.argument);
@@ -1123,7 +1152,7 @@ function evaluateWithConstants(node, constants) {
1123
1152
  const left = evaluateWithConstants(node.left, constants);
1124
1153
  const right = evaluateWithConstants(node.right, constants);
1125
1154
  if (isNumberLiteral(left) && isNumberLiteral(right)) {
1126
- const result = evaluateBinaryOp(node.operator, left.value, right.value);
1155
+ const result = evaluateBinaryOperation(node.operator, left.value, right.value);
1127
1156
  return number(result);
1128
1157
  }
1129
1158
  return {
@@ -1257,7 +1286,7 @@ function basicOptimize(node) {
1257
1286
  const left = basicOptimize(node.left);
1258
1287
  const right = basicOptimize(node.right);
1259
1288
  if (isNumberLiteral(left) && isNumberLiteral(right)) {
1260
- const result = evaluateBinaryOp(node.operator, left.value, right.value);
1289
+ const result = evaluateBinaryOperation(node.operator, left.value, right.value);
1261
1290
  return number(result);
1262
1291
  }
1263
1292
  return {
@@ -1304,46 +1333,6 @@ function basicOptimize(node) {
1304
1333
  }
1305
1334
  return node;
1306
1335
  }
1307
- function evaluateBinaryOp(operator, left, right) {
1308
- switch (operator) {
1309
- case "+":
1310
- return left + right;
1311
- case "-":
1312
- return left - right;
1313
- case "*":
1314
- return left * right;
1315
- case "/":
1316
- if (right === 0) {
1317
- throw new Error("Division by zero in constant folding");
1318
- }
1319
- return left / right;
1320
- case "%":
1321
- if (right === 0) {
1322
- throw new Error("Modulo by zero in constant folding");
1323
- }
1324
- return left % right;
1325
- case "^":
1326
- return left ** right;
1327
- case "==":
1328
- return left === right ? 1 : 0;
1329
- case "!=":
1330
- return left !== right ? 1 : 0;
1331
- case "<":
1332
- return left < right ? 1 : 0;
1333
- case ">":
1334
- return left > right ? 1 : 0;
1335
- case "<=":
1336
- return left <= right ? 1 : 0;
1337
- case ">=":
1338
- return left >= right ? 1 : 0;
1339
- case "&&":
1340
- return left !== 0 && right !== 0 ? 1 : 0;
1341
- case "||":
1342
- return left !== 0 || right !== 0 ? 1 : 0;
1343
- default:
1344
- throw new Error(`Unknown operator: ${operator}`);
1345
- }
1346
- }
1347
1336
  export {
1348
1337
  parseSource,
1349
1338
  optimize,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "littlewing",
3
- "version": "0.5.1",
3
+ "version": "0.5.3",
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",