引言
在文章《php 實作類似pyhon中的Construct函式庫的功能(一)基本設計思路》介紹了用php解析二進位資料的基本思路
在文章《php 實作類似pyhon中的Construct函式庫的功能(二)實作適配器功能》說明如何實作適配器功能。
以上兩篇都是對靜態資料結構進行解析。接下來要逐步實作動態資料結構的解析。也就是說資料結構的定義與上下文有關,要在資料解析時才能真正確定。
推薦相關PHP影片教學:https://www.php.cn/course/list/29/type/2.html
這次要實現的是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,[]]; }
下面是完整的語法規則處理文件的內容
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功能已经实现并通过了验证。
更多相关问题请访问PHP中文网:https://www.php.cn/
以上是php 實作類似pyhon中的Construct函式庫的功能(三)實作if-else功能的詳細內容。更多資訊請關注PHP中文網其他相關文章!