引言
在文章《php 实现类似于pyhon中的Contruct库的功能(一) 基本设计思路》介绍了用php解析二进制数据的基本思路
在文章《php 实现类似于pyhon中的Contruct库的功能(二)实现适配器功能》说明了如何实现适配器功能。
以上两篇都是对静态数据结构进行解析。接下来要逐步实现动态数据结构的解析。也就是说数据结构的定义与上下文有关,要在数据解析时才能真正确定。
这一次要实现的是if-else功能。
基本思路
1,修改词法分析规则,使其可以接受if,else关键字
2,修改语法分析规则,使其可以接受if,else语句
3,修改编码器,生成可运行的php目标代码
其中主要的工作内容是修改语法分析规则文件。
实现内容
准备解析的结构体定义文件
struct student
{
char name[2];
int num;
if(num.value==1 ){
int age;
}else{
char addr[3];
}
};
为了聚焦在if-else功能的实现上,这次只定义了一个结构体 student。与以前的静态结构体定义最大的不同,在于如下定义
if(num.value==1 ){
int age;
}else{
char addr[3];
}
如果解析过程中,num字段的值为1则定义一个age字段,否则定义一个addr字段。
词法规则文件改动不大,增加对if,else关键字的匹配就可以了
['/^if\b/','_if' ,'i'],
['/^else\b/','_else' ,'e'],
语法规则文件需改动的地方比较多
首先,增加一个符号移进时的处理函数。在前面介绍的处理函数都是在归约时被调用,但对于更复杂的情形,有必要在移进时也进行处理。
先看一下script_parser.php中的移进处理的基本操作
//移进
private function shift($token){
//处理记号栈
$this->tokenStack= $this->tokenStack.$token[0];
if($this->debugMode){
echo I('srcline:'),$token[2],I(' shifted :'),$this->tokenStack,"\n";
}
//处理语法栈,栈中元素为[记号名,记号值,起始位置,结束位置,[附加信息]]
array_push($this->syntaxStack, [$token[0],$token[1],$this->tokenIndex-1,$this->tokenIndex-1,[]]);
//调用规则处理中的移进处理函数
$extra=$this->rulesHandler->handleShift($token[0],$this->syntaxStack,$this->coder);
//在栈中保存附加信息
$this->syntaxStack[count($this->syntaxStack)-1][TokenExtraIndex]=$extra;
}
其中放置了一个钩子函数的调用
//调用规则处理中的移进处理函数
$extra=$this->rulesHandler->handleShift($token[0],$this->syntaxStack,$this->coder);
在语法规则处理的基类中定义了一个空的 handleShift 方法。
我们需要做的就是在语法规则处理类中重载 handleShift 方法。
//处理移进,返回附加信息数组
function handleShift($tokenName,$stack,$coder){
if($tokenName=='_if'){
//插入一个空行,空行所在的序号存入附加信息数组,以后可以替换为正确的内容
return [$coder->pushLine('')];
}
if($tokenName=='_else'){
//插入一个空行,空行所在的序号存入附加信息数组,以后可以替换为正确的内容
return [$coder->pushLine('')];
}
return [];
}
从以上代码可以看出,在编译过程中,如果发生了if 或 else的移进操作,则在要生成的目标代码中插入一个空白行,并且将这个空白行的地址保存起来,在对if-else语句进行归约时用最终确定的内容替换空白行。
为什么要这么做呢?
因为 if 语句中,if 或else的块语句先完成归约,整个if语句在此之后才完成归约。而目标代码的生成是边归约边生成,所以要先为if 或else抢占一个位置。
下而是if-else的语法规则处理函数
// if {{{
function _ifStatement_0_ifStatement_else_blockStatement($stack,$coder){
//取出_else 记号中保存的空白行所在的地址,替换为正确的内容
$t1= $this->topItem($stack,2);
$lineIndex=$t1[TokenExtraIndex][0];
$content='else{';
$coder->resetLine($lineIndex,$content);
$coder->pushLine('}');
return $this->pass($stack,3);
}
function _ifStatement_0_if_wholeExpression_blockStatement($stack,$coder){
//取出_if 记号中保存的空白行所在的地址,替换为正确的内容
$t1= $this->topItem($stack,3);
$lineIndex=$t1[TokenExtraIndex][0];
$t2= $this->topItem($stack,2);
$condtionExp=$t2[TokenValueIndex];
$content='if'.$condtionExp.'{';
$coder->resetLine($lineIndex,$content);
$coder->pushLine('}');
return $this->pass($stack,3);
}
// if }}}
着重分析一下if语句的处理,处理函数如下所示
function _ifStatement_0_if_wholeExpression_blockStatement($stack,$coder){
//取出_if 记号中保存的空白行所在的地址,替换为正确的内容
$t1= $this->topItem($stack,3);
$lineIndex=$t1[TokenExtraIndex][0];
$t2= $this->topItem($stack,2);
$condtionExp=$t2[TokenValueIndex];
$content='if'.$condtionExp.'{';
$coder->resetLine($lineIndex,$content);
$coder->pushLine('}');
return $this->pass($stack,3);
}
以下语句
$t1= $this->topItem($stack,3);
的含义是从当前语法栈的栈顶取出元素,第2个参数3 表明是从栈顶计数,取第三个元素。计数时从1开始
_ifStatement_0_if_wholeExpression_blockStatement
所包含的语法规则就是:
当栈顶出现了_if, _wholeExpression, _blockStatement 三个符号是,这三个符号就可以归约为 _ifStatement
_0 是一个分隔串,分隔语法规则的左部与右部。
在ados脚本语言中采用了一种设计技巧,就是以产生式(语法规则)做为函数的名称,语法规则与语法规则处理函数合二为一。
这样做的好处是不用分别维护语法规则与语法规则处理函数,不用时刻保持两者的同步。
$t1= $this->topItem($stack,3);
取出的是_if 这个符号在语法栈中对应的内容。前面已经介绍过,在_if 符号移进时,插入了一个空白行,在符加信息数组中保存了这个空白行的地址。这时将其取出来。
$t2= $this->topItem($stack,2);
$condtionExp=$t2[TokenValueIndex];
从 _wholeExpression 对应的语法栈元素中取出相应的条件表达式,组成一个完整的内容后替换先前的空白行。
请留意,这时if语句块内容已经写入到目标代码中。
接下来补上一个if语句块的结束标记 ‘}’ 就OK了。
接下来实现对 属性运算符 . 的处理,在例子中就是对
num.value
这种形式的表达式的处理
语法规则处理函数如下
function _term_0_term_dot_iden($stack,$coder){
$t1= $this->topItem($stack,3);
$obj = $t1[TokenValueIndex];
$t2= $this->topItem($stack,1);
$var = $t2[TokenValueIndex];
$exp = '$'.$obj.'[\''.$var.'\']';
return [$exp,[]];
}
从例子来看,源代码是 num.value ,最终得到的目标代码是 $num[‘value’]
也就是将类C的源代码变成了php代码
接下来还得实现对 比较运算符 == 的处理:
function _wholeExpression_0_wholeExpression_bieq_expression($stack,$coder){
return $this->biOpertors($stack,3,'==',1,$coder);
}
//二元操作符的通用处理函数
function biOpertors($stack,$op1Index,$op,$op2Index,$coder){
$t1= $this->topItem($stack,$op1Index);
$exp1=$t1[TokenValueIndex];
$t2= $this->topItem($stack,$op2Index);
$exp2=$t2[TokenValueIndex];
$s=$exp1.$op.$exp2;
return [$s,[]];
}
下面是完整的语法规则处理文件的内容
<?php
/*!
* structwkr的语法规则处理器
* 45022300@qq.com
* Version 0.9.0
*
* Copyright 2019, Zhu Hui
* Released under the MIT license
*/
namespace Ados;
require_once 'const.php';
require_once __SCRIPTCORE__.'syntax_rule/base_rules_handler.php';
class StructwkrRulesHandler extends BaseRulesHandler{
//语法分析的起始记号,归约到最后得到就是若干个结构体定义的列表
function startToken(){
return '_structList';
}
//求出放在附加信息中的数组长度
function elementSize($extraArray){
if(count($extraArray)>0){
return intval($extraArray[0]);
}else{
return 0;
}
}
//二元操作符的通用处理函数
function biOpertors($stack,$op1Index,$op,$op2Index,$coder){
$t1= $this->topItem($stack,$op1Index);
$exp1=$t1[TokenValueIndex];
$t2= $this->topItem($stack,$op2Index);
$exp2=$t2[TokenValueIndex];
$s=$exp1.$op.$exp2;
return [$s,[]];
}
//处理移进,返回附加信息数组
function handleShift($tokenName,$stack,$coder){
if($tokenName=='_if'){
//插入一个空行,空行所在的序号存入附加信息数组,以后可以替换为正确的内容
return [$coder->pushLine('')];
}
if($tokenName=='_else'){
//插入一个空行,空行所在的序号存入附加信息数组,以后可以替换为正确的内容
return [$coder->pushLine('')];
}
return [];
}
//语法规则处理函数名由规则右边部分与规则左边部分拼接而成
//语法规则定义的先后决定了归约时匹配的顺序,要根据实际的语法安排
//如果不熟悉语法,随意调整语法规则的先后次序将有可能导致语法错误
// struct list {{{
function _structList_0_structList_struct($stack,$coder){
$coder->pushBlockTail();
return ['#',[]];
}
function _structList_0_struct($stack,$coder){
$coder->pushBlockTail();
return ['#',[]];
}
// struct list }}}
// struct {{{
function _struct_0_structName_blockStatement_semi($stack,$coder){
$t1= $this->topItem($stack,3);
$structName = $t1[TokenValueIndex];
$t2= $this->topItem($stack,2);
$extraArray=$t2[TokenExtraIndex];
return [$structName,$extraArray];
}
// struct }}}
// struct name {{{
function _structName_0_strukey_iden($stack,$coder){
$t1= $this->topItem($stack,1);
$structName = $t1[TokenValueIndex];
$coder->pushBlockHeader($structName);
return $this->pass($stack,1);
}
// struct name }}}
// blockStatement {{{
function _blockStatement_0_lcb_statementList_rcb($stack,$coder){
return $this->pass($stack,2);
}
// blockStatement }}}
// statement list {{{
function _statementList_0_statementList_statement($stack,$coder){
return $this->pass($stack,1);
}
function _statementList_0_statement($stack,$coder){
//此处0表示statementList是上一级节点,要做特殊处理
return $this->pass($stack,1);
}
// statement list }}}
// statement {{{
function _statement_0_wholeExpression_semi($stack,$coder){
$t1= $this->topItem($stack,2);
$elementName = $t1[TokenValueIndex];
$coder->pushCheckBody($elementName);
return $this->pass($stack,2);
}
function _statement_0_ifStatement($stack,$coder){
return $this->pass($stack,1);
}
// statement }}}
// if {{{
function _ifStatement_0_ifStatement_else_blockStatement($stack,$coder){
//取出_else 记号中保存的空白行所在的地址,替换为正确的内容
$t1= $this->topItem($stack,2);
$lineIndex=$t1[TokenExtraIndex][0];
$content='else{';
$coder->resetLine($lineIndex,$content);
$coder->pushLine('}');
return $this->pass($stack,3);
}
function _ifStatement_0_if_wholeExpression_blockStatement($stack,$coder){
//取出_if 记号中保存的空白行所在的地址,替换为正确的内容
$t1= $this->topItem($stack,3);
$lineIndex=$t1[TokenExtraIndex][0];
$t2= $this->topItem($stack,2);
$condtionExp=$t2[TokenValueIndex];
$content='if'.$condtionExp.'{';
$coder->resetLine($lineIndex,$content);
$coder->pushLine('}');
return $this->pass($stack,3);
}
// if }}}
// function expression {{{
//函数表达式
function _term_0_funcTerm($stack,$coder){
$t1= $this->topItem($stack,1);
$funcName=$t1[TokenValueIndex];
$paraArray=$t1[TokenExtraIndex];
$paras = implode(",", $paraArray);
$exp = $funcName.'('.$paras.')';
return [$exp,[]];
}
function _funcTerm_0_funcExpLp_rp($stack,$coder){
return $this->pass($stack,2);
}
function _funcTerm_0_funcExpLeft_rp($stack,$coder){
return $this->pass($stack,2);
}
function _funcExpLeft_0_funcExpLeft_comma_expression($stack,$coder){
$t1= $this->topItem($stack,3);
$t2= $this->topItem($stack,1);
//函数的参数列表存放在附加信息中
$paraArray=$t1[TokenExtraIndex];
array_push($paraArray, $t2[TokenValueIndex]);
return [$t1[TokenValueIndex],$paraArray];
}
function _funcExpLeft_0_funcExpLp_expression($stack,$coder){
$t1= $this->topItem($stack,2);
$t2= $this->topItem($stack,1);
//函数的参数列表存放在附加信息中
$paraArray=$t1[TokenExtraIndex];
array_push($paraArray, $t2[TokenValueIndex]);
return [$t1[TokenValueIndex],$paraArray];
}
function _funcExpLp_0_iden_lp($stack,$coder){
return $this->pass($stack,2);
}
// function expression }}}
// whole Expression {{{
function _wholeExpression_0_wholeExpression_bieq_expression($stack,$coder){
return $this->biOpertors($stack,3,'==',1,$coder);
}
function _wholeExpression_0_expression($stack,$coder){
return $this->pass($stack,1);
}
// whole Expression }}}
// Expression {{{
//表达式可以进行管道运算
function _expression_0_expression_pipe_factor($stack,$coder){
$t1= $this->topItem($stack,1);
$handlerName = $t1[TokenValueIndex];
$t2= $this->topItem($stack,3);
$elementName = $t2[TokenValueIndex];
$coder->pushPipeBody($handlerName,$elementName);
return $this->pass($stack,3);
}
function _expression_0_double_factor($stack,$coder){
$t1= $this->topItem($stack,1);
$elementName = $t1[TokenValueIndex];
$parseFuncName = 'parseDouble';
$coder->pushParseBody($parseFuncName,$elementName);
return $this->pass($stack,1);
}
function _expression_0_float_factor($stack,$coder){
$t1= $this->topItem($stack,1);
$elementName = $t1[TokenValueIndex];
$parseFuncName = 'parseFloat';
$coder->pushParseBody($parseFuncName,$elementName);
return $this->pass($stack,1);
}
function _expression_0_char_factor($stack,$coder){
$t1= $this->topItem($stack,1);
$elementName = $t1[TokenValueIndex];
$size = $this->elementSize($t1[TokenExtraIndex]);
$parseFuncName = 'parseFixStr';
$coder->pushParseBody($parseFuncName,$elementName,$size);
return $this->pass($stack,1);
}
function _expression_0_int_factor($stack,$coder){
$t1= $this->topItem($stack,1);
$elementName = $t1[TokenValueIndex];
$parseFuncName = 'parseInt';
$coder->pushParseBody($parseFuncName,$elementName,4);
return $this->pass($stack,1);
}
function _expression_0_factor($stack,$coder){
return $this->pass($stack,1);
}
// Expression }}}
// factor {{{
function _factor_0_term($stack,$coder){
return $this->pass($stack,1);
}
// factor }}}
// term {{{
function _term_0_lp_wholeExpression_rp($stack,$coder){
$t1= $this->topItem($stack,2);
$s='('.$t1[TokenValueIndex].')';
return [$s,[]];
}
function _term_0_term_dot_iden($stack,$coder){
$t1= $this->topItem($stack,3);
$obj = $t1[TokenValueIndex];
$t2= $this->topItem($stack,1);
$var = $t2[TokenValueIndex];
$exp = '$'.$obj.'[\''.$var.'\']';
return [$exp,[]];
}
function _term_0_iden($stack,$coder){
$t1= $this->topItem($stack,1);
//未指定数据长度时将长度值设为0
$valLen = '0';
$t2= $this->topItem($stack,2);
return [$t1[TokenValueIndex],[$valLen]];
}
function _term_0_num($stack,$coder){
return $this->pass($stack,1);
}
function _term_0_array($stack,$coder){
return $this->pass($stack,1);
}
// term }}}
// array {{{
function _array_0_arrayLb_num_rb($stack,$coder){
$t1= $this->topItem($stack,2);
$valLen = $t1[TokenValueIndex];
$t2= $this->topItem($stack,3);
//将数据长度放入附加信息
return [$t2[TokenValueIndex],[$valLen]];
}
function _arrayLb_0_iden_lb($stack,$coder){
return $this->pass($stack,2);
}
// array }}}
}// end of class
下面是改进过后的编码器的内容
<?php
/*!
* structwkr编码器,
*
* 45022300@qq.com
* Version 0.9.0
*
* Copyright 2019, Zhu Hui
* Released under the MIT license
*/
namespace Ados;
require_once __SCRIPTCORE__.'coder/base_coder.php';
require_once __STRUCT_PARSE_TEMP__.'templateReplaceFuncs.php';
class StructwkrCoder extends BaseCoder{
public function __construct($engine)
{
if($engine){
$this->engine = $engine;
}else{
exit('the engine is not valid in StructwkrCoder construct.');
}
}
//编译得到的最终结果
public function codeLines(){
if(count($this->codeLines)<1){
return '';
}
$script='';
for ($i=0;$i< count($this->codeLines);$i+=1) {
$script.=$this->codeLines[$i];
}
return $script;
}
//输出编译后的结果
public function printCodeLines(){
echo $this->codeLines();
}
//添加一个块解析函数头
public function pushLine($content){
array_push($this->codeLines, $content);
$lineIndex=$this->lineIndex;
$this->lineIndex+=1;
return $lineIndex;
}
//重置一行的内容
public function resetLine($lineIndex,$line){
$this->codeLines[$lineIndex]=$line;
}
//添加一个块解析函数头
public function pushBlockHeader($structName){
$structName=ucfirst($structName);
$content = makeBlockHeader($structName);
return $this->pushLine($content);
}
//添加一个块解析函数体
public function pushParseBody($parseFuncName,$filedName='',$filedSize=0){
$content = makeParseBody($parseFuncName,$filedName,$filedSize);
return $this->pushLine($content);
}
//添加一个管道处理
public function pushPipeBody($handler,$filedName=''){
$content = makePipeBody($handler,$filedName);
return $this->pushLine($content);
}
//添加一个检查结果值的body
public function pushCheckBody($filedName=''){
$content = makeCheckBody($filedName);
return $this->pushLine($content);
}
//添加一个块解析类的tail
public function pushBlockTail(){
$content = makeblockTail();
return $this->pushLine($content);
}
}
实现结果
自动生成的测试文件如下
<?php
namespace Ados;
//加载常量定义文件
require_once 'const.php';
require_once __STRUCT_PARSE_TEMP__.'templateBuidinFuncs.php';
require_once __STRUCT_PARSE_ADAPTER__.'int2str.adapter.php';
require_once __STRUCT_PARSE_ADAPTER__.'intoffset.adapter.php';
$context['pos']=0;
$context['data']="\x41\x43\x01\x00\x00\x00\x02\x00\x00\x00\x41\x42\x43\x41\x42\x01\x00\x00\x00\x41\x42\x43";
$expRes = Student::parse($context);
$context['pos']+=$expRes['size'];
print_r($expRes);
/*
$expRes = Teacher::parse($context);
$context['pos']+=$expRes['size'];
print_r($expRes);
*/
class Student{
static function parse($context,$size=0){
$valueArray=[];
$totalSize = 0;
$name = parseFixStr($context,2);
if($name['error']==0){
$filed = 'name';
if($filed){
$valueArray[$filed]=$name['value'];
}else{
$valueArray[]=$name['value'];
}
$context['pos']+=$name['size'];
$totalSize+= $name['size'];
}else{
return ['value'=>False,'size'=>0,'error'=>$name['error'],'msg'=>$name['msg']];
}
$num = parseInt($context,4);
if($num['error']==0){
$filed = 'num';
if($filed){
$valueArray[$filed]=$num['value'];
}else{
$valueArray[]=$num['value'];
}
$context['pos']+=$num['size'];
$totalSize+= $num['size'];
}else{
return ['value'=>False,'size'=>0,'error'=>$num['error'],'msg'=>$num['msg']];
}
if($num['value']==1){
$age = parseInt($context,4);
if($age['error']==0){
$filed = 'age';
if($filed){
$valueArray[$filed]=$age['value'];
}else{
$valueArray[]=$age['value'];
}
$context['pos']+=$age['size'];
$totalSize+= $age['size'];
}else{
return ['value'=>False,'size'=>0,'error'=>$age['error'],'msg'=>$age['msg']];
}
}else{
$addr = parseFixStr($context,3);
if($addr['error']==0){
$filed = 'addr';
if($filed){
$valueArray[$filed]=$addr['value'];
}else{
$valueArray[]=$addr['value'];
}
$context['pos']+=$addr['size'];
$totalSize+= $addr['size'];
}else{
return ['value'=>False,'size'=>0,'error'=>$addr['error'],'msg'=>$addr['msg']];
}
}
return ['value'=>$valueArray,'size'=>$totalSize,'error'=>0,'msg'=>'ok'];
}
}
运行测试文件的结果
Array
(
[value] => Array
(
[name] => AC
[num] => 1
[age] => 2
)
[size] => 10
[error] => 0
[msg] => ok
)
对比测试数据
$context['data']="\x41\x43\x01\x00\x00\x00\x02\x00\x00\x00\x41\x42\x43\x41\x42\x01\x00\x00\x00\x41\x42\x43";
由于字段 num的值为1,所以接下来产生了一个age字段,而并没有产生addr字段。
结论:if-else功能已经实现并通过了验证。