@thi.ng/parse 2.2.40 → 2.3.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/CHANGELOG.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Change Log
2
2
 
3
- - **Last updated**: 2023-08-22T14:39:27Z
3
+ - **Last updated**: 2023-09-06T13:36:28Z
4
4
  - **Generator**: [thi.ng/monopub](https://thi.ng/monopub)
5
5
 
6
6
  All notable changes to this project will be documented in this file.
@@ -9,6 +9,12 @@ See [Conventional Commits](https://conventionalcommits.org/) for commit guidelin
9
9
  **Note:** Unlisted _patch_ versions only involve non-code or otherwise excluded changes
10
10
  and/or version bumps of transitive dependencies.
11
11
 
12
+ ## [2.3.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/parse@2.3.0) (2023-09-06)
13
+
14
+ #### 🚀 Features
15
+
16
+ - add altS() combinator ([52c76ca](https://github.com/thi-ng/umbrella/commit/52c76ca))
17
+
12
18
  ## [2.2.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/parse@2.2.0) (2022-06-15)
13
19
 
14
20
  #### 🚀 Features
package/README.md CHANGED
@@ -90,7 +90,7 @@ For Node.js REPL:
90
90
  const parse = await import("@thi.ng/parse");
91
91
  ```
92
92
 
93
- Package sizes (brotli'd, pre-treeshake): ESM: 5.07 KB
93
+ Package sizes (brotli'd, pre-treeshake): ESM: 5.09 KB
94
94
 
95
95
  ## Dependencies
96
96
 
@@ -189,7 +189,7 @@ in order to decide about successful matching.
189
189
  Source:
190
190
  [/combinators](https://github.com/thi-ng/umbrella/tree/develop/packages/parse/src/combinators)
191
191
 
192
- - `alt` / `altD`
192
+ - `alt` / `altS` / `altD`
193
193
  - `check`
194
194
  - `expect`
195
195
  - `lookahead`
@@ -418,7 +418,9 @@ rule references in the grammar definition as well:
418
418
  [@thi.ng/defmulti](https://github.com/thi-ng/umbrella/tree/develop/packages/defmulti)
419
419
  as a useful tool for processing/interpreting/compiling the result AST)
420
420
 
421
- ```ts
421
+ ```ts tangle:export/readme-sexpr.ts
422
+ import { defContext, defGrammar, print } from "@thi.ng/parse";
423
+
422
424
  // define language via grammar DSL
423
425
  // the upper-cased rule names are built-ins
424
426
  const lang = defGrammar(`
@@ -435,7 +437,7 @@ const ctx = defContext(`
435
437
  `);
436
438
 
437
439
  // parse & print AST
438
- print(lang.rules.expr)(ctx)
440
+ print(lang!.rules.expr)(ctx);
439
441
  // expr: null
440
442
  // list: null
441
443
  // expr: null
@@ -461,7 +463,7 @@ print(lang.rules.expr)(ctx)
461
463
  // true
462
464
 
463
465
  // the two top-level s-expressions...
464
- ctx.children
466
+ console.log(ctx.children);
465
467
  // [
466
468
  // { id: 'list', state: null, children: [ [Object] ], result: null },
467
469
  // { id: 'list', state: null, children: [ [Object] ], result: null }
@@ -470,28 +472,32 @@ ctx.children
470
472
 
471
473
  ### SVG path parser example
472
474
 
473
- ```ts
475
+ ```ts tangle:export/readme-svg.ts
474
476
  import {
475
477
  INT, WS0,
476
- alt, oneOf, seq, zeroOrMore,
477
- collect, discard, xform,
478
- defContext
478
+ alt, collect, defContext, discard,
479
+ oneOf, seq, xform, zeroOrMore
479
480
  } from "@thi.ng/parse";
480
481
 
481
482
  // whitespace parser
482
- // discard() removes results from AST
483
+ // discard() removes matched result from AST
483
484
  const wsc = discard(zeroOrMore(oneOf(" ,")));
484
485
 
485
- // svg path parser rules
486
+ // SVG path parser rules
486
487
  // collect() collects child results in array, then removes children
487
- // INT & WS0 are preset parsers (see section above)
488
+ // INT & WS0 are preset parsers (see readme section above)
488
489
  const point = collect(seq([INT, wsc, INT]));
489
490
  const move = collect(seq([oneOf("Mm"), WS0, point, WS0]));
490
491
  const line = collect(seq([oneOf("Ll"), WS0, point, WS0]));
491
- const curve = collect(seq([oneOf("Cc"), WS0, point, wsc, point, wsc, point, WS0]));
492
+ const curve = collect(
493
+ seq([oneOf("Cc"), WS0, point, wsc, point, wsc, point, WS0])
494
+ );
492
495
  // xform used here to wrap result in array
493
496
  // (to produce same result format as parsers above)
494
- const close = xform(oneOf("Zz"), ($) => ($.result = [$.result], $));
497
+ const close = xform(
498
+ oneOf("Zz"),
499
+ (scope) => ((scope!.result = [scope!.result]), scope)
500
+ );
495
501
 
496
502
  // main path parser
497
503
  const path = collect(zeroOrMore(alt([move, line, curve, close])));
@@ -499,64 +505,125 @@ const path = collect(zeroOrMore(alt([move, line, curve, close])));
499
505
  // prepare parse context & reader
500
506
  const ctx = defContext("M0,1L2 3c4,5-6,7 8 9z");
501
507
  // parse input into AST
502
- path(ctx);
508
+ console.log(path(ctx));
503
509
  // true
504
510
 
505
511
  // transformed result of AST root node
506
- ctx.result
507
- // [["M", [0, 1]], ["L", [2, 3]], ["c", [4, 5], [-6, 7], [8, 9]], ["z"]]
512
+ console.log(ctx.result);
513
+ // [
514
+ // [ 'M', [ 0, 1 ] ],
515
+ // [ 'L', [ 2, 3 ] ],
516
+ // [ 'c', [ 4, 5 ], [ -6, 7 ], [ 8, 9 ] ],
517
+ // [ 'z' ]
518
+ // ]
508
519
  ```
509
520
 
510
521
  ### RPN parser & interpreter example
511
522
 
512
- ```ts
523
+ ```ts tangle:export/readme-rpn.ts
524
+ import type { Fn, FnN2 } from "@thi.ng/api";
513
525
  import {
514
526
  INT, WS0,
515
- alt, oneOf, xform, zeroOrMore
516
- defContext
527
+ alt, altS, defContext, xform, zeroOrMore
517
528
  } from "@thi.ng/parse";
518
529
 
519
- // data stack for execution
520
- const stack = [];
530
+ type StackFn = Fn<number[], void>;
531
+ type Op = { arity: number; fn: StackFn };
532
+
533
+ // wrapper for pure 2-arity stack functions/ops
534
+ const defOp2 =
535
+ (fn: FnN2): StackFn =>
536
+ (stack) => {
537
+ const b = stack.pop()!;
538
+ const a = stack.pop()!;
539
+ stack.push(fn(a, b));
540
+ };
541
+
542
+ // operator/word implementations
543
+ const ops: Record<string, Op> = {
544
+ "+": { arity: 2, fn: defOp2((a, b) => a + b) },
545
+ "-": { arity: 2, fn: defOp2((a, b) => a - b) },
546
+ "*": { arity: 2, fn: defOp2((a, b) => a * b) },
547
+ "/": { arity: 2, fn: defOp2((a, b) => a / b) },
548
+ // prints top stack item to console
549
+ print: { arity: 1, fn: (stack) => console.log(stack.pop()) },
550
+ // duplicates top stack item
551
+ dup: { arity: 1, fn: (stack) => stack.push(stack[stack.length - 1]) },
552
+ // swaps two topmost stack items
553
+ swap: {
554
+ arity: 2,
555
+ fn: (stack) => {
556
+ const a = stack.pop()!;
557
+ const b = stack.pop()!;
558
+ stack.push(a, b);
559
+ },
560
+ },
561
+ };
521
562
 
522
- // operator implementations
523
- const ops = {
524
- "+": (a, b) => a + b,
525
- "-": (a, b) => a - b,
526
- "*": (a, b) => a * b,
527
- "/": (a, b) => a / b,
563
+ // simple RPN parser & interpreter runtime
564
+ const interpret = (src: string, debug = true) => {
565
+ // data stack for execution
566
+ const stack: number[] = [];
567
+
568
+ // signed integer parser (using INT preset) with transform fn.
569
+ // the user fn here is only used for pushing values on data stack
570
+ // also, if a transform returns null, the parse scope will
571
+ // be removed from the result AST
572
+ const value = xform(INT, (scope) => {
573
+ stack.push(scope!.result);
574
+ return null;
575
+ });
576
+
577
+ // parser (with transform) for any of the registered operators
578
+ // the transform here applies the op in RPN fashion to the data stack
579
+ // stack underflow handling omitted for brevity
580
+ const op = xform(altS(Object.keys(ops)), (scope) => {
581
+ const id = scope!.result;
582
+ const { arity, fn } = ops[id];
583
+ if (debug) console.log(id, stack);
584
+ if (stack.length < arity) {
585
+ throw new Error(
586
+ "stack underflow " +
587
+ `("${id}" needs ${arity} args, got ${JSON.stringify(stack)})`
588
+ );
589
+ }
590
+ fn(stack);
591
+ return null;
592
+ });
593
+
594
+ // parser for complete RPN program, combines above two parsers
595
+ // and the whitespace preset as alternatives
596
+ const program = zeroOrMore(alt([value, op, WS0]));
597
+ // apply parser to source code
598
+ program(defContext(src));
599
+ // return result stack
600
+ return stack;
528
601
  };
529
602
 
530
- // signed integer parser (using INT preset) with transform fn.
531
- // the user fn here is only used for pushing values on data stack
532
- // also, if a transform returns null, the parse scope will
533
- // be removed from the result AST
534
- const value = xform(INT, (scope) => {
535
- stack.push(scope.result);
536
- return null;
537
- });
538
-
539
- // parser for any of the registered operators (again w/ transform)
540
- // the transform here applies the op in RPN fashion to the data stack
541
- // stack underflow handling omitted for brevity
542
- const op = xform(oneOf(Object.keys(ops)), (scope) => {
543
- const b = stack.pop();
544
- const a = stack.pop();
545
- stack.push(ops[scope.result](a, b));
546
- return null;
547
- });
548
-
549
- // parser for complete RPN program, combines above two parsers
550
- // and the whitespace preset as alternatives
551
- const program = zeroOrMore(alt([value, op, WS0]))
552
-
553
- // prepare parser context (incl. reader) and execute
554
- program(defContext("10 5 3 * + -2 * 10 /"));
555
-
556
- // print result data stack, i.e. result of:
557
- // (3 * 5 + 10) * -2 / 10 (in infix notation)
558
- console.log(stack);
559
- // [-5]
603
+ // checking operator arity
604
+ try { interpret("1 +"); } catch (e) { console.warn(e); }
605
+ // Error: stack underflow ("+" needs 2 args, got [1])
606
+
607
+ // execute: (3 * 5 + 10) * -2 / 10 (in infix notation)
608
+ interpret("10 5 3 * + -2 * 10 / print");
609
+ // * [ 10, 5, 3 ]
610
+ // + [ 10, 15 ]
611
+ // * [ 25, -2 ]
612
+ // / [ -50, 10 ]
613
+ // print [ -5 ]
614
+ // -5
615
+
616
+ // execute: ((5 + 3)**2) / 2) - (5 + 3)**2
617
+ interpret("5 3 + dup * dup 2 / swap - print");
618
+ // + [ 5, 3 ]
619
+ // dup [ 8 ]
620
+ // * [ 8, 8 ]
621
+ // dup [ 64 ]
622
+ // / [ 64, 64, 2 ]
623
+ // swap [ 64, 32 ]
624
+ // - [ 32, 64 ]
625
+ // print [ -32 ]
626
+ // -32
560
627
  ```
561
628
 
562
629
  ## Authors
@@ -1,4 +1,16 @@
1
1
  import type { Parser } from "../api.js";
2
2
  export declare const alt: <T>(parsers: Parser<T>[]) => Parser<T>;
3
+ /**
4
+ * Syntax sugar for {@link alt}. Takes an array of strings to match and creates
5
+ * parsers for each before passing them to `alt()`.
6
+ *
7
+ * @param strings
8
+ */
9
+ export declare const altS: (strings: string[]) => Parser<string>;
10
+ /**
11
+ * Wrapped version of {@link alt} which discards results if successful match.
12
+ *
13
+ * @param parsers
14
+ */
3
15
  export declare const altD: <T>(parsers: Parser<T>[]) => Parser<any>;
4
16
  //# sourceMappingURL=alt.d.ts.map
@@ -1,3 +1,4 @@
1
+ import { string } from "../prims/string.js";
1
2
  import { discard } from "../xform/discard.js";
2
3
  export const alt = (parsers) => (ctx) => {
3
4
  if (ctx.done)
@@ -9,4 +10,16 @@ export const alt = (parsers) => (ctx) => {
9
10
  }
10
11
  return false;
11
12
  };
13
+ /**
14
+ * Syntax sugar for {@link alt}. Takes an array of strings to match and creates
15
+ * parsers for each before passing them to `alt()`.
16
+ *
17
+ * @param strings
18
+ */
19
+ export const altS = (strings) => alt(strings.map((x) => string(x)));
20
+ /**
21
+ * Wrapped version of {@link alt} which discards results if successful match.
22
+ *
23
+ * @param parsers
24
+ */
12
25
  export const altD = (parsers) => discard(alt(parsers));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thi.ng/parse",
3
- "version": "2.2.40",
3
+ "version": "2.3.0",
4
4
  "description": "Purely functional parser combinators & AST generation for generic inputs",
5
5
  "type": "module",
6
6
  "module": "./index.js",
@@ -34,19 +34,19 @@
34
34
  "test": "testament test"
35
35
  },
36
36
  "dependencies": {
37
- "@thi.ng/api": "^8.9.4",
38
- "@thi.ng/checks": "^3.4.4",
39
- "@thi.ng/defmulti": "^2.1.43",
40
- "@thi.ng/errors": "^2.3.4",
41
- "@thi.ng/strings": "^3.4.12"
37
+ "@thi.ng/api": "^8.9.5",
38
+ "@thi.ng/checks": "^3.4.5",
39
+ "@thi.ng/defmulti": "^2.1.45",
40
+ "@thi.ng/errors": "^2.3.5",
41
+ "@thi.ng/strings": "^3.4.13"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@microsoft/api-extractor": "^7.36.4",
45
- "@thi.ng/testament": "^0.3.22",
45
+ "@thi.ng/testament": "^0.3.23",
46
46
  "rimraf": "^5.0.1",
47
47
  "tools": "^0.0.1",
48
- "typedoc": "^0.24.8",
49
- "typescript": "^5.1.6"
48
+ "typedoc": "^0.25.0",
49
+ "typescript": "^5.2.2"
50
50
  },
51
51
  "keywords": [
52
52
  "ast",
@@ -241,5 +241,5 @@
241
241
  ],
242
242
  "year": 2020
243
243
  },
244
- "gitHead": "74cfe3fb8de5bfcbfc1109e67604541cbd7b77aa\n"
244
+ "gitHead": "1bbef970bab7f7def0700e587cf0471e1e1ef9f3\n"
245
245
  }