2016年2月12日金曜日

開発環境

  • 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 コメント:

コメントを投稿