Skip to content

Commit

Permalink
Add support for ATN tracing (#32)
Browse files Browse the repository at this point in the history
  • Loading branch information
marcospassos authored Nov 21, 2022
1 parent 64fd1e2 commit ac71eb3
Show file tree
Hide file tree
Showing 9 changed files with 257 additions and 25 deletions.
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
],
"require": {
"php": "^8.0",
"ext-mbstring": "*"
"ext-mbstring": "^8.0",
"psr/log": "^2.0 || ^3.0"
},
"require-dev": {
"ergebnis/composer-normalize": "^2.15",
Expand Down
81 changes: 66 additions & 15 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions phpcs.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,10 @@
<severity>5</severity>
</rule>

<rule ref="SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint">
<exclude-pattern>src/StdoutMessageLogger.php</exclude-pattern>
</rule>

<rule ref="SlevomatCodingStandard.Classes.SuperfluousAbstractClassNaming.SuperfluousPrefix">
<exclude-pattern>src/Atn/Transitions/AbstractPredicateTransition.php</exclude-pattern>
</rule>
Expand Down
8 changes: 4 additions & 4 deletions src/Atn/ATNConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ public function toString(bool $showAlt): string
$buf .= ',[' . $this->context . ']';
}

if ($this->semanticContext->equals(SemanticContext::none())) {
if (!$this->semanticContext->equals(SemanticContext::none())) {
$buf .= ',' . $this->semanticContext;
}

Expand All @@ -177,9 +177,9 @@ public function __toString(): string
$this->state,
$this->alt,
$this->context !== null ? ',[' . $this->context . ']' : '',
$this->semanticContext->equals(SemanticContext::none()) ?
',' . $this->semanticContext :
'',
$this->semanticContext->equals(SemanticContext::none())
? ''
: ',' . $this->semanticContext,
$this->reachesIntoOuterContext > 0 ? ',up=' . $this->reachesIntoOuterContext : '',
);
}
Expand Down
74 changes: 70 additions & 4 deletions src/Atn/ParserATNSimulator.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
use Antlr\Antlr4\Runtime\Error\Exceptions\RecognitionException;
use Antlr\Antlr4\Runtime\IntervalSet;
use Antlr\Antlr4\Runtime\IntStream;
use Antlr\Antlr4\Runtime\LoggerProvider;
use Antlr\Antlr4\Runtime\Parser;
use Antlr\Antlr4\Runtime\ParserRuleContext;
use Antlr\Antlr4\Runtime\PredictionContexts\PredictionContext;
Expand All @@ -35,6 +36,7 @@
use Antlr\Antlr4\Runtime\Utils\BitSet;
use Antlr\Antlr4\Runtime\Utils\DoubleKeyMap;
use Antlr\Antlr4\Runtime\Utils\Set;
use Psr\Log\LoggerInterface as Logger;

