Operations.php

<?php

namespace Tlf\Lexer\PhpNew;

trait Operations {

    // public function op_none(){return false;}
    public function operation_match($lexer, $ast, $token, $directive){
        $buff = $token->buffer();
        $next = $token->remainder()[0] ?? ' ';
        $buffNext = $buff . $next;
        $buffNextNext = $buff . $next . ($token->remainder()[1] ?? ' ');
        $ops = $this->get_operations();

        // does $buff + next == an operation? Then we stop and match fails
        // Does $buff == an operation? then we stop and match succeeds
        if (isset($ops[$buffNext])
            ||isset($ops[$buffNextNext])
            ||!isset($ops[$buff])
            ||$ops[$buff]=='none'){
            $lexer->haltInstructions();
            return;
        }
    }

    public function get_operations(){

        $map = [

            // (i think) 'none' disables default operation handling 
            // this is good if it has it's own directive
            //
            '/*'=>'none',
            '//'=>'none',
            '?>'=>'none', // without this, the `?` gets handled & php_stop never does
            '<<<' => 'none', // has a directive in StringDirectives

            // '&&'=>'none',
            // '||'=>'none',
            '&&'=>'and',
            '||'=>'or',

            //comparison
            '!='=>'does not equal',
            '!=='=>'strictly does not equal',
            '=>'=>'array_keyed',
            '>='=>'is_lt_equal',
            '>='=>'is_gt_equal',
            '=='=>'is_equal',
            '>'=>'is_gt',
            '<'=>'is_lt',
            
            //math
            '+'=>'math',
            '-'=>'math',
            '*'=>'math',
            '**'=>'math',
            '/'=>'math',

            //other
            '='=>'assign',
            ';'=>'terminate',
            ':'=>'return_type',
            '$'=>'var',
            ','=>'comma',
            '.'=>'concat',
            '&'=>'reference',
            '->'=>'method_call',
            '::'=>'static_call',
            '!'=>'not',
            '|'=>'binary_thing',
            '++'=>'increment',
            '--'=>'decrement',
            '??'=>'null coalesce',
            '?'=>'nullable type',
            '@'=>'suppress error',

            //scope
            '{'=>'block_start',
            '}'=>'block_end',
            '('=>'arglist_open',
            ')'=>'arglist_close',
            '['=>'array_open',
            ']'=>'array_close',

        ];

        return $map;
    }


    public function op_array_open($lexer, $ast, $xpn){
        $array = new \Tlf\Lexer\StringAst('array');
        $ast->set('value', $array);
        // $array->abc = 'okay';
        $xpn->push('declaration', '[');
        $xpn->push('words','[');
        $lexer->setHead($array);
    }
    public function op_array_close($lexer, $ast, $xpn){
        $lexer->popHead();
        $xpn->push('words',']');
        $xpn->push('declaration', ']');
    }

    public function op_array_keyed($lexer, $ast, $xpn){
        $xpn->push('words', '=>');
        $xpn->push('declaration', '=>');
    }

    public function op_math($lexer, $ast, $xpn){
        $xpn->push('words', $lexer->getToken()->buffer());
        $xpn->push('declaration', $lexer->getToken()->buffer());
    }

    public function op_reference($lexer, $ast, $xpn){
        if ($ast->type == 'method'){
            $xpn->push('declaration', '&');
            $xpn->push('words', '&');
            $ast->return_by_reference = true;
            $lexer->getToken()->clearBuffer();
        }
    }

    public function op_var($lexer, $ast, $xpn){
        $xpn->push('declaration','$');

        //how do I know if it is a(n):
        // - argument
            // if I'm inside a method/function, its an argument
        // - property
            // if i'm inside a class ast, then its a property
        // - variable
            // If i'm anywhere else, its a variable
        // - Other cases? 
            // like inside a use() statement for an anonymous function


        $head = $lexer->getHead();
        if ($head->type=='class_body'){
            $prop = new \Tlf\Lexer\Ast('property');
            $prop->modifiers = $xpn->words;
            $this->docblock($prop);
            $lexer->setHead($prop);
            $head->push('properties', $prop);
            $prop->name = new \Tlf\Lexer\StringAst('property_name');
            $lexer->setHead($prop->name);
        } else if ($head->type=='method_arglist'){
            $prop = new \Tlf\Lexer\Ast('arg');
            if (count($xpn->words??[])>0){
                $prop->arg_types = $xpn->words;
            }
            $xpn->words = [];
            $this->docblock($prop);
            $head->push('value', $prop);
            $lexer->setHead($prop);

            $propName = new \Tlf\Lexer\StringAst('arg_name');
            $lexer->setHead($propName);
            $prop->set('name', $propName);
        }
    }

    public function op_comma($lexer, $ast, $xpn){
        $head = $lexer->getHead();
        if ($head->type == 'arg'){
            $this->setArgDeclaration($ast);
        } else if ($head->type=='var_assign'){
            $head->value = implode('',$xpn->words);
            $lexer->popHead();
            $this->setArgDeclaration($lexer->getHead());
            $lexer->popHead();
        } else if ($head->type=='array'){
            $xpn->push('words', ',');
        }
        $xpn->push('declaration', ',');
    }
    public function op_concat($lexer, $ast, $xpn){
        $xpn->push('words', '.');
        $xpn->push('declaration', '.');
    }

