@thi.ng/parse 2.4.51 → 2.4.52

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**: 2024-08-18T14:11:34Z
3
+ - **Last updated**: 2024-08-29T12:31:25Z
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,15 @@ 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.4.52](https://github.com/thi-ng/umbrella/tree/@thi.ng/parse@2.4.52) (2024-08-29)
13
+
14
+ #### ⏱ Performance improvements
15
+
16
+ - update ParseState & ParseScope handling (result: 1.2-1.6x faster) ([c94b5cf](https://github.com/thi-ng/umbrella/commit/c94b5cf))
17
+ - refactor ParseState/Scope as data classes (keep same structure)
18
+ - minor update scope transforms
19
+ - update tests
20
+
12
21
  ### [2.4.43](https://github.com/thi-ng/umbrella/tree/@thi.ng/parse@2.4.43) (2024-06-21)
13
22
 
14
23
  #### ♻️ Refactoring
package/api.d.ts CHANGED
@@ -1,18 +1,5 @@
1
1
  import type { Fn, Fn0, IObjectOf, Nullable } from "@thi.ng/api";
2
- import type { ParseContext } from "./context.js";
3
- export interface ParseScope<T> {
4
- id: string;
5
- state: Nullable<ParseState<T>>;
6
- children: Nullable<ParseScope<T>[]>;
7
- result: any;
8
- }
9
- export interface ParseState<T> {
10
- p: number;
11
- l: number;
12
- c: number;
13
- done?: boolean;
14
- last?: T;
15
- }
2
+ import type { ParseContext, ParseScope, ParseState } from "./context.js";
16
3
  export interface IReader<T> {
17
4
  read(state: ParseState<T>): T;
18
5
  next(state: ParseState<T>): void;
@@ -1,4 +1,5 @@
1
1
  import type { Predicate } from "@thi.ng/api";
2
- import type { Parser, ParseScope } from "../api.js";
2
+ import type { Parser } from "../api.js";
3
+ import type { ParseScope } from "../context.js";
3
4
  export declare const check: <T>(parser: Parser<T>, pred: Predicate<ParseScope<T>>, msg?: string) => Parser<T>;
4
5
  //# sourceMappingURL=check.d.ts.map
@@ -3,7 +3,7 @@ const lookahead = (parser, ahead, capture = false, id = "lookahead") => (ctx) =>
3
3
  ctx.start(id);
4
4
  let pass = false;
5
5
  while (true) {
6
- const state = capture ? null : { ...ctx.state };
6
+ const state = capture ? null : ctx.state.copy();
7
7
  if (ahead(ctx)) {
8
8
  !capture && (ctx.state = state);
9
9
  return pass ? ctx.end() : ctx.discard();
package/context.d.ts CHANGED
@@ -1,4 +1,22 @@
1
- import type { ContextOpts, IReader, ParseScope, ParseState } from "./api.js";
1
+ import type { ICopy, Nullable } from "@thi.ng/api";
2
+ import type { ContextOpts, IReader } from "./api.js";
3
+ export declare class ParseState<T> implements ICopy<ParseState<T>> {
4
+ p: number;
5
+ l: number;
6
+ c: number;
7
+ done?: boolean | undefined;
8
+ last?: T | undefined;
9
+ constructor(p: number, l: number, c: number, done?: boolean | undefined, last?: T | undefined);
10
+ copy(): ParseState<T>;
11
+ }
12
+ export declare class ParseScope<T> implements ICopy<ParseScope<T>> {
13
+ id: string;
14
+ state?: Nullable<ParseState<T>>;
15
+ children?: Nullable<ParseScope<T>[]>;
16
+ result?: any;
17
+ constructor(id: string, state?: Nullable<ParseState<T>>, children?: Nullable<ParseScope<T>[]>, result?: any);
18
+ copy(): ParseScope<T>;
19
+ }
2
20
  export declare class ParseContext<T> {
3
21
  reader: IReader<T>;
4
22
  opts: ContextOpts;
@@ -29,7 +47,7 @@ export declare class ParseContext<T> {
29
47
  /**
30
48
  * Returns root node's children or `undefined`.
31
49
  */
32
- get children(): import("@thi.ng/api").Nullable<ParseScope<T>[]>;
50
+ get children(): Nullable<ParseScope<T>[]>;
33
51
  /**
34
52
  * Returns max. recursion depth which was actually reached. Will always be
35
53
  * less or equal configured {@link ContextOpts.maxDepth}.
package/context.js CHANGED
@@ -4,6 +4,34 @@ import { parseError } from "./error.js";
4
4
  import { defArrayReader } from "./readers/array-reader.js";
5
5
  import { defStringReader } from "./readers/string-reader.js";
6
6
  import { __indent } from "./utils.js";
7
+ class ParseState {
8
+ constructor(p, l, c, done, last) {
9
+ this.p = p;
10
+ this.l = l;
11
+ this.c = c;
12
+ this.done = done;
13
+ this.last = last;
14
+ }
15
+ copy() {
16
+ return new ParseState(this.p, this.l, this.c, this.done, this.last);
17
+ }
18
+ }
19
+ class ParseScope {
20
+ constructor(id, state, children, result) {
21
+ this.id = id;
22
+ this.state = state;
23
+ this.children = children;
24
+ this.result = result;
25
+ }
26
+ copy() {
27
+ return new ParseScope(
28
+ this.id,
29
+ this.state,
30
+ this.children,
31
+ this.result
32
+ );
33
+ }
34
+ }
7
35
  class ParseContext {
8
36
  constructor(reader, opts) {
9
37
  this.reader = reader;
@@ -21,28 +49,21 @@ class ParseContext {
21
49
  _debug;
22
50
  _retain;
23
51
  reset() {
24
- this._curr = {
25
- id: "root",
26
- state: { p: 0, l: 1, c: 1 },
27
- children: null,
28
- result: null
29
- };
52
+ this._curr = new ParseScope("root", new ParseState(0, 1, 1));
30
53
  this._scopes = [this._curr];
31
54
  this._peakDepth = 1;
32
55
  this.reader.isDone(this._curr.state);
33
56
  return this;
34
57
  }
35
58
  start(id) {
36
- if (this._scopes.length >= this._maxDepth) {
37
- parseError(this, `recursion limit reached ${this._maxDepth}`);
59
+ const { _scopes: scopes, _maxDepth } = this;
60
+ if (scopes.length >= _maxDepth) {
61
+ parseError(this, `recursion limit reached ${_maxDepth}`);
38
62
  }
39
- const scopes = this._scopes;
40
- const scope = {
63
+ const scope = new ParseScope(
41
64
  id,
42
- state: { ...scopes[scopes.length - 1].state },
43
- children: null,
44
- result: null
45
- };
65
+ scopes[scopes.length - 1].state.copy()
66
+ );
46
67
  scopes.push(scope);
47
68
  this._peakDepth = Math.max(this._peakDepth, scopes.length);
48
69
  this._debug && console.log(
@@ -62,11 +83,10 @@ class ParseContext {
62
83
  const child = scopes.pop();
63
84
  const parent = scopes[scopes.length - 1];
64
85
  const cstate = child.state;
65
- let pstate;
66
86
  this._debug && console.log(
67
87
  `${__indent(scopes.length + 1)}end: ${child.id} (${cstate.p})`
68
88
  );
69
- child.state = this._retain ? (pstate = parent.state, { p: pstate.p, l: pstate.l, c: pstate.c }) : null;
89
+ child.state = this._retain ? parent.state.copy() : null;
70
90
  parent.state = cstate;
71
91
  const children = parent.children;
72
92
  children ? children.push(child) : parent.children = [child];
@@ -75,20 +95,19 @@ class ParseContext {
75
95
  }
76
96
  addChild(id, result = null, newState = false) {
77
97
  const curr = this._curr;
78
- const cstate = curr.state;
79
- const child = {
98
+ const child = new ParseScope(
80
99
  id,
81
- state: this._retain ? { p: cstate.p, l: cstate.l, c: cstate.c } : null,
82
- children: null,
100
+ this._retain ? curr.state.copy() : null,
101
+ null,
83
102
  result
84
- };
103
+ );
85
104
  this._debug && console.log(
86
- `${__indent(this._scopes.length + 1)}addChild: ${id} (${cstate.p})`
105
+ `${__indent(this._scopes.length + 1)}addChild: ${id} (${curr.state.p})`
87
106
  );
88
107
  const children = curr.children;
89
108
  children ? children.push(child) : curr.children = [child];
90
109
  if (newState !== false) {
91
- newState === true ? this.reader.next(cstate) : this._curr.state = newState;
110
+ newState === true ? this.reader.next(curr.state) : this._curr.state = newState;
92
111
  }
93
112
  return true;
94
113
  }
@@ -140,5 +159,7 @@ function defContext(input, opts) {
140
159
  }
141
160
  export {
142
161
  ParseContext,
162
+ ParseScope,
163
+ ParseState,
143
164
  defContext
144
165
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thi.ng/parse",
3
- "version": "2.4.51",
3
+ "version": "2.4.52",
4
4
  "description": "Purely functional parser combinators & AST generation for generic inputs",
5
5
  "type": "module",
6
6
  "module": "./index.js",
@@ -240,5 +240,5 @@
240
240
  ],
241
241
  "year": 2020
242
242
  },
243
- "gitHead": "f6e26ea1142525171de5d36b9c3119f2782bb437\n"
243
+ "gitHead": "66e4c19b3054a6c35500a3e1acea1bf2563b14e8\n"
244
244
  }
package/prims/skip.js CHANGED
@@ -1,5 +1,5 @@
1
1
  const skipWhile = (pred) => (ctx) => {
2
- const state = { ...ctx.state };
2
+ const state = ctx.state.copy();
3
3
  const reader = ctx.reader;
4
4
  while (!state.done) {
5
5
  if (!pred(reader.read(state))) break;
package/prims/string.js CHANGED
@@ -16,7 +16,7 @@ const string = (str, id = "string") => (ctx) => {
16
16
  };
17
17
  const stringD = (str) => (ctx) => {
18
18
  if (ctx.done) return false;
19
- const state = { ...ctx.state };
19
+ const state = ctx.state.copy();
20
20
  const reader = ctx.reader;
21
21
  for (let i = 0, n = str.length; i < n; i++) {
22
22
  if (state.done) return false;
@@ -30,7 +30,7 @@ const stringD = (str) => (ctx) => {
30
30
  return true;
31
31
  };
32
32
  const stringOf = (pred, id = "string", reduce = (x) => x.join("")) => (ctx) => {
33
- const state = { ...ctx.state };
33
+ const state = ctx.state.copy();
34
34
  const reader = ctx.reader;
35
35
  let acc = [];
36
36
  while (!state.done) {
@@ -1,4 +1,5 @@
1
- import type { ParseState, IReader } from "../api.js";
1
+ import type { IReader } from "../api.js";
2
+ import type { ParseState } from "../context.js";
2
3
  export declare class ArrayReader<T> implements IReader<T> {
3
4
  protected _src: ArrayLike<T>;
4
5
  constructor(_src: ArrayLike<T>);
@@ -1,4 +1,5 @@
1
- import type { ParseState, IReader } from "../api.js";
1
+ import type { IReader } from "../api.js";
2
+ import type { ParseState } from "../context.js";
2
3
  export declare class StringReader implements IReader<string> {
3
4
  protected _src: string;
4
5
  constructor(_src: string);
package/xform/count.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { xform } from "../combinators/xform.js";
2
2
  const xfCount = (scope) => {
3
- scope.result = scope.children ? scope.children.length : 0;
3
+ scope.result = scope.children?.length || 0;
4
4
  scope.children = null;
5
5
  return scope;
6
6
  };
package/xform/join.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { Nullable } from "@thi.ng/api";
2
- import type { Parser, ParseScope } from "../api.js";
2
+ import type { Parser } from "../api.js";
3
+ import type { ParseScope } from "../context.js";
3
4
  /**
4
5
  * Recursively joins non-null results of all children into a single
5
6
  * string, then discards children. Also see {@link join}.
package/xform/nest.d.ts CHANGED
@@ -5,7 +5,7 @@ import type { Parser, ScopeTransform } from "../api.js";
5
5
  *
6
6
  * @remarks
7
7
  * The nested parser is applied to a separate {@link ParseContext} and if
8
- * successful, the resulting AST will be transplated into the current parse
8
+ * successful, the resulting AST will be transplanted into the current parse
9
9
  * scope. If the nested parser fails, the scope will remain untouched.
10
10
  *
11
11
  * If the current parse context retains line/column details, the inner parse
package/xform/nest.js CHANGED
@@ -3,7 +3,7 @@ import { defContext } from "../context.js";
3
3
  import { xfJoin } from "./join.js";
4
4
  const xfNest = (parser) => (scope, ctx) => {
5
5
  if (!scope) return;
6
- const src = scope.result || xfJoin({ ...scope }).result;
6
+ const src = scope.result || xfJoin(scope.copy()).result;
7
7
  const inner = defContext(src, ctx.opts);
8
8
  const state = scope.state;
9
9
  if (state) {