@@ -2,58 +2,29 @@ function assert(cond, message = 'Assertion failed') {
22 if ( ! cond ) throw new Error ( message ) ;
33}
44
5- function handleListOf ( child ) {
6- return child . toAST ( this . args . mapping ) ;
7- }
8-
9- function handleEmptyListOf ( ) {
10- return [ ] ;
11- }
12-
13- function handleNonemptyListOf ( first , sep , rest ) {
14- return [ first . toAST ( this . args . mapping ) ] . concat ( rest . toAST ( this . args . mapping ) ) ;
15- }
16-
17- const defaultMapping = {
18- listOf : handleListOf ,
19- ListOf : handleListOf ,
20-
21- emptyListOf : handleEmptyListOf ,
22- EmptyListOf : handleEmptyListOf ,
23-
24- nonemptyListOf : handleNonemptyListOf ,
25- NonemptyListOf : handleNonemptyListOf ,
26- } ;
27-
28- class Visitor {
29- constructor ( mapping ) {
30- this . mapping = mapping ;
31- }
32-
33- visit ( node ) {
34- const ctorName = node . isTerminal ( ) ? '_terminal' : node . isIter ( ) ? '_iter' : node . ruleName ;
35- if ( ctorName in this . mapping && typeof this . mapping [ ctorName ] === 'function' ) {
36- return this . mapping [ ctorName ] . apply ( this , node . children ) ;
37- }
38-
39- if ( node . isTerminal ( ) ) {
40- return this . visitTerminal ( node ) ;
41- } else if ( node . isNonterminal ( ) ) {
42- return this . visitNonterminal ( node ) ;
43- } else if ( node . isIter ( ) ) {
44- return this . visitIter ( node ) ;
45- } else {
46- throw new Error ( `Unknown node type: ${ node . _type } ` ) ;
47- }
48- }
49-
50- visitTerminal ( node , offset ) {
5+ export function toAstWithMapping ( mapping ) {
6+ const handleListOf = child => visit ( child ) ;
7+ const handleEmptyListOf = ( ) => [ ] ;
8+ const handleNonemptyListOf = ( first , sep , rest ) => {
9+ return [ visit ( first ) , ...visit ( rest ) ] ;
10+ } ;
11+
12+ mapping = {
13+ listOf : handleListOf ,
14+ ListOf : handleListOf ,
15+ emptyListOf : handleEmptyListOf ,
16+ EmptyListOf : handleEmptyListOf ,
17+ nonemptyListOf : handleNonemptyListOf ,
18+ NonemptyListOf : handleNonemptyListOf ,
19+ ...mapping ,
20+ } ;
21+
22+ function visitTerminal ( node , offset ) {
5123 return node . sourceString ;
5224 }
5325
54- visitNonterminal ( node ) {
26+ function visitNonterminal ( node ) {
5527 const { children, ruleName} = node ;
56- const { mapping} = this ;
5728
5829 // without customization
5930 if ( ! Object . hasOwn ( mapping , ruleName ) ) {
@@ -65,14 +36,14 @@ class Visitor {
6536 // singular node (e.g. only surrounded by literals or lookaheads)
6637 const realChildren = children . filter ( c => ! c . isTerminal ( ) ) ;
6738 if ( realChildren . length === 1 ) {
68- return this . visit ( realChildren [ 0 ] ) ;
39+ return visit ( realChildren [ 0 ] ) ;
6940 }
7041
7142 // rest: terms with multiple children
7243 }
7344 // direct forward
7445 if ( typeof mapping [ ruleName ] === 'number' ) {
75- return this . visit ( children [ mapping [ ruleName ] ] ) ;
46+ return visit ( children [ mapping [ ruleName ] ] ) ;
7647 }
7748 assert ( typeof mapping [ ruleName ] !== 'function' , "shouldn't be possible" ) ;
7849
@@ -86,7 +57,7 @@ class Visitor {
8657 const mappedProp = mapping [ ruleName ] && mapping [ ruleName ] [ prop ] ;
8758 if ( typeof mappedProp === 'number' ) {
8859 // direct forward
89- ans [ prop ] = this . visit ( children [ mappedProp ] ) ;
60+ ans [ prop ] = visit ( children [ mappedProp ] ) ;
9061 } else if (
9162 typeof mappedProp === 'string' ||
9263 typeof mappedProp === 'boolean' ||
@@ -102,7 +73,7 @@ class Visitor {
10273 ans [ prop ] = mappedProp . call ( this , children ) ;
10374 } else if ( mappedProp === undefined ) {
10475 if ( children [ prop ] && ! children [ prop ] . isTerminal ( ) ) {
105- ans [ prop ] = this . visit ( children [ prop ] ) ;
76+ ans [ prop ] = visit ( children [ prop ] ) ;
10677 } else {
10778 // delete predefined 'type' properties, like 'type', if explicitely removed
10879 delete ans [ prop ] ;
@@ -112,28 +83,42 @@ class Visitor {
11283 return ans ;
11384 }
11485
115- visitIter ( node ) {
86+ function visitIter ( node ) {
11687 const { children} = node ;
11788 if ( node . isOptional ( ) ) {
11889 if ( children . length === 0 ) {
11990 return null ;
12091 } else {
121- return this . visit ( children [ 0 ] ) ;
92+ return visit ( children [ 0 ] ) ;
12293 }
12394 }
12495
125- return children . map ( c => this . visit ( c ) ) ;
96+ return children . map ( c => visit ( c ) ) ;
97+ }
98+
99+ function visit ( nodeOrResult ) {
100+ let node = nodeOrResult ;
101+ if ( typeof nodeOrResult . succeeded === 'function' ) {
102+ assert ( nodeOrResult . succeeded ( ) , 'Cannot convert failed match result to AST' ) ;
103+ node = nodeOrResult . _cst ;
104+ }
105+ const { ctorName} = node ;
106+ if ( ctorName in mapping && typeof mapping [ ctorName ] === 'function' ) {
107+ return mapping [ ctorName ] . apply ( this , node . children ) ;
108+ }
109+ if ( node . isTerminal ( ) ) {
110+ return visitTerminal ( node ) ;
111+ } else if ( node . isIter ( ) ) {
112+ return visitIter ( node ) ;
113+ } else {
114+ assert ( node . isNonterminal ( ) , `Unknown node type: ${ node . _type } ` ) ;
115+ return node . ctorName in mapping && typeof mapping [ node . ctorName ] === 'function' ?
116+ mapping [ node . ctorName ] . apply ( this , node . children ) :
117+ visitNonterminal ( node ) ;
118+ }
126119 }
127- }
128120
129- // Returns a plain JavaScript object that includes an abstract syntax tree (AST)
130- // for the given match result `res` containg a concrete syntax tree (CST) and grammar.
131- // The optional `mapping` parameter can be used to customize how the nodes of the CST
132- // are mapped to the AST (see /doc/extras.md#toastmatchresult-mapping).
133- export function toAST ( result , mapping ) {
134- const visitor = new Visitor ( { ...defaultMapping , ...mapping } ) ;
135- // Note: in the original implementation of toAST, any functions in `mapping`
136- // are removed after copying over to the final mapping. Looking at the code,
137- // it doesn't seem strictly necessary, but it's not 100% clear.
138- return visitor . visit ( result . _cst , 0 ) ;
121+ return visit ;
139122}
123+
124+ export const toAst = toAstWithMapping ( { } ) ;
0 commit comments