动手实现编译器(二)——语法分析_sysy文法-程序员宅基地

技术标签: 编译器  

在这一节中,将介绍语法分析器。
在上一节的词法分析上实现SysY语言语法分析,来解析类似2 - 15 / 7 + 3 * 8 T_EOFT_EOF表示终结符)
SysY 语言的文法采用扩展的 Backus 范式(EBNF,Extended Backus-NaurForm)表示,其中:

  • 符号[…]表示方括号内包含的为可选项
  • 符号{…}表示花括号内包含的为可重复 0 次或多次的项
  • 终结符或者是由单引号括起的串,或者是 Ident、InstConst 这样的记号
    现在,给出SySY语言的关于加减乘除取余的简易语法。

加减表达式: AddExp → UnaryExp (’+’ | ‘−’) UnaryExp
乘除模表达式: MulExp → UnaryExp (’*’ | ‘/’ | ‘%’) UnaryExp

在这里,“UnaryExp”可以简单地看作是整数。
可以看到SysY语言的语法是递归的,因此可以递归解析它。可以编写如下所示的伪代码:

递归解析函数() 
{
    
	扫描并检查第一个令牌是一个数字。如果不是,则返回错误。
	获取下一个单词。
	如果到达输入的末尾,则返回结果。
	否则,调用递归解析函数()}

抽象语法树

为了进行语义分析,我们需要代码来解释识别的输入,或将其转换为另一种格式,例如汇编代码。因此我们首先要将输入转换为语法分析树,也称为 AST。
AST 的节点结构:

// AST节点类型
enum
{
    
    A_ADD, A_SUB, A_MUL, A_DIV, A_MOD, A_INT
};

// 抽象语法树结构
struct ASTnode
{
    
    int op;				        // 节点的操作类型
    struct ASTnode *left;
    struct ASTnode *right;
    int intvalue;				// 整数节点的值,没有子树
};

构建AST

用以下代码构建AST:

struct ASTnode *mkastnode(int op, struct ASTnode *left, struct ASTnode *right, int intvalue)
{
    
    struct ASTnode *n;
    n = (struct ASTnode *) malloc(sizeof(struct ASTnode));
    if (n == NULL)
    {
    
        fprintf(stderr, "Unable to malloc in mkastnode()\n");
        exit(1);
    }
    n->op = op;
    n->left = left;
    n->right = right;
    n->intvalue = intvalue;
    return n;
}


// 生成AST叶子节点
struct ASTnode *mkastleaf(int op, int intvalue)
{
    
    return mkastnode(op, NULL, NULL, intvalue);
}

// 生成只有一个左孩子的一元AST节点
struct ASTnode *mkastunary(int op, struct ASTnode *left, int intvalue)
{
    
    return (mkastnode(op, left, NULL, intvalue));
}

简单的表达式解析器

为了将词法分析中的单词用作AST节点操作值。首先,定义一个函数将单词映射到AST节点操作值。代码如下:

// 将单词转换为AST操作
int token_op(int token)
{
    
    switch (token)
    {
    
        case T_ADD:    return A_ADD;
        case T_SUB:    return A_SUB;
        case T_MUL:    return A_MUL;
        case T_DIV:    return A_DIV;
        case T_MOD:    return A_MOD;
        default:    fprintf(stderr, "unknown token in token_op() on line %d\n", Line);
                    exit(1);
    }
}

然后定义一个函数来检查下一个单词是否为整数,并生成一个AST节点来储存整数值。

为此首先定义一个全局变量Token,表示从输入中扫描来的最新单词

struct token	Token;      //当前扫描单词

函数代码如下:

// 解析一个整数单词并返回表示它的AST节点
struct ASTnode *primary()
{
    
    struct ASTnode *n;
    // 对于整数单词,为其生成一个AST叶子节点并扫描下一个单词。
    // 否则,语法错误。
    switch (Token.token)
    {
    
        case T_INT:  
            n = mkastleaf(A_INT, Token.intvalue);
            scan(&Token);
            return n;
        default:
            fprintf(stderr, "syntax error on line %d\n", Line);
            exit(1);
    }
}

最后将上面的几个函数组装为语法分析器,代码如下:

struct ASTnode *binexpr()
{
    
    struct ASTnode *n, *left, *right;
    int nodetype;

    // 获取左节点的整数,同时获取下一个单词
    left = primary();

    // 如果下一个单词是文件结尾,则返回左节点
    if (Token.token == T_EOF)   return left;

    // 从单词映射到节点类型
    nodetype = token_op(Token.token);

    // 获取下一个单词
    scan(&Token);

    // 递归得到右子树
    right = binexpr();

    // 将左、右子树合并成一棵树
    n = mkastnode(nodetype, left, right, 0);
    return n;
}

为了直观显示结果,添加一个结果输出该函数,代码如下:

// AST操作符
char *ASTop[] = {
    "+", "-", "*", "/", "%"};

// 给定一个AST,返回一个表达式
int interpretAST(struct ASTnode *n)
{
    
    int leftval, rightval;

    // 获得左、右子树值
    if (n->left)    leftval = interpretAST(n->left);
    if (n->right)   rightval = interpretAST(n->right);

    // 调试:打印将要做的事情
    if (n->op == A_INT)    printf("int %d\n", n->intvalue);
    else    printf("%d %s %d\n", leftval, ASTop[n->op], rightval);

    switch (n->op)
    {
    
        case A_ADD:    return leftval + rightval;
        case A_SUB:    return leftval - rightval;
        case A_MUL:    return leftval * rightval;
        case A_DIV:    return leftval / rightval;
        case A_MOD:    return leftval % rightval;
        case A_INT:    return n->intvalue;
        default:    fprintf(stderr, "Unknown AST operator %d\n", n->op);
                    exit(1);
    }
}

同时修改main()函数为以下代码,来验证结果

// 用法 compiler -o -s outfile infile
int main(int argc, char *argv[])
{
    
    struct ASTnode *n;
    if(argc != 5)
    {
    
        fprintf(stderr, "compiler -o -s outfile infile\n");
        exit(1);
    }
    init();
    if ((Infile = fopen(argv[4], "r")) == NULL)
    {
    
        fprintf(stderr, "Unable to open %s: %s\n", argv[1], strerror(errno));
        exit(1);
    }
    scan(&Token);			            // 从输入中获得第一个单词
    n = binexpr();		                // 解析表达式
    printf("%d\n", interpretAST(n));	// 计算最终的结果
}

输入:

2 - 15 / 7 + 3 * 8

输出:

int 2
int 15
int 7
int 3
int 8
3 * 8
7 + 24
15 / 31
2 - 0
2

输入:

2 - 15 / 7 + 3 * * 8

输出:

syntax error on line 1

输入:

2 8 - 15 / 7 + 3 * 8

输出:

unknown token in token_op() on line 1

可以看到这个语法分析器可以识别SysY语言的部分语法,并检查编译器的输入是否符合该语法,但不能处理不同的运算符优先级。就目前而言,该代码将所有运算符都视为具有相同的优先级。在下一节,将加入对表达式进行语义分析的代码以获得正确的数学结果。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_34851605/article/details/117827649

智能推荐

分布式光纤传感器的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告_预计2026年中国分布式传感器市场规模有多大-程序员宅基地

文章浏览阅读3.2k次。本文研究全球与中国市场分布式光纤传感器的发展现状及未来发展趋势,分别从生产和消费的角度分析分布式光纤传感器的主要生产地区、主要消费地区以及主要的生产商。重点分析全球与中国市场的主要厂商产品特点、产品规格、不同规格产品的价格、产量、产值及全球和中国市场主要生产商的市场份额。主要生产商包括:FISO TechnologiesBrugg KabelSensor HighwayOmnisensAFL GlobalQinetiQ GroupLockheed MartinOSENSA Innovati_预计2026年中国分布式传感器市场规模有多大

07_08 常用组合逻辑电路结构——为IC设计的延时估计铺垫_基4布斯算法代码-程序员宅基地

文章浏览阅读1.1k次,点赞2次,收藏12次。常用组合逻辑电路结构——为IC设计的延时估计铺垫学习目的:估计模块间的delay,确保写的代码的timing 综合能给到多少HZ,以满足需求!_基4布斯算法代码

OpenAI Manager助手(基于SpringBoot和Vue)_chatgpt网页版-程序员宅基地

文章浏览阅读3.3k次,点赞3次,收藏5次。OpenAI Manager助手(基于SpringBoot和Vue)_chatgpt网页版

关于美国计算机奥赛USACO,你想知道的都在这_usaco可以多次提交吗-程序员宅基地

文章浏览阅读2.2k次。USACO自1992年举办,到目前为止已经举办了27届,目的是为了帮助美国信息学国家队选拔IOI的队员,目前逐渐发展为全球热门的线上赛事,成为美国大学申请条件下,含金量相当高的官方竞赛。USACO的比赛成绩可以助力计算机专业留学,越来越多的学生进入了康奈尔,麻省理工,普林斯顿,哈佛和耶鲁等大学,这些同学的共同点是他们都参加了美国计算机科学竞赛(USACO),并且取得过非常好的成绩。适合参赛人群USACO适合国内在读学生有意向申请美国大学的或者想锻炼自己编程能力的同学,高三学生也可以参加12月的第_usaco可以多次提交吗

MySQL存储过程和自定义函数_mysql自定义函数和存储过程-程序员宅基地

