src/Code/AstProvider.php

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
<?php

declare(strict_types=1);

namespace Smuuf\Primi\Code;

use \Smuuf\StrictObject;
use \Smuuf\Primi\Ex\SyntaxError;
use \Smuuf\Primi\Ex\InternalSyntaxError;
use \Smuuf\Primi\Parser\ParserHandler;
use \Smuuf\Primi\Handlers\KnownHandlers;

class AstProvider {

    use StrictObject;

    /** Path to temporary directory for storing cached AST files. */
    private ?string $tempDir;

    public function __construct(?string $tempDir = \null) {

        // $tempDir is taken from Config object - that means we're sure
        // it already is an existing directory - or null.
        $this->tempDir = $tempDir;

    }

    public function getAst(Source $source, bool $caching = \true): Ast {

        if (!$caching) {
            return new Ast(self::parseSource($source));
        }

        $key = \json_encode([
            KnownHandlers::getStateId(),
            $source->getSourceCode(),
        ]);

        if ($ast = $this->loadFromCache($key)) {
            return new Ast($ast);
        }

        $ast = self::parseSource($source);

        // Store/cache parsed AST, if caching is enabled.
        $this->storeIntoCache($key, $ast);

        return new Ast($ast);

    }

    /**
     * @return TypeDef_AstNode|null
     */
    private function loadFromCache(string $key): ?array {

        if ($this->tempDir === \null) {
            return \null;
        }

        $path = self::buildCachedPath($key, $this->tempDir);
        if (\is_file($path)) {
            return \json_decode(file_get_contents($path), \true);
        }

        return \null;

    }

    /**
     * @param TypeDef_AstNode $ast
     */
    private function storeIntoCache(string $key, array $ast): void {

        if ($this->tempDir === \null) {
            return;
        }

        $path = self::buildCachedPath($key, $this->tempDir);
        file_put_contents($path, \json_encode($ast));

    }

    /**
     * @return TypeDef_AstNode
     * @throws SyntaxError
     */
    private static function parseSource(Source $source): array {

        $sourceString = $source->getSourceCode();

        try {
            return (new ParserHandler($sourceString))->run();
        } catch (InternalSyntaxError $e) {
            throw SyntaxError::fromInternal($e, $source);
        }

    }

    private static function buildCachedPath(string $key, string $dir): string {
        return \sprintf('%s/ast_cache_%s.json', $dir, \md5($key));
    }

}