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

Php Yacc Laravel Package

ircmaxell/php-yacc

PHP port of kmyacc: a YACC/LALR(1) parser generator. Feed it a YACC grammar plus a parser template to generate a PHP parser. Useful for building fast parsers for structured languages; generation is resource-heavy, runtime parsing is efficient.

View on GitHub
Deep Wiki
Context7
## Getting Started

1. **Installation**: Add to `composer.json` under `require-dev`:
   ```json
   "ircmaxell/php-yacc": "^0.0.7"

Run composer install. Verify with vendor/bin/phpyacc --version.

  1. First Grammar: Create app/Grammar/Simple.y with a minimal rule:

    %token NUMBER
    %%
    input: NUMBER { echo "Parsed: " . $1; }
    
  2. Template Setup: Copy vendor/ircmaxell/php-yacc/examples/php/template.php to app/Grammar/Template.php. Modify the parser_class and action_code sections to match your needs.

  3. Generate Parser: Run in a build script (e.g., php artisan generate:parser):

    vendor/bin/phpyacc -f app/Grammar/Simple.y -t app/Grammar/Template.php -o app/Generated/SimpleParser.php
    
  4. Autoload: Add the generated file to composer.json autoload:

    "autoload": {
        "files": ["app/Generated/SimpleParser.php"]
    }
    
  5. First Parse: Use the generated parser in a Laravel service:

    $parser = new \App\Generated\SimpleParser();
    $result = $parser->parse(explode(' ', '42'));
    

Key Files:

  • app/Grammar/*.y: Your YACC grammars.
  • app/Generated/*.php: Generated parser classes (commit these!).
  • app/Grammar/Template.php: Custom parser template.

Implementation Patterns

1. Build Pipeline Integration

  • Artisan Command: Create a custom command to generate parsers during deployment:
    // app/Console/Commands/GenerateParser.php
    public function handle() {
        $this->callSilently('vendor:publish', ['--tag' => 'php-yacc']);
        $output = Artisan::call('phpyacc', [
            '-f' => 'app/Grammar/MyGrammar.y',
            '-t' => 'app/Grammar/Template.php',
            '-o' => 'app/Generated/MyParser.php',
        ]);
        $this->info("Parser generated successfully.");
    }
    
  • CI/CD Hook: Trigger parser generation in GitHub Actions or GitLab CI before tests:
    # .github/workflows/parser.yml
    jobs:
      generate-parser:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
          - run: composer install
          - run: php artisan generate:parser
    

2. Lexer-Parser Synchronization

  • Token Alignment: Ensure your lexer emits tokens matching the grammar’s %token definitions. Use a shared Token class:
    // app/Services/Token.php
    class Token {
        const NUMBER = 1;
        const PLUS = 2;
        // ...
    }
    
  • Lexer Output: Return arrays with [token_type, value, line_number]:
    $lexer = new MyLexer($input);
    $tokens = [];
    while ($token = $lexer->next()) {
        $tokens[] = [$token->type, $token->value, $token->line];
    }
    $parser->parse($tokens);
    

3. AST Construction

  • Named Semantic Values: Use the -n flag to avoid magic $1, $2 references:
    %name expr
    expr: expr '+' expr { $$ = new AddNode($expr_1, $expr_2); }
    
  • Template Customization: Extend Template.php to include AST node classes:
    // app/Grammar/Template.php
    $parser_class .= "
        class AddNode implements NodeInterface {
            public function __construct(public NodeInterface \$left, public NodeInterface \$right) {}
        }
    ";
    

4. Multi-Grammar Projects

  • Shared Tokens: Use #if directives in grammars to support multiple variants:
    #if defined('PHP7')
    %token T_ARRAY
    #else
    %token T_ARRAY_HASH
    #endif
    
  • Parser Wrappers: Create a facade to switch grammars dynamically:
    // app/Facades/ParserFacade.php
    class ParserFacade {
        public static function parse(string $input, string $grammar = 'default') {
            $parserClass = "App\\Generated\\{$grammar}Parser";
            return (new $parserClass())->parse(explode(' ', $input));
        }
    }
    

5. Error Handling

  • Custom Error Messages: Override the parser’s error() method in your template:
    $parser_class .= "
        public function error(\$message) {
            throw new ParseError(\"Line \$this->line: \$message\");
        }
    ";
    
  • Recovery: Implement a sync() method to reset the parser state after errors:
    $parser_class .= "
        public function sync() {
            \$this->lex = \$this->lexer;
            \$this->token = null;
        }
    ";
    

Gotchas and Tips

1. CLI Instability

  • Flag Changes: The -v and -x flags broke in v0.0.4. Always pin to ~0.0.7 and test upgrades manually.
  • Debugging: Use phpyacc -v to generate conflict logs, but redirect stderr to a file:
    vendor/bin/phpyacc -v -f grammar.y 2> conflicts.log
    
  • Template Paths: Hardcode paths in Template.php if using relative paths:
    $parser_class .= "use App\\Generated\\Tokens;"
    

2. Grammar Pitfalls

  • Shift/Reduce Conflicts: The parser silently accepts ambiguous grammars. Use -v to expose warnings:
    warning: shift/reduce conflict on token NUMBER
    
    Fix: Add precedence rules manually (e.g., expr: expr '*' expr before expr: expr '+' expr).
  • Unused Tokens: Tokens not in the grammar are dropped by default. To preserve them, use the -p flag (undocumented; check phpyacc --help).

3. Template Quirks

  • Action Code Injection: Avoid PHP tags (<?php ?>) in the template’s action_code section. Use <?= for output:
    $action_code = "<?= \$action ?? '' ?>";
    
  • Class Name Collisions: The generated parser class name defaults to Parser. Override in Template.php:
    $parser_class = "class MyCustomParser extends Parser { ... }";
    

4. Performance Caveats

  • Runtime Overhead: The generated parser is fast, but generation is slow. Cache generated files and regenerate only when grammars change.
  • Memory Usage: Complex grammars may exhaust memory. Test with large inputs in a CI environment.

5. Missing Features

  • No %union Support: Workaround: Use named semantic values (-n) and type-check manually:
    %name expr
    expr: NUMBER { $$ = (int)\$expr_1; }
    
  • No %type: Annotate types in semantic actions:
    expr: NUMBER { return (int)\$1; }
    
  • No Mid-Rule Actions: All actions must be at the end of a rule. Use intermediate non-terminals:
    expr: NUMBER temp { $$ = process($temp_1); }
    temp: NUMBER { $$ = $1; }
    

6. Debugging Techniques

  • Token Dump: Log tokens before parsing:
    $tokens = explode(' ', $input);
    file_put_contents('debug.tokens', print_r($tokens, true));
    
  • Parser State: Inspect the parser’s internal state by adding debug hooks:
    $parser_class .= "
        public function debug() {
            return [
                'line' => \$this->line,
                'lookahead' => \$this->lookahead,
                'stack' => \$this->stack,
            ];
        }
    ";
    
  • Line Numbers: Ensure your lexer includes line numbers. The parser uses $this->line for error reporting.

7. Laravel-Specific Tips

  • Service Provider: Register the parser as a singleton:
    // app/Providers/ParserServiceProvider.php
    public function register() {
        $this->app->singleton('parser', function () {
            return new \App\Generated\SimpleParser();
        });
    }
    
  • Configuration: Store grammar paths in config/parser.php:
    return [
        'grammars' => [
            'default' => app_path('Grammar/Default.y'),
            'advanced' => app_path('Gr
    
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.
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope