编译原理(一)

一、编译器前端模型

|---------->符号表------------------------------------------------>|

源程序----(词法分析器)--->词法单元----(语法分析器)---->语法分析树----(中间代码生成器)--->三地址代码

二、语法定义——“上下文无关文法”(context-free grammar)

1.由数位和+、-符号组成

此文法产生式为

expr -> expr + digit
expr -> expr - digit
expr -> digitdigit -> 0|1|2|3|4|5|6|7|8|9

组合起来为:expr -> expr + digit | expr - digit | digit,终结符号为 + - 0 1 2 3 4 5 6 7 8 9,开始符号为expr。

不可以将expr和digit合并为一个非终结符号,这会导致二义性,比如合并为string,此时文法改写为

string -> string + string | string - string |0|1|2|3|4|5|6|7|8|9

假设现在字符流为9 - 5 + 2,则9 - 5匹配第二个产生式,5 + 2 匹配第一个产生式,具有二义,应当避免这样的设计

2. 右结合运算符

某些运算符是右结合,如 =(赋值), **(指数),赋值串比如a = b = c 的文法表示

right -> letter = right | letter
letter -> a|b|c ... | z

指数运算串比如  a**b**c 的文法表示

right -> letter ** right | letter
letter -> a|b|c...|z

假设a**b先运算,得到一个rihgt,那(a**b)**c要么是rihgt**letter,要么是right**right,无法匹配上面任何一个产生式。

3. 运算符号*、/

设定运算符号*、/比 +、- 具有更高的优先级。根据n个优先级需要n+1个非终结符号,创建两个非终结符号term和expr分别对应这两个优先级层次,终结符digit表示数位,如果不考虑使用括号(parentheses),则这些符号足够表示文法了,如果考虑使用括号,即对expr使用括号,变为 (expr) ,则这个expr无法被高优先级符号*、/分开,前面说到digit数位也是一个整体,故合并起来为一个非终结符号,记为factor,其中 factor -> digit|(expr) ,现在给出文法如下

expr -> expr + term | expr - term | term
term -> term * factor | term / factor | factor
factor -> digit | (expr)

4. 例子

大多数语句由一个关键字或一个特殊字符开始,然而例外情况也有,比如赋值语句(不带声明)或过程调用(函数调用)。这里不考虑这些例外情况。

stmt ->    id = expression;
      |    if (expression) stmt
      |    if (expression) stmt else stmt
      |    while (expression) stmt
      |    do stmt while (expression);
      |    { stmts }

stmts -> stmts stmt
      | ε  // 为空语句

注意分号的位置,只出现在非stmt结尾的产生式,否则会导致多余的分号出现

三、语法制导翻译

这是一个通过向一个文法产生式附加一些规则或程序片段实现翻译的过程。

1. 属性

属性表示与某个程序构造相关的任意的量,如表达式的数据类型,生成代码的指令数目或为某个构造生成的代码中第一条指令的位置。

将属性与文法的非终结符号和终结符号相关联。比如文法符号X,用X.a表示X上的属性a的值。如果语法分析树各结点标记了相应的属性,则这个语法分析树为注释语法分析树。对于9 - 5 + 2,其注释分析树为

其定义如下, "||"表示连接运算符

    产生式                                语义规则
expr -> expr1 + term           expr.t = expr1.t || term.t || ‘+‘
expr -> expr1 - term           expr.t = expr1.t|| term.t || ‘-‘
expr -> term                   expr.t = term.t
term -> 0                      term.t = ‘0‘
term -> 0                      term.t = ‘1‘
...                                   ...
term -> 9                      term.t = ‘9‘

2. 树的遍历

树的遍历用于描述属性的求值过程,以及描述一个翻译方案中各个代码片段的执行过程。一个树的遍历从根结点开始,按照某个顺序访问树的各个结点。

3. 语法制导翻译方案

在文法产生式中附加一些程序片段描述翻译结果,被嵌入的程序片段称为语义动作。

expr -> expr1 + term {print(‘+‘)}
expr -> expr1 - term {print(‘-‘)}
expr -> term
term -> 0                 {print(‘0‘)}
term -> 1                 {print(‘1‘)}
...
term -> 9                 {print(‘9‘)}

四、语法分析

1. 自顶向下分析

考虑如下文法

stmt    -> expr
         |    if(expr) stmt
         |    for(optexpr; optexpr; optexpr) stmt
         |    other
