最新消息:

使用Bison和re2c构建词法分析和语法分析器

php admin 4382浏览 0评论

使用说明: 本文需要读者对C语言有一定的基础,对于re2c和bison有一些了解,最好也熟悉linux命令

我们在前面介绍了PHP的语法分析器-Bison入门PHP的词法解析器:re2c,那么如何将re2c与bison集成在一起的呢? 我们以一个从PHP源码中剥离出来的示例来说明整个过程。这个示例的功能与词法分析器的示例类似,作用都是识别输入参数中的字符串类型。 本示例是在其基础上添加了语法解析过程。 首先我们看这个示例的语法文件:demo.y

<span style="color: #e0882f;">%</span><span style="color: #ffffff;">{</span>
<span style="color: #bd48b3; font-style: italic;">#include &lt;stdio.h&gt;</span>
<span style="color: #bd48b3; font-style: italic;">#include "demo_scanner.h"</span>
<span style="color: #cc7833;">extern</span> <span style="color: #1299da;">int</span> yylex<span style="color: #ffffff;">(</span>znode <span style="color: #e0882f;">*</span>zendlval<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
<span style="color: #1299da;">void</span> yyerror<span style="color: #ffffff;">(</span><span style="color: #1299da;">char</span> <span style="color: #1299da;">const</span> <span style="color: #e0882f;">*</span><span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>

<span style="color: #bd48b3; font-style: italic;">#define YYSTYPE znode   //关键点一,znode定义在demo_scanner.h   </span>
<span style="color: #e0882f;">%</span><span style="color: #ffffff;">}</span>

<span style="color: #e0882f;">%</span>pure_parser    <span style="color: #bc9458; font-style: italic;">//  关键点二</span>

<span style="color: #e0882f;">%</span>token T_BEGIN
<span style="color: #e0882f;">%</span>token T_NUMBER
<span style="color: #e0882f;">%</span>token T_LOWER_CHAR
<span style="color: #e0882f;">%</span>token T_UPPER_CHAR
<span style="color: #e0882f;">%</span>token T_EXIT
<span style="color: #e0882f;">%</span>token T_UNKNOWN
<span style="color: #e0882f;">%</span>token T_INPUT_ERROR
<span style="color: #e0882f;">%</span>token T_END
<span style="color: #e0882f;">%</span>token T_WHITESPACE

<span style="color: #e0882f;">%%</span>

begin<span style="color: #e0882f;">:</span> T_BEGIN <span style="color: #ffffff;">{</span><a style="color: #1299da; text-decoration: none;" href="http://www.opengroup.org/onlinepubs/009695399/functions/printf.html"><span style="color: #e2392d;">printf</span></a><span style="color: #ffffff;">(</span><span style="color: #99ff00;">"begin:<span>n</span>token=%d<span>n</span>"</span><span style="color: #e0882f;">,</span> $1.<span style="color: #ffffff;">op_type</span><span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span><span style="color: #ffffff;">}</span>
     <span style="color: #e0882f;">|</span> begin variable <span style="color: #ffffff;">{</span>
        <a style="color: #1299da; text-decoration: none;" href="http://www.opengroup.org/onlinepubs/009695399/functions/printf.html"><span style="color: #e2392d;">printf</span></a><span style="color: #ffffff;">(</span><span style="color: #99ff00;">"token=%d "</span><span style="color: #e0882f;">,</span> $2.<span style="color: #ffffff;">op_type</span><span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
        <span style="color: #ff8400;">if</span> <span style="color: #ffffff;">(</span>$2.<span style="color: #ffffff;">constant</span>.<span style="color: #ffffff;">value</span>.<span style="color: #ffffff;">str</span>.<span style="color: #ffffff;">len</span> <span style="color: #e0882f;">&gt;</span> <span style="color: #1299da;">0</span><span style="color: #ffffff;">)</span> <span style="color: #ffffff;">{</span>
            <a style="color: #1299da; text-decoration: none;" href="http://www.opengroup.org/onlinepubs/009695399/functions/printf.html"><span style="color: #e2392d;">printf</span></a><span style="color: #ffffff;">(</span><span style="color: #99ff00;">"text=%s"</span><span style="color: #e0882f;">,</span> $2.<span style="color: #ffffff;">constant</span>.<span style="color: #ffffff;">value</span>.<span style="color: #ffffff;">str</span>.<span style="color: #ffffff;">val</span><span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
        <span style="color: #ffffff;">}</span>
        <a style="color: #1299da; text-decoration: none;" href="http://www.opengroup.org/onlinepubs/009695399/functions/printf.html"><span style="color: #e2392d;">printf</span></a><span style="color: #ffffff;">(</span><span style="color: #99ff00;">"<span>n</span>"</span><span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
<span style="color: #ffffff;">}</span>

variable<span style="color: #e0882f;">:</span> T_NUMBER <span style="color: #ffffff;">{</span>$$ <span style="color: #e0882f;">=</span> $<span style="color: #1299da;">1</span><span style="color: #e0882f;">;</span><span style="color: #ffffff;">}</span>
<span style="color: #e0882f;">|</span>T_LOWER_CHAR <span style="color: #ffffff;">{</span>$$ <span style="color: #e0882f;">=</span> $<span style="color: #1299da;">1</span><span style="color: #e0882f;">;</span><span style="color: #ffffff;">}</span>
<span style="color: #e0882f;">|</span>T_UPPER_CHAR <span style="color: #ffffff;">{</span>$$ <span style="color: #e0882f;">=</span> $<span style="color: #1299da;">1</span><span style="color: #e0882f;">;</span><span style="color: #ffffff;">}</span>
<span style="color: #e0882f;">|</span>T_EXIT <span style="color: #ffffff;">{</span>$$ <span style="color: #e0882f;">=</span> $<span style="color: #1299da;">1</span><span style="color: #e0882f;">;</span><span style="color: #ffffff;">}</span>
<span style="color: #e0882f;">|</span>T_UNKNOWN <span style="color: #ffffff;">{</span>$$ <span style="color: #e0882f;">=</span> $<span style="color: #1299da;">1</span><span style="color: #e0882f;">;</span><span style="color: #ffffff;">}</span>
<span style="color: #e0882f;">|</span>T_INPUT_ERROR <span style="color: #ffffff;">{</span>$$ <span style="color: #e0882f;">=</span> $<span style="color: #1299da;">1</span><span style="color: #e0882f;">;</span><span style="color: #ffffff;">}</span>
<span style="color: #e0882f;">|</span>T_END <span style="color: #ffffff;">{</span>$$ <span style="color: #e0882f;">=</span> $<span style="color: #1299da;">1</span><span style="color: #e0882f;">;</span><span style="color: #ffffff;">}</span>
<span style="color: #e0882f;">|</span>T_WHITESPACE <span style="color: #ffffff;">{</span>$$ <span style="color: #e0882f;">=</span> $<span style="color: #1299da;">1</span><span style="color: #e0882f;">;</span><span style="color: #ffffff;">}</span>

<span style="color: #e0882f;">%%</span>

<span style="color: #1299da;">void</span> yyerror<span style="color: #ffffff;">(</span><span style="color: #1299da;">char</span> <span style="color: #1299da;">const</span> <span style="color: #e0882f;">*</span>s<span style="color: #ffffff;">)</span> <span style="color: #ffffff;">{</span>
    <a style="color: #1299da; text-decoration: none;" href="http://www.opengroup.org/onlinepubs/009695399/functions/printf.html"><span style="color: #e2392d;">printf</span></a><span style="color: #ffffff;">(</span><span style="color: #99ff00;">"%s<span>n</span>"</span><span style="color: #e0882f;">,</span> s<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
<span style="color: #ffffff;">}</span>

这个语法文件有两个关键点:

1、znode是复制PHP源码中的znode,只是这里我们只保留了两个字段,其结构如下:

<span style="color: #1299da;">typedef</span> <span style="color: #1299da;">union</span> _zvalue_value <span style="color: #ffffff;">{</span>
    <span style="color: #1299da;">long</span> lval<span style="color: #e0882f;">;</span>                  <span style="color: #bd48b3; font-style: italic;">/* long value */</span>
    <span style="color: #1299da;">double</span> dval<span style="color: #e0882f;">;</span>                <span style="color: #bd48b3; font-style: italic;">/* double value */</span>
    <span style="color: #1299da;">struct</span> <span style="color: #ffffff;">{</span>
        <span style="color: #1299da;">char</span> <span style="color: #e0882f;">*</span>val<span style="color: #e0882f;">;</span>
        <span style="color: #1299da;">int</span> len<span style="color: #e0882f;">;</span>
    <span style="color: #ffffff;">}</span> str<span style="color: #e0882f;">;</span>
<span style="color: #ffffff;">}</span> zvalue_value<span style="color: #e0882f;">;</span>

<span style="color: #1299da;">typedef</span> <span style="color: #1299da;">struct</span> _zval_struct <span style="color: #ffffff;">{</span>
    <span style="color: #bd48b3; font-style: italic;">/* Variable information */</span>
    zvalue_value value<span style="color: #e0882f;">;</span>     <span style="color: #bd48b3; font-style: italic;">/* value */</span>
    <span style="color: #1299da;">int</span> type<span style="color: #e0882f;">;</span>    <span style="color: #bd48b3; font-style: italic;">/* active type */</span>
<span style="color: #ffffff;">}</span>zval<span style="color: #e0882f;">;</span>

<span style="color: #1299da;">typedef</span> <span style="color: #1299da;">struct</span> _znode <span style="color: #ffffff;">{</span>
    <span style="color: #1299da;">int</span> op_type<span style="color: #e0882f;">;</span>
    zval constant<span style="color: #e0882f;">;</span>
<span style="color: #ffffff;">}</span>znode<span style="color: #e0882f;">;</span>

这里我们同样也复制了PHP的zval结构,但是我们也只取了关于整型,浮点型和字符串型的结构。 op_type用于记录操作的类型,constant记录分析过程获取的数据。 一般来说,在一个简单的程序中,对所有的语言结构的语义值使用同一个数据类型就足够用了。比如在前一小节的逆波兰记号计算器示例就只有double类型。 而且Bison默认是对于所有语义值使用int类型。如果要指明其它的类型,可以像我们示例一样将YYSTYPE定义成一个宏:

<span style="color: #bd48b3; font-style: italic;">#define YYSTYPE znode</span>

2、%pure_parser 在Bison中声明%pure_parse表明你要产生一个可重入(reentrant)的分析器。默认情况下Bison调用的词法分析函数名为yylex,并且其参数为void,如果定义了YYLEX_PARAM,则使用YYLEX_PARAM为参数, 这种情况我们可以在Bison生成的.c文件中发现其是使用#ifdef实现。

如果声明了%pure_parser,通信变量yylval和yylloc则变为yyparse函数中的局部变量,变量yynerrs也变为在yyparse中的局部变量,而yyparse自己的调用方式并没有改变。比如在我们的示例中我们声明了可重入,并且使用zval类型的变更作为yylex函数的第一个参数,则在生成的.c文件中,我们可以看到yylval的类型变成

一个可重入(reentrant)程序是在执行过程中不变更的程序;换句话说,它全部由纯(pure)(只读)代码构成。 当可异步执行的时候,可重入特性非常重要。例如,从一个句柄调用不可重入程序可能是不安全的。 在带有多线程控制的系统中,一个非可重入程序必须只能被互锁(interlocks)调用。

通过声明可重入函数和使用znode参数,我们可以记录分析过程中获取的值和词法分析过程产生的token。 在yyparse调用过程中会调用yylex函数,在本示例中的yylex函数是借助re2c生成的。 在demo_scanner.l文件中定义了词法的规则。大部分规则是借用了上一小节的示例, 在此基础上我们增加了新的yylex函数,并且将zendlval作为通信变量,把词法分析过程中的字符串和token传递回来。 而与此相关的增加的操作为:

SCNG(yy_text) = YYCURSOR;   //  记录当前字符串所在位置
/*!re2c
  <!*> {yyleng = YYCURSOR - SCNG(yy_text);} //  记录字符串长度

main函数发生了一些改变:

int main(int argc, char* argv[])
{
    BEGIN(INITIAL); //  全局初始化,需要放在scan调用之前
    scanner_globals.yy_cursor = argv[1];    //将输入的第一个参数作为要解析的字符串

    yyparse();
    return 0;
}

在新的main函数中,我们新增加了yyparse函数的调用,此函数在执行过程中会自动调用yylex函数。

如果需要运行这个程序,则需要执行下面的命令:

re2c -o demo_scanner.c -c -t demo_scanner_def.h demo_scanner.l
bison -d demo.y
gcc -o t demo.tab.c demo_scanner.c
chmod +x t
./t "<?php tipi2011"

相关代码下载请移步TIPI项目

转载请注明:爱开源 » 使用Bison和re2c构建词法分析和语法分析器

您必须 登录 才能发表评论!