Skip to content

Commit 535663b

Browse files
committed
progress
1 parent 517ac61 commit 535663b

36 files changed

+896
-222
lines changed

grammar/handlebars.y

Lines changed: 157 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,19 @@
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

4242
program:
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

4646
statement_list:
4747
statement_list statement { if ($self->semStack[$2] !== null) { $self->semStack[$1][] = $self->semStack[$2]; } $$ = $self->semStack[$1]; }
4848
| /* empty */ { $$ = []; }
49+
;
4950

5051
statement:
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

6566
content:
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

7475
content_list:
7576
content_list content { if ($self->semStack[$2] !== null) { $self->semStack[$1][] = $self->semStack[$2]; } $$ = $self->semStack[$1]; }
7677
| /* empty */ { $$ = []; }
7778
;
7879

7980
rawBlock:
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

8390
openRawBlock:
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

8795
block:
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

92100
openBlock:
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

105112
openInverse:
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

117124
openInverseChain:
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

129136
optional_inverseAndProgram:
130137
inverseAndProgram
131138
| /* empty */ { $$ = null; }
132139
;
133140

134141
inverseAndProgram:
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

138149
optional_inverseChain:
139150
inverseChain
@@ -143,53 +154,85 @@ optional_inverseChain:
143154
inverseChain:
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

154165
closeBlock:
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

158170
mustache:
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

166205
partial:
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

179217
partialBlock:
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

183227
openPartialBlock:
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

194237
expr_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

209252
sexpr:
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

221264
hash:
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

225272
optional_hash:
226273
hash
227-
| /* empty */ { $$ = []; }
274+
| /* empty */ { $$ = null; }
228275
;
229276

230277
non_empty_hashSegment_list:
@@ -233,12 +280,18 @@ non_empty_hashSegment_list:
233280
;
234281

235282
hashSegment:
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

239291
arrayLiteral:
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

243296
optional_blockParams:
244297
blockParams
@@ -251,22 +304,24 @@ non_empty_ID_list:
251304
;
252305

253306
blockParams:
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

257311
helperName:
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

267321
dataName:
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

271326
sep:
272327
SEP { $$ = $self->semStack[$1]; }
@@ -275,7 +330,7 @@ sep:
275330

276331
path:
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

281336
pathSegments:

0 commit comments

Comments
 (0)