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

  • 相关阅读:
    Codeforces Gym 100571A A. Cursed Query 离线
    codeforces Gym 100500 J. Bye Bye Russia
    codeforces Gym 100500H H. ICPC Quest 水题
    codeforces Gym 100500H A. Potion of Immortality 简单DP
    Codeforces Gym 100500F Problem F. Door Lock 二分
    codeforces Gym 100500C D.Hall of Fame 排序
    spring data jpa 创建方法名进行简单查询
    Spring集成JPA提示Not an managed type
    hibernate配置文件中的catalog属性
    SonarLint插件的安装与使用
  • 原文地址:https://www.cnblogs.com/lalalagq/p/9979953.html
Copyright © 2011-2022 走看看