编译器之词法分析

最近我们在做一个有关SNL语言的编译器,下面写了一下大概流程

词法分析器是编译过程的第一阶段,功能是

1.对以字符串形式输入的源程序(这里是把源程序从文件读出,也可以在控制台输入)按顺序进行扫描,根据SNL语言的词法规则识别具有独立意义的单词(符号)序列,
  如保留字(由语言系统自身定义的,通常是由字母组成的字符串),
     标识符(一般是由字母开头,字母、数字,或其它符号的任意组合构成的,这里SNL是由字母开头,后面加字母或数字组成的,分析起来较简单),
     常量(包括整数常数、 实数常数、字符串常量等),
     特殊符号(运算符和界限,运算符表示程 序中算术运算、逻辑运算、字符运算、赋值运算的确定的字符或字符串)等,

并输出与其等价的TOKEN序列(这里将Token序列保存在了文件中)。

2.报告词法错误!
   词法错误:语言字母表以外的非法字符
(用确定有限自动机可以容易识别)

**几个问题和处理方法**

1.保留字的识别

把保留字当作一般标识符识别,然后查找保留字表,就是这里的Lex表,如果有,把它当作保留字处理,如果没有按一般标识符处理。

2.复合单词的识别

有一类单词是由两个或者两个以上符号组成的,有时前缀部分也可以是一个独立的单词

3.数的转换

词法分析应把字符串转换成数,“123”看成123

4.向前看几个字符的处理

为了识别出一个单词需要向前看好几个单词

5.控制字符的处理

1)无用的空格符和制表符删掉(这里是自动忽略)

2)字符串内的空格不能删

3)换行符不能直接删除,用于错误定位

4)注释

源程序中的注释没有任何语法和语义上的意义,在进行词法分析时可以直接将注释忽略,不必生成TOKEN

5)直接在语义信息部分存储

语义信息的长度有限制时,直接将标识符或常量本身存储于TOKEN中的语义(语义信息是为了 后面进行的语义分析和代码生成提供信息)

构造词法分析器的步骤:

1.确定词法分析器的接口,即确定词法分析器是作为语法分析的一个子程序还是作为独立一遍

2.确定单次分类和TOKEN结构

3.确定每一类单词的描述正则表达式->NFA->DFA

4.设计算法实现DFA 单词的内部表示:

TOKEN     TOKEN通常包含两部分:token-type,attribute-value     token-type: 说明单词的类别,为语法分析提供信息     attribute-value:单词的属性,为语义分析和代码生成提供信息 (这里  TOKEN包含4部分:在源程序中的行数,Lex表中的词法信息编码,语法信息,语义信息 这里用确定有限自动机DFA识别符号串)

输入:以EOF作为结束符的符号序列    输出:token序列

数据结构:

struct Token{

int line;//行数

LEX lexType;//记录单词的词法信息枚举编码

char LexInfor[StrMaxSize];//词法信息

char SemInfor[StrMaxSize];//语义

};