文章浏览阅读394次。1.1 存储程序1.2 创建存储过程1.3 创建自定义函数1.3.1 示例1.4 自定义函数和存储过程的区别1.5 变量的使用1.6 定义条件和处理程序1.6.1 定义条件1.6.1.1 示例1.6.2 定义处理程序1.6.2.1 示例1.7 光标的使用1.7.1 声明光标1.7.2 打开光标1.7.3 使用光标1.7.4 关闭光标1.8 流程控制的使用1.8.1 IF语句1.8.2 CASE语句1.8.3 LOOP语句1.8.4 LEAVE语句1.8.5 ITERATE语句1.8.6 REPEAT语句。_mysql自定义函数和存储过程

半导体基础知识与PN结_本征半导体电流为0-程序员宅基地

文章浏览阅读188次。半导体二极管——集成电路最小组成单元。_本征半导体电流为0

随便推点

【Unity3d Shader】水面和岩浆效果_unity 岩浆shader-程序员宅基地

文章浏览阅读2.8k次,点赞3次,收藏18次。游戏水面特效实现方式太多。咱们这边介绍的是一最简单的UV动画(无顶点位移),整个mesh由4个顶点构成。实现了水面效果(左图),不动代码稍微修改下参数和贴图可以实现岩浆效果(右图)。有要思路是1,uv按时间去做正弦波移动2,在1的基础上加个凹凸图混合uv3,在1、2的基础上加个水流方向4,加上对雾效的支持,如没必要请自行删除雾效代码(把包含fog的几行代码删除)S..._unity 岩浆shader

广义线性模型——Logistic回归模型(1)_广义线性回归模型-程序员宅基地

文章浏览阅读5k次。广义线性模型是线性模型的扩展,它通过连接函数建立响应变量的数学期望值与线性组合的预测变量之间的关系。广义线性模型拟合的形式为:其中g(μY)是条件均值的函数(称为连接函数)。另外,你可放松Y为正态分布的假设,改为Y 服从指数分布族中的一种分布即可。设定好连接函数和概率分布后,便可以通过最大似然估计的多次迭代推导出各参数值。在大部分情况下,线性模型就可以通过一系列连续型或类别型预测变量来预测正态分布的响应变量的工作。但是,有时候我们要进行非正态因变量的分析,例如:(1)类别型.._广义线性回归模型

HTML+CSS大作业 环境网页设计与实现(垃圾分类) web前端开发技术 web课程设计 网页规划与设计_垃圾分类网页设计目标怎么写-程序员宅基地

文章浏览阅读69次。环境保护、 保护地球、 校园环保、垃圾分类、绿色家园、等网站的设计与制作。 总结了一些学生网页制作的经验:一般的网页需要融入以下知识点:div+css布局、浮动、定位、高级css、表格、表单及验证、js轮播图、音频 视频 Flash的应用、ul li、下拉导航栏、鼠标划过效果等知识点,网页的风格主题也很全面:如爱好、风景、校园、美食、动漫、游戏、咖啡、音乐、家乡、电影、名人、商城以及个人主页等主题,学生、新手可参考下方页面的布局和设计和HTML源码(有用点赞△) 一套A+的网_垃圾分类网页设计目标怎么写

C# .Net 发布后,把dll全部放在一个文件夹中,让软件目录更整洁_.net dll 全局目录-程序员宅基地

文章浏览阅读614次,点赞7次,收藏11次。之前找到一个修改 exe 中 DLL地址 的方法, 不太好使,虽然能正确启动, 但无法改变 exe 的工作目录,这就影响了.Net 中很多获取 exe 执行目录来拼接的地址 ( 相对路径 ),比如 wwwroot 和 代码中相对目录还有一些复制到目录的普通文件 等等,它们的地址都会指向原来 exe 的目录, 而不是自定义的 “lib” 目录,根本原因就是没有修改 exe 的工作目录这次来搞一个启动程序,把 .net 的所有东西都放在一个文件夹,在文件夹同级的目录制作一个 exe._.net dll 全局目录

BRIEF特征点描述算法_breif description calculation 特征点-程序员宅基地

文章浏览阅读1.5k次。本文为转载,原博客地址:http://blog.csdn.net/hujingshuang/article/details/46910259简介 BRIEF是2010年的一篇名为《BRIEF:Binary Robust Independent Elementary Features》的文章中提出,BRIEF是对已检测到的特征点进行描述,它是一种二进制编码的描述子,摈弃了利用区域灰度..._breif description calculation 特征点

房屋租赁管理系统的设计和实现,SpringBoot计算机毕业设计论文_基于spring boot的房屋租赁系统论文-程序员宅基地

文章浏览阅读4.1k次,点赞21次,收藏79次。本文是《基于SpringBoot的房屋租赁管理系统》的配套原创说明文档,可以给应届毕业生提供格式撰写参考,也可以给开发类似系统的朋友们提供功能业务设计思路。_基于spring boot的房屋租赁系统论文