PHP语法分析器,入门教程

 百家乐-前端     |      2020-05-07 05:10

  switch (yyn)
{
case 2:

/* Line 1455 of yacc.c  */
#line 30 “parse.y”
{printf(“startn”); ;}
break;

四、The Scanner as Coroutine(合作程序)

即如何将围观到的标志给别的程序行使,上面包车型大巴例子,希望扫描到+ 或 -时做多少个异样输出。

当调用yylex时,若扫描到return对应的标识时,yylex重返,且值就为return后的值;

若没扫描到return对应的标志,yylex继续实践,不回去。

后一次调用电动早前三回的围观地方处早先。

%{
enum yytokentype {
    ADD = 259,
    SUB = 260, 
};
%}
myadd   "+"
mysub   "-"
myother .
%%
{myadd}    { return ADD; }
{mysub}    { return SUB; }
{myother}  { printf("Mystery charactern"); }
%%
main(int argc, char **argv)
{
    int tok;
    while(tok = yylex()) {              //yylex的返回值只能是ADD 或 SUB.
        if(tok == ADD || tok == SUB) {printf("meet + or -n");}
        else {printf("this else statement will not be printed, 
            because if yylex return,the retrun value must be ADD or SUB.");}
    }
}

 

图片 1

 

/* #include 文件*/
/*宏定义*/
//扫描函数
int scan(char *p){
/*扫描器规则区*/
}
//执行scan扫描函数,返回token到yacc/bison中。
int yylex(){
        int token;
        char *p=YYCURSOR;//YYCURSOR是一个指针,指向我们的PHP文本内容
        while(token=scan(p)){//这里会移动指针p,一个一个判断是不是我们上面定义好的scanner...
                return token;
        }
}
int main(int argc,char**argv){
        BEGIN(INITIAL);//
        YYCURSOR=argv[1];//YYCURSOR是一个指针,指向我们的PHP文本内容,
        yyparse();
}

六、flex和bison相结合。

test.l

%{  
#include "test.tab.h"  
#include <stdio.h>  
#include <stdlib.h>  
%}  
%%  
a   {return A_STATE;}  
b   {return B_STATE;}  
c   {return C_STATE;}  
not   {return NOT;}  
%%

test.y

%{  
#include <stdio.h>  
#include <stdlib.h>  
%}  
%token  A_STATE B_STATE C_STATE NOT  
%%  
program :     
    A_STATE B_STATE {  
        printf("1");  
    }  
    c_state_not_token  {  
        printf("2");  
    }  
    |    NOT {   
        printf("3");  
    }  
c_state_not_token : C_STATE {}  
%% 
yyerror(const char *s)
{
    fprintf(stderr, "error: %sn", s);
} 
int main()
{
    yyparse();
    return 0;
}

编译:

图片 2

 

 

注:图中a.c是 扫描器生成的尾声代码。。

 

或许会有一些绕,重新缕一缕:

补充 :$$ $1 $2….

Each symbol in a bison rule has a value; the value of the target symbol (the one to the
left of the colon) is called $$ in the action code, and the values on the right are numbered
$1, $2, and so forth, up to the number of symbols in the rule.

$$——表示冒号的侧面符号;$1——冒号侧面第三个;$2——冒号左边第四个,就那样推算。

如record : NAME EQ AGE { printf("%s is %s years old!!!n", $1, $3); } ;

相称NAME EQ AGE后,$1即NAME所表示的内容,$3即AGE所表示的剧情。

lex yacc 入门教程(3)正则表明式和lex变量及函数

 图片 3图片 4 图片 5

参考:



深入分析器小编用的是flex和bison。。。

一、简介

推荐介绍书籍《flex&bison》.

在UNIX下是flex和bison.互连网介绍非常多,半数以上是写给懂的人看的,初读书人一头雾水。那样来明白lex和yacc恐怕轻易些:在linux下,有过多系统构造文件,一些linux下的软件也可以有安顿文件,那么程序是什么样读取配置文件中的音信的吗?

首先用到lex词法解析器,读取配置文件中的关键词(前面提起的token标志其实可作为关键词)。然后把首要词

递交给yacc,yacc对一部分至关心注重要词进行相称,看是或不是顺应一定语法逻辑,如若相符就进行相应动作。

地点举得例子是深入分析配置文件内容的,当然可深入分析任何文件内容。

写那篇小说一是因为那上边材料太少,二是把本身的收获总计下来,以便以后参谋,如若能知晓PHP语法解析

申明:原创文章,转载注脚出处

YYCONDTYPE
用-c 形式你能够动用-to参数用来生成三个文书:使用含有枚举类型的当作标准。各样值都会在法规集结里面作为基准来利用。
YYCTYPE
用来保持二个输入符号。平日是 char 也许unsigned char。
YYCTXMARKER
*YYCTYPE类型的表明式,生成的代码回溯音信的前后文子禽保存在 YYCTXMA奥迪Q5KE瑞虎。若是扫描器准绳须要使用上下文中的一个或多少个正则表达式则顾客供给定义那么些宏。
YYCURSOR
*YYCTYPE类型的表明式指针指向当前输入的符号,生成的代码作为标识相相称,在开始的地点,YYCU奥迪Q5SOTiggo假定指向当前token的第八个字符。甘休时,YYCU牧马人SOWrangler将会指向下七个token的首先个字符。
YYDEBUG(state,current)
本条唯有钦点-d标示符的时候才会要求。调用客户定义的函数时能够特别轻松的调理生成的代码。
其一函数应该有以下具名:void YYDEBUG(int state,char current卡塔尔。第三个参数选用 state ,暗中同意值为-1次之个参数选取输入的当下地点。
YYFILL(n)
当缓冲器须求填写的时候,生成的代码将会调用YYFILL(nState of Qatar:起码提供n个字符。YYFILL(n卡塔尔将会依附供给调治YYCU本田UR-VSOPRADO,YYLIMIT,YYMA凯雷德KEOdyssey和 YYCTXMA巴博斯 SLS级KEOdyssey。注意在第一名的程序语言此中,n等于最长的重大词的长度加一。顾客能够在/*!max:re2c*/壹回定义YYMAXFILL来钦定最长长度。倘使接纳了-1,YYMAXFILL将会在/*!re2c*/之后调用壹回堵塞。
YYGETCONDITION()
假若接受了-c形式,这么些定义将会在扫描器代码此前获得条件集。那几个值,必需初叶化为枚举YYCONDTYPE的项目。
YYGETSTATE()
即便-f格局钦赐了,客商就须要定义那么些宏。若是如此,扫描器在带头时为了获取保存的景色,生成的代码将会调用YYGETSTATE(卡塔尔,YYGETSTATE(卡塔尔必需回到三个带符号的卡尺头,那些值就算是-1,告诉扫描器那是首先次实行,不然那几个值等于以前YYSETSTATE(s)保存的境况。不然,扫描器将会回复操作之后立时调用YYFILL(n卡塔尔。
YYLIMIT
表达式的类型 *YYCTYPE 标志缓冲器的末梢(YYLIMIT(-1卡塔尔是缓冲区的结尾四个字符)。生成的代码将会四处的可比YYCOEnclaveSU陆风X8和 YYLIMIT 以调控 何时填充缓冲区。
YYSETCONDITION(c)
本条宏用来在转变准则中安装规范,它只会在内定-c情势 和 使用转换规则时有用。
YYSETSTATE(s)
客户只要求在钦定-f形式时定义那个宏,假如是如此,生成的代码将会在YYFILL(n卡塔尔以前调用YYSETSTATE(s卡塔尔(قطر‎,YYSETSTATE的参数是一个有暗记整型,被称作独一的标识特定的YYFILL(n卡塔尔实例。
YYMARKER
类型为*YYCTYPE的表明式,生成的代码保存回溯音讯到YYMA奥迪Q5KELAND。一些简便的扫描器或许用不到。

五、yacc —— unix下是bison

1、yacc语准绳则部分和BNF类同,先来看BNF巴克斯范式。

(1)<> 内包罗的剧情为必选项;

(2)[]  内的隐含的内容为可筛选;

(3){ } 内富含的为可重复0至广大次的项;

(4) | 表示在其左右两侧任选一项,也等于"O科雷傲"的情致;

(5)::= 是“被定义为”的意趣;

(6)双引号“”内的内容表示这个字符本人;而double _quote用来代表双引号。

(7)BNF范式举个例子,上面包车型客车事例用来定义java中的for语句:

     FOR_STATEMENT ::=

  "for" "(" ( variable_declaration |

  ( expression ";" ) | ";" )

  [ expression ] ";"

  [ expression ]

  ")" statement

2、yacc语法。

result: components { /*
        action to be taken in C */ }
        ;

(1)components是依据准绳放在一同的极点和非终端符号,前面是{}括起来的试行的动作。

3、语法例子。

param : NAME EQ NAME { 
    printf("tName:%stValue(name):%sn", $1,$3); }         
    | NAME EQ VALUE {
    printf("tName:%stValue(value):%sn",$1,$3);}
    ;

simple_sentence: subject verb object
      |     subject verb object prep_phrase ;
subject:    NOUN
      |     PRONOUN
      |     ADJECTIVE subject ;
verb:       VERB
      |     ADVERB VERB
      |     verb VERB ;
object:     NOUN
      |     ADJECTIVE object ;
prep_phrase:     PREPOSITION NOUN ;

(1)精通 |  的意味,|表示左右两侧任选一项,如| subject verb object prep_phrase ;中|的右侧为空,

故而该句表示匹配空或许subject verb object prep_phrase ;而地方还可能有一句subject verb object ,

所以

simple_sentence: subject verb object

              | subject verb object prep_phrase ;

的意趣是相配subject verb object 或 subject verb object prep_phrase ;

大家写的(f卡塔尔lex语准绳则,譬喻大家叫他Parse.y

八、token定义的号子的门类及union的使用。

token定义的暗号的花色默以为int 且 暗中认可赋值从258初叶。如下边的例证,在扭转的头文件

test.tab.h中好似下预编写翻译,

/* Tokens.  */
#ifndef YYTOKENTYPE
# define YYTOKENTYPE
   /* Put the tokens into the symbol table, so that GDB and other debuggers
      know about them.  */
   enum yytokentype {
     NAME = 258,
     EQ = 259,
     AGE = 260
   };
#endif

比如想将token标识定义为任何种类呢?首先将类型定义在一齐中,

%union {
   char *str;
   int  num;
   struct { int num1; int num2; } dnum;
}

然后,如下概念,

%token <str> K_HOST K_ERROR
%token <str> WORD PATH STRING
%token <num> NUM 
%token <dnum> DNUM 

yyparse会依据scan重返的标识做switch,然后goto到对应的代码,举例yyparse.y开掘眼下的token是T_OPEN_TAG,

二、三个简短的lex文件例子

1、来看flex&bison那本书开篇给出的例子:输入几行字符串,输出游数,单词数和字符的个数。

关于yylex即lex中相关变量类别3文章介绍。

/* just like Unix wc */
%{
int chars = 0;
int words = 0;
int lines = 0;
%}
%%
[a-zA-Z]+  { words++; chars += strlen(yytext); }
n         { chars++; lines++; }
.          { chars++; }
%%
main(int argc, char **argv)
{
  yylex();
  printf("%8d%8d%8dn", lines, words, chars);
}

2、依据上面进程编写翻译。

#flex test.l

#gcc lex.yy.c –lfl

#./a.out

 图片 6

 

3、深入分析这一个大约的lex文件:

(1)%%把文件分为3段,第一段是c和lex的大局表明,第二段是准绳段,第三段是c代码。

(2)第一段的c代码要用%{和%}括起来,第三段的c代码不用。

(3)第二段准则段,[a-zA-Z]+  n   . 是正则表明式,{}内的是c编写的动作。

关黄浩可是表明式类别3随笔介绍。

 

4、假设不用-lfl选项,代码可认为上边那样(具体原因见lex的库和函数剖析):

int chars = 0;
int words = 0;
int lines = 0;
int yywrap();
%}
%%
[a-zA-Z]+  { words++; chars += strlen(yytext); }
n         { chars++; lines++; }
.          { chars++; }
%%
main(int argc, char **argv)
{
  yylex();
  printf("%8d%8d%8dn", lines, words, chars);
}
int yywrap()
{
    return 1;
}

三、校勘第叁个例子,将正则表达式放在全局注脚中

%{
int chars = 0;
int words = 0;
int lines = 0;
%}
mywords [a-zA-Z]+ 
mylines n 
mychars .  
%%
{mywords}  { words++; chars += strlen(yytext); }
{mylines}  { chars++; lines++; }
{mychars}  { chars++; }
%%
main(int argc, char **argv)
{
  yylex();
  printf("%8d%8d%8dn", lines, words, chars);
}

编写翻译一齐上。

比方大家PHP代码是 “echo 1″;

七、文件消息深入分析。

tset.l剖析test.txt文件中的关键词(即test.y中的token标识),境遇token再次来到给test.y,test.y判断

是否顺应自然语法,切合则张开对应动作。

test.l

%{
#include "test.tab.h"

#include <stdio.h>
#include <string.h>
%}
char [A-Za-z]
num [0-9]
eq [=]
name {char}+
age {num}+
%%
{name}      { yylval = strdup(yytext); return NAME; }
{eq}        { return EQ; }
{age}       { yylval = strdup(yytext); return AGE; }
%%
int yywrap()
{
    return 1;
}

test.y

%{
#include <stdio.h>  
#include <stdlib.h> 
typedef char* string;
#define YYSTYPE string
%}
%token NAME EQ AGE
%%
file : record file
    | record
;
record : NAME EQ AGE {
                printf("%s is %s years old!!!n", $1, $3); }
;
%%
int main()
{
    extern FILE* yyin;
    if(!(yyin = fopen("test.txt", "r")))
    {
        perror("cannot open parsefile:");
        return -1;
    }    
    yyparse();
    fclose(yyin);
    return 0;
}
int yyerror(char *msg)
{
    printf("Error encountered: %s n", msg);
}

test.txt

ZhangSan=23
LiSi=34
WangWu=43

编译

图片 7

有关flex的公文布局:

举例:

在scanner.l中,调用scan的是个while循环,所以它会检查到php代码的末段,

%{
/*
C代码段将逐字拷贝到lex编写翻译后发出的C源文件中
可以定义一些全局变量,数组,函数例程等…
*/
#include
#include “scanner.h”
extern int yylex(State of Qatar;//它在scanner.l中定义的。。
void yyerror(char *);
# define YYPARSE_PARAM tsrm_ls
# define YYLEX_PARAM tsrm_ls
%}
{定义段,也等于token定义之处}
//那正是着重  token程序是基于那是做switch的。
%token T_OPEN_TAG
%token T_ECHO
%token T_LNUMBER
%%
{规则段}
start:
T_OPEN_TAG{printf(“startn”); }
|start statement
;
statement:
T_ECHO expr {printf(“echo :%sn”,$3)}
;
expr:
T_LNUMBER {$$=$1;}
%%
{顾客代码段}
void yyerror(char *msg){
printf(“error:%sn”,msg);
}

258是bison自动生成的枚举类型数据。

那深入深入分析器呢?

只是由于各样原因,暂停了此项目。

接口代码:

图片 8

bison会扭转超多用来映射的数组,将最终的translate保存到yyn,

在法则段中,start是起头的地点,假诺scan识别到PHP在此以前标签就能够再次回到T_OPEN_TAG,然后实践括号的代码,输出start.

其有的时候候yychar是258,258是何许?

进入正题

个中有二个关键宏: YYLEX

我们写的规规矩矩,生成不一致的token传递给parse。

继续

本条项目思路源于facebook的开源项目 HipHop .

如此bison就能够找到token所对应的代码

#define YYCTYPE char   //输入符号的品类
#define STATE(name)     yyc##name
#define BEGIN(n)        YYSETCONDITION(STATE(n))
#define LANG_SCNG(v)    (sc_globals.v)
#define SCNG    LANG_SCNG
#define YYGETCONDITION()        SCNG(yy_state)
#define YYSETCONDITION(s)       SCNG(yy_state)=s

它的流水生产线如下,读取PHP文件,拆解深入分析PHP代码,对其进展语法解析器,生成对应的ZendAPI,编写翻译成扩张。

YYTRANSLATE宏接收yychar,然后再次回到所对应的值

扫描器函数scan会取得”echo 1″字符串,它对这一段代码进行巡回,要是发掘成echo字符串,那么它就作为最主要字再次来到token:T_ECHO,

环顾个中有八个法则:

图片 9

编译到PHP中,我叫它 phptoc。

re2c提供了一些宏接口,方面我们应用,我回顾做了翻译,韩语水平不佳,也许有误,必要原著的能够去地点十一分地点查看。

这,TOKEN重返给yyparse之后做了何等啊?

到此地,全体的流水生产线就结束了。。剩下的正是细化的干活,假若偶尔间,小编再持续phptoc :)

re2c && yacc/bison,通过援用自个儿的对应文件,然后将他们联合编写翻译成八个*.c文件,最终再gcc编写翻译就能够生

它会经过宏 #line 映射到 parse.y所对应 21行,T_OPEN_TAG的职分,然后试行