程序如下:

  1 /*************
  2         SNL语法分析程序输出TOKEN序列,数据表示是链表形式
  3         巴科斯范式表示: <标识符> := 字母 { 字母 | 数字}
  4
  5 */
  6 #include "scanner.h"
  7  char* LEXSTR[] = {
  8     "0","program", "procedure","type","var","if","then","else","fi",
  9     "while","do","endwh","begin","end","read","write","array",
 10     "of","record","return","integer","char",
 11     "ID", "INTC",
 12     ":=","=","<","+","-","*","/","(",")",".",":",";",",","[","]","..",
 13     "EOF" , "SPACE" , "ERROR" , "\\" ,"#" //"#"为结束标志          //43
 14 };
 15 char* Word[]={
 16     /*********保留字****** */
 17     "0","PROGRAM","PROCEDURE","TYPE","VAR","IF","THEN","ELSE","FI","WHILE",
 18         "DO","ENDWH","BEGIN","END","READ","WRITE","ARRAY","OF","RECORD","RETURN",
 19
 20     "INTEGER","CHAR",/******类型*******/
 21
 22     /******标识符,整数常量**/
 23     "ID","INTC",
 24
 25     /*****单双分界符,数组下标限界符*******/
 26     "ASSIGN","EQ","LT","ADD","SUB","MUL","DIV","LPAREN","RPAREN",
 27     "DOT","COLON","SEMI","COMMA","LMIDPAREN","RMIDPAREN","UNDERANGE",
 28
 29     /*//特殊符号:文件结束符,空格符,出错标号*/
 30     "EOFF","SPACE","ERROR1","UNDIV"
 31
 32 };
 33 TokenNode::TokenNode(int lineNum,int code,const char *lexInfor,const char*semanInfor){
 34             token.line=lineNum;
 35             token.lexType=LEX(code);
 36             strcpy(token.LexInfor,lexInfor);
 37             strcpy(token.SemInfor,semanInfor);
 38 }
 39 List::~List(){
 40     while(start!=NULL){
 41         TokenNode*p=start;
 42         start=start->next;
 43         delete p;
 44     }
 45 }
 46
 47 bool List::OutToFile(const char *filename)
 48 {
 49     if(start==NULL)
 50         return false;
 51     TokenNode* ptr=start;
 52     FILE* file=fopen(filename,"w+");//若文件存在,清零重写
 53     if(NULL==file)
 54         return 0;
 55     fprintf(file,"%5s\t%3s\t%10s\t%2s\n", "LineNUM" , "LEX" , "LexStr" , "SemStr");
 56     for(;ptr!=NULL;ptr=ptr->next)
 57     {
 58             fprintf(file,"%5d\t%3d\t%10s\t%2s\n",ptr->token.line , ptr->token.lexType , ptr->token.LexInfor , ptr->token.SemInfor );
 59     }
 60     fclose(file);
 61     return 1;
 62 }
 63 bool List::OutToScreen()
 64 {
 65     if(start==NULL)
 66         return false;
 67     printf("%5s\t%3s\t%10s\t%s\n", "LineNUM" , "LEX" , "LexStr" , "SemStr");
 68     TokenNode* ptr=start;
 69     for(;ptr!=NULL;ptr=ptr->next)
 70     {
 71             printf("%5d\t%3d\t%10s\t%2s\n",ptr->token.line , ptr->token.lexType , ptr->token.LexInfor , ptr->token.SemInfor );
 72     }
 73     return 1;
 74 }
 75
 76 bool List::insertNode(TokenNode*p){
 77         if(p==NULL)
 78             return false;
 79         if(end==NULL){
 80             start=end=p;
 81             end->next=NULL;
 82         }
 83         else{
 84             end->next=p;
 85             end=p;
 86             end->next=NULL;
 87
 88         }
 89         return true;
 90 }
 91
 92 List* Scanner::scanner(const char* sourceFile,const char* tokenFile){
 93         FILE*fp;
 94         fp=fopen(sourceFile,"r");
 95         if(fp==NULL){
 96                 printf("Failed to open the file.");
 97                 exit(0);
 98                     //return 1;
 99         }
100         int lineNum=1;        //从第一行开始
101         bool flag=true;
102         TokenNode* ptr;
103         List *list=new List;//新建一个TokenList,用以保存分析得到的TOKEN序列
104         while(flag){
105             char ch=getNextChar(fp);//cout<<(int)ch<<" ";
106             switch(theState(ch)){
107                 case S1:                         //字母,,区分标识符还是保留字
108                     ungetNextChar(fp);           //为了后面拼接字符串,而不用传过去
109                     isLetterStr(fp,lineNum,list);
110                     break;
111                 case S2:
112                     ungetNextChar(fp);
113                     isNumStr(fp,lineNum ,list);
114                     break;
115                 case S3:                                   //分隔符
116                     ungetNextChar(fp);
117                     isSignStr(fp,lineNum,list);
118                     break;
119                 case S4:                                             //注释
120                     ungetNextChar(fp);
121                     isCommentStr(fp,lineNum);
122                     break;
123                 case S5:                                               //空格 制表符
124                         //    reservedLookUp(fp);                  //*!不能回退字符,不然在下面的while会死循环
125
126                     while(ch==‘ ‘||ch==‘\t‘)       //怎么写成&&了?
127                         ch=getNextChar(fp);
128                     if(ch!=EOF)
129                         ungetNextChar(fp);
130                     break;
131                 case S6:                                     ////回车换行。回车符‘\r‘
132                     lineNum++;
133                     break;
134                 case S7:
135                     flag=false;
136
137                     ptr=new TokenNode(lineNum,EOFF,"EOF","文件结束符,无语义信息");
138                     insertTokenList(list,ptr);
139                     fclose(fp);
140                     break;
141                 case S8:
142
143                         //ungetNextChar(fp);
144                         cerr<<"Error! line"<<lineNum<<" :出现非法字符"<<endl;
145                          ptr=new TokenNode(lineNum,ERROR1,"ERROR","错误未定义字符");
146                         insertTokenList(list,ptr);
147                 }
148         }
149
150                 fclose(fp);
151                 if(list->Start()!=0)
152                      tokenlist=list;
153                  if(!list->OutToFile(tokenFile))
154                       cerr<<"Error:cannot open the LexFile!"<<endl;
155                  if(!list->OutToScreen())
156                        cerr<<"Error:cannot write on Screen!"<<endl;
157                 return list;
158
159 }
160 State Scanner::theState(char ch){
161
162             if(isLetter(ch)){             //字母
163                 return S1;
164             }else if(isDigit(ch)){        //数字
165                 return S2;
166             }else if(isSymbol(ch)){       //符号
167                 return S3;
168             }else if(ch==‘{‘){          //注释
169                 return S4;
170             }else if(ch==‘ ‘||ch==‘\t‘){
171                 return S5;
172             }else if(ch==‘\n‘||ch==‘\r‘){   //回车换行。回车符‘\r‘
173                 return S6;
174             }else if(ch==EOF){
175                 return S7;
176             }else
177                 return S8;
178 }
179 bool Scanner::isLetter(char ch){
180             if(ch>=‘a‘&&ch<=‘z‘||ch>=‘A‘&&ch<=‘Z‘)
181                 return 1;
182             return 0;
183 }
184 bool Scanner::isDigit(char ch){
185             if(ch>=‘0‘&&ch<=‘9‘)
186                 return 1;
187             return 0;
188 }
189 bool Scanner::isSymbol(char ch){
190             if(ch==‘+‘||ch==‘:‘||ch==‘=‘||ch==‘<‘||ch==‘+‘||ch==‘-‘
191                       ||ch==‘*‘||ch==‘/‘||ch==‘(‘||ch==‘)‘||ch==‘.‘
192                       ||ch==‘;‘||ch==‘,‘||ch==‘[‘||ch==‘]‘||ch==‘\\‘)
193                       return 1;
194             return 0;
195 }
196 int Scanner::getCode(const char* ch){
197             for(int i=0;LEXSTR[i]!="$";i++){
198                 if(strcmp(ch,LEXSTR[i])==0){
199                     return i;
200                 }
201             }
202             return -1;
203 }
204 int Scanner::isReservedWord(const char* ch){
205             for(int i=0;i<22;i++){
206                 if(strcmp(ch,LEXSTR[i])==0){             //!!!!!!!不能用==
207                     return i;
208                 }
209             }
210             return -1;
211 }
212 void Scanner::insertTokenList(List* list,TokenNode* ptr)
213 {
214             ptr->next=NULL;
215             list->insertNode(ptr);
216 }
217 void Scanner::isLetterStr(FILE*fp,int lineNum,List*list){
218     string str1;
219     char ch=getNextChar(fp);
220     while(isLetter(ch)||isDigit(ch))
221     {
222         str1+=ch;
223         ch=getNextChar(fp);
224     }
225     if(ch!=EOF)
226         ungetNextChar(fp);
227     int lex=isReservedWord(str1.c_str());
228     if(lex!=-1)//c_str() 以 char* 形式传回 str 内含字符串
229     {
230         TokenNode*    ptr=new TokenNode(lineNum,lex,Word[lex],"保留字,无语义信息");
231         insertTokenList(list,ptr);
232     }else{
233         TokenNode*ptr=new TokenNode(lineNum,ID,"ID",str1.c_str());
234         insertTokenList(list,ptr);
235     }
236 }
237
238 void Scanner::isNumStr(FILE*fp,int lineNum,List*list) {
239           string str2;
240           char ch;
241           ch=getNextChar(fp);
242           while(isDigit(ch)){
243               str2+=ch;
244               ch=getNextChar(fp);
245           }
246           if(ch!=EOF)
247               ungetNextChar(fp);
248           TokenNode *ptr;
249           ptr=new TokenNode(lineNum,INTC,"INTC",str2.c_str());
250           insertTokenList(list,ptr);
251 }
252 void Scanner::isSignStr(FILE*fp,int lineNum,List*list){
253     char ch;
254     ch=getNextChar(fp);
255     char ch1;
256     TokenNode *ptr;
257     if(ch==‘:‘)
258     {
259         if(getNextChar(fp)==‘=‘)
260         {
261             ptr=new TokenNode(lineNum,getCode(":="),"ASSIGN","双分隔符,无语义信息");
262             insertTokenList(list,ptr);
263             return ;
264         }
265         else
266             ungetNextChar(fp);
267         return;
268     }
269     if(ch==‘.‘)
270     {
271         ch1=getNextChar(fp);
272         if(ch1==‘.‘)
273         {
274             ptr=new TokenNode(lineNum,getCode(".."),"UNDERANGE","数组限界符,无语义信息");
275             insertTokenList(list,ptr);
276             return;
277         }
278         if(ch1!=EOF)
279             ungetNextChar(fp);
280         ptr=new TokenNode(lineNum,getCode("."),"DOT","程序结束符,无语义信息");
281         insertTokenList(list,ptr);
282         return;
283     }
284     string str;
285       str+=ch;
286     ptr=new TokenNode(lineNum,getCode(str.c_str()),Word[getCode(str.c_str())],"单分隔符,无语义信息");
287     insertTokenList(list,ptr);
288 }
289 void Scanner:: isCommentStr(FILE*fp,int lineNum) {
290     char ch;
291
292     ch=getNextChar(fp);
293     TokenNode *ptr;
294     while(ch!=‘}‘)
295     {
296         if(ch==‘\n‘)
297             lineNum++;
298         else if(ch==EOF)
299         {
300             cout<<"Error, line"<<lineNum<<" : 到达文件尾,仍找到不注释结束符,请检查源程序后编译!"<<endl;
301             fclose(fp);
302             break;
303         }
304         ch=getNextChar(fp);
305     }
306
307 }
308 int Scanner:: getlex(const char *ch)
309 {
310     for(int i=0;strcmp(LEXSTR[i],"$")!=0;i++)
311     {
312         if(strcmp(ch,LEXSTR[i])==0)
313             return i;
314     }
315     return -1;
316 }
时间: 2024-10-10 15:57:15

