PhpGrammar.php

<?php

namespace Tlf\Lexer\Test;


class PhpGrammar extends Tester {

    use Directives\Props;
    use Directives\Args;
    use Directives\Methods;
    use Directives\Classes;
    use Directives\ClassIntegration;
    use Directives\Traits;
    use Directives\Namespaces;
    use Directives\UseTrait;
    use Directives\PhpOpenTags;
    use Directives\Consts;
    use Directives\Values;

    protected $directive_tests;

    public function prepare(){
        $this->directive_tests = array_merge(
            $this->_prop_tests, 
            $this->_arg_tests,
            $this->_namespace_tests,
            $this->_method_tests,
            $this->_class_tests,
            $this->_class_integration_tests,
            $this->_trait_tests,
            $this->_php_open_tags_tests,
            $this->_const_tests,
            $this->_use_trait_tests,
            $this->_values_tests,
        );
    }

    public function testMethodBodyIsString(){
        $this->disable();
        // this involves the test/input/php/lex/code-scrawl/IntegrationTest.php file 
        // which keeps showing ast objects in the output
        // where there should be a body string

        $lexer = new \Tlf\Lexer();

        $abs_path = $this->file('test/input/php/lex/code-scrawl/IntegrationTest.php');
        
        $phpGram = new \Tlf\Lexer\PhpGrammarNew();
        $lexer = new \Tlf\Lexer();
        $lexer->useCache = false;
        $lexer->debug = false;
        $lexer->addGrammar($phpGram);

        // the first directive we're listening for
        $lexer->addDirective($phpGram->getDirectives(':php_open')['php_open']);


        // ob_start();
        // runs the lexer with $ast as the head
        $ast = $lexer->lexFile($abs_path);
        // ob_end_clean();

        // echo 'oh';
        // exit;

        print_r($ast->getTree());
        // exit;

    }


    public function testRunFile(){
        $file = $this->options['file'] ?? '';
        $dir = $this->file('test/input/php/lex/');
        if (is_file($dir.$file)){
            // echo 'zeep';
            // exit;
            $this->run_parse_file(substr($file,0,-4), false);
        } else {
            foreach (scandir($dir.$file) as $sub_file){
                if (substr($sub_file,-4)!='.php')continue;
                $this->run_parse_file(substr($file.'/'.$sub_file));
            }
        }


        return;
        if (isset($this->options['file'])){
            $file = $this->options['file'];
            var_dump($file);
            exit;
        }
    }
    public function testLilDb(){
        // $this->run_parse_file('MethodParseErrors',true);
        $this->run_parse_file('lildb/LilDb',true,-1);
    }
    public function testPhad(){
        $this->run_parse_file('phad/FormsTest',true,);
    }

    public function testScrawl(){
        $this->run_parse_file('code-scrawl/functionListTemplate',true,);
    }

    public function testPhtml(){
        // $this->run_parse_file('MethodParseErrors',true);

        // return;
        // $this->run_parse_file('phtml/Compiler',false);
        // // return;
        // $this->run_parse_file('SampleClass', false);
        //
        //
        //
        // $this->run_parse_file('phtml/TextNode',false);
        // //fails
        // $this->run_parse_file('phtml/Phtml',false);

        // return;

        // $this->run_parse_file('phtml/PHPParser',false);
        // return;

        // $this->run_parse_file('phtml/Node',false);
        // return;

        // $this->run_parse_file('phtml/Compiler',false);
        // below, i want to test for docblock count ... use array_walk_recursive()?
    }

    public function run_parse_file($file, bool $debug=true, int $stop_loop = -1){
        // if (!$debug)ob_start();
        echo "\nParse $file\n";
        $in = $this->file('test/input/php/lex/');
        $out = $this->file('test/input/php/tree/');
        $input = file_get_contents($in.$file.'.php');


        $phpGram = new \Tlf\Lexer\PhpGrammarNew();
        $phpGram->directives = array_merge(
            $phpGram->_string_directives,
            $phpGram->_core_directives,
        );
        $lexer = new \Tlf\Lexer();
        $lexer->stop_loop = $stop_loop;
        $lexer->debug = $debug;
        $lexer->addGrammar($phpGram, null, false);
        $lexer->addDirective($phpGram->getDirectives(':php_open')['php_open']);

        $ast = new \Tlf\Lexer\Ast('file');
        $ast = $lexer->lex($input, $ast);

        $tree = $ast->getTree();


        $json = json_encode($tree, JSON_PRETTY_PRINT);
        $tree_print = print_r($tree,true);

        // i like the highlighting better when i make it .js
        file_put_contents($out.$file.'.printr.js', $tree_print);
        file_put_contents($out.$file.'.js', $json);
        // file_put_contents($out.'phtml/TextNode.php', $tree);
        // file_put_contents($out.'phtml/TextNode.json', $json);


        $expect_file = $in.$file.'.expect.js';
        if (!file_exists($expect_file))return;
        $expect = json_decode(file_get_contents($expect_file),true);
        // print_r($expect);
        // exit;
        $counts = $expect['class_counts'];
        $counts = array_merge(['properties'=>0,'methods'=>0,'comments'=>0,'consts'=>0], $counts);
        

        // print_r($tree);
        $class = $tree['namespace']['class'][0] ?? $tree['class'][0];

        foreach ($counts as $key=>$count){
            $this->test($key.' count');
            $this->compare($count, count($class[$key]??[]));
        }


        $tree_counts = $expect['tree_counts'];
        unset($tree_counts['docblocks']);
        // $tree_counts = array_merge(['classes'=>0,'functions'=>0,''])
        foreach ($tree_counts as $key=>$c){
            if ($key=='class'&&!isset($tree[$key])){
                $count = count($tree['namespace']['class']);
            } else $count = count($tree[$key]);
            $this->test($key.' tree count');
            $this->compare($c, $count);
        }

        return;
        $this->test('method count');
        $this->compare($counts['methods'], count($class['methods']??[]));
            
        $this->test('property count');
        $this->compare($counts['properties'], count($class['properties']??[]));

        $this->test('comments count');
        $this->compare($counts['properties'], count($class['comments']??[]));


        return;
        echo "\n\n\n-----------\n\n";

        print_r($tree);



        $expect = 
        [
            'type'=>'class_body',
            'const'=>[
                0=>[
                    'type'=>'const',
                    'name'=>'blm',
                    'value'=>'"yes"',
                    'declaration'=>'const blm = "yes";',
                ],
            ]
        ];
        $this->compare($expect,
            $tree
        );

        
    }



