MappedMethods.php

<?php

namespace Tlf\LexerTrait;

trait MappedMethods {


    public function getCmdMethodMap(){
        return 
        //@export_start(Commands.MethodMap)
        [
            'directive.then'=>'cmdThen',
            'then'=>'cmdThen',
            'then.pop'=>'cmdThenPop',

            'match'=>'cmdMatch',
            'buffer.match'=>'cmdMatch',

            'buffer.notin'=>'cmdBuffer_notin',

            'ast.new'=>'cmdAst_new',
        ];
        //@export_end(Commands.MethodMap)
    }

    public function cmdMatch($args, $directive, $isn, &$directiveList){
        $token = $this->token;
        $debug = $this->debug;

        // echo "\n\n\n\n\n\n---------\n\n";
        // $directive = (object)(array)$directive;
        // unset($directive->_grammar);
        // var_dump($directive);
        // echo "\n\n\n----\n\n";
        // var_dump($args);
        
        if (is_string($args[0])){
            $args[0] = [$args[0]];
        }

        // echo "\n\n\n";

        $matches = $this->doesATargetPass($directive, $args[0], $token->buffer());
        $token->setMatch($matches);
        // print_r($matches);
        if ($matches!==false){
            // $passed[$isn][$directive->_name] = $directive;
            $directive->$isn['_matches'] = $matches;
            if ($isn=='start'){
                $this->directiveStarted($directive, $directiveList);
            } else if ($isn=='stop'){
                $this->directiveStopped($directive, $directiveList);
            }
        } else {
            $this->haltInstructions();
        }
        if ($debug){
            if ($matches===false){
                echo '  [[fail]]';
            } else {
                echo "\033[42m".'[[pass]]'."\033[0m";
            }
        }
    }

    /**
     * Add a directive to the stack & immediately pop the directive layer when it is matched (instead of the directive's normal functioning).
     *
     * @example then.pop :directive_name.stop 2 //this will pop 2 directives when directive_name.stop matches.
     * @experimental there's an automatic rewind component of this feature & that will likely change. Currently, I'm rewinding by the lenght of $matches[1] (the first capture group). 
     */
    public function cmdThenPop($args, $directive, $isn, &$directiveList){
        if ($this->last_then_loop<$this->loop_count){
            $this->newDirectivesLayer();
        }
        $this->last_then_loop = $this->loop_count;

        $targetDirectiveName = $args[0];
        $popAmount = $args[1];
        $rewindLen = strlen($directive->start['_matches'][1]);
        $overrides = [
            'start'=>[
                'rewind'=>$rewindLen,
                'stop',
                'directive.pop'=>$popAmount,
                'halt',
            ],
        ];

        $grammar = $directive->_grammar;


        $directives = $grammar->getDirectives($targetDirectiveName, $overrides);
        foreach ($directives as $d){
            $this->addDirective($d);
        }
    }

    /**
     * Add a directive to the directive stack
     *
     * @example then :directive_name
     */
    public function cmdThen($args, $directive, $isn, &$directiveList){
        if ($this->last_then_loop<$this->loop_count){
            $this->newDirectivesLayer();
        }
        $this->last_then_loop = $this->loop_count;

        // echo "\n\n--";
        // print_r($args);
        // echo "\n\n";
        // exit;
        $targetDirectiveName = $args[0];
        $overrides = $args[1];


        // $grammar = $this->grammars[$directive->_grammar];
        $grammar = $directive->_grammar;


        $directives = $grammar->getDirectives($targetDirectiveName, $overrides);
        foreach ($directives as $d){
            $this->addDirective($d);
        }
    }



    /**
     * Checks if the current buffer matches any strings in `$grammar->notin['arg1']` and invalidates a match if the buffer matches
     *
     * @param $args the list of arguments
     * 
     * @example `notin keyword` checks `$grammar->notin
     * @arg the group of words to check in
     */
    public function cmdBuffer_notin($args, $directive, $isn, &$directiveList){

        $token = $this->token;

        $type = $args[0] ?? false;
        $list = $directive->_grammar->notin[$type] ?? [];
        if (in_array($token->buffer(), $list)){
            echo "\n     - '".$token->buffer()."' is a $type, so match fails"; 
            $this->haltInstructions();   
            // unset($passed[$isn][$directive->_name]);
            unset($directive->$isn['_matches']);
            if ($isn=='start'){
                $this->directiveStopped($directive, $list);
            } else if ($isn=='stop'){
                $this->directiveStarted($directive, $list);
            }
            $token->clearBuffer();
        }
    }


    public function cmdAst_new($args, $directive, $isn, &$list){
        $info = $args[0];
        $type = $info['type'] ?? $info['_type'];
        $type = $this->executeMethodString($type);
        if (isset($info['_class'])){
            $astClass = $info['_class'];
        } else {
            // $ownGrammar = $this->grammars[$directive->_grammar];
            $ownGrammar = $directive->_grammar;
            $astClass = $ownGrammar->getAstClass($directive);
        }
        $ast = new $astClass($type);
        if (isset($info['_setto'])){
            $_setto = $this->executeMethodString($info['_setto']);
            $this->getHead()->set($_setto, $ast);
            unset($info['_setto']);
        } else if (isset($info['_addto'])){
            if ($info['_addto']!==false){
                $_addto = $this->executeMethodString($info['_addto']);
                $this->getHead()->add($_addto, $ast);
            }
            unset($info['_addto']);
        } else {
            $this->getHead()->add($type, $ast);
        }

        if (isset($info['_setPrevious']) && $info['_setPrevious'] != false){
            $this->setPrevious($info['_setPrevious'], $ast);
        }


        foreach ($info as $key=>$value){
            if (is_string($value)){
                $realValue = $this->executeMethodString($value);
            } else $realValue=$value;
            $info[$key] = $realValue;
        }

        $setHead = isset($info['_setHead']) ? $info['_setHead'] : true;
        unset($info['_type'], $info['_setPrevious'], $info['_setHead']);
        $ast->setAll($info);

        if ($setHead){
            $this->setHead($ast);
        }

    }
}