Example
This is a really simple example of a functional, tested grammar. They get much more complex.
- Get further grammar-writing instructions in doc/GrammarWriting.md
- Look at the commands available in doc/GrammarCommands.md
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()));
}
}