/**
* The embodiment of the adaptive LL(*), ALL(*), parsing strategy.
Expand Down Expand Up @@ -231,6 +233,8 @@
*/
final class ParserATNSimulator extends ATNSimulator
{
public static bool $traceAtnSimulation = false;

protected Parser $parser;

/** @var array<DFA> */
Expand Down Expand Up @@ -260,6 +264,8 @@ final class ParserATNSimulator extends ATNSimulator

protected ?DFA $dfa = null;

private Logger $logger;

/**
* @param array<DFA> $decisionToDFA
*/
Expand All @@ -273,6 +279,7 @@ public function __construct(

$this->parser = $parser;
$this->decisionToDFA = $decisionToDFA;
$this->logger = LoggerProvider::getLogger();
}

public function reset(): void
Expand All @@ -296,6 +303,18 @@ public function clearDFA(): void
*/
public function adaptivePredict(TokenStream $input, int $decision, ParserRuleContext $outerContext): int
{
if (self::$traceAtnSimulation) {
$this->logger->debug(
'adaptivePredict decision {decision} exec LA(1)=={token} line {line}:{pos}',
[
'decision' => $decision,
'token' => $this->getTokenName($input->LA(1)),
'line' => $input->LT(1)?->getLine(),
'pos' => $input->LT(1)?->getCharPositionInLine(),
],
);
}

$this->input = $input;
$this->startIndex = $input->getIndex();
$this->outerContext = $outerContext;
Expand Down Expand Up @@ -404,6 +423,19 @@ public function execATN(
int $startIndex,
ParserRuleContext $outerContext,
): ?int {
if (self::$traceAtnSimulation) {
$this->logger->debug(
'execATN decision {decision}, DFA state {state}, LA(1)=={token} line {line}:{pos}',
[
'decision' => $dfa->decision,
'state' => $s0->__toString(),
'token' => $this->getTokenName($input->LA(1)),
'line' => $input->LT(1)?->getLine(),
'pos' => $input->LT(1)?->getCharPositionInLine(),
],
);
}

$previousD = $s0;

$t = $input->LA(1);
Expand Down Expand Up @@ -655,6 +687,12 @@ protected function execATNWithFullContext(
int $startIndex,
ParserRuleContext $outerContext,
): int {
if (self::$traceAtnSimulation) {
$this->logger->debug('execATNWithFullContext {state}', [
'state' => $s0->__toString(),
]);
}

$fullCtx = true;
$foundExactAmbig = false;
$reach = null;
Expand Down Expand Up @@ -890,15 +928,18 @@ protected function computeReachSet(ATNConfigSet $closure, int $t, bool $fullCtx)
* multiple alternatives are viable.*/

if ($skippedStopStates !== null && (!$fullCtx || !PredictionMode::hasConfigInRuleStopState($reach))) {
if (\count($skippedStopStates) === 0) {
throw new \LogicException('Skipped stop states cannot be empty.');
}

foreach ($skippedStopStates as $lValue) {
$reach->add($lValue, $this->mergeCache);
}
}

if (self::$traceAtnSimulation) {
$this->logger->debug('computeReachSet {closure} -> {reach}', [
'closure' => $closure->__toString(),
'reach' => $reach->__toString(),
]);
}

if ($reach->isEmpty()) {
return null;
}
Expand Down Expand Up @@ -964,6 +1005,13 @@ protected function computeStartState(ATNState $p, RuleContext $ctx, bool $fullCt
$initialContext = PredictionContext::fromRuleContext($this->atn, $ctx);
$configs = new ATNConfigSet($fullCtx);

if (self::$traceAtnSimulation) {
$this->logger->debug('computeStartState from ATN state {state} initialContext={initialContext}', [
'state' => $p->__toString(),
'initialContext' => $initialContext->__toString(),
]);
}

foreach ($p->getTransitions() as $i => $t) {
$c = new ATNConfig(null, $t->target, $initialContext, null, $i + 1);
$closureBusy = new Set();
Expand Down Expand Up @@ -1464,6 +1512,12 @@ protected function closureCheckingStopState(
int $depth,
bool $treatEofAsEpsilon,
): void {
if (self::$traceAtnSimulation) {
$this->logger->debug('closure({config})', [
'config' => $config->toString(true),
]);
}

if ($config->state instanceof RuleStopState) {
// We hit rule end. If we have context info, use it run thru all possible stack tops in ctx
$context = $config->context;
Expand Down Expand Up @@ -2150,6 +2204,12 @@ protected function addDFAState(DFA $dfa, DFAState $D): DFAState
$existing = $dfa->states->get($D);

if ($existing instanceof DFAState) {
if (self::$traceAtnSimulation) {
$this->logger->debug('addDFAState {state} exists', [
'state' => $D->__toString(),
]);
}

return $existing;
}

Expand All @@ -2160,6 +2220,12 @@ protected function addDFAState(DFA $dfa, DFAState $D): DFAState
$D->configs->setReadonly(true);
}

if (self::$traceAtnSimulation) {
$this->logger->debug('addDFAState new {state}', [
'state' => $D->__toString(),
]);
}

$dfa->states->add($D);

return $D;
Expand Down
26 changes: 26 additions & 0 deletions src/LoggerProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace Antlr\Antlr4\Runtime;

use Psr\Log\LoggerInterface as PsrLogger;

final class LoggerProvider
{
private static ?PsrLogger $logger = null;

public static function setLogger(PsrLogger $logger): void
{
self::$logger = $logger;
}

public static function getLogger(): PsrLogger
{
if (self::$logger === null) {
self::$logger = new StdoutMessageLogger();
}

return self::$logger;
}
}
Loading

0 comments on commit ac71eb3

Please sign in to comment.