    public function testShowMePhpFeatures(){



        ob_start();
        $phpGram = new \Tlf\Lexer\PhpGrammarNew();
        $docblockGram = new \Tlf\Lexer\DocblockGrammar();
        $grammars = [
            $phpGram,
            $docblockGram,
        ];
        $phpGram->buildDirectives();

        $test_results = $this->runDirectiveTests($grammars, $this->directive_tests);
        ob_end_clean();

        $fh = fopen($this->file('test/output/PhpFeatures.md'),'w');
        foreach ($this->directive_tests as $name=>$test){
            $status = 'fail';
            $s = '-';
            if ($test_results[$name]){
                $s = '+';
                $status = 'pass';
            }
            $str = "\n$s$name: ".$test['input'];
            echo $str;
            $str = "\n- $s$name($status): `".$test['input'].'`';
            fwrite($fh, $str);
        }
        fclose($fh);
        $this->disable();
    }

    public function testPhpGrammarNew(){

        $phpGram = new \Tlf\Lexer\PhpGrammarNew();
        $docblockGram = new \Tlf\Lexer\DocblockGrammar();
        $grammars = [
            $phpGram,
            $docblockGram,
        ];
        $phpGram->buildDirectives();

        $this->runDirectiveTests($grammars, $this->directive_tests);
    }

    /* convert a tree into json to see if i like the output better */
    public function testMakeJson(){
        $file = $this->file('test/input/php/');
        $tree = require($file.'tree/SampleClass.tree.php');
        $json = json_encode($tree, JSON_PRETTY_PRINT);
        file_put_contents($file.'tree/SampleClass.tree.json', $json);
    }


    public function testPhpGrammarNew_SampleClass(){
        // $dir = dirname(__DIR__).'/php-new/';
        // $dir = dirname()
        $dir = $this->file('test/input/php/lex/');
        $file = $dir.'SampleClass.php';
        $targetTree = include($dir.'SampleClass.tree.php');

        $lexer = new \Tlf\Lexer();
        $lexer->debug = true;

        //the string rewrite
        // $lexer->stop_loop = 305; // on loop 293, we start listening for strings. string_double STOPS on loop 299
        //on loop 300, 

        // This is for devving v0.6:
        // $lexer->stop_loop = 18;
        // $lexer->stop_loop = 33;
        // $lexer->stop_loop = 21; 
        // $lexer->stop_loop = 31;
        // $lexer->stop_loop = 41;
        // $lexer->stop_loop = 51;
        // $lexer->stop_loop = 111; // catching 'class'
        // $lexer->stop_loop = 120;
        // $lexer->stop_loop = 135; // block starts at 132
        // $lexer->stop_loop = 150; // use trait is about 150
        // $lexer->stop_loop = 185; // 181 is new line for first comment
        // $lexer->stop_loop = 206; // process # second comment at 203
        // $lexer->stop_loop = 261; // Process first property's docblock
        // $lexer->stop_loop = 280; // 277 'modifier' starts
        // $lexer->stop_loop = 300; // first property processed on 299
        // $lexer->stop_loop = 335; // second property is complete
        // $lexer->stop_loop = 395; // static public property is finished by loop 385
        // $lexer->stop_loop = 445; // method docblock finishes on 441
        // $lexer->stop_loop = 460; // `public ` is captured on 454, starting method modifier
        // $lexer->stop_loop = 492; // method block (opening curly brace) starts on 473
        // $lexer->stop_loop = 525; // closing method curly brace is loop 516
        // $lexer->stop_loop = 553; // issues around `=` around 550
        // $lexer->stop_loop = 585; // see 542 for `const `. `=` is loop 551. string closes on 578
        // $lexer->stop_loop = 800;
        // $lexer->stop_loop = 1200;

        
        // $lexer->stop_loop = 9999999;
        $lexer->useCache = false; // useCache only matters when lexing a file
        $lexer->addGrammar($phpGrammar = new \Tlf\Lexer\PhpGrammarNew());

        $ast = $lexer->lexFile($file);

        $tree = $ast->getTree(); // not what we normally want with json

        echo "\n\n\n --- file AST ---\n\n";

        print_r($tree);

        //
        // write the current tree to a file
        //
        file_put_contents($dir.'SampleClass.tree2.php',var_export($tree,true));
        echo "\n----\n\n\n";
        $this->compare(
            $targetTree, 
            $tree
        );
    }

}