编译原理上机实习 dws@pku.edu.cn.

Slides:



Advertisements
Similar presentations
现代电子技术实验 ——综合实验之单片机部分
Advertisements

基本概論 Basic concepts.
C语言程序设计 主讲教师 :张群燕 电话:
C#程序设计案例教程 第3章 程 序 结 构.
第一章 C语言概述 计算机公共教学部.
计算机硕士专业基础—C语言 赵海英
第二章 JAVA语言基础.
C++程序设计 王希 图书馆三楼办公室.
第三章 控制结构.
Introduction to Lex 電資三 B 盧逸峮
程式設計實作.
2.1 基本資料型別 2.2 變數 2.3 運算式與運算子 2.4 輸出與輸入資料 2.5 資料型別轉換 2.6 實例
第一章 程序设计入门.
主讲教师:吴琼 微信群:C语言2016 QQ群: 密码scu2016 昵称:“真名+学号”
2.2 语法分析器生成器YACC 分析器的构造步骤: 产生式→识别活前缀的DFA→分析表(+驱动器) YACC概述
第二章 C# 基础知识.
C++程序设计 第二讲 清华大学软件学院.
第3章 C 語言的基本知識.
第3章 語法入門 第一個Java程式 文字模式下與程式互動 資料、運算 流程控制.
C 程式設計— 控制敘述 台大資訊工程學系 資訊系統訓練班.
助教:胡光能,解定宝 编译原理讲师:戴新宇
本單元介紹何謂變數,及說明變數的宣告方式。
授课老师:龚涛 信息科学与技术学院 2018年3月 教材: 《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
程序设计期末复习 黎金宁
第三章 C++中的C 面向对象程序设计(C++).
2 C++ 的基本語法和使用環境 親自撰寫和執行程式是學好程式語言的不二法門。本章藉由兩個簡單的程式,介紹C++ 程式的基本結構和開發環境,讓初學者能逐漸建立使用C++ 的信心。
程序设计专题一 结构化程序设计与递归函数 主讲教师: 刘新国.
第5章 堆疊(Stacks) 5-1 堆疊的基礎 5-2 堆疊的表示法 5-3 堆疊的應用 - 運算式的計算與轉換
编译原理与技术 词法分析 (2) 2018/12/30 《编译原理与技术》讲义.
C语言 程序设计基础与试验 刘新国、2012年秋.
本章中將會更詳細地考慮有關重複的概念,並且會 介紹for和do…while等兩種用來控制重複的敘述 式。 也將會介紹switch多重選擇敘述式。 我們會討論直接和迅速離開某種控制敘述式的 break敘述式,以及用來跳過重複敘述式本體剩餘 部份的continue敘述式。 本章會討論用來組合控制條件的邏輯運算子,最後.
2019/1/17 Java语言程序设计-程序流程 教师:段鹏飞.
C++语言程序设计 第二章 C++简单程序设计.
Java程序设计 第2章 基本数据类型及操作.
明解C++教學手冊 柴田望洋 博士 著 書號:PG20269
第三章 数据类型、运算符与表达式.
第三章 C# 基础知识.
第1章 概述 本章要点: C语言程序结构和特点 C语言程序的基本符号与关键字 C语言程序的编辑及运行 学习方法建议:
1.3 C语言的语句和关键字 一、C语言的语句 与其它高级语言一样,C语言也是利用函数体中的可执行 语句,向计算机系统发出操作命令。按照语句功能或构成的不 同,可将C语言的语句分为五类。 goto, return.
第二章Java基本程序设计.
C语言概述 第一章.
第1讲 C语言基础 要求: (1) C程序的组成 (2) C语言的标识符是如何定义的。 (3) C语言有哪些基本数据类型?各种基本数
C语言大学实用教程 第5章 函数与程序结构 西南财经大学经济信息工程学院 刘家芬
程式結構&語法.
第 二 章 数据类型、运算符与表达式.
第二章 Java基本语法 讲师:复凡.
7.1 C程序的结构 7.2 作用域和作用域规则 7.3 存储属性和生存期 7.4 变量的初始化
第十四章 若干深入问题和C独有的特性 作业: 函数指针 函数作参数 函数副作用 运算 语句 位段 存储类别 编译预处理
C程序设计.
C语言程序设计 李祥 QQ:
C++语言程序设计教程 第2章 数据类型与表达式 第2章 数据类型与表达式 制作人:杨进才 沈显君.
第2章 认识C语言 教学要点 2. 1 项目二C语言程序识读 2 .2 项目三班级成绩排名 2 .3 知识链接 返回.
<编程达人入门课程> 本节内容 为什么要使用变量? 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ:
第二章 Java语法基础.
第二章 类型、对象、运算符和表达式.
第二章 基本数据类型 ——数据的表示.
#include <iostream.h>
第二章 Java基本语法 讲师:复凡.
本节内容 指针类型.
第五章 逻辑运算和判断选取控制 §5.1 关系运算符和关系表达式
第十二章 位运算.
第2章 Java语言基础.
基本資料型態 變數與常數 運算子 基本的資料處理 授課:ANT 日期:2014/03/03.
第三章 流程控制 程序的运行流程 选择结构语句 循环结构语句 主讲:李祥 时间:2015年10月.
第二章 Java基础语法 北京传智播客教育
编译原理实践 7.PL/0的词法分析程序构造.
本节内容 指针类型 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
编译原理实践 --词法分析程序的自动生成器LEX
第2章 Arduino编程.
C++语言程序设计 C++语言程序设计 第二章 基本数据类型与表达式 第十一组 C++语言程序设计.
Presentation transcript:

