目标:
扩展vm翻译器,增加语言的程序控制流和函数调用命令。
思路:
本质是将.vm文本按照特定的规则转换为.asm文本,先将两种语言的命令归类:
vm: 算数逻辑 push/pop 跳转(label, goto, if-goto) 函数(function, return, call)
↓
asm: A命令 C命令 L命令(label)
在动手之前:
可以使用内置的vm模拟器,观察内存的分配;
也可以将vm先翻译成asm文件,再进行对比观察映射规则,最后自己实现。
这一章没有自己实现下面的代码来自:https://github.com/ThomasCJY/The_Elements_of_Computing_Systems
CodeWriter.py
#!/usr/bin/python
import sys,os
import Parser
CODEFLAG1=0
CODEFLAG2=0
RETURNFLAG=1
def setFileName(filename):
filetuple=os.path.splitext(filename)
wfile = open(filetuple[0]+'.asm','w')
return wfile
def writeArithmatic(wfile,command):
global CODEFLAG1,CODEFLAG2
if command == 'add':
wfile.write('@SP\nM=M-1\nA=M\nD=M\n@SP\nM=M-1\nA=M\nM=D+M\n@SP\nM=M+1\n')
elif command == 'sub':
wfile.write('@SP\nM=M-1\nA=M\nD=M\n@SP\nM=M-1\nA=M\nM=M-D\n@SP\nM=M+1\n')
elif command == 'neg':
wfile.write('@SP\nM=M-1\nA=M\nM=-M\n@SP\nM=M+1\n')
elif command == 'and':
wfile.write('@SP\nM=M-1\nA=M\nD=M\n@SP\nM=M-1\nA=M\nM=D&M\n@SP\nM=M+1\n')
elif command == 'or':
wfile.write('@SP\nM=M-1\nA=M\nD=M\n@SP\nM=M-1\nA=M\nM=D|M\n@SP\nM=M+1\n')
elif command == 'not':
wfile.write('@SP\nM=M-1\nA=M\nM=!M\n@SP\nM=M+1\n')
elif command == 'eq':
wfile.write('@SP\nM=M-1\nA=M\nD=M\n@SP\nM=M-1\nA=M\nD=D-M\n@RET_TRUE'+str(CODEFLAG1)+'\
\nD;JEQ\nD=0\n@CONTINUE'+str(CODEFLAG2)+'\n0;JMP\n(RET_TRUE'+str(CODEFLAG1)+')\nD=-1\
\n(CONTINUE'+str(CODEFLAG2)+')\n@SP\nA=M\nM=D\n@SP\nM=M+1\n')
CODEFLAG1+=1
CODEFLAG2+=1
elif command == 'gt':
wfile.write('@SP\nM=M-1\nA=M\nD=M\n@SP\nM=M-1\nA=M\nD=M-D\n@RET_TRUE'+str(CODEFLAG1)+'\
\nD;JGT\nD=0\n@CONTINUE'+str(CODEFLAG2)+'\n0;JMP\n(RET_TRUE'+str(CODEFLAG1)+')\nD=-1\
\n(CONTINUE'+str(CODEFLAG2)+')\n@SP\nA=M\nM=D\n@SP\nM=M+1\n')
CODEFLAG1+=1
CODEFLAG2+=1
elif command == 'lt':
wfile.write('@SP\nM=M-1\nA=M\nD=M\n@SP\nM=M-1\nA=M\nD=M-D\n@RET_TRUE'+str(CODEFLAG1)+'\
\nD;JLT\nD=0\n@CONTINUE'+str(CODEFLAG2)+'\n0;JMP\n(RET_TRUE'+str(CODEFLAG1)+')\nD=-1\
\n(CONTINUE'+str(CODEFLAG2)+')\n@SP\nA=M\nM=D\n@SP\nM=M+1\n')
CODEFLAG1+=1
CODEFLAG2+=1
def writePushPop(wfile,command,segment,index,filename):
if command == 'C_PUSH':
if segment == 'constant':
wfile.write('@'+index+'\nD=A\n@SP\nA=M\nM=D\n@SP\nM=M+1\n')
elif segment == 'local':
wfile.write('@LCL\nD=M\n@'+index+'\nA=A+D\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n')
elif segment == 'argument':
wfile.write('@ARG\nD=M\n@'+index+'\nA=A+D\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n')
elif segment == 'this':
wfile.write('@THIS\nD=M\n@'+index+'\nA=A+D\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n')
elif segment == 'that':
wfile.write('@THAT\nD=M\n@'+index+'\nA=A+D\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n')
elif segment == 'pointer':
if index == '0':
wfile.write('@3\n')
elif index == '1':
wfile.write('@4\n')
wfile.write('D=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n')
elif segment == 'static':
staticname=filename.strip('.vm')+'.'+index
wfile.write('@'+staticname+'\nD=A\nA=D\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n')
elif command =='C_POP':
if segment == 'local':
wfile.write('@SP\nM=M-1\nA=M\nD=M\n@LCL\nA=M\n')
for i in range (0,int(index)):
wfile.write('A=A+1\n')
wfile.write('M=D\n')
if segment =='argument':
wfile.write('@SP\nM=M-1\nA=M\nD=M\n@ARG\nA=M\n')
for i in range (0,int(index)):
wfile.write('A=A+1\n')
wfile.write('M=D\n')
if segment == 'this':
wfile.write('@SP\nM=M-1\nA=M\nD=M\n@THIS\nA=M\n')
for i in range (0,int(index)):
wfile.write('A=A+1\n')
wfile.write('M=D\n')
if segment == 'that':
wfile.write('@SP\nM=M-1\nA=M\nD=M\n@THAT\nA=M\n')
for i in range (0,int(index)):
wfile.write('A=A+1\n')
wfile.write('M=D\n')
if segment == 'pointer':
wfile.write('@SP\nM=M-1\nA=M\nD=M\n')
if index == '0':
wfile.write('@3\n')
else:
wfile.write('@4\n')
wfile.write('M=D\n')
if segment == 'static':
staticname=filename.strip('.vm')+'.'+index
wfile.write('@SP\nM=M-1\nA=M\nD=M\n@'+staticname+'\nM=D\n')
if segment == 'temp':
wfile.write('@SP\nM=M-1\nA=M\nD=M\n@R5\n')
for i in range (0,int(index)):
wfile.write('A=A+1\n')
wfile.write('M=D\n')
def writeLabel(wfile,labelstring):
wfile.write('('+labelstring+')\n')
def writeGoto(wfile,labelstring):
wfile.write('@'+labelstring+'\n0;JMP\n')
def writeIf(wfile,labelstring):
wfile.write('@SP\nM=M-1\nA=M\nD=M\n@'+labelstring+'\nD;JNE\n')
def writeFunction(wfile,functionName,numlocals):
wfile.write('('+functionName+')\n@LCL\nD=M\n@SP\nM=D\n')
for i in range(0,int(numlocals)):
wfile.write('@SP\nA=M\nM=0\nD=A+1\n@SP\nM=D\n')
def writeCall(wfile,functionName,numArgs):
global RETURNFLAG
wfile.write('@return_address'+str(RETURNFLAG)+'\nD=A\n@SP\nA=M\nM=D\n@SP\nM=M+1\
\n@LCL\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n@ARG\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\
\n@THIS\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n@THAT\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\
\n@'+numArgs+'\nD=A\n@5\nD=D+A\n@SP\nD=M-D\n@ARG\nM=D\n@SP\nD=M\n@LCL\nM=D\
\n@'+functionName+'\n0;JMP\n(return_address'+str(RETURNFLAG)+')\n')
RETURNFLAG+=1
def writeReturn(wfile):
wfile.write('@LCL\nD=M\n@R13\nM=D\n@5\nD=D-A\nA=D\nD=M\
\n@R14\nM=D\n@SP\nM=M-1\nA=M\nD=M\n@ARG\nA=M\
\nM=D\n@ARG\nD=M+1\n@SP\nM=D\n@R13\nD=M\nD=D-1\
\nA=D\nD=M\n@THAT\nM=D\n@R13\nD=M\nD=D-1\nD=D-1\
\nA=D\nD=M\n@THIS\nM=D\n@R13\nD=M\nD=D-1\nD=D-1\
\nD=D-1\nA=D\nD=M\n@ARG\nM=D\n@R13\nD=M\nD=D-1\
\nD=D-1\nD=D-1\nD=D-1\nA=D\nD=M\n@LCL\nM=D\n@R14\nA=M\n0;JMP\n')
def Close(wfile):
wfile.close()
Parser.py:
#!/usr/bin/python
Arith=('add','sub','neg','eq','gt','lt','and','or','not')
def hasMoreCommands(line):
if not line:
return 0
else:
return 1
def advance(rfile):
line=rfile.readline()
return line
def commandType(line):
if line.find('push')>=0:
return 'C_PUSH'
elif line.find('pop')>=0:
return 'C_POP'
elif line.startswith('label'):
return 'C_LABEL'
elif line.startswith('goto'):
return 'C_GOTO'
elif line.startswith('if-goto'):
return 'C_IF'
elif line.strip() in Arith:
return 'C_ARITHMATIC'
elif line.startswith('function'):
return 'C_FUNCTION'
elif line.startswith('return'):
return 'C_RETURN'
elif line.startswith('call'):
return 'C_CALL'
def arg1(line):
if commandType(line) is 'C_ARITHMATIC':
return line.strip()
else:
spline=line.split(' ')
return spline[1]
def arg2(line):
if commandType(line) in ('C_POP','C_PUSH','C_FUNCTION','C_CALL'):
spline=line.split(' ')
return spline[2]
Vmtranslator.py:
import sys,os
import Parser
import CodeWriter
DELETEFLAG=0
def Main(rfile,wfile,filename):
line=Parser.advance(rfile)
flag=Parser.hasMoreCommands(line)
while flag:
while line == '\n' or line.startswith('//'):
line=rfile.readline()
if '//' in line:
line=line[:line.find('//')]
ctype=Parser.commandType(line)
if ctype == 'C_ARITHMATIC':
attribute1=Parser.arg1(line).strip()
CodeWriter.writeArithmatic(wfile,attribute1)
elif ctype in ('C_PUSH','C_POP'):
attribute1=Parser.arg1(line).strip()
attribute2=Parser.arg2(line).strip()
CodeWriter.writePushPop(wfile,ctype,attribute1,attribute2,filename)
elif ctype =='C_LABEL':
attribute1=Parser.arg1(line).strip()
CodeWriter.writeLabel(wfile,attribute1)
elif ctype =='C_GOTO':
attribute1=Parser.arg1(line).strip()
CodeWriter.writeGoto(wfile,attribute1)
elif ctype =='C_IF':
attribute1=Parser.arg1(line).strip()
CodeWriter.writeIf(wfile,attribute1)
elif ctype =='C_FUNCTION':
attribute1=Parser.arg1(line).strip()
attribute2=Parser.arg2(line).strip()
CodeWriter.writeFunction(wfile,attribute1,attribute2)
elif ctype =='C_RETURN':
CodeWriter.writeReturn(wfile)
elif ctype =='C_CALL':
attribute1=Parser.arg1(line).strip()
attribute2=Parser.arg2(line).strip()
CodeWriter.writeCall(wfile,attribute1,attribute2)
line=Parser.advance(rfile)
flag=Parser.hasMoreCommands(line)
filename=sys.argv[1]
#if filename exists in the dir, open the file directly
if os.path.isfile(filename):
rfile = open(filename,'r')
wfile = CodeWriter.setFileName(filename)
wfile.write('@256\nD=A\n@SP\nM=D\n')
Main(rfile,wfile,filename)
#if filename doesn't exist, find all the .vm files
elif os.path.isfile('Sys.vm'):
wfile = CodeWriter.setFileName(filename)
wfile.write('@256\nD=A\n@SP\nM=D\n')
wfile.write('@return_address0\nD=A\n@SP\nA=M\nM=D\n@SP\nM=M+1\
\n@LCL\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n@ARG\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\
\n@THIS\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n@THAT\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\
\n@0\nD=A\n@5\nD=D+A\n@SP\nD=M-D\n@ARG\nM=D\n@SP\nD=M\n@LCL\nM=D\
\n@Sys.init\n0;JMP\n(return_address0)\n')
#check all the files in the dir
for i in os.walk(os.getcwd()):
filelist=i[2]
for j in range(0,len(filelist)):
if filelist[j].endswith('.vm'):
#readfile, copy to the end
filename=filelist[j]
rfile = open(filename,'r')
Main(rfile,wfile,filename)
DELETEFLAG=1
else:
print 'Wrong Instruction!'
exit()
rfile.close()
CodeWriter.Close(wfile)
if DELETEFLAG == 1:
os.remove(filename)