LET'S BUILD A COMPILER!(5)

转载 2004年08月18日 17:06:00

LET'S BUILD A COMPILER!(4)---第三部分:再论表达式续

空白字符

结束本章之前,我们再来讨论一下空白字符的问题。现在这个版本的分析器会在读到一个空白字符的地方停下来。这是相当不友好的行为。所以让我们消除这个最后的限制,使分析器的表现更有商业产品的味道。

处理空白字符的关键在于制定一条规则,规定分析器改如何处理对待输入的空白字符,并在整个分析器中都遵守它。目前为止,空白字符还是不被允许的,我们可以假定在每个分析动作之后,先行字符Look都包含的是个有意义的字符,然后可以立即验证这点。我们设计的方法就是基于上述原则。

具体来说,就是每个需要先行读取输入流的过程都必须忽略空白字符,并且最后Look变量中只能保留非空白字符。所幸,我们是用GetName,GetNum和Match完成大部分了输入操作,所以只有三个过程(再加上Init过程)需要修改。

首先,定义一个新的识别空白字符的过程。

{--------------------------------------------------------------}
{ Recognize White Space }

function IsWhite(c: char): boolean;
begin
   IsWhite := c in [' ', TAB];
end;
{--------------------------------------------------------------}

还需要一个过程“吃掉”空白字符,直到遇见一个非空白字符。

{--------------------------------------------------------------}
{ Skip Over Leading White Space }

procedure SkipWhite;
begin
   while IsWhite(Look) do
      GetChar;
end;
{--------------------------------------------------------------}

现在,在Match,GetName和GetNum过程中调用SkipWhite:

{--------------------------------------------------------------}
{ Match a Specific Input Character }