编译原理上机实习 dws@pku.edu.cn

实习题目 利用词法分析程序的自动构造工具LEX构造一个识别C语言的单词的词法分析程序; 利用语法分析程序的自动构造工具Yacc构造一个识别C语言的程序的语法分析程序

LEX简介 YACC简介 实习要求

LEX简介 Lex 是一种生成扫描器的工具。扫描器是一种识别文本中的词汇模式的程序。这些词汇模式(或者常规表达式)在一种特殊的句子结构中定义

一种匹配的常规表达式可能会包含相关的动作。这一动作可能还包括返回一个标记。当 Lex 接收到文件或文本形式的输入时,它试图将文本与常规表达式进行匹配。它一次读入一个输入字符,直到找到一个匹配的模式。如果能够找到一个匹配的模式,Lex 就执行相关的动作(可能包括返回一个标记)。另一方面,如果没有可以匹配的常规表达式,将会停止进一步的处理,Lex 将显示一个错误消息。

Lex 的常规表达式 常规表达式是一种使用元语言的模式描述。表达式由符号组成。符号一般是字符和数字,但是 Lex 中还有一些具有特殊含义的其他标记。下面两个表格定义了 Lex 中使用的一些标记并给出了几个典型的例子。

A-Z, 0-9, a-z 构成了部分模式的字符和数字。 A-Z, 0-9, a-z  构成了部分模式的字符和数字。  .  匹配任意字符,除了 \n。  -  用来指定范围。例如:A-Z 指从 A 到 Z 之间的所有字符。  [ ]  一个字符集合。匹配括号内的任意 字符。如果第一个字符是 ^ 那么它表示否定模式。例如: [abC] 匹配 a, b, 和 C中的任何一个。 

匹配0个或者多个上述的模式。 + 匹配1个或者多个上述模式。 *  匹配0个或者多个上述的模式。  +  匹配1个或者多个上述模式。  ?  匹配0个或1个上述模式。  $  作为模式的最后一个字符匹配一行的结尾。  { }  指出一个模式可能出现的次数。 例如: A{1,3} 表示 A 可能出现1次或3次。  \  用来转义元字符。同样用来覆盖字符在此表中定义的特殊意义,只取字符的本意。  ^  否定。 

|  表达式间的逻辑或。  "<一些符号>;"  字符的字面含义。元字符具有。  /  向前匹配。如果在匹配的模版中的“/”后跟有后续表达式,只匹配模版中“/”前面的部分。如:如果输入 A01,那么在模版 A0/1 中的 A0 是匹配的。  ( )  将一系列常规表达式分组。

常规表达式举例 joke[rs]  匹配 jokes 或 joker。  A{1,2}shis+  匹配 AAshis, Ashis, AAshi, Ashi。  (A[b-e])+  匹配在 A 出现位置后跟随的从 b 到 e 的所有字符中的 0 个或 1个。 

Lex 中的标记声明类似 C 中的变量名。每个标记都有一个相关的表达式。(下表中给出了标记和表达式的例子。)使用这个表中的例子,我们就可以编一个字数统计的程序了。我们的第一个任务就是说明如何声明标记。

