Lexer2.php

<?php

namespace Tlf;

class Lexer2 {

    public Lexer2\Program $program;

    public Lexer2\Buffer $buffer;

    public function __construct(){
        $this->program = new \Tlf\Lexer2\Program();
        $this->program->addLanguage(new \Tlf\Lexer2\Language\StdLibLanguage());
    }

    /**
     * Add code to the Program using a generic Language with no StdLib.
     * (The Lexer's StdLib is already added to the Program)
     *
     * @param $parser_code string parser code
     * @param $namespace string the namespace of your code
     *
     * @return array ast of the parser_code. @see Tlf\Lexer2\Parser
     */
    public function addCode(string $parser_code, string $namespace): array {
        $generic_language = new \Tlf\Lexer2\Language\GenericLanguage($parser_code, $namespace);
        
        $this->program->addLanguage($generic_language);

        return $generic_language->get_directives();
    }

    /**
     * Add a language to the Program.
     *
     * @param $language
     */
    public function addLanguage(\Tlf\Lexer2\Language $language): array {
        $this->program->addLanguage($language);
        return $language->get_directives();
    }

    /**
     * Get an AST from input text, by executing loaded code.
     */
    public function parse(string $input_text): \Tlf\Lexer2\Ast {

        $this->buffer = new \Tlf\Lexer2\Buffer($input_text);

        $stdlib_main = new \Tlf\Lexer2\StdLib\Main($this->program);
        $stdlib_buffer = new \Tlf\Lexer2\StdLib\Buffer($this->program, $this->buffer);
        $stdlib_head = new \Tlf\Lexer2\StdLib\AstHead($this->program);

        $this->program->setObject('head', $stdlib_head);
        $this->program->setObject('buffer', $stdlib_buffer);
        $this->program->setObject('StdLib', $stdlib_main);
        $this->program->setObject('lexer', $this);

        $languages = [];
        foreach ($this->program->languages as $l_meta){
            $language = $l_meta['language'];
            $directives = $l_meta['initial_directives'];
            $languages[] = ['language'=>get_class($language), 'initial_directives'=>$directives];
        }

        $root_ast = new \Tlf\Lexer2\Ast('Program',
            ['input'=>$input_text,
            'input_type'=>'string',
            'context'=>[
                'git_origin_url' => shell_exec("git remote get-url origin"),
                'git_hash' => shell_exec("git rev-parse --short HEAD"),
                'languages'=> $languages,
                ]
            ],
        );
        $this->program->ast_stack[] = $root_ast;

        $this->program->will_execute_command_hooks[] = [$stdlib_buffer, 'store_state'];

        $this->program->initialize();

        $loop_count = 0;
        // terminal color codes from https://gitlab.com/taeluf/bash/cli/-/blob/v0.1/code/lib/colors.bash?ref_type=heads
        while ($this->buffer->next()){
            $this->print_loop_and_buffer_status($this->buffer, $loop_count++);

            $this->program->execute();
        }

        return $this->program->ast_stack[0];

    }

    /** print newlines as literal `\n` so they can be read in the terminal without actually causing a newline */
    protected function make_newlines_printable(string $dirty_text): string {
        return str_replace(["\r","\n"], ["\\r", "\\n"], $dirty_text);
    }
    public function print_loop_and_buffer_status(\Tlf\Lexer2\Buffer $buffer, int $loop_count){
            $color_off = "\033[0m";
            $color_black = "\033[1;30m";
            $bg_yellow = "\033[0;103m";

            echo "\n\n\033[42m$color_black ### Loop $loop_count ### $color_off";

            echo "\n  \033[4;32mBUFFER INFO$color_off  ";

            echo "\n   Add Char: $bg_yellow$color_black ".
                $this->make_newlines_printable(substr($buffer->buffer(),-1)." $color_off");
            echo "\n   Current Buffer: $bg_yellow$color_black"
                    . $this->make_newlines_printable(
                        $buffer->buffer()
                    ). $color_off;
            echo "\n   Buffer Length: ".strlen($buffer->buffer());
            $next_five = $this->make_newlines_printable(
                substr($buffer->remainder(),0,5)
            );
            
            if (($len = strlen($next_five)) < 5){
                $next_five .= "\033[41m". str_repeat(" ", 5-$len);
            }
            echo "\n   Next 5 Chars: \033[0;103m$color_black"
                .$next_five
                . "$color_off"
                ;
    }

}