procedure Match(x: char);
begin
   if Look <> x then Expected('''' + x + '''')
   else begin
      GetChar;
      SkipWhite;
   end;
end;


{--------------------------------------------------------------}
{ Get an Identifier }

function GetName: string;
var Token: string;
begin
   Token := '';
   if not IsAlpha(Look) then Expected('Name');
   while IsAlNum(Look) do begin
      Token := Token + UpCase(Look);
      GetChar;
   end;
   GetName := Token;
   SkipWhite;
end;


{--------------------------------------------------------------}
{ Get a Number }

function GetNum: string;
var Value: string;
begin
   Value := '';
   if not IsDigit(Look) then Expected('Integer');
   while IsDigit(Look) do begin
      Value := Value + Look;
      GetChar;
   end;
   GetNum := Value;
   SkipWhite;
end;
{--------------------------------------------------------------}

(注意,我对Match过程进行了重新组织,但是没有改变它的功能。)

最后,还要在Init中略过源文件开头的空白符。

{--------------------------------------------------------------}
{ Initialize }

procedure Init;
begin
   GetChar;
   SkipWhite;
end;
{--------------------------------------------------------------}

如上改写程序并编译,注意把Match过程放在SkipWhite的后面,否则通不过编译。测试这个新版本,以保证它能正常运行。

由于本章中我们对原来的程序做了不少的修改,现在我在下面列出修改后的完整程序:

{--------------------------------------------------------------}
program parse;

{--------------------------------------------------------------}
{ Constant Declarations }

const TAB = ^I;
       CR = ^M;

{--------------------------------------------------------------}
{ Variable Declarations }

var Look: char;              { Lookahead Character }

{--------------------------------------------------------------}
{ Read New Character From Input Stream }

procedure GetChar;
begin
   Read(Look);
end;

{--------------------------------------------------------------}
{ Report an Error }

procedure Error(s: string);
begin
   WriteLn;
   WriteLn(^G, 'Error: ', s, '.');
end;


{--------------------------------------------------------------}
{ Report Error and Halt }
                            
procedure Abort(s: string);
begin
   Error(s);
   Halt;
end;


{--------------------------------------------------------------}
{ Report What Was Expected }

procedure Expected(s: string);
begin
   Abort(s + ' Expected');
end;


{--------------------------------------------------------------}
{ Recognize an Alpha Character }

function IsAlpha(c: char): boolean;
begin
   IsAlpha := UpCase(c) in ['A'..'Z'];
end;


{--------------------------------------------------------------}
{ Recognize a Decimal Digit }

function IsDigit(c: char): boolean;
begin
   IsDigit := c in ['0'..'9'];
end;


{--------------------------------------------------------------}
{ Recognize an Alphanumeric }

function IsAlNum(c: char): boolean;
begin
   IsAlNum := IsAlpha(c) or IsDigit(c);
end;


{--------------------------------------------------------------}
{ Recognize an Addop }

function IsAddop(c: char): boolean;
begin
   IsAddop := c in ['+', '-'];
end;


{--------------------------------------------------------------}
{ Recognize White Space }
                            
function IsWhite(c: char): boolean;
begin
   IsWhite := c in [' ', TAB];
end;


{--------------------------------------------------------------}
{ Skip Over Leading White Space }

procedure SkipWhite;
begin
   while IsWhite(Look) do
      GetChar;
end;


{--------------------------------------------------------------}
{ Match a Specific Input Character }

procedure Match(x: char);
begin
   if Look <> x then Expected('''' + x + '''')
   else begin
      GetChar;
      SkipWhite;
   end;
end;


{--------------------------------------------------------------}
{ Get an Identifier }

function GetName: string;
var Token: string;
begin
   Token := '';
   if not IsAlpha(Look) then Expected('Name');
   while IsAlNum(Look) do begin
      Token := Token + UpCase(Look);
      GetChar;
   end;
   GetName := Token;
   SkipWhite;
end;


{--------------------------------------------------------------}
{ Get a Number }

function GetNum: string;
var Value: string;
begin
   Value := '';
   if not IsDigit(Look) then Expected('Integer');
   while IsDigit(Look) do begin
      Value := Value + Look;
      GetChar;
   end;
   GetNum := Value;
   SkipWhite;
end;


{--------------------------------------------------------------}
{ Output a String with Tab }

procedure Emit(s: string);
begin
   Write(TAB, s);
end;


{--------------------------------------------------------------}
{ Output a String with Tab and CRLF }

procedure EmitLn(s: string);
begin
   Emit(s);
   WriteLn;
end;


{---------------------------------------------------------------}
{ Parse and Translate a Identifier }

procedure Ident;
var Name: string[8];
begin
   Name:= GetName;
   if Look = '(' then begin
      Match('(');
      Match(')');
      EmitLn('BSR ' + Name);
      end
   else
      EmitLn('MOVE ' + Name + '(PC),D0');
end;


{---------------------------------------------------------------}
{ Parse and Translate a Math Factor }

procedure Expression; Forward;

procedure Factor;
begin
   if Look = '(' then begin
      Match('(');
      Expression;
      Match(')');
      end
   else if IsAlpha(Look) then
      Ident
   else
      EmitLn('MOVE #' + GetNum + ',D0');
end;


{--------------------------------------------------------------}
{ Recognize and Translate a Multiply }

procedure Multiply;
begin
   Match('*');
   Factor;
   EmitLn('MULS (SP)+,D0');
end;


{-------------------------------------------------------------}
{ Recognize and Translate a Divide }

procedure Divide;
begin
   Match('/');
   Factor;
   EmitLn('MOVE (SP)+,D1');
   EmitLn('EXS.L D0');
   EmitLn('DIVS D1,D0');
end;


{---------------------------------------------------------------}
{ Parse and Translate a Math Term }

procedure Term;
begin
   Factor;
   while Look in ['*', '/'] do begin
      EmitLn('MOVE D0,-(SP)');
      case Look of
       '*': Multiply;
       '/': Divide;
      end;
   end;
end;


{--------------------------------------------------------------}
{ Recognize and Translate an Add }

procedure Add;
begin
   Match('+');
   Term;
   EmitLn('ADD (SP)+,D0');
end;


{-------------------------------------------------------------}
{ Recognize and Translate a Subtract }

procedure Subtract;
begin
   Match('-');
   Term;
   EmitLn('SUB (SP)+,D0');
   EmitLn('NEG D0');
end;


{---------------------------------------------------------------}
{ Parse and Translate an Expression }

procedure Expression;
begin
   if IsAddop(Look) then
      EmitLn('CLR D0')
   else
      Term;
   while IsAddop(Look) do begin
      EmitLn('MOVE D0,-(SP)');
      case Look of
       '+': Add;
       '-': Subtract;
      end;
   end;
end;


{--------------------------------------------------------------}
{ Parse and Translate an Assignment Statement }

procedure Assignment;
var Name: string[8];
begin
   Name := GetName;
   Match('=');
   Expression;
   EmitLn('LEA ' + Name + '(PC),A0');
   EmitLn('MOVE D0,(A0)')
end;


{--------------------------------------------------------------}
{ Initialize }
                            
procedure Init;
begin
   GetChar;
   SkipWhite;
end;


{--------------------------------------------------------------}
{ Main Program }

begin
   Init;
   Assignment;
   If Look <> CR then Expected('NewLine');
end.
{--------------------------------------------------------------}

上面已经是完整的分析器了。它已经具有了一个“行编译器”的所有特征。建议你把它保存起来。下次我们将转向一个新主题,但是仍然要谈一点表达式的问题。下一章,我计划介绍一些关于解释器(interpreter)的知识,并展示当我们改变分析器的动作时,它的结构是如何跟着改动的。我们在下一章中要学到的东西以后还会用到的,即使你对解释器不感兴趣。下章见

LET'S BUILD A COMPILER!(1)

前言    本文档包含了Jack Crenshaw的编译器开发教程的所有部分,包括新增的第15章。预定的读者群是那些非计算机专业,而又热爱计算机并且想知道编译器到底是如何工作的人们。在本教程中许多的编...
  • pc2s
  • pc2s
  • 2004年08月18日 17:04
  • 783

LET'S BUILD A COMPILER!(3)

                       LETS BUILD A COMPILER!(3)---第二部分:表达式分析续括号我们可以为分析器添加进处理括号的部分。括号是一种强制改变运算优先次序的机...
  • pc2s
  • pc2s
  • 2004年08月18日 17:05
  • 743

Let's Encrypt 给网站加 HTTPS 完全指南

使用 HTTPS 前的一些疑惑 现在是 2016 年,使用 HTTPS 已经不像几年前是一件昂贵的事情。当然我也是自己了解了一圈才消除了自己的疑惑,主要是: 我的网站(一个简单的博客)可能没必...
  • andylau00j
  • andylau00j
  • 2017年01月18日 19:26
  • 781

如何为Ubuntu 14.04上的多Apache虚拟主机环境设置Let's Crypt安全证书

提供:ZStack云计算 内容简介SSL证书负责对Web服务器之内服务器与客户端之流量提供加密保护,同时亦可为访问应用程序的用户带来额外安全保障。Let’s Encrypt作为一款免费工具,能够显著简...
  • zstack_org
  • zstack_org
  • 2016年11月29日 09:54
  • 800

https研究(四)用Let's Encrypt实现Https双向认证)

使用 Let's Encrypt申请SSL证书,在tomcat服务器中配置,实现https
  • qq_27424559
  • qq_27424559
  • 2017年03月28日 22:16
  • 767

如何使用Let's Encrypt永久免费SSL证书

Let's Encrypt作为一个公共且免费SSL的项目逐渐被广大用户传播和使用,是由Mozilla、Cisco、Akamai、IdenTrust、EFF等组织人员发起,主要的目的也是为了推进网站从H...
  • ownfire
  • ownfire
  • 2017年03月13日 16:23
  • 3372

Let's Encrypt 发布 ACME v2,开始测试通配符证书

免费数字证书颁发机构 Let's Encrypt 发布了 ACME v2 协议 API 端点,正式宣布开始测试支持签发通配符数字证书的 ACME V2 版 API 接口。 可以使用以下的目录 U...
  • chenhaifeng2016
  • chenhaifeng2016
  • 2018年01月09日 09:47
  • 57

Let's Encrypt永久免费SSL证书过程教程及常见问题

Let's Encrypt作为一个公共且免费SSL的项目逐渐被广大用户传播和使用,是由Mozilla、Cisco、Akamai、IdenTrust、EFF等组织人员发起,主要的目的也是为了推进网站从H...
  • helen_shw
  • helen_shw
  • 2017年02月17日 11:57
  • 5489

使用 Let's Encrypt 和 Nginx 从同一服务器托管多个 HTTPS 域名

现在网站越来越需要 HTTPS,而这正是顺应了发展趋势。Chrome 现已将带有密码或信用卡字段的 HTTP 网站明确标记为“不安全的”。在过去的一年里,我一直在将我的客户端网站切换到 HTTPS 上...
  • hj7jay
  • hj7jay
  • 2017年02月17日 09:15
  • 1218

免费SSL安全证书Let's Encrypt安装使用教程(附Nginx/Apache配置)

https://www.vpser.net/build/letsencrypt-free-ssl.html Let's Encrypt是最近很火的一个免费SSL证书发...
  • Minute_Man
  • Minute_Man
  • 2016年12月19日 16:49
  • 2050
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:LET'S BUILD A COMPILER!(5)
举报原因:
原因补充:

(最多只允许输入30个字)