Grammar.php

<?php

namespace Tlf\Lexer\Test\Main;

/*
 * Test features of the grammar class, not lexing, and not any language-specific grammars
 */
class Grammar extends \Tlf\Tester {

    protected $grammar;

    public function prepare(){
        $this->grammar = new \Tlf\Lexer\Grammar();
    }


    /**
     * @test `$grammar->getDirectives($name, $overrides)`
     */
    public function testGetDirectives(){
        $grammar = new \Tlf\Lexer\Grammar();
        $grammar->directives = $this->getSourceDirectives();
        foreach ($this->getDirectivesToLookup() as $testName=>$pieces){
            if ($testName!='Is Directive'){
                //var_dump("")
                //exit;
                //@todo REMOVE THIS continue
                continue;
            }
            $this->test($testName);
            $lookupName = $pieces[0];
            $overrides = $pieces[1];
            $expectedList = $pieces[2];

            $actualList = $grammar->getDirectives($lookupName, $overrides);

            foreach ($actualList as $name=>$d){
                unset($d->_name);
                unset($d->_grammar);
                $actualList[$name] = (array)$d;
            }

            $this->compare($expectedList, $actualList);
        }
    }

    /**
     * Test grammar->normalizeDirective();
     * @test directive `:bear` normalizes to `:bear => []`
     * @test `:bear ...` normalizes to to `:bear => []`
     * @test `:dog=>['override']` is unchanged by normalization
     *
     * Rules:
     * @rule `"cmd var"` normalizes to `":cmd var" => []`
     * @rule `"cmd var" => []` does not change
     *
     */
    public function testDirectiveNormalization(){
        foreach ($this->getNormalizeDirectives() as $name=>$comparators){
            $this->test($name);
            $directive = $comparators[0];
            $expect = $comparators[1];
            $normal = $this->grammar->normalizeDirective($directive);
            $this->is_object($normal);
            $this->compare($expect, (array)$normal);
        }
    }


    /**
     * Test that an `is` directive expands into an array of the directives it names
     *
     * @test `is => [ ":target1", ":target2", ":target3" ]` expands into `[ $target1, $target2, $target3 ]` where `$target1` is a directive, possibly containing starts & stops
     */
    public function testExpandIsDirective(){
        $grammar = new \Tlf\Lexer\Grammar();
        $dRoot = $grammar->directives = $this->getSourceDirectives();
        
        $sourceDirective = (object)$grammar->directives['is_directive'];
        $sourceDirective->_name = 'is_directive';
        $sourceDirective->_grammar = 'grammar';
        $directiveList = $grammar->expandDirectiveWithIs($sourceDirective);

        foreach ($directiveList as $directiveName=>$directive){
            unset($directive->_name);
            unset($directive->_grammar);
            $directiveList[$directiveName] = (array)$directive;
        }

        $this->compare(
            [
                'is_target'=>$dRoot['is_target'],
                'is_target_2'=>$dRoot['is_target_2'],
                'is_target_3'=>$dRoot['is_target_3'],
            ],
            $directiveList
        );
    }

    /**
     * Rules:
     * @rule source + override = override, source 
     * @rule source[key] + override[key] = override[key]
     * @rule source[key //] + override[key] = override[key], source[key //]
     *
     *
     * @test `:source=>[]` overridden by `:override=>[]` yields `:override=>[], :source=>[]`
     * @test that `then :cat, match uhoh` overridden by `match // => abc, hide` yields `match //=>abc, hide, then :cat, match uhoh`
     * @test `then :cat` overridden by `hide nothing, match abc` yields `hide nothing, match abc, then :cat`
     * @test `match abc, then :cat` overriden by `hide nothing` yields `match, hide, then` (match goes first)
     * @test `match abc, then cats` overridden by `match dog, rewind 1` yields `match dog, rewind 1, then cats`
     */
    public function testDirectiveOverrides(){
    
        foreach ($this->getOverrideDirectives() as $testName=>$pieces){
            $source = (object)$pieces[0];
            $overrides = (object)$pieces[1];
            $expect = $pieces[2];
            $actual = $this->grammar->getOverriddenDirective($overrides, $source);
            $this->is_object($actual);
            $this->test($testName);
            $this->compare($expect, (array)$actual,true);
        }
    }



    /**
     *
     * @return multi dimensional array
     * @array.key is the thing being tested
     * @array.child is an array with 3 child arrays
     * @array.child.index1 is the name of the directive to load
     * @array.child.index2 is the the overrides
     * @array.child.index3 is the expected directive list to be returned
     */
    public function getDirectivesToLookup(){

        return [
            'Empty Directive'=>[
                ':empty_directive',
                [],
                ['empty_directive'=>[]],
            ],
            'Is Directive'=>[
                ':is_directive',
                [],
                [
                    'is_target'=>[
                        'stop'=>[
                            'match'=>['/is/'],
                        ],
                    ],
                    'is_target_2'=>[
                        'stop'=>[
                            'match'=>['/is_2/'],
                        ],
                    ],
                    'is_target_3'=>[
                        'stop'=>[
                            'match'=>['/is_3/'],
                        ],
                    ],
                ],
            ],
        ];
    }

    public function getSourceDirectives(){
        return [
            'empty_directive'=>[
            ],
            'is_directive'=>[
                'is'=>[
                    ':is_target'=>[],
                    ':is_target_2'=>[],
                    ':is_target_3'=>[],
                ],
            ],
            'is_target'=>[
                'stop'=>[
                    'match'=>['/is/'],
                ],
            ],
            'is_target_2'=>[
                'stop'=>[
                    'match'=>['/is_2/'],
                ],
            ],
            'is_target_3'=>[
                'stop'=>[
                    'match'=>['/is_3/'],
                ],
            ],
        ];
    }