编译器之词法分析的相关文章

自己动手写编译器之TINY编译器词法分析

TINY是<编译原理与实践>一书中介绍的教学编程语言,该语言缺少真正程序设计语言的主要特征,但足以例证编译器的主要特征了.本文将介绍该编译器的实现过程,完整的实现代码loucomp_linux中,供编译原理初学者参考. 小试牛刀: 下载源码后,进入loucomp_linux, 在命令行输入 $make 便生成tiny程序,然后输入 $tiny sample.tny tiny 将sample.tny中的TINY源码生成tm指令.tm指令是TM虚拟机的汇编代码,TM虚拟机的源码在tm.c中,输入如

编译原理词法分析

#include<iostream>#include<String>#include <strstream>using namespace std; #define N 200;/*关键字结构体定义*/typedef struct keyword{ char name[20];}KeyWord;/*符号表结构体定义*/typedef struct symboltable{ char name[20]; //token值 string attribute; //属性值}S

编译原理 词法分析

原文地址:编译原理 词法分析 编译原理 词法分析 词法分析的主要任务是从左至右逐个字符地对源程序进行扫描,产生一个个单词序列,用于语法分析. 1.正则表达式 对给定的字符集∑={c1,c2,...,cn},归纳定义: 1.空串ε是正则表达式 2.对于任意c∈∑,c是正则表达式 3.如果M和N是正则表达式,则下列表达式也是正则表达式 (1)选择 M|N={M,N} (2)连接 MN={mn|m∈M,n∈N} (3)闭包 M*={ε,M,MM,MMM,...} 2.正则表达式的扩展 (1)[c1-c

编译原理词法分析程序

预头文件 /*----Head file for analysis programs in....----*/ #include <stdio.h> #include <string.h> #include <ctype.h>                  //使用到的字符分类函数定义在的头文件 /** *TEST编译器:词法分析头文件 *@author mohui *@date 2015/04/12 ***/ //--------预定义常量-------- #de

自己动手实现简单编译器之(一)形式语言理论

一·预备知识(编译概述) 翻译程序是指这样一个程序,它把一种语言(源语言)所写的程序(源程序)翻译成等价的另一种语言(目标语言)的程序(目标程序). 编译程序是一种翻译程序,它将高级语言所写的源程序翻译成等价的机器语言或汇编语言的目标程序.其工作过程一般可划分为如下五个阶段: 1:词法分析 词法分析阶段的任务是对构成源程序的字符串从左到右进行扫描和分解,根据语言的词法规则,识别出一个一个具有独立意义的单词( 也称单词符号, 简称符号 ). 注:词法规则就是单词符号的形成规则,它规定了哪样的字符串

我的第一个编译器之词法分析器

用Java写java的编译器和jvm 为什么用java,java的结构最便于理解,其丰富的设计模式能使编译器的结构十分鲜明 一个编译器的前端模型 源代码–词法分析器-(词法单元)-语法分析器-(语法分析树)-中间代码生成器–三地址代码 再加上一个符号表连接所有的结构 文法定义 一个上下文无关方法由四个元素组成 1.一个终结符号集合,也就是"词法单元"终结符号是该文法定义的语言的基本符号的集合 2.一个非终结符号集合,"语法变量"每个非终结符号表示一个终结符号串的集合

编译器之神vim大改造

相信码农都听过两大神器,一是emacs,一是vim.大家都知道e党和v党老是吵架.其实,这并没有什么好吵的,只是每个人都有自己适用的东西.作者接触了vim,也接触过emacs.对于作者,觉得vim更加适用. 下面,给大家介绍一下作者自己的vim配置.github地址:https://github.com/tenghui0425/VimScripts 首先给大家看一下大图.这个是vimrc配置的图目录情况. 配置主要是以下几块. 基本配置,放在.vimrc下,比如一下全局的map.最后加载一个基本

读龙书学编译原理 词法分析(2)...

第二种词法分析的方式当然是词法分析器的自动生成器. 如lex, jlex 等等. 那么如何来让生成器知道我们想要生成的Token呢 ? 这就涉及到统一的声明式规范的问题, 换句话讲, 你将按照生成器的形式要求将声明式的规范交给生成器, 那么它就能够对其进行识别. 所以我们只需要完成声明式的规范即可完成词法分析部分, 到最后词法解析器自动生成器就能为我们生成对应的自动机... 那么如何来给生成器一个统一的声明式的规范呢 ? 先要了解的一个数学工具就是正则表达式, 如图所示, 这是对正则表达式的基本

用java开发编译器之Thompson构造:正则表达式的词法解析

Thompson构造:正则表达式的词法解析 大家好,欢迎大家来到coding迪斯尼,阅读博客的朋友可以到我的网易云课堂中,通过视频的方式查看代码的调试和执行过程: http://study.163.com/course/courseMain.htm?courseId=1002830012 继上一节我们开发了闭包替换功能后,这一节,我们继续推进Thompson 构造算法的开发.我们的目标是,给定一组正则表达式后,把他转换为NFA有限状态自动机.无论是正则表达式,还是最终的有限状态自动机,他们的本质