Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Parsica Laravel Package

parsica-php/parsica

Parsica is a PHP parser combinator library for building custom parsers from small reusable pieces. Compose complex grammars with a fluent API, parse strings into structured results, and handle errors cleanly—ideal for DSLs, config formats, and language tooling.

View on GitHub
Deep Wiki
Context7

title: Mapping to Objects

Parser types

Most of the parsers that come with Parsica, return strings as outputs.

<?php
$parser = digitChar();
assertInstanceOf('Parsica\Parsica\Parser', $parser);

$result = $parser->tryString('1');
assertIsString('Parsica\Parsica\StringStream', $result->output());
assertEquals('1', $result->output());

In PHP 7.x, the type of $parser is Parser, but you can think of it having the type Parser<string>. PHP doesn't support generics, so it doesn't enforce that. However, working with Parsica is easier if you always think of parsers having an inner type.

Parser<T> means that if we successfully run the parser on an input, it will output a value of type T.

Here's an example of a parser of type Parser<array<string>>:

<?php
$parser = sepBy(char(','), atLeastOne(digitChar()));
$result = $parser->tryString('123,9,55');
assertEquals(["123", "9", "55"], $result->output());

The map combinator

The point of parsing to turn strings into more useful data structures. The combinator map can help you with that. It does the same thing as PHP's array_map function. You combine a parser and a callable, and you get a new parser. This new parser will apply the callable to the output of the parser.

We can use it for manipulating the output. Here's a simple example:

<?php
$parser = atLeastOne(alphaChar())
    ->map(fn(string $val) => strtolower($val));
$result = $parser->tryString('PaRsIcA');
assertEquals("parsica", $result->output());

If the parser fails, the callable is not applied to the output (because there is no output). So you don't need to worry about error handling.

Casting to scalars

We can now use this to cast the parser's output to scalars:

<?php
$parser = atLeastOne(digitChar())
    ->map(fn(string $val) => intval($val));
$result = $parser->tryString("123"); // input is still a string
assertSame(123, $result->output()); // output is an int

It also works inside nested parsers. We can use this on the sepBy example from above:

<?php
$parser = sepBy(
    char(','), 
    atLeastOne(digitChar())
        ->map(fn($val) => intval($val))
);
$result = $parser->tryString('123,9,55');
assertSame([123, 9, 55], $result->output()); // array of ints

The type of this last parser is now Parser<array<int>> instead of the original Parser<array<string>>.

Casting to objects

We'll want to cast to much more interesting data structures than scalars and arrays. Let's parse some monetary values into a nested value object structure. Money is composed of an integer value and a Currency value object:



final class Currency
{
    private string $currency;

    function __construct(string $currency)
    {
        $this->currency = $currency;
    }
}

// Side warning: don't actually use floats to do computations with money.
final class Money
{
    private float $amount;
    private Currency $currency;

    function __construct(float $amount, Currency $currency)
    {
        $this->amount = $amount;
        $this->currency = $currency;
    }
}

// $currency is a parser of type Parser<Currency>
$currency = repeat(3, upperChar())
    ->map(fn(string $c) => new Currency($c));

// $amount has type Parser<float>
$amount = float()
    ->map(fn(string $val) => floatval($val));

// $money has type Parser<[Currency, float]) because collect() has type Parser<[T]>
$money = collect($currency, skipHSpace()->followedBy($amount));

// Let's change $money to type Parser<Money>
$money = $money->map(fn(array $a) => new Money($a[1], $a[0]));

$result = $money->tryString('EUR 12.34');
assertEquals(new Money(12.34, new Currency('EUR')), $result->output());

// We can now composer our Parser<Money> in larger parsers
// $pricelist has type Parser<array<Money>>
$priceList = collect(
    string("exVAT ")->followedBy($money)->thenIgnore(whitespace()),
    string("incVAT ")->followedBy($money)
);
$result = $priceList->tryString('exVAT EUR 100.00 incVAT EUR 121.00');

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport
twbs/bootstrap4
php-http/client-implementation
phpcr/phpcr-implementation
cucumber/gherkin-monorepo
haydenpierce/class-finder
psr/simple-cache-implementation