    /**
     *
     * @return multi dimensional array
     * @array.key is the thing being tested
     * @array.child is an array with 3 child arrays
     * @array.child.index1 is the source/root directive
     * @array.child.index2 is the overrides
     * @array.child.index3 is the expected result
     *
     */
    public function getOverrideDirectives(){
        return [
            'Is overrides'=>[
                [
                    'is'=>[
                        ':source'=>[],
                    ],
                ],
                [
                    'is'=>[
                        ':override'=>[],
                    ],
                ],
                [
                    'is'=>[
                        ':override'=>[],
                        ':source'=>[],
                    ],
                ],
            ],
            'Source match is second'=>[


                [
                    'start'=>[
                        'then'=>[':cat'],
                        'match'=>['uhoh'],
                    ],
                ],
                [
                    'start'=>[
                        'match //'=>['abc'],
                        'hide'=>['nothing'],
                    ],
                ],
                [
                    'start'=>[
                        'match //'=>['abc'],
                        'hide'=>['nothing'],
                        'then'=>[':cat'],
                        'match'=>['uhoh'],
                    ]
                ],
            ],

            'Source nomatch' => [
                [
                    'start'=>[
                        'then'=>[':cat'],
                    ],
                ],
                [
                    'start'=>[
                        'hide'=>['nothing'],
                        'match'=>['abc'],
                    ],
                ],
                [
                    'start'=>[
                        'hide'=>['nothing'],
                        'match'=>['abc'],
                        'then'=>[':cat'],
                    ]
                ],
            ],

            'Source match, then overrides, then other source instructions'=>[
                [
                    'start'=>[
                        'match'=>['abc'],
                        'then'=>[':cat'],
                    ],
                ],
                [
                    'start'=>[
                        'hide'=>['nothing'],
                    ],
                ],
                [
                    'start'=>[
                        'match'=>['abc'],
                        'hide'=>['nothing'],
                        'then'=>[':cat'],
                    ]
                ],
            ],

            'Direct Key Overrides'=>[
                [
                    'stop'=>[
                        'match'=> ['abc'],
                        'then'=> ['cats'],
                    ]
                ],
                [
                    'stop'=>[
                        'match'=> ['dog'],
                        'rewind'=>1,
                    ]
                ],
                [
                    'stop'=>[
                        'match'=>['dog'],
                        'rewind'=>1,
                        'then'=>['cats'],
                    ],
                ],
            ]

        ];
    }


    /**
     *
     * Get directives as they would be defined & those same directives as they would be after normaliztion
     *
     * @test 
     * @return multi dimensional array
     * @child.index1 is the input directive, as one would define it
     * @child.index2 is the expected output directive, as returned from the normalize method
     */
    public function getNormalizeDirectives(){
        return [
            'Stop'=>[
                [
                    'stop'=>[
                        'then :cat',
                        'then :dog'=>['override'],
                        'then :bear ...',
                    ],
                ],
                [
                    'stop'=>[
                        'then :cat'=>[],
                        'then :dog'=>['override'],
                        'then :bear ...'=>[],
                    ],
                ],
            ],
            'Is'=>[
                [
                    //input
                    'is'=>[
                        ':cat',
                        ':dog'=>['override'],
                        ':bear',
                    ],
                ],
                [
                    //normalized
                    'is'=>[
                        ':cat'=>[],
                        ':dog'=>['override'],
                        ':bear'=>[],
                    ],
                ],
            ],
        ];
    }


    ////////////////////
    //
    ///// abandoned test code that i might return to eventually
    //
    ////////////////////


    /** I wrote this for thinking purposes, and I will probably use it for some testing maybe????
     * Might be able to delete it.
     */
    protected $sampleDirectiveList = [
            'grp'=>[
                'stop'=>[
                    //overrides target.stop.rewind & target_2.stop.rewind
                    'rewind'=>1
                ],
                'is'=>[
                    ':target'=>[
                        //overrides grp.stop.rewind & target.stop.rewind
                        'rewind'=>2,
                    ],
                    ':target_2'=>[],
                ]
            ],
            'target'=>[
                'stop'=>[
                    'match'=>'abc',
                    // overrides nothing
                    'rewind'=>3,
                ]
            ],
            'parent'=>[
                'stop'=>[
                    'then :grp'=>[
                        //highest priority. This always overrides whatever its loading
                        'rewind'=>4
                    ],
                ]
            ],

            'target_2'=>[],
        ];





    /**
     * Test that is directives accept overrides 
     * The functionality exists, i think, but the test is not implemented
     */
    public function testExpandIsDirectiveWithOverrides(){
        $this->disable();
        echo "\n\n";
            
            echo "This test is disabled because I haven't actually implemented it yet. Its currently a copy+paste from testExpandIsDirective() (no overrides) ";

        echo "\n\n";

        return false;
        $grammar = new \Tlf\Lexer\Grammar();
        $dRoot = $grammar->directives = $this->getSourceDirectives();
        
        $sourceDirective = (object)$grammar->directives['is_directive'];
        $sourceDirective->_name = 'is_directive';
        $sourceDirective->_grammar = 'grammar';
        $directiveList = $grammar->expandDirectiveWithIs($sourceDirective);


        foreach ($directiveList as $directiveName=>$directive){
            unset($directive->_name);
            unset($directive->_grammar);
            $directiveList[$directiveName] = (array)$directive;
        }

        $this->compare(
            [
                'is_target'=>$dRoot['is_target'],
                'is_target_2'=>$dRoot['is_target_2'],
                'is_target_3'=>$dRoot['is_target_3'],
            ],
            $directiveList
        );
    }
}