開発環境
- OS X El Capitan - Apple (OS)
- Emacs(Text Editor)
- Java (実行環境)
- Python 3.5(プログラミング言語)
コンピュータシステムの理論と実装 (Noam Nisan (著)、Shimon Schocken (著)、斎藤 康毅(翻訳)、オライリージャパン)の7章(バーチャルマシン#1 : スタック操作)、7.5(プロジェクト)を取り組んでみる。
7.5(プロジェクト)
コード(Emacs)
VMTranslator.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import sys
class Parser:
def nextLine(self):
for line in self.file:
i = line.find('//')
if i != -1:
line = line[:i]
line = line.strip()
if line != '':
self.next = line
break
else:
self.next = ''
def __init__(self, file):
self.file = file
self.cur = None
self.nextLine()
def hasMoreCommands(self):
return self.next != ''
def advance(self):
self.cur = self.next
self.nextLine()
def commandType(self):
cmd = self.cur.split()[0]
if cmd == 'add' or cmd == 'sub' or cmd == 'neg' or cmd == 'eq' or \
cmd == 'gt' or cmd == 'lt' or cmd == 'and' or cmd == 'or' or \
cmd == 'not':
return 'C_ARITHMETIC'
if cmd == 'push':
return 'C_PUSH'
if cmd == 'pop':
return 'C_POP'
def arg1(self):
if self.commandType() == 'C_ARITHMETIC':
return self.cur.split()[0]
if self.commandType() != 'C_RETURN':
return self.cur.split()[1]
def arg2(self):
cmd_type = self.commandType()
if cmd_type == 'C_PUSH' or cmd_type == 'C_POP' or \
cmd_type == 'C_FUNCTION' or cmd_type == 'C_CALL':
return int(self.cur.split()[2])
class CodeWriter:
label_i = 0
def makeLabel():
label = 'label{0}'.format(CodeWriter.label_i)
CodeWriter.label_i += 1
return label
def __init__(self, fileName):
self.setFileName(fileName)
def setFileName(self, fileName):
self.file = open(fileName, 'w')
def writeArithmetic(self, command):
if command == 'add':
print('@SP',
'M=M-1',
'A=M',
'D=M',
'A=A-1',
'M=D+M', sep='\n', file=self.file)
elif command == 'sub':
print('@SP',
'M=M-1',
'A=M',
'D=M',
'A=A-1',
'M=M-D', sep='\n', file=self.file)
elif command == 'neg':
print('@SP',
'A=M-1',
'M=-M', sep='\n', file=self.file)
elif command == 'eq':
label_true = CodeWriter.makeLabel()
label_end = CodeWriter.makeLabel()
print('@SP',
'M=M-1',
'A=M',
'D=M',
'A=A-1',
'D=M-D',
'@{0}'.format(label_true),
'D;JEQ',
'D=0',
'@{0}'.format(label_end),
'0;JMP',
'({0})'.format(label_true),
'D=-1',
'({0})'.format(label_end),
'@SP',
'A=M-1',
'M=D', sep='\n', file=self.file)
elif command == 'gt':
label_true = CodeWriter.makeLabel()
label_end = CodeWriter.makeLabel()
print('@SP',
'M=M-1',
'A=M',
'D=M',
'A=A-1',
'D=M-D',
'@{0}'.format(label_true),
'D;JGT',
'D=0',
'@{0}'.format(label_end),
'0;JMP',
'({0})'.format(label_true),
'D=-1',
'({0})'.format(label_end),
'@SP',
'A=M-1',
'M=D', sep='\n', file=self.file)
elif command == 'lt':
label_true = CodeWriter.makeLabel()
label_end = CodeWriter.makeLabel()
print('@SP',
'M=M-1',
'A=M',
'D=M',
'A=A-1',
'D=M-D',
'@{0}'.format(label_true),
'D;JLT',
'D=0',
'@{0}'.format(label_end),
'0;JMP',
'({0})'.format(label_true),
'D=-1',
'({0})'.format(label_end),
'@SP',
'A=M-1',
'M=D', sep='\n', file=self.file)
elif command == 'and':
print('@SP',
'M=M-1',
'A=M',
'D=M',
'A=A-1',
'M=D&M', sep='\n', file=self.file)
elif command == 'or':
print('@SP',
'M=M-1',
'A=M',
'D=M',
'A=A-1',
'M=D|M', sep='\n', file=self.file)
elif command == 'not':
print('@SP',
'A=M-1',
'M=!M', sep='\n', file=self.file)
def writePushPop(self, command, segment, index):
if command == 'C_PUSH':
if segment == 'argument':
print('@ARG',
'A=M', sep='\n', file=self.file)
for _ in range(index):
print('A=A+1', file=self.file)
print('D=M',
'@SP',
'A=M',
'M=D',
'@SP',
'M=M+1', sep='\n', file=self.file)
elif segment == 'local':
print('@LCL',
'A=M', sep='\n', file=self.file)
for _ in range(index):
print('A=A+1', file=self.file)
print('D=M',
'@SP',
'A=M',
'M=D',
'@SP',
'M=M+1', sep='\n', file=self.file)
elif segment == 'static':
s = os.path.basename(self.file.name).replace('.asm', '')
print('@{0}.{1}'.format(s, index),
'D=M',
'@SP',
'A=M',
'M=D',
'@SP',
'M=M+1', sep='\n', file=self.file)
elif segment == 'constant':
print('@{0}'.format(index),
'D=A',
'@SP',
'A=M',
'M=D',
'@SP',
'M=M+1', sep='\n', file=self.file)
elif segment == 'this':
print('@THIS',
'A=M', sep='\n', file=self.file)
for _ in range(index):
print('A=A+1', file=self.file)
print('D=M',
'@SP',
'A=M',
'M=D',
'@SP',
'M=M+1', sep='\n', file=self.file)
elif segment == 'that':
print('@THAT',
'A=M', sep='\n', file=self.file)
for _ in range(index):
print('A=A+1', file=self.file)
print('D=M',
'@SP',
'A=M',
'M=D',
'@SP',
'M=M+1', sep='\n', file=self.file)
elif segment == 'pointer':
if index == 0:
print('@THIS',
'D=M',
'@SP',
'A=M',
'M=D',
'@SP',
'M=M+1', sep='\n', file=self.file)
elif index == 1:
print('@THAT',
'D=M',
'@SP',
'A=M',
'M=D',
'@SP',
'M=M+1', sep='\n', file=self.file)
elif segment == 'temp':
i = 5 + index
print('@{0}'.format(i),
'D=M',
'@SP',
'A=M',
'M=D',
'@SP',
'M=M+1', sep='\n', file=self.file)
elif command == 'C_POP':
if segment == 'argument':
print('@SP',
'M=M-1',
'A=M',
'D=M',
'@ARG',
'A=M', sep='\n', file=self.file)
for _ in range(index):
print('A=A+1', file=self.file)
print('M=D', file=self.file)
elif segment == 'local':
print('@SP',
'M=M-1',
'A=M',
'D=M',
'@LCL',
'A=M', sep='\n', file=self.file)
for _ in range(index):
print('A=A+1', file=self.file)
print('M=D', file=self.file)
elif segment == 'static':
s = os.path.basename(self.file.name).replace('.asm', '')
print('@SP',
'M=M-1',
'A=M',
'D=M',
'@{0}.{1}'.format(s, index),
'M=D', sep='\n', file=self.file)
elif segment == 'this':
print('@SP',
'M=M-1',
'A=M',
'D=M',
'@THIS',
'A=M', sep='\n', file=self.file)
for _ in range(index):
print('A=A+1', file=self.file)
print('M=D', file=self.file)
elif segment == 'that':
print('@SP',
'M=M-1',
'A=M',
'D=M',
'@THAT',
'A=M', sep='\n', file=self.file)
for _ in range(index):
print('A=A+1', file=self.file)
print('M=D', file=self.file)
elif segment == 'pointer':
if index == 0:
print('@SP',
'M=M-1',
'A=M',
'D=M',
'@THIS',
'M=D', sep='\n', file=self.file)
elif index == 1:
print('@SP',
'M=M-1',
'A=M',
'D=M',
'@THAT',
'M=D', sep='\n', file=self.file)
elif segment == 'temp':
i = 5 + index
print('@SP',
'M=M-1',
'A=M',
'D=M',
'@{0}'.format(i),
'M=D', sep='\n', file=self.file)
def close(self):
self.file.close()
if __name__ == '__main__':
filename = sys.argv[1]
if os.path.isfile(filename):
outfilename = filename.replace('.vm', '.asm')
with open(filename) as f:
parser = Parser(f)
codeWriter = CodeWriter(outfilename)
while parser.hasMoreCommands():
parser.advance()
cmd_type = parser.commandType()
if cmd_type == 'C_ARITHMETIC':
codeWriter.writeArithmetic(parser.arg1())
elif cmd_type == 'C_PUSH' or cmd_type == 'C_POP':
codeWriter.writePushPop(cmd_type,
parser.arg1(), parser.arg2())
入出力結果(Terminal, IPython)
$ ./VMTranslator.py StackArithmetic/SimpleAdd/SimpleAdd.vm $ ./VMTranslator.py StackArithmetic/StackTest/StackTest.vm $ ./VMTranslator.py MemoryAccess/BasicTest/BasicTest.vm $ ./VMTranslator.py MemoryAccess/PointerTest/PointerTest.vm $ ./VMTranslator.py MemoryAccess/StaticTest/StaticTest.vm $
0 コメント:
コメントを投稿