小白说编译原理-5-变量支持计算器

简介

本章依然专注于使用yacc实现计算器,主要的特点是给算术运算增加变量支持。

模块拆分

它主要分为3个模块

1. lex词法分析器

2. yacc语法分析器

3. 符号表

功能描述

1. lex词法分析器

正规式的定义如下:

    delim [ \t]
    ws    {delim}+
    letter [a-zA-Z]
    digit [0-9]
    id    {letter}({letter}|{digit})*
    /* can support 12.34 */
    number {digit}+(\.{digit}+)?

相当于是给一些经常使用的正则表达式起一个别名,然后利用别名即可以构造更复杂的正则表达式。例如id标示符是由letter以及digit的组合形成的,表示必须以字母开头,后面可以字母和数字的任意组合。

词法分析器的动作定义(转换规则)

    {ws} {/* do nothing */}
    "int"  {print_token(INT, yytext); return INT;}
    "double"  {print_token(DOUBLE, yytext);}
    "char"  {print_token(CHAR, yytext);}

    "+"         {print_token(PLUS, yytext); return PLUS;}
    "-"         {print_token(MINUS, yytext); return MINUS;}
    "*"         {print_token(TIMES, yytext); return TIMES;}
    "/"         {print_token(OVER, yytext); return OVER;}
    "("         {return LP;}
    ")"         {return RP;}
    "\n"        {return EOL;}
    "="         {return ASSIGN;}
    {id}        {
                    int p = sym_table.lookup(yytext);
                    if(p == -1){//not find
                        p = sym_table.insert(yytext);//insert the default value 0.0
                    }
                    yylval = p;//return the position
                    return ID;
                }
    {number}    {yylval = atof(yytext);return NUMBER;}//yylval保存数字的值,NUMBER只是token标记
    "//".*      {return COMMENT;}
    "."         {printf("Mystery character %s\n", yytext); }

上述每一组代表一个转换规则,对于一个规则,它的左边代表要匹配的模式串,右边表示要执行的词法动作。例如对于规则 “+” {print_token(PLUS, yytext); return PLUS;}, 它表示在解析出”+”后,它将执行print_token,并返回PLUS标记(PLUS在yacc中定义)。

运算符识别:

从上面定义的规则可以看出,此词法分析支持+,-,*,/,(,)这些基本的算术运算。

number的识别:

{number} {yylval = atof(yytext);return NUMBER;} //yylval保存数字的值,NUMBER只是token标记

不再像(小白说编译原理-3)那样,将number的识别放在yacc中,利用cin的方式得到数字,而是利用词法分析的正规式的表达,将得到的结果赋值给yylval**(yylval = atof(yytext))**,然后返回NUMBER标记。

标示符的识别:

这个规则的执行动作比较复杂,它利用了一个符号表的模块(后面讲述),当识别到标示符后,它向符号表中查找(没找到,就插入一条)并返回它的位置信息。

        {id}        {
                        int p = sym_table.lookup(yytext);
                        if(p == -1){//not find
                            p = sym_table.insert(yytext);//insert the default value 0.0
                        }
                        yylval = p;//return the position
                        return ID;
                    }

yytext是标示符的字符串,p保存它在符号表的位置,当找到后将位置信息赋值给yylval,同时返回标示符类型ID。 yylval和ID信息都会在yacc实现中用到。

2. yacc语法分析器

token定义如下:

    %token NUMBER ID
    %token PLUS MINUS TIMES OVER
    %token LP RP EOL COMMENT
    %TOKEN INT DOUBLE CHAR
    %token ASSIGN

    %left PLUS MINUS
    %left TIMES OVER
    %right UMINUS

上述定义的token在lex词法分析器中使用。

yacc语法转换规则定义如下:

    lines   :   lines expr EOL  { printf("%g\n", $2); }
            |   lines EOL
            |   lines COMMENT
            |
            ;

    expr    :   expr PLUS expr  { $$ = $1 + $3; }
			|	expr MINUS expr	{ $$ = $1 - $3; }
			|	expr TIMES expr	{ $$ = $1 * $3; }
			|	expr OVER expr	{ $$ = $1 / $3; }
			|	LP expr RP	{ $$ = $2; }
			|	‘-‘ expr %prec UMINUS	{ $$ = -$2; }
			|	NUMBER {$$=$1;} //$$=$1 can be ignored
            |   ID {$$ = sym_table.getValue($1);}//get value from sym_table
			|   ID ASSIGN expr {sym_table.setValue($1, $3); $$=$3; }//modify the value

