计算机系统要素:第十章 编译器:语法分析

来源:互联网 发布:淘宝举报了哪里可以看 编辑:程序博客网 时间:2024/06/05 03:08

通常来讲,做一个完整的编译器是非常浩大的工程,往往需要许多人经年累月的工作才能实现,但所幸,本书的作者将编译器的实现做到了最简化,我们只需要按照书上7、8、10、11章的框架,便可以在最短的时间内完成一个简易的编译器。

 

这一章的内容是对Jack语言进行语法分析,将每一个Jack文件转化为结构化的xml文件。如果说前面几章的难度已经逐渐由菜鸟变为中等级别的话,最后这几章的难度便开始向达人级别靠拢了。无论是逻辑理解层面,还是代码编写层面,这一章的难度都比前几章要高得多。

 

我的建议是无论有没有理解书本内容,尽快上手写代码,因为绝大多数的知识点在写代码的过程中自然就能理解。

 

构建过程大致有如下几步:

1,从文件中逐个读取字元

2,标明字元类型,进行字元转换

3,分析语法,理解每一种语句类型的结构

4,完成整个递归型的语法分析

 

Step 1:

首先要完成的是从文件中逐个读取字元的功能,主要由advance函数实现。这个功能看起来似乎很简单,但操作起来却相当复杂。

 

在这之前,你必须先消除文档中的所有注释,而一边消除注释一边读取字元这种做法并不可取,因此,我的做法是先将消除注释的文档另存于临时文件copyfile中,做字元分析时,从copyfile中读取内容,最后,再将copyfile删除。

 

紧接着便是读取字元,读取字元的关键点在于,每一次读取都必须返回紧接着的、正确的字元,不能是空格、不能是\n、更不能漏掉一个或者两个字节。在实现这个功能的过程中,我的整体逻辑是:

 

//设置返回值token,起始值为None//读取下一个字节//跳过开头的所有无意义字节 if选择逻辑//判断是否为结束//处理普通字符串//处理单个符号//处理在 ” 中的字符串


具体实现可以参考代码,其中细节很多,像rfile.seek(-1,1)函数的运用,就是为了防止漏读问题的发生。

 

Step 2:

这一步的实现相对较简单,只需要给所有的字元加上对应的标识就行,大家可以用xxxT.xml进行比较。在这儿有一个建议,就是可以不按照书上的API进行编程,我就创建了给相应字元加上标识的函数writeToken(wfile,token),并将它放在JackTokenizer中,这么操作对于接下去的工作很有帮助。

 

Step 3:

分析语法的关键在于图10.5,这一张图是接下去工作的重中之重,因为它表明了每一种语句可能的语句结构,大家一定要看清楚,每一个语句的开始在哪儿,结束在哪儿。举个例子,(expression) 和 ’(’expression’)’是不同的,后者表示的是,每一个expression前后都会有()符号进行标注,而前者只是写书的时候为了不引起歧义加上去的括号,没有实际意义。所以,在’(’expression’)’中,<symbol> ) </symbol>应该是在</expression>之后的。所以,一定要理解图10.5中每一个语句表示的含义,然后再进行下一步工作。

 

Step 4:

最后一步的工作量比较大,我的建议是从基本语句写起,所谓基本语句,就是不必调用其他语句的语句,由简到繁,最后再将整个架构中最高级的递归层次class语句写好。建议顺序:varDec—parameterList—classVarDec—term—expression—expressionList—subroutineCall—doStatement—letStatement—returnStatement—whileStatement—ifStatement—statements—subroutineBody—subroutineDec—class

 

其中,term的书写是一定要注意的,它涉及了很多种不同的情况,因此在写的时候一定要好好参照书上的语法内容,不能漏掉任何一种情况。(如(expression)就容易被忽略,但是类似于while(~(expression))就属于这种情况。)

 

最后要注意的是,有些函数的调用需要提前看一个token才能确定,因此,我的CompilationEngine文件中,部分函数有三个参数,就是为了传递那个提前被看到的token。那如果看到的token不符合要求怎么办呢?那也只好再用f.seek()函数倒退回去了。

 

