首頁  >  文章  >  後端開發  >  php 實作類似pyhon中的Construct函式庫的功能(三)實作if-else功能

php 實作類似pyhon中的Construct函式庫的功能(三)實作if-else功能

王林
王林轉載
2019-08-19 16:48:502127瀏覽

引言

在文章《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__.&#39;coder/base_coder.php&#39;;
require_once __STRUCT_PARSE_TEMP__.&#39;templateReplaceFuncs.php&#39;;


class StructwkrCoder extends BaseCoder{

	public function __construct($engine)
	{
		if($engine){ 
			$this->engine = $engine;
		}else{ 
			exit(&#39;the engine is not valid in StructwkrCoder construct.&#39;);
		}
	}

	//编译得到的最终结果
	public function codeLines(){
		if(count($this->codeLines)<1){
			return &#39;&#39;;
		}
		$script=&#39;&#39;;		
		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=&#39;&#39;,$filedSize=0){
		$content = makeParseBody($parseFuncName,$filedName,$filedSize);
		return $this->pushLine($content);
	}

	//添加一个管道处理
	public function pushPipeBody($handler,$filedName=&#39;&#39;){
		$content = makePipeBody($handler,$filedName);		
		return $this->pushLine($content);
	}

	//添加一个检查结果值的body
	public function pushCheckBody($filedName=&#39;&#39;){
		$content = makeCheckBody($filedName);
		return $this->pushLine($content);
	}

	//添加一个块解析类的tail
	public function pushBlockTail(){
		$content = makeblockTail();
		return $this->pushLine($content);
	}	

}

实现结果

自动生成的测试文件如下

<?php

namespace Ados;

//加载常量定义文件
require_once &#39;const.php&#39;;
require_once __STRUCT_PARSE_TEMP__.&#39;templateBuidinFuncs.php&#39;;
require_once __STRUCT_PARSE_ADAPTER__.&#39;int2str.adapter.php&#39;;
require_once __STRUCT_PARSE_ADAPTER__.&#39;intoffset.adapter.php&#39;;

$context[&#39;pos&#39;]=0;
$context[&#39;data&#39;]="\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[&#39;pos&#39;]+=$expRes[&#39;size&#39;];
print_r($expRes);

/*
$expRes = Teacher::parse($context);
$context[&#39;pos&#39;]+=$expRes[&#39;size&#39;];
print_r($expRes);
*/



class Student{

	static function parse($context,$size=0){
		$valueArray=[];
		$totalSize = 0;
	
		$name = parseFixStr($context,2);
	
		if($name[&#39;error&#39;]==0){
			$filed = &#39;name&#39;;
			if($filed){
				$valueArray[$filed]=$name[&#39;value&#39;];
			}else{
				$valueArray[]=$name[&#39;value&#39;];
			}		
			$context[&#39;pos&#39;]+=$name[&#39;size&#39;];
			$totalSize+= $name[&#39;size&#39;];
		}else{
			return [&#39;value&#39;=>False,&#39;size&#39;=>0,&#39;error&#39;=>$name[&#39;error&#39;],&#39;msg&#39;=>$name[&#39;msg&#39;]];
		}
	
		$num = parseInt($context,4);
	
		if($num[&#39;error&#39;]==0){
			$filed = &#39;num&#39;;
			if($filed){
				$valueArray[$filed]=$num[&#39;value&#39;];
			}else{
				$valueArray[]=$num[&#39;value&#39;];
			}		
			$context[&#39;pos&#39;]+=$num[&#39;size&#39;];
			$totalSize+= $num[&#39;size&#39;];
		}else{
			return [&#39;value&#39;=>False,&#39;size&#39;=>0,&#39;error&#39;=>$num[&#39;error&#39;],&#39;msg&#39;=>$num[&#39;msg&#39;]];
		}
	if($num[&#39;value&#39;]==1){
		$age = parseInt($context,4);
	
		if($age[&#39;error&#39;]==0){
			$filed = &#39;age&#39;;
			if($filed){
				$valueArray[$filed]=$age[&#39;value&#39;];
			}else{
				$valueArray[]=$age[&#39;value&#39;];
			}		
			$context[&#39;pos&#39;]+=$age[&#39;size&#39;];
			$totalSize+= $age[&#39;size&#39;];
		}else{
			return [&#39;value&#39;=>False,&#39;size&#39;=>0,&#39;error&#39;=>$age[&#39;error&#39;],&#39;msg&#39;=>$age[&#39;msg&#39;]];
		}
	}else{
		$addr = parseFixStr($context,3);
	
		if($addr[&#39;error&#39;]==0){
			$filed = &#39;addr&#39;;
			if($filed){
				$valueArray[$filed]=$addr[&#39;value&#39;];
			}else{
				$valueArray[]=$addr[&#39;value&#39;];
			}		
			$context[&#39;pos&#39;]+=$addr[&#39;size&#39;];
			$totalSize+= $addr[&#39;size&#39;];
		}else{
			return [&#39;value&#39;=>False,&#39;size&#39;=>0,&#39;error&#39;=>$addr[&#39;error&#39;],&#39;msg&#39;=>$addr[&#39;msg&#39;]];
		}
	}
		return [&#39;value&#39;=>$valueArray,&#39;size&#39;=>$totalSize,&#39;error&#39;=>0,&#39;msg&#39;=>&#39;ok&#39;];
	}
}	

运行测试文件的结果

Array
(
    [value] => Array
        (
            [name] => AC
            [num] => 1
            [age] => 2
        )

    [size] => 10
    [error] => 0
    [msg] => ok
)

对比测试数据

$context[&#39;data&#39;]="\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中文網其他相關文章!

陳述:
本文轉載於:csdn.net。如有侵權,請聯絡admin@php.cn刪除