Example

This is a really simple example of a functional, tested grammar. They get much more complex.

StarterGrammar's Directives

Most grammars will have more directives & multiple traits to facilitate organization. This example only uses one trait.

<?php  
  
namespace Tlf\Lexer\Starter;  
  
trait OtherDirectives {  
  
    protected $_other_directives = [  
  
        'parenthesis'=>[  
            'start'=>[  
                'match'=>'(',  
                'ast.new'=>[  
                    '_type'=>'arglist',  
                    '_addto'=>'argsets',  
                ],  
                'buffer.clear',  
                'then :comma',  
                'then.pop :parenthesis.stop 1',  
            ],  
            'stop'=>[  
                'match'=>')',  
                'inherit :comma.start',  
                'ast.pop',   
            ]  
        ],  
        'comma'=>[  
            'start'=>[  
                'match'=>'/,$/', //any string starting with a `/` will be treated as regex  
                'rewind 1',  
                'this:trimBuffer',  
                'ast.push args',  
                'forward 1',  
                'buffer.clear',  
                'stop',  
            ]  
        ],  
  
    ];  
  
}  

StarterGrammar

See that directives are built during onGrammarAdded() from the traits.

<?php  
  
namespace Tlf\Lexer;  
  
/** An extremely simple grammar that builds sets of arglist from `(arg1,arg2,c) (list2_arg1,arg2)` */  
class StarterGrammar extends Grammar {  
  
    // use Starter\LanguageDirectives;  
    use Starter\OtherDirectives;  
  
    /** The actual array of directives, built during onGrammarAdded() */  
    protected $directives;  
  
    /** Defaults to 'startergrammar' */  
    public function getNamespace(){return 'starter';}  
  
    /** Combine the directives from traits */  
    public function buildDirectives(){  
        $this->directives = array_merge(  
            // $this->_language_directives,  
            $this->_other_directives,  
        );  
    }  
  
    public function onGrammarAdded(\Tlf\Lexer $lexer){  
        $this->buildDirectives();  
        /** The first directive(s) to listen for. We're looking for an opening `(` */  
        $lexer->addDirective($this->getDirectives(':parenthesis')['parenthesis']);  
        // you can add more directives  
    }  
  
    public function onLexerStart(\Tlf\Lexer $lexer,\Tlf\Lexer\Ast $ast,\Tlf\Lexer\Token $token){  
        // $lexer->addGrammar(new DocblockGrammar()); //if your language uses docblocks  
  
        /** Just an example of setting an empty namespace at the start, so that all files have a namespace, even if its empty. */  
        if ($ast->type=='file'){  
            $ast->set('namespace', '');  
        }  
    }  
  
    /** A method this grammar uses as an instruction to trim() the buffer */  
    public function trimBuffer(\Tlf\Lexer $lexer, \Tlf\Lexer\Ast $ast, \Tlf\Lexer\Token $token, \stdClass $directive, array $args){  
        $token->setBuffer(trim($token->buffer()));  
    }  
}