@ohm-js/wasm 0.6.9 → 0.6.11

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.
@@ -3,6 +3,9 @@ export type AstNodeTemplate<R> = {
3
3
  [property: string]: number | string | boolean | object | null | ((this: AstBuilder, children: CstNodeChildren) => R);
4
4
  };
5
5
  export type AstMapping<R> = Record<string, AstNodeTemplate<R> | number | ((this: AstBuilder, ...children: CstNodeChildren) => R)>;
6
+ export declare function createToAst<TNode = any>(mapping: AstMapping<TNode>, opts?: {
7
+ debug?: boolean;
8
+ }): (nodeOrResult: MatchResult | CstNode) => TNode;
6
9
  export declare class AstBuilder<TNode = any> {
7
10
  currNode?: CstNode;
8
11
  private _mapping;
@@ -6,6 +6,10 @@ function childAt(children, idx, ruleName, propName = '') {
6
6
  }
7
7
  return checkNotNull(children[idx]);
8
8
  }
9
+ export function createToAst(mapping, opts = {}) {
10
+ const builder = new AstBuilder(mapping, opts);
11
+ return (nodeOrResult) => builder.toAst(nodeOrResult);
12
+ }
9
13
  export class AstBuilder {
10
14
  currNode;
11
15
  _mapping;
@@ -5,5 +5,5 @@ export interface Grammar extends WasmGrammar {
5
5
  }
6
6
  export declare function grammar(source: string): Grammar;
7
7
  export declare function grammars(source: string): Record<string, Grammar>;
8
- export { AstBuilder } from './AstBuilder.js';
8
+ export { createToAst } from './createToAst.ts';
9
9
  //# sourceMappingURL=compat.d.ts.map
@@ -21,5 +21,5 @@ export function grammars(source) {
21
21
  }
22
22
  return ans;
23
23
  }
24
- export { AstBuilder } from './AstBuilder.js';
24
+ export { createToAst } from "./createToAst.js";
25
25
  //# sourceMappingURL=compat.js.map
