VMトランスレーター / Parserを作る

Chapter 7

今度はVMトランスレーター。假想マシン(ここではスタックマシン)をアセンブリに変換する。第7章ではpushpop、算術・論理演算だけを実装する。まずはParserを作る。各コマンドをオペレーターとオペランドとに分ける。アセンブラのときと同じくPythonを使う。

class Parser:
    def __init__(self, file_path):
        self.file = open(file_path, "r")
        self.EOF_POS = self.__get_EOF_position(self.file)
        self.current_command = ""

    def has_more_lines(self):
        return self.file.tell() != self.EOF_POS

    def advance(self):
        self.current_command = self.__read_command_then_strip()

    def command_type(self):
        current_command = self.current_command
        
        if current_command == "":
            return ""
        else:
            operator = current_command.split()[0]
            if (
                (operator == "add") or
                (operator == "sub") or
                (operator == "neg") or
                (operator == "eq") or
                (operator == "gt") or
                (operator == "lt") or
                (operator == "and") or
                (operator == "or") or
                (operator == "not")
            ): return "C_ARITHMETIC"
        
            elif operator == "push":
                return "C_PUSH"
            elif operator == "pop":
                return "C_POP"
            elif operator == "label":
                return "C_LABEL"
            elif operator == "goto":
                return "C_GOTO"
            elif operator == "if-goto":
                return "C_IF"
            elif operator == "function":
                return "C_FUNCTION"
            elif operator == "return":
                return "C_RETURN"
            elif operator == "call":
                return "C_CALL"
    
    def arg1(self):
        command_type = self.command_type()
        if command_type == "":
            return ""
        elif command_type == "C_ARITHMETIC":
            return self.current_command
        else:
            return self.current_command.split()[1]

    def arg2(self):
        command_type = self.command_type()
        if (
            (command_type == "C_PUSH") or
            (command_type == "C_POP") or
            (command_type == "C_FUNCTION") or
            (command_type == "C_CALL")
        ): return int(self.current_command.split()[2])
        
        else:
            return ""

    def close(self):
        self.file.close()

        
    def __get_EOF_position(self, file):
        file.seek(0, 2) # ファイル末尾に移動して、
        pos = file.tell() # その位置を取得して、
        self.file.seek(0) # 再度ファイル先頭に戻って、
        return pos # 取得したファイル末尾位置を返す。

    def __read_command_then_strip(self):
        command = self.file.readline()
        command = command.strip()
        command = command.split("//")[0]
        if command:
            return command
        else:
            return ""
        

########################################################################
if __name__ == "__main__":
    s = Parser("SimpleAdd.vm")
    #s = Parser("StackTest.vm")
    #s = Parser("BasicTest.vm")
    #s = Parser("PointerTest.vm")
    #s = Parser("StaticTest.vm")
    while (s.has_more_lines()):
        s.advance()
        print("--------------------------")
        print("コマンド      :", s.current_command)
        print("コマンドタイプ:", s.command_type())
        print("第1引数       :", s.arg1())
        print("第2引数       :", s.arg2())
    s.close()

テストファイル(SimpleAdd.vm):

// This file is part of www.nand2tetris.org
// and the book "The Elements of Computing Systems"
// by Nisan and Schocken, MIT Press.
// File name: projects/07/StackArithmetic/SimpleAdd/SimpleAdd.vm

// Pushes and adds two constants.
push constant 7
push constant 8
add

実行結果: