Token.php

<?php

namespace ROF;

class Token {



    // protected $start;
    protected $name;
    protected $text = '';
    protected $remainder = [];
    protected $next;
    protected $children = [];
    protected $parent;
    protected $prev;
    // protected $stack;
    // protected $parent;
    // protected $next;
    // protected $previous;

    public function asArray(){
        $remainderInfo = implode($this->remainder);
        $remainderInfo = 'string('.strlen($remainderInfo).'): '.substr($remainderInfo,0,10).'...';

        $data = [
            'name'=>$this->name,
            'text'=>$this->text,
            'remainder'=>$remainderInfo
        ];
        if (count($this->children)>0){
            $data['children']  = [];
            foreach ($this->children as $child){
                $data['children'][] = $child->asArray();
            }
        }
        if ($this->parent===null){
            $data['next'] = $this->next;
        }
        // foreach(['next'] as $param){
        //     if ($this->$param!=null){
        //         $data[$param] = $this->$param->asArray();
        //     }
        // }
        return $data;
    }

    public function __construct($name,$text=''){
        $this->name = $name;
        $this->append($text);
        // $this->text = $text;
        // $this->text = $fullText;
        // $this->start = $index;
        // // $this->stack = $stack;
        // $this->name = $name;

        // $lsit = [];
        // foreach (str_split($stack) as $index=>$char){
        //     $list[] = $this->next($char);
        // }
    }

    protected function pop($str,$offsetText=''){
        if ($str=='')return;
        $len = strlen($str);
        $end = substr($this->text,-($len+strlen($offsetText)));
        $popped = $end;
        if (strlen($offsetText)>0){
            $popped = substr($end,0,-strlen($offsetText));
        }
        // var_dump($str);
        // var_dump($offsetText);
        // var_dump($end);
        // var_dump($popped);
        // var_dump(-strlen($offsetText));
        // var_dump($this->text);
        if ($popped!==$str){
            throw new \Exception("Attempted to pop '{$str}' from token '{$this->name}', but the end was '{$end}' and the end-offset string was '{$offsetText}',"
                ." and the strpos-based poppable text was '{$popped}'");
        }
        $this->text = substr($this->text,0,-strlen($end)).$offsetText;
    }




    public function append($text){
        // if (strlen($text))
        $this->remainder = str_split($text);
        $this->remainderText = $text;
        // var_dump($text);exit;
        // if ($this->name=='chapter-name'){
        //     var_dump($text);
        // }
        while (count($this->remainder)>0){  
            // $char = $this->remainder[0];
            $this->remainderText = substr($this->remainderText,1);
            $char = array_shift($this->remainder);
            // unset($this->remainder[0]);
            if ($char==='')break;
            $this->next($char);
        }
        // foreach ($remainder as $index => $char){
        //     $this->next($char);
        // }
    }
    
    public function next($char){
        if (strlen($char)==0){
            throw new Exception("uh oh:".$char);
        }
        $this->text .= $char;

        $typeParts = explode('-',$this->name);
        $camelType = '';
        foreach ($typeParts as $part){
            $camelType .= ucfirst($part);
        }
        $willEnd = 'willEnd'.$camelType;
        $end = 'end'.$camelType;
        if (!method_exists($this,$willEnd)
            ||!method_exists($this,$end)){
            throw new \Exception("both '{$willEnd}' and '{$end}' must exist for token type '{$this->name}'");
        }
        $matches = [];
        if (!$this->$willEnd($matches)){
            return;
        }
        $end = 'end'.$camelType;
        $this->$end($matches);
    }

    public function __toString(){
        return "\n<br><b>".$this->name.":</b>\n"
            ."<i>".$this->text."</i>";
    }


    protected function nextToken($name,$text,$offsetText=''){
        $upcoming = $text.implode($this->remainder);
        $this->remainder = [];
        $this->pop($text,$offsetText);
        $class = get_class($this);
        $token = new $class($name,'');
        if ($this->parent!=null)$this->parent->appendChild($token);
        $token->prev = $this;
        $this->next = $token;
        // exit;
        $token->append($upcoming);
        return $token;
    }

    protected function childToken($name,$text){
        $upcoming = $text.implode($this->remainder);
        $this->remainder = [];
        $this->pop($text);
        $class = get_class($this);
        $token = new $class($name,'');
        $this->children[] = $token;
        $token->parent = $this;
        // exit;
        $token->append($upcoming);
        return $token;
    }
    protected function appendChild($token){
        $this->children[] = $token;
        $token->parent = $this;
    }



    
    protected function willEndInit(&$matches){
        return true;
    }
    protected function endInit($matches){
        $this->childToken('start',$this->text);
    }

    protected function willEndStart(&$matches){
        // echo $this->text;
        return preg_match('/[a-z]{100,}$/',$this->text,$matches);
    }
    protected function endStart($matches){
        $this->nextToken('chapter-number','CHAPTER');
    }
    

}