470 likes | 825 Views
编译原理. 主讲 : 肖秀春 E-Mail: xiaoxxc@sohu.com. 第二章 一个微小编译器. 2.1 Micro 语言规则. 2.2 Micro 语言的词法分析. 2.3 Micro 语言的语法分析. 2.4 Micro 语言的语义分析. 2.5 Micro 语言的目标代码. 本章主要内容 (Chapter 1). 变量声明序列 语句序列. 程序 组成. 赋值语句 输入语句 输出语句. 2.1 Micro 语言规则. 1 ) Micro 程序组成( Pascal 语言的子集). 2.1 Micro 语言规则.
E N D
编译原理 主讲:肖秀春 E-Mail: xiaoxxc@sohu.com
2.1 Micro 语言规则 2.2 Micro 语言的词法分析 2.3 Micro 语言的语法分析 2.4 Micro 语言的语义分析 2.5 Micro 语言的目标代码 本章主要内容(Chapter 1)
变量声明序列 语句序列 程序 组成 赋值语句 输入语句 输出语句 2.1 Micro 语言规则 1) Micro程序组成(Pascal语言的子集)
2.1 Micro 语言规则 2) Micro程序结构 见教材P12 图2.1.0.1
2.1 Micro 语言规则 3) Micro程序示例 begin var x1:real; var z1:real; x1:=0.525; z1:=x1+5.6; write(x1); write(z1) end.
2.1 Micro 语言规则 4) Micro程序编程练习 练习:用Micro语言写一个求圆周长和面积的程序。 Micro语言 真容易学, 呵呵!
2.2 Micro 词法分析 1) 词法分析的特点 其特点是:不依赖于语法分析,只依赖于单词的文法定义
输入 输出 程序的字符串序列 Token序列 (单词的内部表示) 2.2 Micro 词法分析 2) 词法分析的任务
2.2 Micro 词法分析 3) Micro的有用单词的种类 以字母开头: 保留字:begin,end,var,read,write,int,real 标识符:字母/数字串 以数字开头: 整常数:数字开头的数字串 实常数:整数.整数 符号词:+,-,*,/,(,),:,:=,;,. 控制词:enter
2.2 Micro 词法分析 4) 词法分析后的单词内部表示 保留字:$begin, $end, $var, $read, $write, $int, $real 标识符:($id,标识符) 整常数:($intC,整常数) 实常数:($reaC,实常数) 符号词:$plus,$sub,$mult,$div,$Lparen,$Rparen,$colon, $assig,$semi,$line,$stop (+,-,*,/,(,),:,:=,;,enter,.) 单词的词法信息和语义信息
2.2 Micro 词法分析 5) 一个简单程序的词法分析 例: begin var x1:real; var z1:real; x1:=0.5; z1:=x1+56; write(z1+2.3); read(x1) end.
2.2 Micro 词法分析 6)Micro词法分析的程序实现 a.数据结构的选择 b.程序算法流程图
2.2 Micro 词法分析 6)Micro词法分析的程序实现(续) Procedure scanner(); begin while ~Eof do { Noblank(ch); case ch of ‘A’..’Z’|’a’..’z’ {Identifier(name); case name of “begin” GenToken($begin); “end” GenToken($end); “var” GenToken($var); “int” GenToken($int); “real” GenToken($real); “read” GenToken($read); “write” GenToken($write); other GenToken($id,name); end }; Noblank(ch):跳过空格符串将第一个非空格字符读到ch中(删除空格符)
2.2 Micro 词法分析 6)Micro词法分析的程序实现(续) ‘0’..’9’ {Constant(class,C); GenToken(Class,C)}; ‘(’ GenToken($Lparen); Read(ch); ‘)’ GenToken($Rparen); Read(ch); ‘+’ GenToken($plus); Read(ch); ‘*’ GenToken($mult); Read(ch); ‘;’ GenToken($semi); Read(ch); ‘:’ {Read(ch); if ch=‘=’ then {GenToken($assig); Read(ch)} else GenToken($colon)} ‘.’ GenToken($stop); Read(ch); ‘enter’ GenToken($line); Read(ch); ‘other’ LexicalError(ch) end } end
2.2 Micro 词法分析 7)Micro词法分析的相关子程序实现 标识符的词法分析 Identifier(name):从输入流把当前标识符名读到name中(它可能是保留字)。在调用时,当前字符一定是字母,且已被读到ch中。 Procedure Identifier(name:string); Begin name:=“ ”; Append(name,ch); Read(ch); while isLetter(ch) or isDigit(ch) do { Append(name,ch); Read(ch);} End isLetter(ch):如果ch是字母,则取true,否则取false。 isDigit(ch):如果ch是数字,则取true,否则取false。
2.2 Micro 词法分析 7)Micro词法分析的相关子程序实现(续) 常数(整常数或实常数)的词法分析 Procedure IntConst(N,L); Begin N:=Num(ch); L:=1; Read(ch); while isDigit(ch) do {N:=N*10+Num(ch); L:=L+1; Read(ch) } End IntConst(N,L):从输入流读当前整常数,并在N中给出所读常数的二(十)进制值,在L中给出整常数的位数。调用时当前字符应是数字。 Num(ch):表示数字字符ch对应的二(十)进制值。
整数(小数点左部) Procedure Constant(class:classType,C:ConsstType); Begin IntConst(N1,L1); if ch=‘.’ then {Read(ch); if (~IsDigit(ch)) LexicalError(ch); IntConst(N2,L2); class:=$realC; C:=N1+N2*(1.0/10)^L2 } Else {class:=$intC; C:=N1} End 整数(小 数点右部) 2.2 Micro 词法分析 7)Micro词法分析的相关子程序实现(续) 常数(实常数)的词法分析 Constant(class,C):从输入流读到当前常数,并在class中给出常数的类型标志,C中给出常数的二进制数值。调用时当前字符定是数字(在ch中)
2.3 Micro 语法分析 1)语法分析的特点 语法分析在遇到语法错误时能够继续进行语法检查。 语法分析只给出错误提示,并不修改Token序列。 语法分析只利用Token的词法信息,忽略其中的语义信息。
2.3 Micro 语法分析 2)语法分析的任务 a.检查程序是否有语法(语法结构)上的错误,并输出语法错误提示。 b.输入:词法分析后得到的Token表。 c.输出:具体语法错误提示或语法全部正确提示。
声明检查:begin var id: real; (声明的位置) 赋值语句 标识符 单词 输入语句 表达式 整/实常数 语句检查: 输出语句 左括号 后继符(语句末符号)(;enter end) 2.3 Micro 语法分析 3)语法分析的思路
2.3 Micro 语法分析 4)Micro语法分析的程序实现 程序算法流程图
Procedure Parser(); begin Match($begin,1); Match($var,2); LD: Match($id,3); Match($colon,4); Match($intC/$reaC,5); Match($semi,6); ReadToken(token); if token=$line then ReadToken(token); if token=$var then goto LD; 声 明 2.3 Micro 语法分析 4)Micro语法分析的程序实现(续) Match(kind,n):读当前Token,并检查Token.LH=kind?若不等,则打出错误编号n。 Token.LH:Token的左半部。 Token.RH:Token的右半部。 ReadToken(token): 把当前Token读到token中
LS:case token of $write {Match($Lparen,7); Expr(); Match($Rparen,8)}; $read {Match($Lparen,9); Match($id,10); Match($Rparen,11)}; $id {Match($assig,12); Expr()}; other {error(13)} end; ReadToken(token); //读语句的后继符 语句 单词 后继符 2.3 Micro 语法分析 4)Micro语法分析的程序实现(续)
2.3 Micro 语法分析 4)Micro语法分析的程序实现(续) case token of $semi {ReadToken(token); if token=$line then ReadToken(token); goto LS}; $line Match($end); $end {ReadToken(token); if token=$stop then STOP else Error(14)} other {Error(15)} end end
Procedure Expr(); begin LF: ReadToken(token); case token of ($id,..) skip; ($intC,..) skip; ($reaC,..) skip; $Lparen begin Expr(); Match($Rparen,16) end; other error(17) end; ReadToken(token); case token of (接左) $plus goto LF; $mult goto LF; Other BackToken End end 表达式语法检查 Token指针回溯一步 2.3 Micro 语法分析 5). Micro语言的语法分析子程序的程序实现
2.3 Micro 语法分析 6). Micro语言的语法分析错误提示 Error 1 程序头不是begin Error 2 变量声明头不是var Error 3 var后不是标识符 Error 4 “var id”后不是“:” Error 5 “var id:”后 不是类型符 Error 6 变量声明后不是“;” Error 7 write后不是“(” Error 8 “write(E”后不是“)” Error 9 read后不是“(” Error 10 “read (”后 不是”id” Error 11 “read (id”后 不是”)” Error 12 赋值语句左部 不是“:=” Error 13 语句头单词错 Error 14 程序结束符错 Error 15 语句后继符错 Error 16 缺“(E)”中的 闭括号 Error 17 运算分量的 后继符错
2.4 Micro 语义分析 1). Micro语言的语义分析的特点 语义分析在遇到语义错误时一般能够继续进行语义检查。 语义分析除了给出错误提示,还修改Token序列中特殊Token。 语义分析需要利用Token的语义信息。 语义分析还对标识符建立属性表。
2.4 Micro 语义分析 2). Micro语言的语义分析的任务 语法分析:只检查语法结构是否正确,这一阶段忽略Token的语义信息,而只使用其词法信息(即词类码,如$id,$intC等)。不检查他本身(语义)的正确性。 a. 检查语义错误; 语义分析: b. 在分析声明部分时,构造标识符的属性表,同时检查重复声明错误。 c. 分析语句部分时,检查变量无声明和类型不相容等错误,并且将Token序列中的标识符转换为($id,entry)(entry:变量的属性表(符号表)地址)。 (X*表示变量名(或常数)X的符号表地址)
a. 构造变量标识符的属性表; b. 变量标识符没有声明。 c. 变量标识符被重复声明。 d. 赋值语句左右部分的类型不相容。 a.当遇到标识符声明时,构造标识符的属性表(以下简称符号表); b.当语句中遇到被使用的标识符时,就去查符号表,若查不到,则表示没有声明; c.在构造属性表时,通过检查是否在表中有同名项的办法检查出重复声明的错误; d.当处理赋值语句时,通过求出其左右部类型来检查其类型是否相容。 2.4 Micro 语义分析 2). Micro语言的语义分析的任务(续) Micro语言只需要做: 上述对变量标识符的处理,主要思想为:
2.4 Micro 语义分析 2). Micro语言的语义分析的任务(续) 例: begin var x1:real; var z1:real; x1:=0.5; z1:=x1+56; write(z1+2.3); read(x1) end.
2.4 Micro 语义分析 3). Micro语言的语义分析的程序实现 Enter(name,Entry,s):将name填入符号表,并在entry中给出其表项地址。若已有同名项,则s取true值,否则取false。 Procedure Semantic(); begin Creat; //建空符号表 ReadToken(); //读$begin LD: ReadToken(token); case token.LH of $var {ReadToken(token); Enter(token.RH,Entry,s); if s=true then Error(1); // 重复声明错 ReadToken(); ReadToken(token);//: $int/$real case token.LH of $int SetAttribute(entry,newAddr,intType); $real SetAttribute(entry,newAddr,realType) end; ReadToken(); goto LD}; //; SetAttribute(entry,Addr,Type):将标识符的地址和类型填入符号表的entry项内
2.4 Micro 语义分析 3). Micro语言的语义分析的程序实现(续) $other while token!=$stop do { if token.LH=$id then Find(token.RH,entry,s) ; if s=false then Error(2); //无声明错 ChangeToken(entry) } end; Find(name,entry,s):用name查找符号表,并在addr和type中给出其name的地址和类型。若已有同名项,则s取true值,否则取false。 ChangeToken(entry):将被读token(标识符)的右半部改为entry地址
2.5 Micro 目标代码生成 1). Micro语言的目标语言指令 采用三地址形式的目标语言,其指令功能如下: add d1 d2 d3 (d3)=(d1)+(d2) mul d1 d2 d3 (d3)=(d1)*(d2) sto d1 d2 (d2)=(d1) inp d1 输入d1 out d1 输出d1
2.5 Micro 目标代码生成 2). Micro语言的表达式的目标语言生成 例:表达式 a*b+c*d 的目标代码
2.5 Micro 目标代码生成 2). Micro语言的表达式的目标语言生成(续) 练习:表达式 b+c*d 的目标代码生成
2.5 Micro 目标代码生成 3). Micro语言的表达式的目标语言生成要点 1)表达式处理用到一个语义栈,用于保存分量和运算符。 2)表达式为常数时,代码为空,其地址和类型可从常数的Token序列中得到。 3)表达式为变量时,代码为空,其地址和类型可从符号表中得到。
2.5 Micro 目标代码生成 3). Micro语言的表达式的目标语言生成要点(续) 4)包含运算符时: a. 当前Token为分量,将其地址和类型压入语义栈。 b. 当前Token为运算符q时: • 若栈中无运算符,将q压入语义栈: • 若栈中有运算符,且先前压入的运算符q’优先级低于q,则将q压入语义栈: • 若栈中有运算符,且先前压入的运算符优先级高于q,则产生q’ 的指令代码: c. 当前Token为常数、变量、运算符以外的Token: • 若栈中无运算符,将q压入语义栈: • 若栈中有运算符,则产生先前压入的运算符的指令代码: d. 重复上述过程,直到栈中没有运算符为止:
2.5 Micro 目标代码生成 3). Micro语言的表达式的目标语言生成要点(续) 5)每当产生代码时要弹出三个栈顶元素,并将运算结果的地址和类型再压入栈,其中的地址由系统产生,而类型则根据分量的类型来确定。 6)每当产生代码时检查类型是否相容。 7)对运算符建立优先关系。
Procedure GenCodeS(); begin LS: ReadNewToken(tk); case LH(tk) of $id {Search(RH(tk),vAddr,vType); ReadNewToken(); //:= GenCodeE(); eAddr:=SemanStack(top).addr; eType:=SemanStack(top).Type; if ~Equ(vType,eType) then Error(1); Sendcode(STO,eAddr,vAddr); POP}; 赋值的左边 赋值的右边 类型不相容错 2.5 Micro 目标代码生成 4).表达式无括号的目标语言指令生成的程序实现 SemanStack(top):表示SemanStack的顶元素 Sendcode(code):把代码code送入代码区 Search(RH(tk),vAddr,vType):用符号表的某项地址entry求变量的地址和类型。
2.5 Micro 目标代码生成 4).表达式无括号的目标语言生成的程序实现(续) $write {ReadNewToken(); //读 ( GenCodeE(); eAddr:=SemanStack(top).addr; Sendcode(OUT,eAddr); POP; ReadNewToken()};//读 ) $read {ReadNewToken(tk); Search(RH(tk),vAddr,vType);//求变量地址 Sendcode(INP,vAddr)}; end; ReadNewToken(tk); if tk=$semi then goto LS else SendCode(STOP) end
Procedure GenCodeE(); begin L0: ReadNewToken(tk); L1: case tk of ($id,entry) {Search(entry,vAddr,vType) ;//求变量地址 push(vAddr,vType); goto L0 }; ($intC,val) {push(val,int); goto L0 }; ($reaC,val) {push(val,real); goto L0 }; $plus|$mult if top>1 && SemanStack(top-1)<<tok then push(tok); goto L0 Else ProduceCode; goto L1 Other if top>1 then { ProduceCode; goto L1} end 判断优先级 2.5 Micro 目标代码生成 5).表达式无括号的目标语言生成的程序实现 ProduceCode:用语义栈SemanStack的内容生成代码
2.5 Micro 目标代码生成 5).表达式有括号的目标语言生成的程序实现(续) Procedure ProduceCode; begin if ~Equa(typ1,typ2) then Error() else { temp:=NewAddr; SendCode(OP,Addr1,Addr1,temp); Pop(3);push(temp,typ1) } end ProduceCode:用语义栈SemanStack的内容生成代码
2.5 Micro 目标代码生成 5).表达式有括号的目标语言生成的程序实现(续) Procedure GenCodeE(); begin pushOperator(); L0: ReadNewToken(tk); L1: case tk of ($id,entry) {Search(entry,vAddr,vType); pushOperand(vAddr,vType); goto L0 }; ($intC,val) {pushOperand(val,int); goto L0 }; ($reaC,val) {pushOperand(val,real); goto L0 }; $Lparen {pushOperator(); goto L0 }; OperandStack:分量栈 OperatorStack:运算符栈
2.5 Micro 目标代码生成 5).表达式有括号的目标语言生成的程序实现(续) $plus,$mult case OperatorStack(top) of {pushOperator(tok); goto L0}; $plus|$mult if OperatorStack(top)<<tok then {pushOperator(tok); goto L0}; else {ProduceCode; goto L1} end $Rparen case OperatorStack(top) of {popOperatorStack; goto L0}; $plus|$mult {ProduceCode; goto L1} end
2.5 Micro 目标代码生成 5).表达式有括号的目标语言生成的程序实现(续) Other case OperatorStack(top) of {popOperatorStack; return}; $plus|$mult {ProduceCode; goto L1} end end 请验证表达式 5*(a+b*(c+d))
2.6 作业 P261,2 P263