optexpr -> ε
        | expr

这里为了简化,我们将expr看作终结符的一个表达式,类似地,other是一个代表其他语句的终结符号。对于如下输入串

for(; expr; expr) other

我们从非终结符号stmt根结点开始,目标是构造出语法分析树的其余部分,使得这棵树与输入符号匹配。

输入中当前被扫描的称作向前看(lookahead)符号,此时为for,根据前面的文法,只有一个产生式可以推导这样的串,所以我们选择这个产生式,产生式中的符号作为根结点stmt的子结点,如图

此时,根结点stmt这个结点的子结点全部构造完毕,于是我们开始考虑这个结点的最左子结点,即for结点,而这个标号for是一个终结符号,与上文提到的向前看符号for匹配,那输入串中的下一个终结符成为新的当前看符号,这里是“(”,同时考虑语法分析树的下一个子结点,也是"(",匹配,那再下一步输入中的向前看符号为";",而语法分析树的下一个子结点为optexpr,这是一个非终结符号,我们需要为它选择一个产生式,即optexpr的 ε 产生式,如此,最后构造的语法分析树为

2. 预测分析法

采用一种递归下降分析法,各个非终结符号对应的过程的控制流可以由向前看符号无二义地确定,故而不需要回溯。

对上一部分中的文法,给出预测分析器的伪代码如下

void stmt() {
    switch (lookahead) {
    case expr:
            match(expr); match(‘;‘); break;
    case if:
            match(if); match(‘(‘); match(expr); match(‘)‘); stmt();
            break;
    case for:
            match(for); match(‘(‘);
            optexpr(); match(‘;‘); optexpr(); match(‘;‘); optexpr();
            match(‘)‘); stmt(); break;
    case other:
            match(other); break;
    default:
            report("syntax error");
    }
}

void optexpr() {
    if (lookahead == expr) match(expr);
}

void  match(terminal t) {
    if (lookahead == t) lookahead = nextTerminal;
    else report("syntax error");
}

预测分析需要知道哪些符号可能成为一个产生式体所生成串的第一个符号。令α是一个文法符号(终结符号或非终结符号)串,定义FIRST(α)为可以由α生成的一个或多个终结符号串的第一个符号的集合.如果α就是ε或者可以生成ε,那么ε也在FIRST(ε)中。

对上面的情况,FIRST的计算方式如下:

FIRST(stmt) = { expr, if, for, other}
FIRST(expr;) = {expr}

如果有两个产生式A->α和A->β,考虑相应的FIRST集合。如果不考虑ε产生式,预测分析法要求FIRST(ε)和FIRST(β)不相交,这样才能用向前看符号确定使用哪个产生式,即使用预测分析法时,要求任何非终结符号的各个产生式的FIRST集合互不相交。

3. 递归

左递归产生式:

expr -> expr + term

显然这会导致无限循环。一种消除方法是

A -> Aα|β

非终结符号A的产生式,其中α和β是不以A开头的终结符号或者非终结符号。一个具体的例子是

expr -> expr + term | term

注意这里α是 +term

右递归产生式:

A -> βR
R -> αR | ε

右递归的产生式会使得树向右下方向生长,如下图

声明:本篇文章完全参考编译原理龙书

时间: 2024-08-01 15:31:35

编译原理(一)的相关文章

编译原理123

1.编译原理就是什么? 个人理解,编译就是像翻译一样,将一种语言翻译成另一种语言,编译就是将高级语言或汇编语言翻译成电脑能识别的二进制机器语言,编译原理就是讲解这 个编译的过程.内容包括语言和文法.词法分析.语法分析.语法制导翻译.中间代码生成.存储管理.代码优化和目标代码生成. 编译原理是计算机专业设置的一门重要的专业课程.虽然只有少数人从事编译方面的工作,但是这门课在理论.技术.方法上都对学生提供了系统而有效的训练,有 利于提高软件人员的素质和能力. 2.学习编译原理有什么好处? 学习编译原

编译原理的理解

编译原理内容包括语言和文法.词法分析.语法分析.语法制导翻译.中间代码生成.存储管理.代码优化和目标代码生成.大一点的应用可以做到一定的技术先进性,从而让你在本行业站稳脚跟:分析和分解用户输入的SQL语句,理解是否有害和是否有SQL注入等.如果不学,对于不是不是本行业的人来说就没损失,如果是本行业的专业人士,不学只会令自己的编译理解更慢.

编译原理

1.编译原理是什么? (1)就是对程序语言进行翻译: (2)介绍编译程序构造的原理和基本方法: (3)从本质上讲是一个算法问题: 2.学习编译原理有什么好处? (1)接触一些新的算法,如,贪心算法,图论算法等: (2)学习一些新的思想,如,有限状态机,递归下降等: (3)多了一项技能: 3.不学有什么损失? (1)将来出社会可能比较难混: (2)不学好这门课程期末可能不及格: (3)有学不一定有用,不学肯定没用: 4.如何学习编译原理? (1)上课跟上老师的步伐,课前预习,课后复习: (2)多从

编译原理第二次作业 编译器任务总结

在学习了编译原理后我开始明白编译的工作原理了,也更了解编译语言的结构.明白了编译器的编写中需要注意的各项问题,更了解了编译器的编译过程为我之后的编程提供了一些必不可少的经验,还是我的改错能力有所提高.因为写编译器使我在编程发生错误后能及时了解程序在编译过程中的原理是什么,这样我就能知道我的程序是何处的问题. 而且在学习编译原理的时候,学到了一些比较难理解的东西,通过实践不断地消化书本上的理论,最终就会有一个雏形出现.而且在编写的过程时候有一些不知道如何解决的问题时,我就会和组员讨论以得出一个可行

编译原理简单介绍

编译原理学习导论 大学课程为什么要开设编译原理呢?这门课程关注的是编译器方面的产生原理和技术问题,似乎和计算机的基础领域不沾边,可是编译原理却一直作为大学本科的必修课程,同时也成为了研究生入学考试的必考内容.编译原理及技术从本质上来讲就是一个算法问题而已,当然由于这个问题十分复杂,其解决算法也相对复杂.我们学的数据结构与算法分析也是讲算法的,不过讲的基础算法,换句话说讲的是算法导论,而编译原理这门课程讲的就是比较专注解决一种的算法了.在20世纪50年代,编译器的编写一直被认为是十分困难的事情,第

编译原理基础概念介绍

关于编译原理 语法树 句柄 简单短语 短语 的区分,通过两个例子来理解概念以及方法: 例子1——语法树 S -> a|b|(T)  T -> TdS|S Vt={a,b,d,(,)}.Vn={S,T},S是开始符 句型(Sd(T)db)是S的一个推导,其中___是句柄;____是最左素短语:____是该句型的直接短语,_____是短语.     素短语的概念:它是一个递归的定义,至少含有一个终结符,并且除它自身之外不再含任何更小的素短语,所谓最左素短语就是处于句型最左边的素短语的短语.而一个算

编译原理之扫描器

https://github.com/zzusunjs/Scanner 编译原理实验课要求自己实现一个简单一些的扫描器. 我是用c++实现的.具体的原理的话,完全是按照书上的算法实现的,也没有什么特别难懂的地方,不过想要写的比较完备的话还是有一定难度的. Go ->  preProcess() -> init() -> delete-space() ->  Scanner() -> 首字符 ->.... 代码大致的结构就是上面的那个样子.

python实现算术表达式的词法语法语义分析(编译原理应用)

本学期编译原理的一个大作业,我的选题是算术表达式的词法语法语义分析,当时由于学得比较渣,只用了递归下降的方法进行了分析. 首先,用户输入算术表达式,其中算术表达式可以包含基本运算符,括号,数字,以及用户自定义变量. 词法分析,检查单词变量是否正确:语法分析,检查算术表达式语法是否正确并输出生成语法树:语义分析,输出四元表达式. 最终效果图: 例如输入: 词法分析结果: 语法分析结果: 语义分析结果: 算术表达式的组成语法如下: 无符号整数 = 〈数字〉{〈数字〉} 〈标识符〉= 〈字母〉{〈字母

编译原理的实验报告一

实验一 词法分析程序实验 专业 商软2班   姓名 黄仲浩  学号 201506110166 一. 实验目的      编制一个词法分析程序. 二. 实验内容和要求 输入:源程序字符串 输出:二元组(种别,单词符号本身). 三. 实验方法.步骤及结果测试 源程序名:bianyiyuanli.c 可执行程序名:bianyiyuanli.exe 原理分析及流程图 通过一些for循环和while循环进行一个个的翻译. 源程序如下: #include<stdio.h> #include<stri

软考(编译原理)

软件设计师考试编译原理部分,笔记记录.分享.2016-09-14