资讯专栏INFORMATION COLUMN

用PHP写一个最简单的解释器Part4(写一个最简单的脚本语言)

happen / 1480人阅读

摘要:大家对解释器的吸引,绝对没有自己动手写一个脚本语言更有吸引力。前几篇文章已经实现一个简易的加减法计算器,想必看了文章之后可以很快的写出来一个乘除法计算器。

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

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

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

恩!我就是xxoo语言之父。

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

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

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://www.ucloud.cn/yun/29037.html

相关文章

  • PHP一个简单释器Part5(计算器后一节,下节开始如何脚本语言

    摘要:经过几天的努力,用已经实现了一个完整的基础计算器,如下图上代码定义整数类型描述定义操作符号类型描述加法定义操作符号类型描述减法定义操作符号类型描述乘法定义操作符号类型描述除法定义操作符号类型描述定义操作符号类型描述定义空格用来存储输入字符的 showImg(https://segmentfault.com/img/bVbdNO5?w=900&h=377); 经过几天的努力,用PHP已经...

    yanest 评论0 收藏0
  • PHP一个简单释器Part1

    摘要:偶然间在朋友圈发现有人在看一本两周自制脚本语言,觉得写个脚本语言挺不错的,方便自己对语言本身进一步了解。,不过同样,该教程采用的也不是。在这里写出代码方便自己查找,同时也希望一些对解释器感兴趣的朋友一同学习。 偶然间在朋友圈发现有人在看一本《两周自制脚本语言》,觉得写个脚本语言挺不错的,方便自己对语言本身进一步了解。于是乎,买了下来看了看,写的挺通俗易懂,但是不便的是,采用的语言是Ja...

    molyzzx 评论0 收藏0
  • SegmentFault 技术周刊 Vol.40 - 2018,来学习一门新编程语言吧!

    摘要:入门,第一个这是一门很新的语言,年前后正式公布,算起来是比较年轻的编程语言了,更重要的是它是面向程序员的函数式编程语言,它的代码运行在之上。它通过编辑类工具,带来了先进的编辑体验,增强了语言服务。 showImg(https://segmentfault.com/img/bV1xdq?w=900&h=385); 新的一年不知不觉已经到来了,总结过去的 2017,相信小伙们一定有很多收获...

    caspar 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<