标记声明举例 标记 相关表达式 含义  数字(number) ([0-9])+ 1个或多个数字  字符(chars) [A-Za-z] 任意字符  空格(blank) " " 一个空格  字(word) (chars)+ 1个或多个 chars  变量(variable) (字符)+(数字)*(字符)*(数字)* 

Lex 编程 Lex 编程可以分为三步: 以 Lex 可以理解的格式指定模式相关的动作。  在这一文件上运行 Lex,生成扫描器的 C 代码。  编译和链接 C 代码,生成可执行的扫描器。 

一个 Lex 程序分为三个段:第一段是 C 和 Lex 的全局声明,第二段包括模式(C 代码),第三段是补充的 C 函数。 例如, 第三段中一般都有 main() 函数。这些段以%%来分界。

例:字数统计的 Lex 程序 C 和 Lex 的全局声明 这一段中我们可以增加 C 变量声明。这里我们将为字数统计程序声明一个整型变量,来保存程序统计出来的字数。我们还将进行 Lex 的标记声明。

字数统计程序的声明 %{         int wordCount = 0;         %}         chars [A-za-z\抃'\.\"]         numbers ([0-9])+         delim [" "\n\t]         whitespace {delim}+         words {chars}+         %% 两个百分号标记指出了 Lex 程序中这一段的结束和三段中第二段的开始。

Lex 的模式匹配规则 字数统计程序中的 Lex 规则 {words} { wordCount++; /*         increase the word count by one*/ }         {whitespace} { /* do         nothing*/ }         {numbers} { /* one may         want to add some processing here*/ }         %%

C 代码 Lex 编程的第三段,也就是最后一段覆盖了 C 的函数声明(有时是主函数)。注意这一段必须包括 yywrap() 函数。 Lex 有一套可供使用的函数和变量。 其中之一就是 yywrap。一般来说,yywrap() 的定义如下例。

字数统计程序的 C 代码段 void main()         {         yylex(); /* start the         analysis*/         printf(" No of words:         %d\n", wordCount);         }         int yywrap()         {         return 1;         }

高级 Lex Lex 变量 yyin FILE* 类型。 它指向 lexer 正在解析的当前文件。  yyout FILE* 类型。 它指向记录 lexer 输出的位置。 缺省情况下,yyin 和 yyout 都指向标准输入和输出。  yytext 匹配模式的文本存储在这一变量中(char*)。  yyleng 给出匹配模式的长度。  yylineno 提供当前的行数信息。(lexer不一定支持。) 

高级 Lex ---Lex 函数 yylex() 这一函数开始分析。 它由 Lex 自动生成。  yywrap() 这一函数在文件(或输入)的末尾调用。如果函数的返回值是1,就停止解析。 因此它可以用来解析多个文件。代码可以写在第三段,这就能够解析多个文件。 方法是使用 yyin 文件指针(见上表)指向不同的文件,直到所有的文件都被解析。最后,yywrap() 可以返回 1 来表示解析的结束。  yyless(int n) 这一函数可以用来送回除了前憂? 个字符外的所有读出标记。  yymore() 这一函数告诉 Lexer 将下一个标记附加到当前标记后。 

YACC语言简介 什么是语法 : 我们看到 Lex 从输入序列中识别标记。如果你在查看标记序列,你可能想在这一序列出现时执行某一动作。这种情况下有效序列的规范称为语法。Yacc 语法文件包括这一语法规范。它还包含了序列匹配时你想要做的事。

以英语为例, 这一套标记可能是:名词, 动词, 形容词等等。为了使用这些标记造一个语法正确的句子,你的结构必须符合一定的规则。一个简单的句子可能是名词+动词或者名词+动词+名词。 所以在我们这里,标记本身来自语言(Lex),并且标记序列允许用 Yacc 来指定这些标记(标记序列也叫语法)。

终端和非终端符号 终端符号: 代表一类在语法结构上等效的标记。 终端符号: 代表一类在语法结构上等效的标记。 终端符号有三种类型: 命名标记: 这些由 %token 标识符来定义。按照惯例,它们都是大写。 字符标记: 字符常量的写法与 C 相同。例如, ?? 就是一个字符标记。 字符串标记 : 写法与 C 的字符串常量相同。例如,"<<" 就是一个字符串标记。 lexer 返回命名标记。

非终端符号: 是一组非终端符号和终端符号组成的符号。按照惯例,它们都是小写。 在例子中,file 是一个非终端标记而 NAME 是一个终端标记。

用 Yacc 来创建一个编译器包括四个步骤: 编写一个 .y 的语法文件(同时说明 C 在这里要进行的动作)。  编写一个词法分析器来处理输入并将标记传递给解析器。 这可以使用 Lex 来完成。  编写一个函数,通过调用 yyparse() 来开始解析。  编写错误处理例程(如 yyerror())。  编译 Yacc 生成的代码以及其他相关的源文件。

用 Yacc 编写语法 如同 Lex 一样, 一个 Yacc 程序也用双百分号分为三段。它们是:声明、语法规则和 C 代码。 我们将解析一个格式为 姓名 = 年龄的文件作为例子,来说明语法规则。我们假设文件有多个姓名和年龄,它们以空格分隔。 在看 Yacc 程序的每一段时,我们将为我们的例子编写一个语法文件。

C 与 Yacc 的声明 C 声明可能会定义动作中使用的类型和变量,以及宏。还可以包含头文件。每个 Yacc 声明段声明了终端符号和非终端符号(标记)的名称,还可能描述操作符优先级和针对不同符号的数据类型。 lexer (Lex) 一般返回这些标记。所有这些标记都必须在 Yacc 声明中进行说明。

在文件解析的例子中我们感兴趣的是这些标记:name, equal sign, 和 age。Name 是一个完全由字符组成的值。 Age 是数字。于是声明段就会像这样: 文件解析例子的声明        %         #typedef char* string; /*         to specify token types as char* */         #define YYSTYPE string /*         a Yacc variable which has the value of returned token */         %}         %token NAME EQ AGE         %%

类似 Lex, Yacc 也有一套变量和函数可供用户来进行功能扩展。 YYSTYPE 定义了用来将值从 lexer 拷贝到解析器或者 Yacc 的 yylval (另一个 Yacc 变量)的类型。默认的类型是 int。 由于字符串可以从 lexer 拷贝,类型被重定义为 char*。

Yacc 语法规则 Yacc 语法规则具有以下一般格式:        result: components { /*         action to be taken in C */ }         ; 在这个例子中,result 是规则描述的非终端符号。Components 是根据规则放在一起的不同的终端和非终端符号。 如果匹配特定序列的话 Components 后面可以跟随要执行的动作。

param : NAME EQ NAME {         printf("\tName:%s\tValue(name):%s\n", $1,$3);}             | NAME EQ VALUE{             printf("\tName:%s\tValue(value):%s\n",$1,$3);}         ;

如果上例中序列 NAME EQ NAME 被匹配,将执行相应的 { } 括号中的动作。 这里另一个有用的就是 $1 和 $3 的使用, 它们引用了标记 NAME 和 NAME(或者第二行的 VALUE)的值。

标记 NAME 的 Lex 代码是这样的 char [A-Za-z]         name {char}+         %%         {name} { yylval = strdup(yytext);         return NAME; }

文件解析例子的规则段是这样的: 文件解析的语法        file : record file         | record         ;         record: NAME EQ AGE {         printf("%s is now %s years old!!!", $1, $3);}         ;         %%

附加 C 代码 一个函数如 main() 调用 yyparse() 函数(Yacc 中 Lex 的 yylex() 等效函数)。 一般来说,Yacc 最好提供 yyerror(char msg) 函数的代码。 当解析器遇到错误时调用 yyerror(char msg)。错误消息作为参数来传递。

一个简单的 yyerror( char. ) 可能是这样的: int yyerror(char 一个简单的 yyerror( char* ) 可能是这样的:        int yyerror(char* msg)         {         printf("Error: %s         encountered at line number:%d\n", msg, yylineno);         }

这一段还包括文件解析例子的主函数: 附加 C 代码 void main() { yyparse(); } int yyerror(char 这一段还包括文件解析例子的主函数: 附加 C 代码        void main()         {             yyparse();         }         int yyerror(char* msg)         {         printf("Error: %s         encountered \n", msg);

实习要求 用LEX编写C语言词法分析器 输入:C语言程序 输出:词法分析结果 用YACC编写C语言语法分析器 提交:.c文件和.exe文件 提交时间:最晚考试前提交 ftp上有Pascal的例子

需要的软件 Parser Generator 在ftp上包括这个软件的使用方法和安装文件 VC6或7

C语言 auto break case char const continue default do double else enum extern float for goto if int long register return short signed sizeof static struct switch typedef union unsigned void volatile while