总之,实现不同细节的方法有很多,我的答案仅供参考,希望大家能够在实践的过程中收获真知。


JackTokenizer.py

#!/usr/bin/pythonSTable=('{','}','(',')','[',']','.',',',';','+','-','*','/','&','|','<','>','=','~')KWtable=('class','constructor','function','method','field','static','var','int','char','boolean',\'void','true','false','null','this','let','do','if','else','while','return')def hasMoreTokens(token):if not token:return 0else:return 1def advance(rfile):token=''temp=rfile.read(1)while temp==' ' or temp =='\n' or temp =='\t':temp = rfile.read(1)if not temp:return ''elif temp.isalpha() or temp.isdigit() or temp == '_':while temp.isalpha() or temp.isdigit() or temp == '_':token+=temptemp=rfile.read(1)if temp in STable or temp =='"':rfile.seek(-1,1)return tokenelif temp == ' ' or temp == '\n':rfile.seek(-1,1)return tokenelif temp in STable:return tempelif temp =='"':token += '"'temp=rfile.read(1)while temp != '"':token+=temptemp=rfile.read(1)token+='"'return tokendef tokenType(token):if token in KWtable:return 'KEYWORD'elif token in STable:return 'SYMBOL'elif token.isdigit():return 'INT_CONSTANT'elif token.startswith('"'):return 'STRING_CONSTANT'else:return 'IDENTIFIER'def writeToken(wfile,token):tType=tokenType(token)if tType == 'KEYWORD':wfile.write('<keyword> '+token+' </keyword>\n')elif tType == 'SYMBOL':if token=='>':wfile.write('<symbol> > </symbol>\n')elif token=='<':wfile.write('<symbol> < </symbol>\n')elif token=='&':wfile.write('<symbol> & </symbol>\n')else:wfile.write('<symbol> '+token+' </symbol>\n')elif tType == 'IDENTIFIER':wfile.write('<identifier> '+token+' </identifier>\n')elif tType == 'INT_CONSTANT':wfile.write('<integerConstant> '+token+' </integerConstant>\n')elif tType == 'STRING_CONSTANT':wfile.write('<stringConstant> '+token.strip('"')+' </stringConstant>\n')

JackAnalyzer.py

#!/usr/bin/pythonimport JackTokenizerimport CompilationEngineimport sys,osfilename=sys.argv[1]readfile = open(filename,'r')#clear all the // /* ... notes, create a new file to save the resultcopyfile = open('copyfile','w')line=readfile.readline()while line:while line == '\n' or line.startswith('//'):line=readfile.readline()if '//' in line:line=line[:line.find('//')]if '/*' in line:aline=line[:line.find('/*')]while line.find('*/')<0:line=readfile.readline()bline=line[line.find('*/')+2:]line=aline+blinecopyfile.write(line)line=readfile.readline()copyfile.close()readfile.close()rfile=open('copyfile','r')wfile=open(filename.strip('.jack')+'.xml','w')CompilationEngine.compileClass(rfile,wfile)rfile.close()wfile.close()os.remove('copyfile')

CompilationEngine.py

