ircmaxell/php-yacc
PHP port of kmyacc: a YACC/LALR(1) parser generator. Feed it a YACC grammar plus a parser template to generate fast PHP parsers. Useful for building language/DSL parsers; generation is resource-heavy, but produced parsers run efficiently.
Install the Package:
composer require ircmaxell/php-yacc
Ensure your composer.json includes the package under require or require-dev based on your use case.
Prepare Grammar and Template Files:
grammar.y) defining your language syntax.parser.template) to define how the parser should be generated. Refer to the examples folder for templates like parser.template or parser_ast.template.Generate the Parser:
Use the phpyacc CLI tool to generate the parser from your grammar and template:
vendor/bin/phpyacc -o Parser.php grammar.y parser.template
This generates a Parser.php file containing the parser logic.
Integrate into Laravel:
Parser.php in app/Generated/ or a similar directory.First Use Case: Parse a simple input string:
require_once app_path('Generated/Parser.php');
$parser = new Parser();
$result = $parser->parse('your input string');
Offline Generation:
Semantic Actions:
statement:
ID '=' expression ';' { $$ = new AssignNode($1, $3); }
Error Handling:
Log::error()).if ($this->expecting != null) {
throw new ParseError("Unexpected token: " . $this->token, $this->line);
}
Laravel Integration:
// app/Providers/AppServiceProvider.php
public function register()
{
$this->app->singleton('parser', function () {
return new \Generated\Parser();
});
}
phpyacc execution in a custom Artisan command for developer convenience:
// app/Console/Commands/GenerateParser.php
public function handle()
{
$this->call('vendor:phpyacc', [
'grammar' => 'grammar.y',
'template' => 'parser.template',
'output' => 'app/Generated/Parser.php',
]);
}
AST Processing:
Collection, Eloquent models, or DTOs):
$ast = $parser->parse($input);
$collection = collect($ast->nodes)->map(function ($node) {
return (new YourModel())->fillFromNode($node);
});
Development Workflow:
vendor/bin/phpyacc -o app/Generated/Parser.php grammar.y parser.template
CI/CD Pipeline:
# .github/workflows/parser.yml
jobs:
generate-parser:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: composer install
- run: vendor/bin/phpyacc -o app/Generated/Parser.php grammar.y parser.template
- run: git add app/Generated/Parser.php && git commit -m "Regenerate parser"
Testing:
public function testParser()
{
$parser = new \Generated\Parser();
$result = $parser->parse('valid input');
$this->assertInstanceOf(\Generated\AstNode::class, $result);
}
Shift/Reduce Conflicts:
vendor/bin/phpyacc -v -o Parser.php grammar.y parser.template
Performance Overhead:
phpyacc during web requests or in production.Template Complexity:
examples/parser.template and modify incrementally.-n flag to reference semantic values by name in actions:
vendor/bin/phpyacc -n -o Parser.php grammar.y parser.template
PHP Version Compatibility:
strict_types=1 in generated parsers if targeting PHP 7.4+.Generated Code Stability:
phpyacc versions. Pin the package version and avoid updates unless necessary.Lexer Limitations:
symfony/flex) or write a custom lexer for complex tokenization needs.Verbose Output:
vendor/bin/phpyacc -v -o Parser.php grammar.y parser.template
stderr for Shift/Reduce conflicts or unexpected tokens.Semantic Action Debugging:
expression:
term { $$ = $1; echo "Term: " . $1; }
Token Inspection:
// In your parser template
$this->token = $lexer->getToken();
error_log("Token: " . $this->token);
Conflict Resolution:
%precedence or %left/%right directives in your grammar to resolve ambiguity:
%left '+' '-'
%left '*' '/'
Integration Testing:
public function testMalformedInput()
{
$this->expectException(\ParseError::class);
$parser->parse('invalid input');
}
Custom Templates:
// In parser.template
class Parser {
protected $container;
public function __construct($container = null) {
$this->container = $container;
}
}
AST Builders:
// In parser.template
class AssignNode {
public function toModel()
{
return (new YourModel())->fill(['key' => $this->key, 'value' => $this->value]);
}
}
Lexer Integration:
symfony/flex) by extending the parser template.Error Handling:
// In parser.template
public function error($message)
{
\Log::error($message);
throw new \ParseError($message);
}
Multi-Phase Parsing:
program:
statement_list { $$ = new ProgramNode($1); }
statement_list:
statement { $$ = [$1]; }
| statement_list statement { $$ = array_merge($1, [$2]); }
How can I help you explore Laravel packages today?