用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中进行运算的。
文中很多描述存在很多不当,我也在积极的学习如何将知识讲的通俗易懂。如果您有好的办法,可以一同探讨
相关推荐
zyyjay 2020-11-09
xuebingnan 2020-11-05
samtrue 2020-11-22
stefan0 2020-11-22
yifangs 2020-10-13
songshijiazuaa 2020-09-24
hebiwtc 2020-09-18
天步 2020-09-17
83911535 2020-11-13
whatsyourname 2020-11-13
zhouyuqi 2020-11-10
Noneyes 2020-11-10
mathchao 2020-10-28
王志龙 2020-10-28
wwwsurfphpseocom 2020-10-28
diskingchuan 2020-10-23
savorTheFlavor 2020-10-23