280 likes | 575 Views
第二章 一个微小编译器. 2.1 Micro 语言描述 2.2 Micro 语言的词法分析 2.3 Micro 语言的语法分析 2.4 Micro 语言的语义分析 2.5 Micro 语言的目标代码. 变量声明序列 语句序列. 程序 组成. 赋值语句 输入语句 输出语句. 第二章 一个微小编译器. 2.1 Micro 语言描述 Micro 语言 ( 称 Micro,Pascal 语言的子集 ). begin var x1:real; var z1:real; x1:=0.5; z1:=x1+56; write(z1+2.3);
E N D
第二章 一个微小编译器 2.1 Micro语言描述 2.2 Micro语言的词法分析 2.3 Micro语言的语法分析 2.4 Micro语言的语义分析 2.5 Micro语言的目标代码
变量声明序列 语句序列 程序 组成 赋值语句 输入语句 输出语句 第二章 一个微小编译器 2.1 Micro语言描述 Micro语言(称Micro,Pascal语言的子集) begin var x1:real; var z1:real; x1:=0.5; z1:=x1+56; write(z1+2.3); read(x1) end.
2.2 Micro语言的词法分析(单词的分析) 1. 特点: 不依赖于语法分析,只依赖于单词的文法定义 Micro的有用单词的种类: 以字母开头: 保留字:begin,end,var,read,write,int,real 标识符:字母/数字串 以数字开头: 整常数:数字开头的数字串 实常数:整数.整数 符号词:+,-,*,/,(,),:,:=,;,. 控制词:enter
输入 输出 2. 任务: 程序的字符串序列 Token序列 (单词的内部表示) 保留字:$begin, $end, $var, $read, $write, $int, $real 标识符:($id,标识符) 整常数:($intC,整常数) 实常数:($reaC,实常数) 符号词:$plus,$sub,$mult,$div,$Lparen,$Rparen,$colon, $assig,$semi,$line,$stop (+,-,*,/,(,),:,:=,;,enter,.)
程序的字符串序列 Token序列 例: begin var x1:real; var z1:real; x1:=0.5; z1:=x1+56; write(z1+2.3); read(x1) end.
例: begin var x1:real; var z1:real; x1:=0.5; z1:=x1+56; write(z1+2.3); read(x1) end. 3.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中(删除空格符)
‘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
Procedure Identifier(name:string); Begin name:=“ ”; Append(name,ch); Read(ch); while isLetter(ch) or isDigit(ch) do { Append(name,ch); Read(ch);} End Micro语言的词法分析程序的相关子程序(1)——读标识符,保留字(关键字) Identifier(name):从输入流把当前标识符名读到name中(它可能是保留字)。在调用时,当前字符一定是字母,且已被读到ch中。 isLetter(ch):如果ch是字母,则取true,否则取false。 isDigit(ch):如果ch是数字,则取true,否则取false。
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 Micro语言的词法分析程序的相关子程序(2)——读整/实常数 整数(小数点左部) 整数(小 数点右部) Constant(class,C):从输入流读到当前常数,并在class中给出常数的类型标志,C中给出常数的二进制数值。调用时当前字符定是数字(在ch中)
Micro语言的词法分析程序的相关子程序(3)——读整常数Micro语言的词法分析程序的相关子程序(3)——读整常数 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对应的二(十)进制值。
2.3 Micro语言的语法分析 1.任务: 检查程序是否有语法(语法结构)上的错误 2.输入:词法分析后得到的Token表 输出:具体语法错误提示和语法全部正确提示 位置的正确性 3.语法分析程序思路: 声明检查:begin var id: real; (声明的位置) 赋值语句 标识符 单词 输入语句 表达式 整/实常数 语句检查: 输出语句 左括号 后继符(语句末符号)(;enter end)
4.Micro语言的语法分析程序 Match(kind,n):读当前Token,并检查Token.LH=kind?若不等,则打出错误编号n。 Token.LH:Token的左半部。 Token.RH:Token的右半部。 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; 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); //读语句的后继符 后继符
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指针回溯一步 Micro语言的语法分析子程序—表达式的语法检查
Micro语言的具体语法错误: Error 11 “read (id”后 不是”)” Error 12 赋值语句左部 不是“:=” Error 13 语句头单词错 Error 14 程序结束符错 Error 15 语句后继符错 Error 16 缺“(E)”中的 闭括号 Error 17 运算分量的 后继符错 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”
2.4 Micro语言的语义分析 1.任务: 语法分析:只检查位置的正确性 不检查他本身(语义)的正确性 语义分析:主要是类型匹配问题 将Token序列中的标识符转换为($id,entry) (entry:变量的属性表(符号表)地址) (X*表示变量名(或常数)X的符号表地址)
2.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项内
$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.采用三地址形式的目标语言,其指令如下: 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. Micro语言的目标代码生成程序 Search(RH(tk),vAddr,vType):用符号表的某项地址entry求变量的地址和类型。 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}; 赋值的左边 SemanStack(top):表示SemanStack的顶元素 Sendcode(code):把代码code送入代码区 赋值的右边 类型不相容错
$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
无括号表达式的目标代码生成程序 ProduceCode:用语义栈SemanStack的内容生成代码 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 判断优先级
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的内容生成代码
带括号表达式的目标代码生成程序 OperandStack:分量栈 OperatorStack:运算符栈 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 };
带括号表达式的目标代码生成程序(接上) $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
带括号表达式的目标代码生成程序(接上) Other case OperatorStack(top) of {popOperatorStack; return}; $plus|$mult {ProduceCode; goto L1} end end 请验证表达式 5*(a+b*(c+d))