Skip to content

Commit 9621bb6

Browse files
committed
More fixes
1 parent 7ceb29b commit 9621bb6

File tree

7 files changed

+104
-42
lines changed

7 files changed

+104
-42
lines changed

grammar/handlebars.y

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
*/
4343

4444
program:
45-
statement_list { $$ = $this->prepareProgram($1, locInfo()); }
45+
statement_list { $$ = $this->prepareProgram($1); }
4646
;
4747

4848
statement_list:
@@ -154,7 +154,7 @@ optional_inverseChain:
154154

155155
inverseChain:
156156
openInverseChain program optional_inverseChain {
157-
$inverse = $this->prepareBlock($1, $2, $3, $3, false);
157+
$inverse = $this->prepareBlock($1, $2, $3, $3, false, locInfo());
158158
$program = $this->prepareProgram([$inverse], $2->loc);
159159
$program->chained = true;
160160

@@ -309,9 +309,9 @@ blockParams:
309309
helperName:
310310
path { $$ = $1; }
311311
| dataName { $$ = $1; }
312-
| STRING { $$ = new StringLiteral($1->text, $1->text, locInfo()); }
313-
| NUMBER { $$ = new NumberLiteral($1->text + 0, $1->text + 0, locInfo()); }
314-
| BOOLEAN { $$ = new BooleanLiteral($1->text === 'true', $1->text === 'true', locInfo()); }
312+
| STRING { $$ = new StringLiteral($1, $1, locInfo()); }
313+
| NUMBER { $$ = new NumberLiteral($1 + 0, $1 + 0, locInfo()); }
314+
| BOOLEAN { $$ = new BooleanLiteral($1 === 'true', $1 === 'true', locInfo()); }
315315
| UNDEFINED { $$ = new UndefinedLiteral(locInfo()); }
316316
| NULL { $$ = new NullLiteral(locInfo()); }
317317
;
@@ -352,10 +352,10 @@ path:
352352

353353
pathSegments:
354354
pathSegments sep ID {
355-
push($1, new PathSegment($this->id($3), $3->text, $2));
355+
push($1, new PathSegment($this->id($3), $3, $2));
356356
}
357357
| ID {
358-
init(new PathSegment($this->id($1), $1->text, null));
358+
init(new PathSegment($this->id($1), $1, null));
359359
}
360360
;
361361

src/Ast/BlockStatement.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ public function __construct(
1111
string $type,
1212
public PathExpression $path,
1313
public array $params,
14-
public Hash $hash,
15-
public Program $program,
14+
public ?Hash $hash,
15+
public ?Program $program,
1616
public ?Program $inverse,
1717
public StripFlags $openStrip,
1818
public ?StripFlags $inverseStrip,
19-
public StripFlags $closeStrip,
19+
public ?StripFlags $closeStrip,
2020
SourceLocation $loc,
2121
) {
2222
parent::__construct($type, $loc);

src/Ast/ContentStatement.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ class ContentStatement extends Statement
66
{
77
public function __construct(
88
public string $value,
9-
public StripFlags $original,
9+
public string $original,
1010
SourceLocation $loc,
1111
) {
1212
parent::__construct('ContentStatement', $loc);

src/Ast/OpenBlock.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
* @param string[] $blockParams
1212
*/
1313
public function __construct(
14-
public Token $open,
14+
public string $open,
1515
PathExpression | Literal $path,
1616
array $params,
1717
?Hash $hash,

src/Ast/PathSegment.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@
99
public function __construct(
1010
public string $part,
1111
public string $original,
12-
public ?Token $separator,
12+
public ?string $separator,
1313
) {}
1414
}

src/Parser.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ protected function initReduceCallbacks(): void {
244244
$this->reduceCallbacks = [
245245
0 => null,
246246
1 => static function ($self, $stackPos) {
247-
$self->semValue = $self->prepareProgram($self->semStack[$stackPos-(1-1)], $self->locInfo($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
247+
$self->semValue = $self->prepareProgram($self->semStack[$stackPos-(1-1)]);
248248
},
249249
2 => static function ($self, $stackPos) {
250250
if ($self->semStack[$stackPos-(2-2)] !== null) { $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; } $self->semValue = $self->semStack[$stackPos-(2-1)];
@@ -368,7 +368,7 @@ protected function initReduceCallbacks(): void {
368368
},
369369
26 => static function ($self, $stackPos) {
370370

371-
$inverse = $self->prepareBlock($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-2)], $self->semStack[$stackPos-(3-3)], $self->semStack[$stackPos-(3-3)], false);
371+
$inverse = $self->prepareBlock($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-2)], $self->semStack[$stackPos-(3-3)], $self->semStack[$stackPos-(3-3)], false, $self->locInfo($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
372372
$program = $self->prepareProgram([$inverse], $self->semStack[$stackPos-(3-2)]->loc);
373373
$program->chained = true;
374374

@@ -533,13 +533,13 @@ protected function initReduceCallbacks(): void {
533533
$self->semValue = $self->semStack[$stackPos-(1-1)];
534534
},
535535
57 => static function ($self, $stackPos) {
536-
$self->semValue = new StringLiteral($self->semStack[$stackPos-(1-1)]->text, $self->semStack[$stackPos-(1-1)]->text, $self->locInfo($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
536+
$self->semValue = new StringLiteral($self->semStack[$stackPos-(1-1)], $self->semStack[$stackPos-(1-1)], $self->locInfo($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
537537
},
538538
58 => static function ($self, $stackPos) {
539-
$self->semValue = new NumberLiteral($self->semStack[$stackPos-(1-1)]->text + 0, $self->semStack[$stackPos-(1-1)]->text + 0, $self->locInfo($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
539+
$self->semValue = new NumberLiteral($self->semStack[$stackPos-(1-1)] + 0, $self->semStack[$stackPos-(1-1)] + 0, $self->locInfo($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
540540
},
541541
59 => static function ($self, $stackPos) {
542-
$self->semValue = new BooleanLiteral($self->semStack[$stackPos-(1-1)]->text === 'true', $self->semStack[$stackPos-(1-1)]->text === 'true', $self->locInfo($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
542+
$self->semValue = new BooleanLiteral($self->semStack[$stackPos-(1-1)] === 'true', $self->semStack[$stackPos-(1-1)] === 'true', $self->locInfo($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
543543
},
544544
60 => static function ($self, $stackPos) {
545545
$self->semValue = new UndefinedLiteral($self->locInfo($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
@@ -585,12 +585,12 @@ protected function initReduceCallbacks(): void {
585585
},
586586
67 => static function ($self, $stackPos) {
587587

588-
$self->semStack[$stackPos-(3-1)][] = new PathSegment($self->id($self->semStack[$stackPos-(3-3)]), $self->semStack[$stackPos-(3-3)]->text, $self->semStack[$stackPos-(3-2)]); $self->semValue = $self->semStack[$stackPos-(3-1)];
588+
$self->semStack[$stackPos-(3-1)][] = new PathSegment($self->id($self->semStack[$stackPos-(3-3)]), $self->semStack[$stackPos-(3-3)], $self->semStack[$stackPos-(3-2)]); $self->semValue = $self->semStack[$stackPos-(3-1)];
589589

590590
},
591591
68 => static function ($self, $stackPos) {
592592

593-
$self->semValue = [new PathSegment($self->id($self->semStack[$stackPos-(1-1)]), $self->semStack[$stackPos-(1-1)]->text, null)];
593+
$self->semValue = [new PathSegment($self->id($self->semStack[$stackPos-(1-1)]), $self->semStack[$stackPos-(1-1)], null)];
594594

595595
},
596596
];

src/ParserAbstract.php

Lines changed: 84 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,16 @@ public function __construct(Lexer $lexer)
146146
public function parse(string $code): Program
147147
{
148148
$this->tokens = $this->lexer->tokenize($code);
149+
$numTokens = count($this->tokens);
150+
151+
// add sentinel token
152+
if ($numTokens === 0) {
153+
$this->tokens[] = new Token('EOF', "\0", 0);
154+
} else {
155+
$lastToken = $this->tokens[$numTokens - 1];
156+
$this->tokens[] = new Token('EOF', "\0", $lastToken->line);
157+
}
158+
149159
$result = $this->doParse();
150160

151161
// Clear out some of the interior state, so we don't hold onto unnecessary
@@ -170,7 +180,7 @@ protected function doParse(): Program
170180
{
171181
// We start off with no lookahead-token
172182
$symbol = self::SYMBOL_NONE;
173-
$token = null;
183+
$tokenValue = null;
174184
$this->tokenPos = -1;
175185

176186
// Keep stack of start and end attributes
@@ -223,10 +233,11 @@ protected function doParse(): Program
223233
*/
224234
if ($action > 0) {
225235
/* shift */
236+
//$this->traceShift($symbol);
226237

227238
++$stackPos;
228239
$stateStack[$stackPos] = $state = $action;
229-
$this->semStack[$stackPos] = $token;
240+
$this->semStack[$stackPos] = $tokenValue;
230241
$this->tokenStartStack[$stackPos] = $this->tokenPos;
231242
$this->tokenEndStack[$stackPos] = $this->tokenPos;
232243
$symbol = self::SYMBOL_NONE;
@@ -252,10 +263,12 @@ protected function doParse(): Program
252263
for (;;) {
253264
if ($rule === 0) {
254265
/* accept */
266+
//$this->traceAccept();
255267
return $this->semValue;
256268
}
257269
if ($rule !== $this->unexpectedTokenRule) {
258270
/* reduce */
271+
//$this->traceReduce($rule);
259272

260273
$ruleLength = $this->ruleToLength[$rule];
261274
$callback = $this->reduceCallbacks[$rule];
@@ -310,8 +323,10 @@ protected function doParse(): Program
310323
throw new \Exception('Parse error: failed to recover from error');
311324
}
312325
$state = $stateStack[--$stackPos];
326+
//$this->tracePop($state);
313327
}
314328

329+
//$this->traceShift($this->errorSymbol);
315330
++$stackPos;
316331
$stateStack[$stackPos] = $state = ($action ?? 0);
317332

@@ -327,6 +342,7 @@ protected function doParse(): Program
327342
throw new \Exception('Parse error: reached EOF without recovering from error');
328343
}
329344

345+
//$this->traceDiscard($symbol);
330346
$symbol = self::SYMBOL_NONE;
331347
break 2;
332348
}
@@ -399,7 +415,7 @@ protected function getExpectedTokens(int $state): array {
399415
* @return array<string, int>
400416
*/
401417
protected function createTokenMap(): array {
402-
$tokenMap = [];
418+
$tokenMap = ['EOF' => 0]; // for sentinel token
403419

404420
foreach ($this->symbolToName as $name) {
405421
if ($name === 'EOF' || $name === 'error') {
@@ -422,6 +438,40 @@ protected function createTokenMap(): array {
422438
return $fullTokenMap;
423439
}
424440

441+
/*
442+
* Tracing functions used for debugging the parser.
443+
*/
444+
445+
/*
446+
protected function traceNewState($state, $symbol): void {
447+
echo '% State ' . $state
448+
. ', Lookahead ' . ($symbol == self::SYMBOL_NONE ? '--none--' : $this->symbolToName[$symbol]) . "\n";
449+
}
450+
451+
protected function traceRead($symbol): void {
452+
echo '% Reading ' . $this->symbolToName[$symbol] . "\n";
453+
}
454+
455+
protected function traceShift($symbol): void {
456+
echo '% Shift ' . $this->symbolToName[$symbol] . "\n";
457+
}
458+
459+
protected function traceAccept(): void {
460+
echo "% Accepted.\n";
461+
}
462+
463+
protected function traceReduce($n): void {
464+
echo '% Reduce by (' . $n . ') ' . $this->productions[$n] . "\n";
465+
}
466+
467+
protected function tracePop($state): void {
468+
echo '% Recovering, uncovered state ' . $state . "\n";
469+
}
470+
471+
protected function traceDiscard($symbol): void {
472+
echo '% Discard ' . $this->symbolToName[$symbol] . "\n";
473+
}*/
474+
425475
/*
426476
* Helper functions invoked by semantic actions
427477
* Based on https://github.com/handlebars-lang/handlebars-parser/blob/master/lib/helpers.js
@@ -447,31 +497,41 @@ protected function locInfo(int $tokenStartPos, int $tokenEndPos): SourceLocation
447497
return new SourceLocation($source, new Position($startToken->line, -1), new Position($endToken->line, -1));
448498
}
449499

450-
protected function id(Token $token): string
500+
protected function id(string $token): string
451501
{
452-
if (preg_match('/^\\[.*]$/', $token->text)) {
453-
return substr($token->text, 1, -1);
502+
if (preg_match('/^\\[.*]$/', $token)) {
503+
return substr($token, 1, -1);
454504
} else {
455-
return $token->text;
505+
return $token;
456506
}
457507
}
458508

459-
protected function stripFlags(Token $open, Token $close): StripFlags
509+
protected function stripFlags(string $open, string $close): StripFlags
460510
{
461-
return new StripFlags($open->text[2] === '~', $close->text[strlen($close->text) - 3] === '~');
511+
return new StripFlags($open[2] === '~', $close[strlen($close) - 3] === '~');
462512
}
463513

464-
protected function stripComment(Token $comment): string
514+
protected function stripComment(string $comment): string
465515
{
466-
$comment = preg_replace('/^\\{\\{~?!-?-?/', '', $comment->text);
516+
$comment = preg_replace('/^\\{\\{~?!-?-?/', '', $comment);
467517
return preg_replace('/-?-?~?}}$/', '', $comment);
468518
}
469519

470520
/**
471521
* @param Statement[] $statements
472522
*/
473-
protected function prepareProgram(array $statements, SourceLocation $loc): Program
523+
protected function prepareProgram(array $statements, ?SourceLocation $loc = null): Program
474524
{
525+
if (!$loc) {
526+
if ($statements) {
527+
$firstLoc = $statements[0]->loc;
528+
$lastLoc = $statements[count($statements) - 1]->loc;
529+
$loc = new SourceLocation($firstLoc->source, $firstLoc->start, $lastLoc->end);
530+
} else {
531+
$loc = new SourceLocation('', new Position(0, -1), new Position(0, -1));
532+
}
533+
}
534+
475535
return new Program($statements, [], $loc);
476536
}
477537

@@ -482,13 +542,13 @@ protected function prepareMustache(
482542
SubExpression | PathExpression | Literal $path,
483543
array $params,
484544
?Hash $hash,
485-
Token $open,
545+
string $open,
486546
StripFlags $strip,
487547
SourceLocation $loc,
488548
): MustacheStatement {
489-
$escapeFlag = $open->text[3] ?? $open->text[2];
549+
$escapeFlag = $open[3] ?? $open[2];
490550
$escaped = $escapeFlag !== '{' && $escapeFlag !== '&';
491-
$decorator = preg_match('/\\*/', $open->text);
551+
$decorator = preg_match('/\\*/', $open);
492552

493553
return new MustacheStatement(
494554
type: $decorator ? 'Decorator' : 'MustacheStatement',
@@ -501,9 +561,11 @@ protected function prepareMustache(
501561
);
502562
}
503563

504-
private function validateClose(OpenHelper $open, CloseBlock | Token $close): void
564+
private function validateClose(OpenHelper $open, CloseBlock | string $close): void
505565
{
506-
$close = $close instanceof CloseBlock ? $close->path->original : $close->text;
566+
if ($close instanceof CloseBlock) {
567+
$close = $close->path->original;
568+
}
507569

508570
if ($open->path->original !== $close) {
509571
throw new \Exception("{$open->path->original} doesn't match {$close}");
@@ -531,9 +593,9 @@ protected function preparePath(bool $data, ArrayLiteral | HashLiteral | SubExpre
531593
$isLiteral = $parts[$i]->original !== $part;
532594
$separator = $parts[$i]->separator;
533595

534-
$partPrefix = $separator?->text === '.#' ? '#' : '';
596+
$partPrefix = $separator === '.#' ? '#' : '';
535597

536-
$original .= ($separator ? $separator->text : '') . $part;
598+
$original .= ($separator ?? '') . $part;
537599

538600
if (!$isLiteral && ($part === '..' || $part === '.' || $part === 'this')) {
539601
if (count($tail) > 0) {
@@ -563,7 +625,7 @@ protected function preparePath(bool $data, ArrayLiteral | HashLiteral | SubExpre
563625
/**
564626
* @param ContentStatement[] $contents
565627
*/
566-
protected function prepareRawBlock(OpenHelper $openRawBlock, array $contents, Token $close, SourceLocation $loc): BlockStatement
628+
protected function prepareRawBlock(OpenHelper $openRawBlock, array $contents, string $close, SourceLocation $loc): BlockStatement
567629
{
568630
$this->validateClose($openRawBlock, $close);
569631
$program = new Program($contents, [], $loc);
@@ -595,7 +657,7 @@ protected function prepareBlock(
595657
$this->validateClose($openBlock, $close);
596658
}
597659

598-
$decorator = preg_match('/\\*/', $openBlock->open->text);
660+
$decorator = preg_match('/\\*/', $openBlock->open);
599661

600662
$program->blockParams = $openBlock->blockParams;
601663

@@ -635,7 +697,7 @@ protected function prepareBlock(
635697
inverse: $inverse,
636698
openStrip: $openBlock->strip,
637699
inverseStrip: $inverseStrip,
638-
closeStrip: $close->strip,
700+
closeStrip: $close?->strip,
639701
loc: $loc,
640702
);
641703
}

0 commit comments

Comments
 (0)