The phplrt is a set of tools for programming languages recognition. The library provides lexer, parser, grammar compiler, library for working with errors, text analysis and so on.
Phplrt is available as composer repository and can be installed using the following command in a root of your project:
composer require phplrt/phplrtThe grammar compiler only runs while you develop, so a typical project splits the dependency in two:
composer require phplrt/runtime # lexer, parser and sources
composer require phplrt/compiler --dev # reads grammars and generates codeMore detailed installation instructions are here.
A grammar says which words the text is made of, how they may be arranged, and what to build out of them. Here is one that adds numbers up:
// grammar.pp3
%skip T_WHITESPACE \s++
%token T_NUMBER \d++
%token T_PLUS \+
// Recognition starts from this rule
%pragma root Sum
Sum -> { return \is_array($children) ? \array_sum($children) : $children; }
: Number() (::T_PLUS:: Number())*
;
Number -> { return (int) $children->value; }
: <T_NUMBER>
;
<T_NUMBER> reads a token and keeps it, ::T_PLUS:: reads one and throws it
away, and * means "zero or more times". The -> blocks are reducers - PHP
that runs when the rule matches, turning what was read into a value.
The Quick Start builds a real configuration format step by step, and the grammar syntax is described in full.
Load the grammar and ask for a parser. Because the reducers above run as the rules match, what comes back is a number rather than a syntax tree:
<?php
use Phplrt\Compiler\Compiler;
use Phplrt\Source\FileSource;
use Phplrt\Source\StringSource;
$parser = new Compiler()
->load(FileSource::createFromPathname(__DIR__ . '/grammar.pp3'))
->getParser();
echo $parser->parse(StringSource::createFromString('2 + 2')); // 4
echo $parser->parse(StringSource::createFromString('1 + 2 + 3')); // 6There is also analyze(), which reports what it made of a source instead of
throwing - for validating without building, or for reading a source the
grammar is not meant to describe in full.
Anything that cannot be recognized points at the exact spot in the source:
use Phplrt\Parser\Exception\UnexpectedTokenException;
use Phplrt\Source\VirtualSource;
try {
$parser->parse(VirtualSource::createFromString('expr.txt', "1 + 2\n3 + + 4\n"));
} catch (UnexpectedTokenException $e) {
echo $e;
}error[UnexpectedTokenException]: Syntax error, unexpected "3" (T_NUMBER), T_PLUS expected
--> expr.txt:2:1
1 | 1 + 2
2 | 3 + + 4
| ^
3 |
Rendering the snippet is the job of phplrt/exception. It comes with
phplrt/phplrt; alongside the separate runtime packages it is a suggestion
rather than a requirement, so add it if you install them by hand.
Reading a grammar file costs time, and the grammar does not change between
requests. Once it is ready and tested, compile it into a PHP file and commit
that file - after which the phplrt/compiler dependency is no longer needed
(see https://phplrt.org/docs/guide/installation#which-packages-do-i-actually-ship).
vendor/bin/phplrt compile grammar.pp3 \
src/CalculatorParser.php \
--namespace='App\Calculator' \
--class=CalculatorParserThe same thing from PHP, if you would rather do it from a build script:
new Compiler()
->load(FileSource::createFromPathname(__DIR__ . '/grammar.pp3'))
->generate()
->withNamespaceName('App\Calculator')
->withClassName('CalculatorParser')
->save(__DIR__ . '/src/CalculatorParser.php');What you get is an ordinary class: the whole lexer is one regular expression, each rule is an array entry, and every reducer is a real method you can step through in a debugger.
namespace App\Calculator;
readonly class CalculatorParser extends \Phplrt\Parser\Parser
{
public const int T_WHITESPACE = 0;
public const int T_NUMBER = 1;
public const int T_PLUS = 2;
public function __construct()
{
parent::__construct(/* the whole grammar, inlined */);
}
private static function reduceNumber(\Phplrt\Parser\Context $ctx, mixed $children): mixed
{
return (int) $children->value;
}
}Use it like any other class - no compiler, no grammar file, no build step at runtime:
$parser = new App\Calculator\CalculatorParser();
echo $parser->parse(StringSource::createFromString('2 + 2')); // 4Every component is published on its own, so you can install only what you use.
| Package | What it does |
|---|---|
phplrt/source |
Reads source code from files, strings and streams |
phplrt/position |
Turns a byte offset into the line and column it points at |
phplrt/lexer |
Splits source code into tokens |
phplrt/parser |
Recognizes tokens against a grammar and builds a result |
phplrt/exception |
Renders errors with a snippet of the code around them |
phplrt/lexer-builder |
Describes a lexer in PHP and compiles it |
phplrt/parser-builder |
Describes a grammar in PHP and compiles it |
phplrt/compiler |
Reads *.pp2 or *.pp3 grammars and generates PHP code |
To maintain code quality, readability, and complete comprehension in production, the following LLM usage rules apply:
- Production code must be hand-written. LLMs should not be used to write production code. Code generated by LLMs is not accepted.
- LLM-generated comments are allowed. However, comments describing contracts (classes, methods, interfaces, etc.) must answer the question "WHAT is this?", not "HOW does it work?". Comments should describe the purpose, responsibility, or contract of the code, not its implementation details.
- LLM-generated tests are allowed. Each AI-generated test must treat the code under test as a "black box". Tests may rely on the public contract and observable behavior, but must not depend on or make assumptions about the internal implementation.
Phplrt is open-sourced software licensed under the MIT license.