引言
在文章《php 实现类似于pyhon中的Contruct库的功能(一) 基本设计思路》介绍了用php解析二进制数据的基本思路
在文章《php 实现类似于pyhon中的Contruct库的功能(二)实现适配器功能》说明了如何实现适配器功能。
在文章《php 实现类似于pyhon中的Contruct库的功能(三)实现if-else功能》说明了如何if-else功能。
这一次要实现的是do-while功能。
推荐PHP视频教程:https://www.php.cn/course/list/29/type/2.html
基本思路
1,修改词法分析规则,使其可以接受do,while关键字,为了引用内部变量,还需要识别 $ 符号
2,修改语法分析规则,使其可以接受do,while语句
3,修改编码器,生成可运行的php目标代码
4,目标代码的模板文件也要做一些小的修改
其中主要的工作内容还是修改语法分析规则文件。
实现内容
准备解析的结构体定义文件
struct student { char name[2]; do{ int; }while($lastError == 0); };
为了聚焦在do-while功能的实现上,这次只定义了一个结构体 student。循环语句定义如下:
do{ int; }while($lastError == 0);
解析过程中有一个内部变量 $lastError,保存每次解析的错误代号,如果为0则表示没有错误。
下面是完整的语法规则处理文件的内容
<?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('')]; } if($tokenName=='_do'){ //插入一个空行,空行所在的序号存入附加信息数组,以后可以替换为正确的内容 $coder->isInLoop = True; return [$coder->pushLine('')]; } if($tokenName=='_while'){ //插入一个空行,空行所在的序号存入附加信息数组,以后可以替换为正确的内容 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); } // statement }}} // if {{{ function _statement_0_ifStatement($stack,$coder){ return $this->pass($stack,1); } 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 }}} // do while {{{ function _statement_0_whileStatement($stack,$coder){ return $this->pass($stack,1); } function _whileStatement_0_doBlock_whileExpression_semi($stack,$coder){ $t1= $this->topItem($stack,2); $condtionExp=$t1[TokenValueIndex]; $content='while('.$condtionExp.');'; $coder->pushLine($content); $coder->isInLoop = False; return $this->pass($stack,3); } function _whileExpression_0_whileLp_wholeExpression_rp($stack,$coder){ return $this->pass($stack,2); } function _whileLp_0_while_lp($stack,$coder){ return $this->pass($stack,2); } function _doBlock_0_do_blockStatement($stack,$coder){ //取出_do 记号中保存的空白行所在的地址,替换为正确的内容 $t1= $this->topItem($stack,2); $lineIndex=$t1[TokenExtraIndex][0]; $content='do {'; $coder->resetLine($lineIndex,$content); $coder->pushLine('}'); return $this->pass($stack,1); } // do while }}} // 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_int($stack,$coder){ $elementName = ''; $parseFuncName = 'parseInt'; $coder->pushParseBody($parseFuncName,$elementName,4); return ['',[]]; } 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_dollar_iden($stack,$coder){ $t1= $this->topItem($stack,1); $exp = '$'.$t1[TokenValueIndex]; 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.'); } $this->isInLoop = False; } //编译得到的最终结果 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=''){ if($this->isInLoop){ $content = makeCheckBodyInLoop($filedName); }else{ $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\x42\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); $lastError = $name['error']; /*checkBodyInLoop{{*/ if($name['error']==0){ $filed = 'name'; if($filed){ $valueArray[$filed]=$name['value']; }else{ $valueArray[]=$name['value']; } $context['pos']+=$name['size']; $totalSize+= $name['size']; } /*checkBodyInLoop}}*/ else{ return ['value'=>False,'size'=>0,'error'=>$name['error'],'msg'=>$name['msg']]; } do { $expRes = parseInt($context,4); $lastError = $expRes['error']; if($expRes['error']==0){ $filed = ''; if($filed){ $valueArray[$filed]=$expRes['value']; }else{ $valueArray[]=$expRes['value']; } $context['pos']+=$expRes['size']; $totalSize+= $expRes['size']; } }while($lastError==0); return ['value'=>$valueArray,'size'=>$totalSize,'error'=>0,'msg'=>'ok']; } }
可以看出在测试文件中自动生成了 do-while循环结构
运行测试文件的结果
Array ( [value] => Array ( [name] => AB [0] => 1 [1] => 2 [2] => 1094926913 [3] => 322 [4] => 1128415488 ) [size] => 22 [error] => 0 [msg] => ok )
对比测试数据
$context['data']="\x41\x42\x01\x00\x00\x00\x02\x00\x00\x00\x41\x42\x43\x41\x42\x01\x00\x00\x00\x41\x42\x43";
在解析完两个字节的定长字符串 ‘AB’之后,开始按4个节一段 循环解析 整数值,直到剩下的内容无法解析为一个整数为止。
结论:do-while功能已经实现并通过了验证。
更多相关问题请访问PHP中文网:https://www.php.cn/
以上是php 实现类似于pyhon中的Construct库的功能(四)实现do-while功能的详细内容。更多信息请关注PHP中文网其他相关文章!