@@ -0,0 +1,22 @@
1
+ import type { CstNode, CstNodeChildren, MatchResult, NonterminalNode, TerminalNode } from './miniohm.ts';
2
+ export type AstNodeTemplate<R> = {
3
+ [property: string]: number | string | boolean | object | null | ((children: CstNodeChildren) => R);
4
+ };
5
+ export type AstMapping<R> = Record<string, AstNodeTemplate<R> | number | ((...children: CstNodeChildren) => R)>;
6
+ export declare function createToAst<TNode = any>(mapping: AstMapping<TNode>, opts?: {
7
+ debug?: boolean;
8
+ }): (nodeOrResult: MatchResult | CstNode) => TNode;
9
+ export declare class AstBuilder<TNode = any> {
10
+ currNode?: CstNode;
11
+ private _mapping;
12
+ private _depth;
13
+ private _debug;
14
+ constructor(mapping: AstMapping<TNode>, opts?: {
15
+ debug?: boolean;
16
+ });
17
+ private _debugLog;
18
+ _visitTerminal(node: TerminalNode): string;
19
+ _visitNonterminal(node: NonterminalNode): unknown;
20
+ toAst(nodeOrResult: MatchResult | CstNode): TNode;
21
+ }
22
+ //# sourceMappingURL=createToAst.d.ts.map
@@ -0,0 +1,140 @@
1
+ import { assert, checkNotNull } from "./assert.js";
2
+ function childAt(children, idx, ruleName, propName = '') {
3
+ if (idx > children.length) {
4
+ const path = propName ? `${ruleName}.${propName}` : ruleName;
5
+ throw new Error(`${path}: Child index ${idx} out of range`);
6
+ }
7
+ return checkNotNull(children[idx]);
8
+ }
9
+ export function createToAst(mapping, opts = {}) {
10
+ const builder = new AstBuilder(mapping, opts);
11
+ return (nodeOrResult) => builder.toAst(nodeOrResult);
12
+ }
13
+ export class AstBuilder {
14
+ currNode;
15
+ _mapping;
16
+ _depth = -1;
17
+ _debug = false;
18
+ constructor(mapping, opts = {}) {
19
+ const handleListOf = (child) => this.toAst(child);
20
+ const handleEmptyListOf = () => [];
21
+ const handleNonemptyListOf = (first, sepAndElemList) => {
22
+ assert(!!sepAndElemList?.isList(), 'Expected a ListNode');
23
+ return [this.toAst(first), ...sepAndElemList.collect((_, elem) => this.toAst(elem))];
24
+ };
25
+ this._mapping = {
26
+ listOf: handleListOf,
27
+ ListOf: handleListOf,
28
+ emptyListOf: handleEmptyListOf,
29
+ EmptyListOf: handleEmptyListOf,
30
+ nonemptyListOf: handleNonemptyListOf,
31
+ NonemptyListOf: handleNonemptyListOf,
32
+ ...mapping,
33
+ };
34
+ this._debug = opts.debug ?? false;
35
+ }
36
+ _debugLog(...data) {
37
+ if (this._debug)
38
+ console.log(' '.repeat(this._depth), ...data);
39
+ }
40
+ _visitTerminal(node) {
41
+ return node.sourceString;
42
+ }
43
+ _visitNonterminal(node) {
44
+ const { children, ctorName } = node;
45
+ const mapping = this._mapping;
46
+ this._debugLog(`> ${ctorName}`);
47
+ const dbgReturn = (val) => {
48
+ this._debugLog(`| ${ctorName} DONE`);
49
+ return val;
50
+ };
51
+ // without customization
52
+ if (!Object.hasOwn(mapping, ctorName)) {
53
+ // lexical rule
54
+ if (node.isLexical()) {
55
+ return node.sourceString;
56
+ }
57
+ // singular node (e.g. only surrounded by literals or lookaheads)
58
+ const realChildren = children.filter(c => !c.isTerminal());
59
+ if (realChildren.length === 1) {
60
+ return dbgReturn(this.toAst(realChildren[0]));
61
+ }
62
+ // rest: terms with multiple children
63
+ }
64
+ // direct forward
65
+ if (typeof mapping[ctorName] === 'number') {
66
+ const idx = mapping[ctorName];
67
+ return dbgReturn(this.toAst(childAt(children, idx, ctorName)));
68
+ }
69
+ assert(typeof mapping[ctorName] !== 'function', "shouldn't be possible");
70
+ // named/mapped children or unnamed children ('0', '1', '2', ...)
71
+ const propMap = mapping[ctorName] || children;
72
+ const ans = {
73
+ type: ctorName,
74
+ };
75
+ // eslint-disable-next-line guard-for-in
76
+ for (const prop in propMap) {
77
+ const mappedProp = mapping[ctorName] && mapping[ctorName][prop];
78
+ if (typeof mappedProp === 'number') {
79
+ // direct forward
80
+ ans[prop] = this.toAst(childAt(children, mappedProp, ctorName, prop));
81
+ }
82
+ else if (typeof mappedProp === 'string' ||
83
+ typeof mappedProp === 'boolean' ||
84
+ mappedProp === null) {
85
+ // primitive value
86
+ ans[prop] = mappedProp;
87
+ }
88
+ else if (typeof mappedProp === 'object' && mappedProp instanceof Number) {
89
+ // primitive number (must be unboxed)
90
+ ans[prop] = Number(mappedProp);
91
+ }
92
+ else if (typeof mappedProp === 'function') {
93
+ // computed value
94
+ ans[prop] = mappedProp.call(this, children);
95
+ }
96
+ else if (mappedProp === undefined) {
97
+ const child = children[Number(prop)];
98
+ if (child && !child.isTerminal()) {
99
+ ans[prop] = this.toAst(child);
100
+ }
101
+ else {
102
+ // delete predefined 'type' properties, like 'type', if explicitly removed
103
+ delete ans[prop];
104
+ }
105
+ }
106
+ }
107
+ return dbgReturn(ans);
108
+ }
109
+ toAst(nodeOrResult) {
110
+ let node = nodeOrResult;
111
+ if (typeof nodeOrResult.succeeded === 'function') {
112
+ const matchResult = nodeOrResult;
113
+ assert(matchResult.succeeded(), 'Cannot convert failed match result to AST');
114
+ node = matchResult.grammar.getCstRoot();
115
+ }
116
+ let ans;
117
+ this._depth++;
118
+ if (node.isTerminal()) {
119
+ ans = this._visitTerminal(node);
120
+ }
121
+ else if (node.isOptional()) {
122
+ ans = node.ifPresent((child) => this.toAst(child), () => null);
123
+ }
124
+ else if (node.isList() || node.isSeq()) {
125
+ ans = node.children.map(c => this.toAst(c));
126
+ }
127
+ else {
128
+ assert(node.isNonterminal(), `Unknown node type: ${node.type}`);
129
+ this.currNode = node;
130
+ ans =
131
+ typeof this._mapping[node.ctorName] === 'function'
132
+ ? this._mapping[node.ctorName].apply(this, node.children)
133
+ : this._visitNonterminal(node);
134
+ this.currNode = undefined;
135
+ }
136
+ this._depth--;
137
+ return ans;
138
+ }
139
+ }
140
+ //# sourceMappingURL=createToAst.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ohm-js/wasm",
3
- "version": "0.6.9",
3
+ "version": "0.6.11",
4
4
  "description": "Compile Ohm.js grammars to WebAsssembly",
5
5
  "main": "dist/index.js",
6
6
  "exports": {