<?php
namespace Tlf\Lexer;
/**
* Version 2 attempt at making an easily extensible command system
*
*/
class Commands {
public function __construct(protected \Tlf\Lexer $lexer){
}
/**
*
*/
public function can_run(string $command_name): bool {
$clean_command_name = str_replace('.','_',$command_name);
$method_name = "run_".$clean_command_name;
//var_dump($method_name);
if (method_exists($this,$method_name))return true;
return false;
}
/**
* Execute a simple command thats defined inside our switch/case
*
* @param $command a string like `halt`, `match`, or one of many others
* @param $args an array of arguments for the command
* @param $directive the directive currently being executed
* @param $isn the instruction set name currently being executed on the directive. Like `start` or `stop`
* @return boolean true if the command execution was successful, false otherwise
*/
public function run(string $command, array $args, object $directive, string $isn, array &$directiveList) {
$clean_command_name = str_replace('.','_',$command);
$method_name = "run_".$clean_command_name;
if (!method_exists($this,$method_name)){
throw new \BadMethodCallException("Instruction '$command' (method $method_name) does not exist on '".get_class($this)."'.");
}
$this->$method_name($command, $args, $directive, $isn, $directiveList);
}
/**
*
* Move the current directive to the start of the named stack. This directive must already be in the named stack.
*
* @usage `stack.move_to_front unstarted` - may be 'started', 'match', or 'unstarted'
*
* @arg name_of_stack The name of the stack to move this directive to the front of.
*/
public function run_stack_move_to_front(string $command, array $args, object $directive, string $isn, array &$directiveList){
$stack_name = $args[0];
$directive_name = $directive->_name;
$source_directive = $directiveList[$stack_name][$directive_name];
unset($directiveList[$stack_name][$directive_name]);
$directiveList[$stack_name] = [$directive_name => $source_directive] + $directiveList[$stack_name];
}
/**
*
* Get the previous 'key', which MUST be an object, and set `object->property = value` on that object.
*
* @usage `previous.assign [key] [property] [value]`
* @usage `'previous.assign [key] [property] !' => '_object:method'`
*
* @arg1 key the key in the 'previous' key/value list
* @arg2 property the property name on the object you're retrieving from the previous stack.
* @arg3 the value to set to the object's property.
*/
public function run_previous_obj_set(string $command, array $args, object $directive, string $isn, array &$directiveList){
$key = $args[0];
$property = $args[1];
$value = $args[2];
$obj = $this->lexer->previous($key);
$obj->$property = $value;
}
/**
*
* Get the previous 'key', which MUST be an an AST object, and append value to the named array property. i.e. `object->property[] = value` on that object.
*
* @usage `previous.ast_push [key] [property] [value]`
* @usage `'previous.ast_push [key] [property] !' => '_object:method'`
*
* @arg1 key the key in the 'previous' key/value list
* @arg2 property the array property name on the object you're retrieving from the previous stack.
* @arg3 the value to set to the object's property.
*/
public function run_previous_ast_push(string $command, array $args, object $directive, string $isn, array &$directiveList){
$key = $args[0];
$property = $args[1];
$value = $args[2];
$obj = $this->lexer->previous($key);
//if (!isset($obj->$property))$obj->$property = [];
$obj->push($property, $value);
//$obj->$property = $obj->$property + [$value];
//var_dump($obj);
//exit;
}
/**
*
* Trim whitespace from around the buffer. Does not modify the token state, except for the actual buffer.
*
* @usage `buffer.trim`
*
*/
public function run_buffer_trim(string $command, array $args, object $directive, string $isn, array &$directiveList){
$token = $this->lexer->token;
$trimmed_buffer = trim($token->buffer());
$token->setBuffer($trimmed_buffer);
}
/**
*
* Add a directive to the current directive layer's 'started' list.
*
* @usage `directive.add_started grammar:directive_name`. Can provide overrides, same as then
*
*/
public function run_directive_add_started(string $command, array $args, object $directive, string $isn, array &$directiveList){
$targetDirectiveName = $args[0];
$overrides = $args[1];
if (!is_array($overrides))$overrides = [];
// $grammar = $this->grammars[$directive->_grammar];
$grammar = $directive->_grammar;
$directives = $grammar->getDirectives($targetDirectiveName, $overrides);
foreach ($directives as $d){
$this->lexer->addDirective($d, true);
}
}
/**
*
*/
public function run_buffer_match_next(string $command, array $args, object $directive, string $isn, array &$directiveList){
var_dump("buffer match next");
exit;
}
/**
* Halts a directive if the next characters match.
*
* @usage `'buffer.not_match_next [num_chars]' => '/regex/'`.
*
* @arg int num_chars
* @arg string regex pattern
*/
public function run_buffer_not_match_next(string $command, array $args, object $directive, string $isn, array &$directiveList){
//var_dump("buffer not match next");
//exit;
$num_chars = (int) $args[0];
$pattern = (string) $args[1];
$token = $this->lexer->getToken();
$remainder = $token->remainder();
$chars = substr($remainder,0,$num_chars);
if (preg_match($pattern, $chars)){
$this->lexer->haltInstructions();
return;
}
}
/**
*
* Halts a directive if the current buffer matches
*
* 'not_match' as in 'this must not match'
*
* @usage `not_match /regex/`.
*
* @arg pattern a regex pattern
*
*/
public function run_not_match(string $command, array $args, object $directive, string $isn, array &$directiveList){
$token = $this->lexer->getToken();
$pattern = $args[0];
if (preg_match($pattern, $token->buffer())){
$this->lexer->haltInstructions();
return;
}
}
}