用PHP寫一個最簡單的解釋器Part4(寫一個最簡單的腳本語言)

好吧!我認可我想標題黨了。你們對解釋器的吸引,絕對沒有本身動手寫一個腳本語言更有吸引力。不過若是看到標題過來的,可能也是php

clipboard.png

我認可,以前收藏的減肥視頻,我都是這樣對待他們的。編程

不過我仍是相信不少程序猿or程序媛不單單但願能夠作出一個牛逼的產品,更想作出來一個牛逼閃閃的編程語言。而這裏就是朝着開發一個腳本語言的方向去努力編程語言

clipboard.png

恩!我就是xxoo語言之父。函數

前幾篇文章已經實現一個簡易的加減法計算器,想必看了文章以後能夠很快的寫出來一個乘除法計算器。那麼若是加減乘除混合運算呢?學習

這節將實現相似6-1*2+4/2這樣的運算方式,你們看到這個式子以後應該已經瞭解難點在哪裏了。好了下面開始,首先展現完整的代碼。this

<?php
define('ISINTEGER','ISINTEGER');//定義整數類型描述
define('PLUS','PLUS');//定義操做符號類型描述 加法
define('MINUS','MINUS');//定義操做符號類型描述 減法
define('MUL','MUL');//定義操做符號類型描述 乘法
define('DIV','DIV');//定義操做符號類型描述 除法
define('WHITESPACE',' ');//定義空格
/**
Token  用來存儲輸入字符的類型
*/
class Token{
    private $type;
    private $value;
    /**
    $type ISINTEGER/PLUS/MINUS
    $value 對應的字符串
    */
    public function __construct($type,$value)
    {
        $this->type=$type;
        $this->value=$value;
    }
    
    /**
    經過該方法來獲取類的私有屬性
    */
    public function __get($name)
    {
        return $this->{$name};
    }
    /**
    用於調試
    */
    public function __toString()
    {
        return 'type:'.$this->type.' value:'.$this->value;
    }
}

class Lexer{
    private $current_char ;
    private $current_token ;
    private $text;
    private $pos=0;
    /***
    $text 須要進行解釋的字符串
    */
    public function __construct($text){
        //去除先後可能存在的空格 這些空格是無效的
        $this->text=trim($text);
        //初始化 獲取第一個字符
        $this->current_char = $this->text[$this->pos];
    }
    
    public function error()
    {
        throw new \Exception('Lexer eroor');
    }
    
    /*
    步進方法,每操做一個字符後前進一位
    */
    public function advance()
    {
        $this->pos++;
        if ($this->pos>strlen($this->text)-1){
            $this->current_char=null;
        }else{
            $this->current_char=$this->text[$this->pos];
        }
    }
    
    /*
    去除空格
    */
    public function skip_whitespace()
    {
        if ($this->current_char!=null&&$this->current_char==WHITESPACE){
            $this->advance();
        }
    }
    
    /*
    若是要支持多位的整數,則須要將每位數字存儲起來
    */
    public function integers()
    {
        $result='';//用於存儲數字
        while($this->current_char!=null&&is_numeric($this->current_char)){//只要當前字符是數字就一直循環並將數字存儲於$result
            $result.=$this->current_char;
            $this->advance();//步進方法,每操做一個字符後前進一位
        }
        return intval($result);//將數字字符串轉成整數
    }
    
    //獲取當前字符的Token  
    public function get_next_token()
    {
        while($this->current_char!=null){
            if ($this->current_char==WHITESPACE){
                $this->skip_whitespace();
                continue;
            }
            if (is_numeric($this->current_char)){
                return new Token(ISINTEGER,$this->integers());
            }
            
            if ($this->current_char=="+"){
                $this->advance();
                return new Token(PLUS,'+');
            }
            
            if ($this->current_char=="-"){
                $this->advance();
                return new Token(MINUS,'-');
            }
            
            if ($this->current_char=="*"){
                $this->advance();
                return new Token(MUL,'*');
            }
            
            if ($this->current_char=="/"){
                $this->advance();
                return new Token(DIV,'/');
            }
            return new Token('EOF', null);
        }
    }
}

//解釋器
class Interpreter{
    private $current_token ;
    private $lexer ;
    
    public function __construct($lexer){
        //去除先後可能存在的空格 這些空格是無效的
        $this->lexer=$lexer;
        //初始化 獲取第一個字符
        $this->current_token=$this->lexer->get_next_token();
    }
    
    //若是字符類型和判斷的類型一致,則繼續,不然輸入錯誤
    public function eat($token_type)
    {
        if ($this->current_token->type==$token_type){
            $this->current_token=$this->lexer->get_next_token();
        }else{
            $this->error();
        }
    }
    
    public function error()
    {
        throw new \Exception('eroor');
    }
    public function factor()
    {
        $token=$this->current_token;
        $this->eat(ISINTEGER);
        return $token->value;
    }
    
