3434%%
3535
3636/*
37- * Should match the grammar (as of 2025-01-15 ) of
37+ * Should match the grammar (as of 2025-01-20 ) of
3838 * https://github.com/handlebars-lang/handlebars-parser/blob/master/src/handlebars.yy
3939 * EBNF grammar has been converted to BNF.
4040 */
4141
4242program :
43- statement_list { $$ = $self- >prepareProgram ($self->semStack[$1 ]); }
43+ statement_list { $$ = $self- >prepareProgram ($self->semStack[$1 ], $self-> locInfo ($self->tokenStartStack[$ 1 ], $self->tokenEndStack[$stackPos]) ); }
4444;
4545
4646statement_list:
4747 statement_list statement { if ($self->semStack [$2 ] !== null) { $self->semStack [$1 ][] = $self->semStack [$2 ]; } $$ = $self->semStack [$1 ]; }
4848 | /* empty */ { $$ = []; }
49+ ;
4950
5051statement:
5152 mustache { $$ = $self->semStack [$1 ]; }
@@ -55,85 +56,95 @@ statement:
5556 | partialBlock { $$ = $self->semStack [$1 ]; }
5657 | content { $$ = $self->semStack [$1 ]; }
5758 | COMMENT {
58- $$ = [
59- ' type ' => ' CommentStatement ' ,
60- ' value ' => $self- >stripComment ($ 1 ),
61- 'strip' => $self->stripFlags($ 1 , $1 ),
62- ] ;
59+ $$ = new CommentStatement (
60+ value: $self-> stripComment ($self-> semStack [$ 1 ]) ,
61+ strip: $self-> stripFlags ($self-> semStack [$ 1 ], $self->semStack [$ 1 ] ),
62+ loc: $self->locInfo ($self-> tokenStartStack [$ 1 ] , $self-> tokenEndStack [$stackPos] ),
63+ ) ;
6364 };
6465
6566content:
6667 CONTENT {
67- $$ = [
68- ' type ' => ' ContentStatement ' ,
69- ' value ' => $self- >semStack[$1 ],
70- ];
71- }
72- ;
68+ $$ = new ContentStatement (
69+ value: $self-> semStack [$ 1 ] ,
70+ original: $self->semStack [$1 ],
71+ loc: $self-> locInfo ($self-> tokenStartStack [$ 1 ], $self-> tokenEndStack [$stackPos]),
72+ );
73+ } ;
7374
7475content_list:
7576 content_list content { if ($self->semStack [$2 ] !== null) { $self->semStack [$1 ][] = $self->semStack [$2 ]; } $$ = $self->semStack [$1 ]; }
7677 | /* empty */ { $$ = []; }
7778;
7879
7980rawBlock:
80- openRawBlock content_list END_RAW_BLOCK { $self- >prepareRawBlock ($self->semStack[$1 ], $self->semStack[$2 ], $self->semStack[$3 ]); }
81- ;
81+ openRawBlock content_list END_RAW_BLOCK {
82+ $$ = $self->prepareRawBlock (
83+ $self->semStack [$1 ],
84+ $self->semStack [$2 ],
85+ $self->semStack [$3 ],
86+ $self->locInfo ($self->tokenStartStack [$1 ], $self->tokenEndStack [$stackPos]),
87+ );
88+ };
8289
8390openRawBlock:
84- OPEN_RAW_BLOCK helperName expr_list optional_hash CLOSE_RAW_BLOCK { $$ = [' path' => $self- >semStack[$2 ], ' params' => $self- >semStack[$3 ], ' hash' => $self- >semStack[$4 ]]; }
85- ;
91+ OPEN_RAW_BLOCK helperName expr_list optional_hash CLOSE_RAW_BLOCK {
92+ $$ = new OpenHelper ($self->semStack [$2 ], $self->semStack [$3 ], $self->semStack [$4 ]);
93+ };
8694
8795block:
88- openBlock program optional_inverseChain closeBlock { $$ = $self- >prepareBlock ($1 , $2 , $3 , $4 , false ); }
89- | openInverse program optional_inverseAndProgram closeBlock { $$ = $self- >prepareBlock ($1 , $2 , $3 , $4 , true ); }
96+ openBlock program optional_inverseChain closeBlock { $$ = $self->prepareBlock ($self-> semStack [$ 1 ] , $self-> semStack [$ 2 ] , $self-> semStack [$ 3 ] , $self-> semStack [$ 4 ] , false ); }
97+ | openInverse program optional_inverseAndProgram closeBlock { $$ = $self->prepareBlock ($self-> semStack [$ 1 ] , $self-> semStack [$ 2 ] , $self-> semStack [$ 3 ] , $self-> semStack [$ 4 ] , true ); }
9098;
9199
92100openBlock:
93101 OPEN_BLOCK helperName expr_list optional_hash optional_blockParams CLOSE {
94- $$ = [
95- ' open' => $self- >semStack[$1 ],
96- ' path' => $self- >semStack[$2 ],
97- ' params' => $self- >semStack[$3 ],
98- ' hash' => $self- >semStack[$4 ],
99- ' blockParams' => $self- >semStack[$5 ],
100- ' strip' => $self- >stripFlags ($self->semStack[$1 ], $self->semStack[$6 ])
101- ];
102- }
103- ;
102+ $$ = new OpenBlock (
103+ open: $self->semStack [$1 ],
104+ path: $self->semStack [$2 ],
105+ params: $self->semStack [$3 ],
106+ hash: $self->semStack [$4 ],
107+ blockParams: $self->semStack [$5 ],
108+ strip: $self->stripFlags ($self->semStack [$1 ], $self->semStack [$6 ]),
109+ );
110+ };
104111
105112openInverse:
106113 OPEN_INVERSE helperName expr_list optional_hash optional_blockParams CLOSE {
107- $$ = [
108- ' path ' => $self- >semStack[$2 ],
109- ' params ' => $self- >semStack[$3 ],
110- ' hash ' => $self- >semStack[$4 ],
111- ' blockParams ' => $self- >semStack[$5 ],
112- ' strip ' => $self- >stripFlags ($self-> semStack[$1 ], $self->semStack[$ 6 ])
113- ];
114- }
115- ;
114+ $$ = new OpenBlock (
115+ open: $self->semStack [$1 ],
116+ path: $self->semStack [$2 ],
117+ params: $self->semStack [$3 ],
118+ hash: $self->semStack [$4 ],
119+ blockParams: $self->semStack [$5 ],
120+ strip: $self-> stripFlags ($self-> semStack [$ 1 ], $self-> semStack [$ 6 ]),
121+ );
122+ } ;
116123
117124openInverseChain:
118125 OPEN_INVERSE_CHAIN helperName expr_list optional_hash optional_blockParams CLOSE {
119- $$ = [
120- ' path ' => $self- >semStack[$2 ],
121- ' params ' => $self- >semStack[$3 ],
122- ' hash ' => $self- >semStack[$4 ],
123- ' blockParams ' => $self- >semStack[$5 ],
124- ' strip ' => $self- >stripFlags ($self-> semStack[$1 ], $self->semStack[$ 6 ])
125- ];
126- }
127- ;
126+ $$ = new OpenBlock (
127+ open: $self->semStack [$1 ],
128+ path: $self->semStack [$2 ],
129+ params: $self->semStack [$3 ],
130+ hash: $self->semStack [$4 ],
131+ blockParams: $self->semStack [$5 ],
132+ strip: $self-> stripFlags ($self-> semStack [$ 1 ], $self-> semStack [$ 6 ]),
133+ );
134+ } ;
128135
129136optional_inverseAndProgram:
130137 inverseAndProgram
131138 | /* empty */ { $$ = null; }
132139;
133140
134141inverseAndProgram:
135- INVERSE program { $$ = [' strip' => $self- >stripFlags ($self->semStack[$1 ], $self->semStack[$1 ]), 'program' => $self->semStack[$2]]; }
136- ;
142+ INVERSE program {
143+ $$ = new InverseChain (
144+ strip: $self->stripFlags ($self->semStack [$1 ], $self->semStack [$1 ]),
145+ program: $self->semStack [$2 ],
146+ );
147+ };
137148
138149optional_inverseChain:
139150 inverseChain
@@ -143,53 +154,85 @@ optional_inverseChain:
143154inverseChain:
144155 openInverseChain program optional_inverseChain {
145156 $inverse = $self->prepareBlock ($self->semStack [$1 ], $self->semStack [$2 ], $self->semStack [$3 ], $self->semStack [$3 ], false );
146- $program = $self- >prepareProgram ([$inverse], $self->semStack[$2 ][ ' loc' ] );
157+ $program = $self->prepareProgram ([$inverse], $self->semStack [$2 ]-> loc );
147158 $program->chained = true ;
148159
149- $$ = [ ' strip ' => $self- >semStack[$1 ][ ' strip' ], ' program ' => $program , ' chain ' => true ] ;
160+ $$ = new InverseChain ( $self->semStack [$1 ]-> strip , $program, true ) ;
150161 }
151162 | inverseAndProgram { $$ = $self->semStack [$1 ]; }
152163;
153164
154165closeBlock:
155- OPEN_ENDBLOCK helperName CLOSE { $$ = [' path' => $self- >semStack[$2 ], ' strip' => $self- >stripFlags ($self->semStack[$1 ], $self->semStack[$3 ])]; }
156- ;
166+ OPEN_ENDBLOCK helperName CLOSE {
167+ $$ = new CloseBlock ($self->semStack [$2 ], $self->stripFlags ($self->semStack [$1 ], $self->semStack [$3 ]));
168+ };
157169
158170mustache:
159- // Parsing out the ' &' escape token at AST level saves ~500 bytes after min due to the removal of one parser node.
160- // This also allows for handler unification as all mustache node instances can utilize the same handler
161- OPEN hash CLOSE { $$ = $self- >prepareMustache ($self->syntax->hash ($self->semStack[$2 ], [' syntax' => ' expr' ]), [], null, $self->semStack[$1], $self->stripFlags($self->semStack[$1 ], $self->semStack[$3 ])); }
162- | OPEN expr expr_list optional_hash CLOSE { $$ = $self->prepareMustache ($self->semStack [$2 ], $self->semStack [$3 ], $self->semStack [$4 ], $self->semStack [$1 ], $self->stripFlags ($self->semStack [$1 ], $self->semStack [$5 ])); }
163- | OPEN_UNESCAPED expr expr_list optional_hash CLOSE_UNESCAPED { $$ = $self->prepareMustache ($self->semStack [$2 ], $self->semStack [$3 ], $self->semStack [$4 ], $self->semStack [$1 ], $self->stripFlags ($self->semStack [$1 ], $self->semStack [$5 ])); }
171+ // Parsing out the '&' escape token at AST level saves ~500 bytes after min due to the removal of one parser node.
172+ // This also allows for handler unification as all mustache node instances can utilize the same handler
173+ OPEN hash CLOSE {
174+ $$ = $self->prepareMustache (
175+ path: new HashLiteral ($self->semStack [$2 ]->pairs , $self->semStack [$2 ]->loc ),
176+ params: [],
177+ hash: null,
178+ open: $self->semStack [$1 ],
179+ strip: $self->stripFlags ($self->semStack [$1 ], $self->semStack [$3 ]),
180+ loc: $self->locInfo ($self->tokenStartStack [$1 ], $self->tokenEndStack [$stackPos]),
181+ );
182+ }
183+ | OPEN expr expr_list optional_hash CLOSE {
184+ $$ = $self->prepareMustache (
185+ path: $self->semStack [$2 ],
186+ params: $self->semStack [$3 ],
187+ hash: $self->semStack [$4 ],
188+ open: $self->semStack [$1 ],
189+ strip: $self->stripFlags ($self->semStack [$1 ], $self->semStack [$5 ]),
190+ loc: $self->locInfo ($self->tokenStartStack [$1 ], $self->tokenEndStack [$stackPos]),
191+ );
192+ }
193+ | OPEN_UNESCAPED expr expr_list optional_hash CLOSE_UNESCAPED {
194+ $$ = $self->prepareMustache (
195+ path: $self->semStack [$2 ],
196+ params: $self->semStack [$3 ],
197+ hash: $self->semStack [$4 ],
198+ open: $self->semStack [$1 ],
199+ strip: $self->stripFlags ($self->semStack [$1 ], $self->semStack [$5 ]),
200+ loc: $self->locInfo ($self->tokenStartStack [$1 ], $self->tokenEndStack [$stackPos]),
201+ );
202+ }
164203;
165204
166205partial:
167206 OPEN_PARTIAL expr expr_list optional_hash CLOSE {
168- $$ = [
169- ' type' => ' PartialStatement' ,
170- ' name' => $self->semStack [$2 ],
171- ' params' => $self->semStack [$3 ],
172- ' hash' => $self->semStack [$4 ],
173- ' indent' => ' ' ,
174- ' strip' => $self->stripFlags ($self->semStack [$1 ], $self->semStack [$5 ]),
175- ];
176- }
177- ;
207+ $$ = new PartialStatement (
208+ name: $self->semStack [$2 ],
209+ params: $self->semStack [$3 ],
210+ hash: $self->semStack [$4 ],
211+ indent: ' ' ,
212+ strip: $self->stripFlags ($self->semStack [$1 ], $self->semStack [$5 ]),
213+ loc: $self->locInfo ($self->tokenStartStack [$1 ], $self->tokenEndStack [$stackPos]),
214+ );
215+ };
178216
179217partialBlock:
180- openPartialBlock program closeBlock { $$ = $self->preparePartialBlock ($self->semStack [$1 ], $self->semStack [$2 ], $self->semStack [$3 ]); }
181- ;
218+ openPartialBlock program closeBlock {
219+ $$ = $self->preparePartialBlock (
220+ open: $self->semStack [$1 ],
221+ program: $self->semStack [$2 ],
222+ close: $self->semStack [$3 ],
223+ loc: $self->locInfo ($self->tokenStartStack [$1 ], $self->tokenEndStack [$stackPos]),
224+ );
225+ };
182226
183227openPartialBlock:
184228 OPEN_PARTIAL_BLOCK expr expr_list optional_hash CLOSE {
185- $$ = [
186- ' path' => $self->semStack [$2 ],
187- ' params' => $self->semStack [$3 ],
188- ' hash' => $self->semStack [$4 ],
189- ' strip' => $self->stripFlags ($self->semStack [$1 ], $self->semStack [$5 ]),
190- ];
191- }
192- ;
229+ $$ = new OpenPartialBlock (
230+ path: $self->semStack [$2 ],
231+ params: $self->semStack [$3 ],
232+ hash: $self->semStack [$4 ],
233+ strip: $self->stripFlags ($self->semStack [$1 ], $self->semStack [$5 ]),
234+ );
235+ };
193236
194237expr_list:
195238 expr_list expr { if ($self->semStack [$2 ] !== null) { $self->semStack [$1 ][] = $self->semStack [$2 ]; } $$ = $self->semStack [$1 ]; }
@@ -207,24 +250,28 @@ exprHead:
207250;
208251
209252sexpr:
210- OPEN_SEXPR hash CLOSE_SEXPR { $$ = $self->syntax -> hash ( $self->semStack [$2 ], [ ' syntax ' => ' expr ' ] ); }
253+ OPEN_SEXPR hash CLOSE_SEXPR { $$ = new HashLiteral ( $self->semStack [$ 2 ]-> pairs , $self->semStack [$2 ]-> loc ); }
211254 | OPEN_SEXPR expr expr_list optional_hash CLOSE_SEXPR {
212- $$ = [
213- ' type ' => ' SubExpression ' ,
214- ' path ' => $self->semStack [$2 ],
215- ' params ' => $self->semStack [$3 ],
216- ' hash ' => $self->semStack [$ 4 ] ,
217- ] ;
255+ $$ = new SubExpression (
256+ path: $self-> semStack [$ 2 ] ,
257+ params: $self->semStack [$3 ],
258+ hash: $self->semStack [$4 ],
259+ loc: $self-> locInfo ($self-> tokenStartStack [$ 1 ], $self->tokenEndStack [$stackPos]) ,
260+ ) ;
218261 }
219262;
220263
221264hash:
222- non_empty_hashSegment_list { $$ = [' type' => ' Hash' , ' pairs' => $self->semStack [$1 ]]; }
223- ;
265+ non_empty_hashSegment_list {
266+ $$ = new Hash (
267+ pairs: $self->semStack [$1 ],
268+ loc: $self->locInfo ($self->tokenStartStack [$1 ], $self->tokenEndStack [$stackPos]),
269+ );
270+ };
224271
225272optional_hash:
226273 hash
227- | /* empty */ { $$ = [] ; }
274+ | /* empty */ { $$ = null ; }
228275;
229276
230277non_empty_hashSegment_list:
@@ -233,12 +280,18 @@ non_empty_hashSegment_list:
233280;
234281
235282hashSegment:
236- ID EQUALS expr { $$ = [' type' => ' HashPair' , ' key' => $self->id ($self->semStack [$1 ]), ' value' => $self->semStack [$3 ]]; }
237- ;
283+ ID EQUALS expr {
284+ $$ = new HashPair (
285+ key: $self->id ($self->semStack [$1 ]),
286+ value: $self->semStack [$3 ],
287+ loc: $self->locInfo ($self->tokenStartStack [$1 ], $self->tokenEndStack [$stackPos]),
288+ );
289+ };
238290
239291arrayLiteral:
240- OPEN_ARRAY expr_list CLOSE_ARRAY { $$ = $self->syntax ->square ($self->semStack [$2 ], [' syntax' => ' expr' ]); }
241- ;
292+ OPEN_ARRAY expr_list CLOSE_ARRAY {
293+ $$ = new ArrayLiteral ($self->semStack [$2 ]);
294+ };
242295
243296optional_blockParams:
244297 blockParams
@@ -251,22 +304,24 @@ non_empty_ID_list:
251304;
252305
253306blockParams:
254- OPEN_BLOCK_PARAMS non_empty_ID_list CLOSE_BLOCK_PARAMS { $$ = $self->id ($self->semStack [$2 ]); }
255- ;
307+ OPEN_BLOCK_PARAMS non_empty_ID_list CLOSE_BLOCK_PARAMS {
308+ $$ = array_map ($self->id (...), $self->semStack [$2 ]);
309+ };
256310
257311helperName:
258312 path { $$ = $self->semStack [$1 ]; }
259313 | dataName { $$ = $self->semStack [$1 ]; }
260- | STRING { $$ = [ ' type ' => ' StringLiteral' , ' value ' => $self->semStack [$1 ]] ; }
261- | NUMBER { $$ = [ ' type ' => ' NumberLiteral ' , ' value ' => $self->semStack [$1 ] + 0 ] ; }
262- | BOOLEAN { $$ = [ ' type ' => ' BooleanLiteral ' , ' value ' => $self->semStack [$1 ] === ' true' ]; }
263- | UNDEFINED { $$ = [ ' type ' => ' UndefinedLiteral' , ' value ' => null] ; }
264- | NULL { $$ = [ ' type ' => ' NullLiteral' , ' value ' => null] ; }
314+ | STRING { $$ = new StringLiteral ($self-> semStack [$ 1 ]-> text , $self-> semStack [$ 1 ]-> text , $self->locInfo ($self-> tokenStartStack [$1 ], $self-> tokenEndStack [$stackPos])) ; }
315+ | NUMBER { $$ = new NumberLiteral ($self-> semStack [$ 1 ]-> text + 0 , $self->semStack [$1 ]-> text + 0 , $self-> locInfo ($self-> tokenStartStack [$ 1 ], $self-> tokenEndStack [$stackPos])) ; }
316+ | BOOLEAN { $$ = new BooleanLiteral ($self-> semStack [$ 1 ]-> text === ' true ' , $self->semStack [$1 ]-> text === ' true' , $self-> locInfo ($self-> tokenStartStack [$ 1 ], $self-> tokenEndStack [$stackPos]));
317+ | UNDEFINED { $$ = new UndefinedLiteral ($self-> locInfo ($self-> tokenStartStack [$ 1 ], $self-> tokenEndStack [$stackPos])) ; }
318+ | NULL { $$ = new NullLiteral ($self-> locInfo ($self-> tokenStartStack [$ 1 ], $self-> tokenEndStack [$stackPos])) ; }
265319;
266320
267321dataName:
268- DATA pathSegments {$$ = $self->preparePath (true , false , $self->semStack [$2 ]);}
269- ;
322+ DATA pathSegments {
323+ $$ = $self->preparePath (true , false , $self->semStack [$2 ], $self->locInfo ($self->tokenStartStack [$1 ], $self->tokenEndStack [$stackPos]));
324+ };
270325
271326sep:
272327 SEP { $$ = $self->semStack [$1 ]; }
275330
276331path:
277332 exprHead sep pathSegments {$$ = $self->preparePath (false , $self->semStack [$1 ], $self->semStack [$3 ]);}
278- | pathSegments {$$ = $self->preparePath (false , false , $self->semStack [$1 ]); }
333+ | pathSegments { $$ = $self->preparePath (false , false , $self->semStack [$1 ], $self-> locInfo ($self-> tokenStartStack [$ 1 ], $self-> tokenEndStack [$stackPos])); }
279334;
280335
281336pathSegments:
0 commit comments