与词法分析器类似的是依然是根据规则是否匹配,然后执行相应的语法动作,具体请参见小白说编译原理-3(http://blog.csdn.net/lpstudy/article/details/51225953)中的yacc的描述。

数字的动作:

NUMBER {$$=$1;} //$$=$1 can be ignored

它在识别出数字之后,将此数字的值(词法分析器得到的yylval)赋值给$$,$$表示当前结果。

注意$1表示是第一个对应的语法规则中第一个token的value,同理,对于$2,$3也是如此,表示第2个,第3个token的value。 而对于本规则,$1就是NUMBER的value。这个value是在词法分析中赋值的yylval(yylval=atof(yytext))。

标示符的动作:

标示符的动作分为两类,一种是赋值动作,一种是取值动作。 例如a=2,这表示给变量a赋值为2, 然后a+4表示将a变量加上4,因此结果为6. 简单起见,本程序不考虑变量的定义操作(例如c语言中的int a;),所有的变量默认值为0.0,可直接使用,可使用赋值运算修改它的值。

赋值动作

ID ASSIGN expr {sym_table.setValue($1, $3); $$\=\$3; }//modify the value

上述规则表明如果遇到a=2这样的输入后,会执行符号表的setValue方法,\$1表示词法分析器返回ID时候设置的yylval值(标示符的位置), \$3表示语法分析中expr的结果,这样setValue就会将\$1位置上的标示符设置为\$3.

取值动作

ID {\$\$ = sym_table.getValue(\$1);}//get value from sym_table

上述规则说明:首先词法分析器返回的ID标示符,同时\$1中存储标示符的位置,根据位置取出相应的value并赋值给\$\$当前值。

lex和yacc协同策略

lex传递到yacc两个重要的信息,类型和值, 类型由return实现,值由yylval存储。

yacc中的语法规则碰到的token标记是由lex的return得到的,而通过$number取值实际上取出的是lex中的yylval值。

$1和$3这种是根据前面规则中标记的位置来确定1和3的,它们的值或者由词法分析器通过yylval给出,或者由赋值$$得到。例如 ID ASSIGN expr中expr的值为$3,它不是由词法分析器给出的,而是使用了expr进行分析时候得到的$$值。

3. 符号表

符号表是支撑词法和语法分析的数据保存区。 词法分析过程中遇到id标示符,需要将其插入到符号表中,并设置默认值为0.0, 语法分析过程中遇到取值id标示符,使用符号表提供的取值函数得到符号的值,当遇到赋值id,则更新符号表中对应符号的值。示例代码,使用数组进行了简单实现。

    #include <iostream>
    #include <map>
    #include <vector>
    #include "yacc.h"
    #include "lex.h"
    using namespace std;

    struct Node
    {
        string name;
        double value;
    };
    class SymTable
    {
    public:
        SymTable(){

        }
    public:
        int lookup(const string& name){
            for (int i = 0; i < idValueTable.size(); ++i)
            {
                if(idValueTable[i].name.compare(name) == 0){
                    return i;
                }
            }
            return -1;//not find
        }

        int insert(const string& name){//when parser x=2 (current we get x)
            Node node;
            node.name = name;
            node.value = 0.0;
            idValueTable.push_back(node);
            return idValueTable.size()-1;
        }
        void setValue(int pos, double value){//when parser x=2 (current we get x)
            idValueTable[pos].value = value;
        }
        double getValue(int pos){
            return idValueTable[pos].value;
        }
    private:
        vector<Node> idValueTable;
    };
    extern SymTable sym_table;

运行效果

本人lpstudy,转载请注明出处: http://blog.csdn.net/lpstudy/article/details/51328851

时间: 2024-08-30 05:40:59

小白说编译原理-5-变量支持计算器的相关文章

C#面向过程之编译原理、变量、运算符

.net基础:.net与C# .net是一个平台 c#是一门语言 .net的用途a.桌面应用程序 b.网站应用程序 c.专业游戏开发(XBOX360) d.嵌入式设备软件开发 e.智能手机APP开发 f.平板电脑APP开发 正题:static void Main(string [] args) 主函数.方法入口 编译原理:1.编写源代码:2.C#编译器将C#源代码编译成MSIL:3.将中间代码交给CLR的即时编译器(JIT)将微软中间语言转化成相对应的机器码(CPU指令)4.交给CPU运行机器码

小白说编译原理-6-lex和yacc环境配置-多图

lex和yacc的使用很简单,但环境配置却是各种问题,本章说明lex和yacc在windows下的环境配置. 软件需求: 系统 win7-64位(win7-32, win8, win10全部通过) c++编译器: vs2010(2008,2013,2015也全部通过) lex和yacc编译器: ParGen.exe 基本流程: 安装Pargen.exe,采用的默认目录安装在C:\Program Files (x86)\Parser Generator 2\ 安装vs2010,这个各种教程,不再赘

小白说编译原理-9-最简单minus-c语言编译器

简介 继上节8说到利用手动构建的语法树解析下面的c语言代码: a = 1 sum = 0 input(x) while(a <= x){ sum = sum + a a = a+1; } print(sum) 而一个编译器不应该依赖用户去手动构建对应语言的语法树,我们需要的是一种支持自动构建语法树的策略.本节将要说明的就是如何利用前面1-6节学到的lex,yacc以及符号表,7-8节学到的语法树来支持给定c语言代码自动构建过程. 动机 在第5节变量支持的计算器中,对于expr的语法解析有如下的y

编译原理-如何使用flex和yacc工具构造一个高级计算器

Flex工具的使用方法 Lex 是一种生成扫描器的工具. Lex是Unix环境下非常著名的工具,主要功能是生成一个扫描器(Scanner)的C源码. 扫描器是一种识别文本中的词汇模式的程序. 这些词汇模式(或者常规表达式)在一种特殊的句子结构中定义.一种匹配的常规表达式可能会包含相关的动作.这一动作可能还包括返回一个标记. 当 Lex 接收到文件或文本形式的输入时,它试图将文本与常规表达式进行匹配. 它一次读入一个输入字符,直到找到一个匹配的模式. 如果能够找到一个匹配的模式,Lex 就执行相关

用antlr4来实现《按编译原理的思路设计的一个计算器》中的计算器

上次在公司内部讲<词法分析--使用正则文法>是一次失败的尝试--上午有十几个人在场,下午就只来了四个听众. 本来我还在构思如何来讲"语法分析"的知识呢,但现在看来已不太可能. 这个课程没有预想中的受欢迎,其原因可能是: 1.课程内容相对复杂,听众知识背景与基础差异比较大. 2.授课技巧不够,不能把复杂的知识简单化的呈现给基础稍差一点的人. 针对这两个可能的原因,我要尝试做出以下调整: 1.使用antlr来实现词法和语法的部分. 2.暂时把"编译"过程改为

编译原理与javacc初探

1.前序 真是书到用时方恨少啊,在大学的时候,虽然学过编译原理,但当时真是不懂啊,只是为了应付考试,死记硬背了一点点.现在呢,由于工作上的需要,不得不弥补一下啊. 这两天把编译原理的书又看了一遍,其实也就是主要看了文法,词法分析,语法分析而已,为了备忘,赶紧先记一下吧. 2.定义 词法分析,就是把源码中的一行行代码按照事先规定好的格式分隔成一个个单词符号(token),比如数字,变量名称,函数等等. 语法分析呢,主要就是分析词法分析后的一个个token,是否能够拼装,组成事先规定好的语法中的一个

编译原理第六单元习题

获得更多资料欢迎进入我的网站或者 csdn或者博客园 这几篇关于编译原理的文章是,我学习中国科学技术大学<编译原理>时,所做的习题总结.之后会将这门课的所有习题补上,用于给大家参考: 题目要求 在这个题目中,你将完整的实现抽象语法树(包括数据结构的定义.语法树的生成等).首先,请下载我们提供的代码包: http://staff.ustc.edu.cn/~bjhua/mooc/ast.zip 代码的运行方式是: 首先生成语法分析器: $ bison exp.y 然后生成编译器: $ gcc ma

编译原理 (预处理&gt;编译&gt;汇编&gt;链接)(转)

一般高级语言程序编译的过程:预处理.编译.汇编.链接.gcc在后台实际上也经历了这几个过程,我们可以通过-v参数查看它的编译细节,如果想看某个具体的编译过程,则可以分别使用-E,-S,-c和 -O,对应的后台工具则分别为cpp,cc1,as,ld.下面我们将逐步分析这几个过程以及相关的内容,诸如语法检查.代码调试.汇编语言等. 1.预处理 预处理是C语言程序从源代码变成可执行程序的第一步,主要是C语言编译器对各种预处理命令进行处理,包括头文件的包含.宏定义的扩展.条件编译的选择等.打印出预处理之

编译原理学习:TINY语言词法扫描程序实现

最近对解释型程序(类似python或者是linux里的bc计算器)非常感兴趣,就开始学习一下编译原理.今天自己实现了TINY语言的词法扫描程序.大部分参考<编译原理及实践>一书.但是我做了一些小小的改进. 先说一下TINY语言: 1.注释:放在一对大括号内.书上的注释不能嵌套,我做了一点改进,允许嵌套. 2.关键字:read write if end repeat until else 3.类型:只支持整型和布尔型. 4.计算:+ - * / ( ) < = :=,其中:=为赋值运算,=