    public function term()
    {
        $result=$this->factor();
        while(in_array($this->current_token->type,[MUL,DIV])){
            $token=$this->current_token;
            if ($token->type==MUL){
                $this->eat(MUL);
                $result=$result*$this->factor();
            }
            else if ($token->type==DIV){
                $this->eat(DIV);
                $result=$result/$this->factor();
            }
        }
        
        return $result;
        
    }
    
    //解釋方法
    public function expr()
    {
        $result=$this->term();
        while(in_array($this->current_token->type,[PLUS,MINUS])){
            $token=$this->current_token;
            if ($token->type==PLUS){
                $this->eat(PLUS);
                $result=$result+$this->term();
            }
            else if ($token->type==MINUS){
                $this->eat(MINUS);
                $result=$result-$this->term();
            }
            
        }
        return $result;
    }
}

do{
    fwrite(STDOUT,'xav>');;
    $input=fgets(STDIN);
    $Interpreter=new Interpreter(new Lexer($input));
    echo $Interpreter->expr();
    unset($Interpreter);
    
}while(true);

看過前面幾篇文章的已經瞭解過,這裏的代碼結構發生了變化。spa

首先,分離出來詞法分析類,該類的做用:將字符串進行相似分詞處理,並將每一個詞的含義返回調試

class Lexer{
    private $current_char ;
    private $current_token ;
    private $text;
    private $pos=0;
    /***
    $text 須要進行解釋的字符串
    */
    public function __construct($text){
        //去除先後可能存在的空格 這些空格是無效的
        $this->text=trim($text);
        //初始化 獲取第一個字符
        $this->current_char = $this->text[$this->pos];
    }
    
    public function error()
    {
        throw new \Exception('Lexer eroor');
    }
    
    /*
    步進方法,每操做一個字符後前進一位
    */
    public function advance()
    {
        $this->pos++;
        if ($this->pos>strlen($this->text)-1){
            $this->current_char=null;
        }else{
            $this->current_char=$this->text[$this->pos];
        }
    }
    
    /*
    去除空格
    */
    public function skip_whitespace()
    {
        if ($this->current_char!=null&&$this->current_char==WHITESPACE){
            $this->advance();
        }
    }
    
    /*
    若是要支持多位的整數,則須要將每位數字存儲起來
    */
    public function integers()
    {
        $result='';//用於存儲數字
        while($this->current_char!=null&&is_numeric($this->current_char)){//只要當前字符是數字就一直循環並將數字存儲於$result
            $result.=$this->current_char;
            $this->advance();//步進方法,每操做一個字符後前進一位
        }
        return intval($result);//將數字字符串轉成整數
    }
    
    //獲取當前字符的Token  
    public function get_next_token()
    {
        while($this->current_char!=null){
            if ($this->current_char==WHITESPACE){
                $this->skip_whitespace();
                continue;
            }
            if (is_numeric($this->current_char)){
                return new Token(ISINTEGER,$this->integers());
            }
            
            if ($this->current_char=="+"){
                $this->advance();
                return new Token(PLUS,'+');
            }
            
            if ($this->current_char=="-"){
                $this->advance();
                return new Token(MINUS,'-');
            }
            
            if ($this->current_char=="*"){
                $this->advance();
                return new Token(MUL,'*');
            }
            
            if ($this->current_char=="/"){
                $this->advance();
                return new Token(DIV,'/');
            }
            return new Token('EOF', null);
        }
    }
}

其實四則運算最爲複雜的部分就是若是解決先乘除後加減。這裏將其分紅了兩個部分。term函數就是在進行乘除法部分,expr則進行加法code

public function term()
    {
        $result=$this->factor();
        while(in_array($this->current_token->type,[MUL,DIV])){
            $token=$this->current_token;
            if ($token->type==MUL){
                $this->eat(MUL);
                $result=$result*$this->factor();
            }
            else if ($token->type==DIV){
                $this->eat(DIV);
                $result=$result/$this->factor();
            }
        }
        
        return $result;
        
    }
    
    //解釋方法
    public function expr()
    {
        $result=$this->term();
        while(in_array($this->current_token->type,[PLUS,MINUS])){
            $token=$this->current_token;
            if ($token->type==PLUS){
                $this->eat(PLUS);
                $result=$result+$this->term();
            }
            else if ($token->type==MINUS){
                $this->eat(MINUS);
                $result=$result-$this->term();
            }
            
        }
        return $result;
    }

clipboard.png

圖很醜,不過仍是但願你能夠看懂,也就是4-2*3-1 ,expr中乘除都是一個總體,其會被在term中進行運算返回,這樣這個式子就分紅了,4-積-1.而積是在term中進行運算的。視頻

文中不少描述存在不少不當,我也在積極的學習如何將知識講的通俗易懂。若是您有好的辦法,能夠一同探討

相關文章
相關標籤/搜索