zoukankan      html  css  js  c++  java
  • 用PHP写一个最简单的解释器Part4(写一个最简单的脚本语言)

    好吧!我承认我想标题党了。大家对解释器的吸引,绝对没有自己动手写一个脚本语言更有吸引力。不过如果看到标题过来的,可能也是

    我承认,之前收藏的减肥视频,我都是这样对待他们的。

    不过我还是相信很多程序猿or程序媛不仅仅希望可以做出一个牛逼的产品,更想做出来一个牛逼闪闪的编程语言。而这里就是朝着开发一个脚本语言的方向去努力

    恩!我就是xxoo语言之父。

    前几篇文章已经实现一个简易的加减法计算器,想必看了文章之后可以很快的写出来一个乘除法计算器。那么如果加减乘除混合运算呢?

    这节将实现类似6-1*2+4/2这样的运算方式,大家看到这个式子之后应该已经了解难点在哪里了。好了下面开始,首先展示完整的代码。

    <?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);
    
    
    
    

    看过前面几篇文章的已经了解过,这里的代码结构发生了变化。

    首先,分离出来词法分析类,该类的作用:将字符串进行类似分词处理,并将每个词的含义返回

    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则进行加法

    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;
        }
    

    图很丑,不过还是希望你可以看懂,也就是4-2*3-1 ,expr中乘除都是一个整体,其会被在term中进行运算返回,这样这个式子就分成了,4-积-1.而积是在term中进行运算的。

    文中很多描述存在很多不当,我也在积极的学习如何将知识讲的通俗易懂。如果您有好的办法,可以一同探讨

    原文地址:https://segmentfault.com/a/1190000015623736

  • 相关阅读:
    排序
    自动生成存储过程的工具
    感悟javascript
    VS.net 2008 beta2 新功能
    北极光合伙人邓锋:要别人跳火坑,你自己先跳
    [转]驱动开发中应该注意的事项
    [转]CryptoAPI的应用(一)概述以及初始化模块
    [转]强制重启N种法
    Different ways of handling IRPs
    [转]分层驱动模型中IRP的传递与完成
  • 原文地址:https://www.cnblogs.com/lalalagq/p/9979953.html
Copyright © 2011-2022 走看看