    public function op_terminate($lexer, $ast, $xpn){

            // $head->declaration = trim(implode('', $xpn->declaration).';');
            $declaration = trim(implode('', $xpn->declaration??[]).';');
            if ($ast->type=='var_assign'){
                $ast->value = implode('',$xpn->words);
                $lexer->popHead();
                $lexer->getHead()->declaration = $declaration;
                $lexer->popHead();
            } else if ($ast->type == 'property'){
                $ast->declaration = $declaration;
                $lexer->popHead();
            } else if ($ast->type == 'namespace' || $ast->type=='use_trait'){
                $ast->set('name', implode('',$xpn->words));
                $ast->declaration = $declaration;
                if ($ast->type=='use_trait')$lexer->popHead(); 
            } 

            // $newXpn = new \Tlf\Lexer\Ast('expression');
            // $lexer->setPrevious('xpn', $newXpn);
            $xpn->declaration = [];
            $xpn->words = [];
    }

    public function op_block_start($lexer, $ast, $xpn){


    
        // echo ("\n\n\nAST TYPE({): ".$ast->type."\n\n\n");
        $ast = $lexer->getHead();


        if ($ast->type == 'return_types'){
            $lexer->popHead();
            $ast = $lexer->getHead();
        }

        if ($ast->type == 'method' || $ast->type=='function'){
            // $body = new \Tlf\Lexer\ArrayAst('method_body');
            $body = new \Tlf\Lexer\StringAst('method_body');
            $lexer->setHead($body);
            $ast->set('body', $body);
            $ast->declaration = trim(implode('', $xpn->declaration));
            $xpn->declaration = [];
            $xpn->words = [];
            $lexer->setPrevious('method_start', $lexer->token->index+1);
            // $index = $lexer->token->index+1;
// echo "\n\n\n-----------\n\n";
            // var_dump($lexer->token->source);
// echo "\n\n\n-----------\n\n";
            // var_dump($index);
            // exit;
        } else if ($ast->type=='class' || $ast->type=='class_implements' || $ast->type == 'trait'){
            // echo 'okay, now stop';
            // exit;
            $body = new \Tlf\Lexer\Ast('class_body');
            $lexer->setHead($body);
            $ast->addPassthrough($body);
            $ast->declaration = trim(implode('',$xpn->declaration));
            $xpn->declaration = [];
            $xpn->words = [];
        } else if ($ast->type=='method_body' || $ast->type == 'block_body'){
            $body = new \Tlf\Lexer\ArrayAst('block_body');
            $lexer->setHead($body);
        }


    }
    public function op_block_end($lexer, $ast, $xpn){
        // echo ("\n\n\nAST TYPE(}): ".$ast->type."\n\n\n");
        if ($ast->type == 'method_body'){
            $start = $lexer->unsetPrevious('method_start');
            $end = $lexer->token->index - $start; 
            $body = substr($lexer->token->source,$start,$end);
            $body = \Tlf\Lexer\Utility::trim_indents($body);
            $body = \Tlf\Lexer\Utility::trim_trailing_whitespace($body);
            $ast->value = $body;
            $lexer->popHead();
            $lexer->popHead();
        } else if ($ast->type=='class_body'){
            $lexer->popHead();
            $lexer->popHead();
        } else if ($ast->type=='block_body'){
            // $lexer->popHead();
            $lexer->popHead();
            $lexer->setPrevious('xpn', new \Tlf\Lexer\Ast('expression'));
            // var_dump($xpn);
            // exit;
        } 
        // else if ($ast->type=='function'){
        //     $xpn->declaration = [];
        //     $xpn->last_word = null;
        //     $xpn->words = [];
        //     // var_dump($xpn);
        //     // exit;
        //     $lexer->popHead();
        // }
        // else {
            // echo 'else block end';
            // exit;
        // }

    }

    public function op_assign($lexer, $ast, $xpn){
        $ast = $lexer->getHead();
        if ($ast->type!='arg'&&$ast->type!='property' && $ast->type!='var'
            &&$ast->type!='const'
            || isset($ast->value))return;


        $xpn->push('declaration', '=');
        $xpn->set('words',[]);
        $var_assign = new \Tlf\Lexer\StringAst('var_assign');
        $lexer->setHead($var_assign);
        $ast->set('value', $var_assign);
    }

    public function op_arglist_open($lexer, $ast, $xpn){
        if ($ast->type!='method'){
            $xpn->parenthesisCount = $xpn->parenthesisCount + 1;
            $xpn->push('declaration', '(');
            $xpn->push('words', '(');
            return;
        }
        $arglist = new \Tlf\Lexer\ArrayAst('method_arglist');
        $ast->set('args', $arglist);
        $xpn->push('declaration', '(');
        $xpn->words = [];
        $lexer->setHead($arglist);
    }

    public function op_arglist_close($lexer, $ast, $xpn) {

        if ($xpn->parenthesisCount > 0){
            $xpn->parenthesisCount = $xpn->parenthesisCount - 1;
            $xpn->push('declaration', ')');
            $xpn->push('words', ')');
            // if ($xpn->parenthesisCount == 0){
                // var_dump($xpn->getTree());
                // exit;
            // }
            return;
        }


        $head = $lexer->getHead();
        if ($head->type == 'method_arglist'){
            $lexer->popHead();
        } else if ($head->type == 'arg'){
            $this->setArgDeclaration($ast);
            $lexer->popHead();
        } else if ($head->type == 'var_assign'){
            $head->value = implode('', $xpn->words);
            $lexer->popHead();
            $this->setArgDeclaration($lexer->getHead());
            $lexer->popHead();
        }

        $xpn->push('declaration', ')');
    }

    public function op_return_type($lexer, $ast, $xpn){
        if ($lexer->getHead()->type=='method' || $lexer->getHead()->type=='function'){
            $xpn->push('declaration',':');
            $ast = new \Tlf\Lexer\ArrayAst('return_types');
            $lexer->getHead()->set('return_types', $ast);
            $lexer->setHead($ast);
        }
    }
}