#!/usr/bin/pythonimport JackTokenizerdef compileClass(rfile,wfile):wfile.write('<class>\n')token=JackTokenizer.advance(rfile)while token != '{':JackTokenizer.writeToken(wfile,token)token=JackTokenizer.advance(rfile)wfile.write('<symbol> { </symbol>\n')#classVarDec*token=JackTokenizer.advance(rfile)while token in ('static','field'):compileClassVarDec(rfile,wfile,token)token=JackTokenizer.advance(rfile)#subroutineDec*while token in ('constructor','function','method'):compileSubroutine(rfile,wfile,token)token=JackTokenizer.advance(rfile)wfile.write('<symbol> } </symbol>\n')wfile.write('</class>\n')def compileSubroutine(rfile,wfile,tokenAtt):wfile.write('<subroutineDec>\n<keyword> '+tokenAtt+' </keyword>\n')#(void|type) subroutineName (parameterList)token=JackTokenizer.advance(rfile)while token != '(':JackTokenizer.writeToken(wfile,token)token=JackTokenizer.advance(rfile)wfile.write('<symbol> ( </symbol>\n')compileParameterList(rfile,wfile)wfile.write('<symbol> ) </symbol>\n')#subroutinBodycompileSubroutineBody(rfile,wfile)wfile.write('</subroutineDec>\n')def compileSubroutineBody(rfile,wfile):wfile.write('<subroutineBody>\n')#{varDec* statements}token=JackTokenizer.advance(rfile)JackTokenizer.writeToken(wfile,token)token=JackTokenizer.advance(rfile)while token == 'var':compileVarDec(rfile,wfile)token=JackTokenizer.advance(rfile)lennum=-len(token)rfile.seek(lennum,1)compileStatements(rfile,wfile)wfile.write('<symbol> } </symbol>\n')wfile.write('</subroutineBody>\n')def compileClassVarDec(rfile,wfile,tokenAtt):wfile.write('<classVarDec>\n<keyword> '+tokenAtt+' </keyword>\n')token=JackTokenizer.advance(rfile)while token != ';':JackTokenizer.writeToken(wfile,token)token=JackTokenizer.advance(rfile)JackTokenizer.writeToken(wfile,token)wfile.write('</classVarDec>\n')def compileParameterList(rfile,wfile):wfile.write('<parameterList>\n')token=JackTokenizer.advance(rfile)while token != ')':JackTokenizer.writeToken(wfile,token)token=JackTokenizer.advance(rfile)wfile.write('</parameterList>\n')def compileVarDec(rfile,wfile):wfile.write('<varDec>\n<keyword> var </keyword>\n')token=JackTokenizer.advance(rfile)while token != ';':JackTokenizer.writeToken(wfile,token)token=JackTokenizer.advance(rfile)JackTokenizer.writeToken(wfile,token)wfile.write('</varDec>\n')def compileExpression(rfile,wfile):wfile.write('<expression>\n')compileTerm(rfile,wfile)token=JackTokenizer.advance(rfile)while not (token in (')',']',';',',')):JackTokenizer.writeToken(wfile,token)compileTerm(rfile,wfile)token=JackTokenizer.advance(rfile)wfile.write('</expression>\n')def compileExpressionList(rfile,wfile):wfile.write('<expressionList>\n')token=JackTokenizer.advance(rfile)if token != ')':lennum=-len(token)rfile.seek(lennum,1)while token != ')':compileExpression(rfile,wfile)rfile.seek(-1,1)token=JackTokenizer.advance(rfile)if token == ',':wfile.write('<symbol> , </symbol>\n')wfile.write('</expressionList>\n')def compileTerm(rfile,wfile):wfile.write('<term>\n')token=JackTokenizer.advance(rfile)tType=JackTokenizer.tokenType(token)if tType == 'IDENTIFIER':temp=rfile.read(1)if temp=='.':lennum=-len(token)-1rfile.seek(lennum,1)subroutinCall(rfile,wfile)elif temp=='[':JackTokenizer.writeToken(wfile,token)wfile.write('<symbol> [ </symbol>\n')compileExpression(rfile,wfile)wfile.write('<symbol> ] </symbol>\n')elif temp=='(':JackTokenizer.writeToken(wfile,token)wfile.write('<symbol> ( </symbol>\n')compileExpression(rfile,wfile)wfile.write('<symbol> ) </symbol>\n')else:rfile.seek(-1,1)JackTokenizer.writeToken(wfile,token)elif token in ('-','~'):JackTokenizer.writeToken(wfile,token)compileTerm(rfile,wfile)elif token == '(':wfile.write('<symbol> ( </symbol>\n')compileExpression(rfile,wfile)wfile.write('<symbol> ) </symbol>\n')else:JackTokenizer.writeToken(wfile,token)wfile.write('</term>\n')def subroutinCall(rfile,wfile):token=JackTokenizer.advance(rfile)JackTokenizer.writeToken(wfile,token)temp = rfile.read(1)if temp=='.':wfile.write('<symbol> . </symbol>\n')token=JackTokenizer.advance(rfile)JackTokenizer.writeToken(wfile,token)rfile.read(1)wfile.write('<symbol> ( </symbol>\n')compileExpressionList(rfile,wfile)wfile.write('<symbol> ) </symbol>\n')elif temp=='(':wfile.write('<symbol> ( </symbol>\n')compileExpressionList(rfile,wfile)wfile.write('<symbol> ) </symbol>\n')def compileStatements(rfile,wfile):wfile.write('<statements>\n')token=JackTokenizer.advance(rfile)while token != '}':if token=='let':compileLet(rfile,wfile)elif token == 'if':compileIf(rfile,wfile)elif token == 'while':compileWhile(rfile,wfile)elif token == 'do':compileDo(rfile,wfile)elif token == 'return':compileReturn(rfile,wfile)else:print 'Error!'+tokenexit()token=JackTokenizer.advance(rfile)wfile.write('</statements>\n')def compileDo(rfile,wfile):wfile.write('<doStatement>\n')wfile.write('<keyword> do </keyword>\n')subroutinCall(rfile,wfile)token=JackTokenizer.advance(rfile)JackTokenizer.writeToken(wfile,token)wfile.write('</doStatement>\n')def compileLet(rfile,wfile):wfile.write('<letStatement>\n')wfile.write('<keyword> let </keyword>\n')token=JackTokenizer.advance(rfile)JackTokenizer.writeToken(wfile,token)temp=JackTokenizer.advance(rfile)if temp=='[':wfile.write('<symbol> [ </symbol>\n')compileExpression(rfile,wfile)wfile.write('<symbol> ] </symbol>\n')token=JackTokenizer.advance(rfile)JackTokenizer.writeToken(wfile,token)elif temp == '=':wfile.write('<symbol> = </symbol>\n')compileExpression(rfile,wfile)wfile.write('<symbol> ; </symbol>\n')wfile.write('</letStatement>\n')def compileIf(rfile,wfile):wfile.write('<ifStatement>\n')wfile.write('<keyword> if </keyword>\n')#(expression)token=JackTokenizer.advance(rfile)JackTokenizer.writeToken(wfile,token)compileExpression(rfile,wfile)wfile.write('<symbol> ) </symbol>\n')#{statements}token=JackTokenizer.advance(rfile)JackTokenizer.writeToken(wfile,token)compileStatements(rfile,wfile)wfile.write('<symbol> } </symbol>\n')#(else {statements})?token=JackTokenizer.advance(rfile)if token=='else':JackTokenizer.writeToken(wfile,token)token=JackTokenizer.advance(rfile)JackTokenizer.writeToken(wfile,token)compileStatements(rfile,wfile)wfile.write('<symbol> } </symbol>\n')else:lennum=-len(token)rfile.seek(lennum,1)wfile.write('</ifStatement>\n')def compileWhile(rfile,wfile):wfile.write('<whileStatement>\n')wfile.write('<keyword> while </keyword>\n')#(expression)token=JackTokenizer.advance(rfile)JackTokenizer.writeToken(wfile,token)compileExpression(rfile,wfile)wfile.write('<symbol> ) </symbol>\n')#{statements}token=JackTokenizer.advance(rfile)JackTokenizer.writeToken(wfile,token)compileStatements(rfile,wfile)wfile.write('<symbol> } </symbol>\n')wfile.write('</whileStatement>\n')def compileReturn(rfile,wfile):wfile.write('<returnStatement>\n')wfile.write('<keyword> return </keyword>\n')#expression?token=JackTokenizer.advance(rfile)if token == ';':JackTokenizer.writeToken(wfile,token)else:lennum= -len(token)rfile.seek(lennum,1)compileExpression(rfile,wfile)wfile.write('<symbol> ; </symbol>\n')wfile.write('</returnStatement>\n')


0 0
原创粉丝点击