250 likes | 509 Views
编译原理实践 -- PL/0 的词法分析程序构造. PL/0 的词法分析程序构造. 词法分析方法的构造方法分类 词法分析程序的任务 词法分析程序的设计. 1. 词法分析程序的构造方法分类. 讲述正规式(正则表达式)和有穷自动机理论,目的是为了介绍词法分析程序自动构造工具 Lex 的原理。词法分析程序不是通过编程,而是执行 Lex 产生 采用“ Loop-and-Swtich ”(循环和分支)方法 本课程采用第二种方法, Lex 方法将在后续时间补充介绍. 2. 词法分析程序的任务. 词法分析程序的任务:对源程序进行扫描,提供一个个符号给语法分析程序。
E N D
PL/0的词法分析程序构造 • 词法分析方法的构造方法分类 • 词法分析程序的任务 • 词法分析程序的设计
1.词法分析程序的构造方法分类 • 讲述正规式(正则表达式)和有穷自动机理论,目的是为了介绍词法分析程序自动构造工具Lex的原理。词法分析程序不是通过编程,而是执行Lex产生 • 采用“Loop-and-Swtich”(循环和分支)方法 本课程采用第二种方法,Lex方法将在后续时间补充介绍
2.词法分析程序的任务 • 词法分析程序的任务:对源程序进行扫描,提供一个个符号给语法分析程序。 • 简称为扫描器(scanner)或扫描程序 • 词法分析程序实现的2种方案: 1.先单独工作一遍,把字符流源程序先变为符号序列,输出到一个中间文件上,然后将这个文件作为语法分析程序的输入继续第二遍的编译过程 字符串源程序 词法分析 符号串源程序
取符号 字符串源程序 词法分析器 语法分析器 送符号 词法分析程序实现的2种方案 2.有些编译程序将词法分析和语法分析安排在同一遍中,此时词法分析作为语法分析程序的一个子程序。每当语法分析需要一个新的符号时,就调用词法分析子程序,词法分析子程序从字符串源程序中识别出一个具有独立意义的单词,将其符号返给语法分析。这种方法避免了中间文件,省去了送取符号工作,有利于提高编译程序的效率。书中采用这种方案。
程序getsym • 本课程采用第2种方案,程序名getsym,预先审视源程序下一个符号,并将读入的符号放在变量sym中,语法分析的判断分析将以这个读入的符号为基础 • 具体任务: • 跳过空格字符 • 识别像begin、end、if、while等这样的保留字 • 识别非保留字,作为标识符处理 • 识别数字 • 识别专用符号组合,如:=、<=、>= • 识别特殊的单个字符,如+、-、/、* • 跳过注释行(书中例子程序没有体现)
辅助过程getch • getsym需要一个辅助过程getch,每被调用一次就读入下一个字符 • 除此之外的任务: • 识别行结束标志,作为空格符处理 • 拷贝原文输出 • 在输出文件每行开始添加坐标(书中例子程序没有体现)
3.词法分析程序的设计 • 每调用一次getsym,首先用循环结构在源程序上向前读入一个非空格字符,然后对此字符进行分析,转相应部分处理 • getsym大致可以分为三个部分 • 处理保留字和标识符 • 处理常数 • 处理组合字符和单个字符
说明 • 过程getch的功能:getch被getsym每调用一次,就在inf输入文件上向前读取一个字符给ch变量(程序P84) • getch相关变量说明 inf:text (*inf文本文件用于指向输入的源程序文件*) ch:char (*最近一次从文件中读出的字符*) cc:integer (*行缓冲区指针,初始为0*) ll:integer (*行缓冲区长度,初始为0 *) line:array[1..81] of char (*行缓冲区*)
说明 procedure getch; begin if cc = ll then begin if eof(inf) then begin write('program incomplete'); halt end; ll:= 0; cc:= 0; while not eoln(inf) do begin ll:=ll+1; read(inf,ch); write(ch); line[ll]:= ch end; writeln; ll:=ll+1; readln(inf); line[ll]:=' ' end; cc:=cc+1; ch:=line[cc] end
处理保留字和标识符 • 在词法分析程序getsym中,当读入的字符为a-z的字母时候,就进入处理保留字和标识符的程序部分(程序P84) • 相关类型说明: symbol=(nul,ident,number,plus,minus,times,slash,…) alfa=packed array [1..al] of char (*alfa类型用于标识符*) • 相关常量说明: al=10 (*标识符最长长度*) norw=11 (*保留字个数*)
处理保留字和标识符 word:array[1..norw] of alfa word[1]~word[11] (*保留字表,长度为10的保留字,多余用空格填充,以便词法分析时用二分法查找保留字*) wsym:array[1..norw] of symbol wsym[1]~wsym[11] (*在上面的保留字表中找到保留字后可以在本表中相应位置找到保留字类型*) ssym:array [char] of symbol ssym[‘+’]~ssym[‘;’] (*符号表,把可能出现的符号赋予上相应的类型,其余符号为nul*) • 相关变量说明 sym:symbol (*词法分析器输出结果之用*) id:alfa (*词法分析器输出结果之用*) num:integer (*词法分析器输出结果之用*) i,j,k:integer (*局部变量*) a:alfa (*词法分析器中用于临时存放正在分析的词*) kk:integer (*引入此变量为了性能,初始为10 *)
处理保留字和标识符 if ch in ['a'..'z'] then begin (* identifier or reserved word *) k:=0; repeat if k < al then begin k:=k+1; a[k]:=ch end; getch until not (ch in ['a'..'z','0'..'9']); if k>=kk then kk:=k else repeat a[kk]:=' '; kk:=kk-1 until kk=k;
处理保留字和标识符 id:=a; i:=1; j:=norw; (*二分查找法*) repeat k:=(i+j)div 2; (*k指向中间的保留字*) if id<=word[k] then j:=k-1; if id>=word[k] then i:=k+1 until i>j; (*循环直到找完保留字表*) if i-1>j then sym:=wsym[k] else sym:=ident end else…
处理常数 • 相关变量/常量说明 num:integer (*词法分析器输出结果之用*) nmax=14 (*数字允许的最长位数*)
处理常数 if ch in ['0'..'9'] then begin (* number *) k:=0; num:=0; sym:=number; repeat num:=10*num+(ord(ch)-ord('0')); k:=k+1; getch; until not (ch in ['0'..'9']); if k> nmax then error(30) end else…
总结 • 词法分析过程getsym总结:从源文件中读出若干有效字符,组成一个token串,识别它的类型 为保留字/标识符/数字或是其它符号。如果是保留字,把sym置成相应的保留字类型,如果是 标识符,把sym置成ident表示是标识符,于此同时,id变量中存放的即为保留字字符串或标识 符名字。如果是数字,把sym置为number,同时num变量中存放该数字的值。如果是其它的操作符, 则直接把sym置成相应类型
词法分析举例 • const m=7;
注意 • 完整的词法分析程序getsym在下一章program4中,program4是PL/0语言完整的语法分析程序
补充:另一个版本的词法分析 • C语言实现 • 先单独工作一遍,把字符流源程序先变为符号序列,输出到一个中间文件上 • 增加了去除注释的处理/*…*/,但注释不能嵌套 • 没有优化,容易理解,可供大家参考