引言
在文章《php 实现类似于pyhon中的Contruct库的功能(一) 基本设计思路》介绍了用php解析二进制数据的基本思路
在文章《php 实现类似于pyhon中的Contruct库的功能(二)实现适配器功能》说明了如何实现适配器功能。
在文章《php 实现类似于pyhon中的Contruct库的功能(三)实现if-else功能》说明了如何if-else功能。
这一次要实现的是do-while功能。
基本思路
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功能已经实现并通过了验证。