A parser combinator library for PHP
Simple to use & extend • Fast & lightweight • Reliable
PPC stands for PHP Parser Combinator. What an obvious name for such a library!
As its name tells us, PPC is just another parser combinator library with a clear goal: make writing efficient parsers a breeze. Writing parser with PPC does not require you to know how parser combinators works internally nor it requires you to learn a complex object-oriented API.
PPC is a set of functions which you will love to compose to build complex parsers! Parsers are plain closures: they never copy the input, and backtracking costs nothing.
PPC requires PHP 8.5 and the Multibyte String (mbstring)
extension.
composer require "jubianchi/ppc" "dev-master"Here is a quick example demonstrating how easy it is to write a parser:
<?php
use jubianchi\PPC\SyntaxError;
use function jubianchi\PPC\{char, enclosed, eos, map, optional, parse, regex, separated, seq, terminated};
$digit = regex('/[0-9]/') |> map(intval(...));
$separator = seq(char(','), char(' ') |> optional());
$list = terminated(enclosed(char('['), separated($separator, $digit), char(']')), eos());
assert(parse($list, '[0, 1, 2]') === [0, 1, 2]);
try {
parse($list, '[0, 1 2]');
} catch (SyntaxError $error) {
assert($error->getMessage() === 'Expected "," or "]", got " " at line 1, column 6');
}Easy, right? Be sure to read the documentation to understand how it works.
PPC has four kinds of functions, each with a single job:
| Kind | Job | Examples |
|---|---|---|
| Parsers | read the input | char(), word(), regex(), eos() |
| Combinators | assemble parsers into a new parser, without reading the input themselves | seq(), alt(), many(), separated() |
| Modifiers | transform the result of a parser, without reading more input; always applied with the pipe operator | |> map(), |> label() |
| Runners | run a parser on an input and give back its value, or the syntax error | parse(), stream(), tryParse(), tryStream() |
Any combination of parsers, combinators and modifiers is a parser, which can be combined again. And reading a definition tells what it does: functions taking parsers build the grammar, and the pipe operator never reads more input.
See CONTRIBUTING.md for the detailed rules.