diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..5a3b1fe --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,22 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Python Debugger: Current File", + "type": "debugpy", + "request": "launch", + "program": "${file}", + "console": "integratedTerminal", + "cwd": "${workspaceFolder}\\nand2tetris\\projects\\7\\VMTranslator", + "env": { + "PYTHONPATH": "${workspaceFolder}\\nand2tetris\\projects\\7\\VMTranslator" + } + }, + { + "name": "Python Debugger: Python File", + "type": "debugpy", + "request": "launch", + "program": "${file}" + } + ] +} \ No newline at end of file diff --git a/nand2tetris/.gitignore b/nand2tetris/.gitignore new file mode 100644 index 0000000..3d29a9c --- /dev/null +++ b/nand2tetris/.gitignore @@ -0,0 +1 @@ +tools/* \ No newline at end of file diff --git a/nand2tetris/projects/7/MemoryAccess/.gitignore b/nand2tetris/projects/7/MemoryAccess/.gitignore new file mode 100644 index 0000000..bf82470 --- /dev/null +++ b/nand2tetris/projects/7/MemoryAccess/.gitignore @@ -0,0 +1,2 @@ +*.asm +*.out \ No newline at end of file diff --git a/nand2tetris/projects/7/Mytests/popLocal.asm b/nand2tetris/projects/7/Mytests/popLocal.asm new file mode 100644 index 0000000..198f39f --- /dev/null +++ b/nand2tetris/projects/7/Mytests/popLocal.asm @@ -0,0 +1,23 @@ +// push constant 10 +@10 +D = A +@SP +A = M +M = D +@SP +M = M + 1 +// pop local 2 +@2 +D = A +@LCL +D = D + A +@SP +M = M - 1 +@R13 +M = D +@SP +A = M +D = M +@R13 +A = M +M = D diff --git a/nand2tetris/projects/7/Mytests/popLocal.tst b/nand2tetris/projects/7/Mytests/popLocal.tst new file mode 100644 index 0000000..f107e65 --- /dev/null +++ b/nand2tetris/projects/7/Mytests/popLocal.tst @@ -0,0 +1,14 @@ + +load popLocal.asm, + +set RAM[0] 256, // initializes the stack pointer + +repeat 450 { // enough cycles to complete the execution + ticktock; +} + +// Outputs the value at the stack's base, THIS, THAT, and +// some values from the the this and that segments +output-list RAM[256]%D1.6.1 RAM[3]%D1.6.1 + RAM[4]%D1.6.1 RAM[3032]%D1.6.1 RAM[3046]%D1.6.1; +output; diff --git a/nand2tetris/projects/7/Mytests/popLocal.vm b/nand2tetris/projects/7/Mytests/popLocal.vm new file mode 100644 index 0000000..0ecca3b --- /dev/null +++ b/nand2tetris/projects/7/Mytests/popLocal.vm @@ -0,0 +1,2 @@ +push constant 10 +pop local 2 \ No newline at end of file diff --git a/nand2tetris/projects/7/Mytests/popLocalVME.tst b/nand2tetris/projects/7/Mytests/popLocalVME.tst new file mode 100644 index 0000000..c621e1b --- /dev/null +++ b/nand2tetris/projects/7/Mytests/popLocalVME.tst @@ -0,0 +1,11 @@ +load popLocal.vm, + +set sp 256, // stack pointer +set local 300, // base address of the local segment +set argument 400, // base address of the argument segment +set this 3000, // base address of the this segment +set that 3010, // base address of the that segment + +repeat 2 { + vmstep; +} diff --git a/nand2tetris/projects/7/Mytests/testPointer.asm b/nand2tetris/projects/7/Mytests/testPointer.asm new file mode 100644 index 0000000..8de1362 --- /dev/null +++ b/nand2tetris/projects/7/Mytests/testPointer.asm @@ -0,0 +1,37 @@ +// push constant 3000 +@3000 +D = A +@SP +A = M +M = D +@SP +M = M + 1 +// pop pointer 0 +@THIS +D = A +@SP +M = M - 1 +@R13 +M = D +@SP +A = M +D = M +@R13 +A = M +M = D +// push constant 5 +@5 +D = A +@SP +A = M +M = D +@SP +M = M + 1 +// push pointer 0 +@THIS +D = M +@SP +A = M +M = D +@SP +M = M + 1 diff --git a/nand2tetris/projects/7/Mytests/testPointer.tst b/nand2tetris/projects/7/Mytests/testPointer.tst new file mode 100644 index 0000000..7378f0c --- /dev/null +++ b/nand2tetris/projects/7/Mytests/testPointer.tst @@ -0,0 +1,8 @@ + +load testPointer.asm, + +set RAM[0] 256, // initializes the stack pointer + +repeat 450 { // enough cycles to complete the execution + ticktock; +} diff --git a/nand2tetris/projects/7/Mytests/testPointer.vm b/nand2tetris/projects/7/Mytests/testPointer.vm new file mode 100644 index 0000000..ec61f8b --- /dev/null +++ b/nand2tetris/projects/7/Mytests/testPointer.vm @@ -0,0 +1,4 @@ +push constant 3000 +pop pointer 0 +push constant 5 +push pointer 0 \ No newline at end of file diff --git a/nand2tetris/projects/7/Mytests/testPointerVME.tst b/nand2tetris/projects/7/Mytests/testPointerVME.tst new file mode 100644 index 0000000..97587f1 --- /dev/null +++ b/nand2tetris/projects/7/Mytests/testPointerVME.tst @@ -0,0 +1,11 @@ +load testPointer.vm, + +set sp 256, // stack pointer +set local 300, // base address of the local segment +set argument 400, // base address of the argument segment +set this 3000, // base address of the this segment +set that 3010, // base address of the that segment + +repeat 6 { + vmstep; +} diff --git a/nand2tetris/projects/7/Mytests/testPush.asm b/nand2tetris/projects/7/Mytests/testPush.asm new file mode 100644 index 0000000..fcdda6a --- /dev/null +++ b/nand2tetris/projects/7/Mytests/testPush.asm @@ -0,0 +1,71 @@ +// push constant 2 +@2 +D = A +@SP +A = M +M = D +@SP +M = M + 1 +// push local 3 +@3 +D = A +@LCL +A = D + A +D = M +@SP +A = M +M = D +@SP +M = M + 1 +// push argument 4 +@4 +D = A +@ARG +A = D + A +D = M +@SP +A = M +M = D +@SP +M = M + 1 +// push this 0 +@0 +D = A +@THIS +A = D + A +D = M +@SP +A = M +M = D +@SP +M = M + 1 +// push that 0 +@0 +D = A +@THAT +A = D + A +D = M +@SP +A = M +M = D +@SP +M = M + 1 +// push pointer 0 +@0 +D = A +@None +A = D + A +D = M +@SP +A = M +M = D +@SP +M = M + 1 +// push static 5 +@testPush.5 +D = A +@SP +A = M +M = D +@SP +M = M + 1 diff --git a/nand2tetris/projects/7/Mytests/testPush.vm b/nand2tetris/projects/7/Mytests/testPush.vm new file mode 100644 index 0000000..753a3e5 --- /dev/null +++ b/nand2tetris/projects/7/Mytests/testPush.vm @@ -0,0 +1,9 @@ + + +push constant 2 +push local 3 +push argument 4 +push this 0 +push that 0 +push pointer 0 +push static 5 \ No newline at end of file diff --git a/nand2tetris/projects/7/Mytests/testPushVME.tst b/nand2tetris/projects/7/Mytests/testPushVME.tst new file mode 100644 index 0000000..abfdd5a --- /dev/null +++ b/nand2tetris/projects/7/Mytests/testPushVME.tst @@ -0,0 +1,11 @@ +load testPush.vm, + +set sp 256, // stack pointer +set local 300, // base address of the local segment +set argument 400, // base address of the argument segment +set this 3000, // base address of the this segment +set that 3010, // base address of the that segment + +repeat 7 { // testPush.vm has 25 VM commands + vmstep; +} diff --git a/nand2tetris/projects/7/Mytests/testStatic.vm b/nand2tetris/projects/7/Mytests/testStatic.vm new file mode 100644 index 0000000..a14dda1 --- /dev/null +++ b/nand2tetris/projects/7/Mytests/testStatic.vm @@ -0,0 +1,6 @@ +// Testing static + +push static 5 +push static 6 + +add diff --git a/nand2tetris/projects/7/StackArithmetic/.gitignore b/nand2tetris/projects/7/StackArithmetic/.gitignore new file mode 100644 index 0000000..bf82470 --- /dev/null +++ b/nand2tetris/projects/7/StackArithmetic/.gitignore @@ -0,0 +1,2 @@ +*.asm +*.out \ No newline at end of file diff --git a/nand2tetris/projects/7/StackArithmetic/SimpleAdd/SimpleAdd - Kopi.vm b/nand2tetris/projects/7/StackArithmetic/SimpleAdd/SimpleAdd - Kopi.vm new file mode 100644 index 0000000..df4c9dc --- /dev/null +++ b/nand2tetris/projects/7/StackArithmetic/SimpleAdd/SimpleAdd - Kopi.vm @@ -0,0 +1,10 @@ +// 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/7/StackArithmetic/SimpleAdd/SimpleAdd.vm + +// Pushes and adds two constants. + +push constant 7 +puhhh constant 8 +add diff --git a/nand2tetris/projects/7/VMTranslator/.gitignore b/nand2tetris/projects/7/VMTranslator/.gitignore new file mode 100644 index 0000000..22872b8 --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/.gitignore @@ -0,0 +1,5 @@ +## https://stackoverflow.com/questions/3719243/best-practices-for-adding-gitignore-file-for-python-projects + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] \ No newline at end of file diff --git a/nand2tetris/projects/7/VMTranslator/README.md b/nand2tetris/projects/7/VMTranslator/README.md new file mode 100644 index 0000000..e69de29 diff --git a/nand2tetris/projects/7/VMTranslator/VMTranslator.py b/nand2tetris/projects/7/VMTranslator/VMTranslator.py new file mode 100644 index 0000000..b7dda95 --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/VMTranslator.py @@ -0,0 +1,120 @@ +## How the program is supposed to be run: +# https://www.coursera.org/learn/nand2tetris2/lecture/qmJl3/unit-1-8-vm-translator-proposed-implementation + + +#### ---- import ---- +# https://stackoverflow.com/a/37867717/3560695 + +import re +import sys + +## Import parser +## We have to use import_module since the path contains an illegal directory name (7). +# parser_module = importlib.import_module("src.parser.parser") +import src.parser.parser as p + +# writer_module = importlib.import_module("src.codewriter.codewriter") +import src.codewriter.codewriter as cw + + +#### ---- main ---- + +#def main(): + + +def main(filename = None): + print("VMTranslator start ...") + if filename is None: + # Check if the correct number of command-line arguments are provided + if len(sys.argv) != 2: + print("Usage: py VMTranslator.py ") + return + filename = sys.argv[1] + + ## https://regex101.com/r/x0v99o/3 + ## https://stackoverflow.com/a/59696768/3560695 + regex = r'^(?P.*[\\\/])?(?P\.*.*?)(?P\.[^.]+?|)$' + + filename_write = re.sub(regex, r"\1\2.asm", filename) + + + Parser = p.parser + codewriter = cw.codewriter + + with open(filename, 'r') as file, open(filename_write, "w") as file_write: + parser = Parser(file) + writer = codewriter(file_write) + + lines = None + + while parser.hasMoreCommands(): ## As long as file has more lines, do: + parser.advance() ## Go to the next line in the file. + parser.getinstruction() + if not parser.instruction: ## If instruction is blank, skip. Line consisted only of a comment. + continue + parser.getVMinstruction() ## sets parser.VMinstruction to current instruction + + # print(f"---- Line {parser.lineNo} ----") + # print(f"instruction = {parser.instruction}") + # print(f"commandType() = {parser.commandType()}, type = {type(parser.commandType())}") + # print(f"arg1() = {parser.arg1()}") + # print(f"arg2() = {parser.arg2()}") + + + commandType = parser.commandType() + if parser.commandType() == "C_ARITHMETIC": + # Write arithmetic from arithmetic.asm + arg1 = parser.arg1() + lines = writer.writeArithmetic(arg1) + + elif parser.commandType() in ["C_PUSH", "C_POP"]: + arg1 = parser.arg1() + arg2 = parser.arg2() + lines = writer.writePushPop(commandType, arg1, arg2, filename = filename) + else: + ... + + ## lines is now the full .asm script. Write to output asm file. + file_write.write(f'// {parser.instruction}\n') + for line in lines: + file_write.write(f'{line}\n') + print("VMTranslator finished!") + + + + + +import importlib + +# ## Testing +# filename = '.../StackArithmetic/SimpleAdd/SimpleAdd.vm' +# filename = '.../StackArithmetic/Stacktest/Stacktest.vm' +# filename = '.../StackArithmetic/Stacktest/Stacktest.vm' + +# importlib.reload(p) +# importlib.reload(cw) + +# main(filename[1:]) + +if __name__ == "__main__": + main() + + + + + + + + + + + + + + + + + + + + diff --git a/nand2tetris/projects/7/VMTranslator/__init__.py b/nand2tetris/projects/7/VMTranslator/__init__.py new file mode 100644 index 0000000..d65e1a9 --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/__init__.py @@ -0,0 +1 @@ +__package__ = '' \ No newline at end of file diff --git a/nand2tetris/projects/7/VMTranslator/docs/Resources.md b/nand2tetris/projects/7/VMTranslator/docs/Resources.md new file mode 100644 index 0000000..d02ffab --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/docs/Resources.md @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/nand2tetris/projects/7/VMTranslator/docs/pushConstant.md b/nand2tetris/projects/7/VMTranslator/docs/pushConstant.md new file mode 100644 index 0000000..448677d --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/docs/pushConstant.md @@ -0,0 +1,10 @@ +`push constant i` +---- + +Output +---- +``` +D <- i # D_eq_i.asm +RAM[SP] <- D # RAM_SP_eq_D.asm +SP++ # SPpp.asm +``` \ No newline at end of file diff --git a/nand2tetris/projects/7/VMTranslator/docs/pushPointer.md b/nand2tetris/projects/7/VMTranslator/docs/pushPointer.md new file mode 100644 index 0000000..0e03cdb --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/docs/pushPointer.md @@ -0,0 +1,20 @@ +`push pointer i` +---- + +For `pointer` we get the _address_ pointed to by THIS/THAT. + +We translate +* `pointer 0` to `THIS` and +* `pointer 1` to `THAT`. + +Store it in `segmentPointer`. + +Output +---- + +``` +addr <- segmentPointer # D_eq_RAM_i.asm +D <- RAM[addr] # ^-- +RAM[SP] <- D # RAM_SP_eq_D.asm +SP++ # SPpp.asm +``` \ No newline at end of file diff --git a/nand2tetris/projects/7/VMTranslator/docs/pushSegment.md b/nand2tetris/projects/7/VMTranslator/docs/pushSegment.md new file mode 100644 index 0000000..5ae97f2 --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/docs/pushSegment.md @@ -0,0 +1,14 @@ +`push segment i` +---- + +where `segment in ["local", "argument", "this", "that"]` + +Output +---- + +``` +addr <- segmentPointer + i # D_eq_RAM_segmentPointer_p_i.asm +D <- RAM[addr] # ^-- +RAM[SP] <- D # RAM_SP_eq_D.asm +SP++ # SPpp.asm +``` \ No newline at end of file diff --git a/nand2tetris/projects/7/VMTranslator/docs/pushStatic.md b/nand2tetris/projects/7/VMTranslator/docs/pushStatic.md new file mode 100644 index 0000000..658ca4b --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/docs/pushStatic.md @@ -0,0 +1,26 @@ +`push static i` +---- + +<<<<<<< Updated upstream +For `static` we set `index = "Filename.i"`, hence all we have to do is `D <- Filename.i`. + +This also means `push static i` is equivalent to `push segment i` documented in [`pushSegment.md`](pushSegment.md). +======= +For `static` we set `index = "filename.i"`, hence what we have to do is `D <- RAM[filename.i]`. +>>>>>>> Stashed changes + +Output +---- + +``` +<<<<<<< Updated upstream +D <- i # D_eq_i.asm +RAM[SP] <- RAM[D] # RAM_SP_eq_RAM_D.asm +SP++ # SPpp.asm +======= +addr <- i # D_eq_RAM_i.asm +D <- RAM[addr] # ^-- +RAM[SP] <- D # RAM_SP_eq_D.asm +SP++ # SPpp.asm +>>>>>>> Stashed changes +``` \ No newline at end of file diff --git a/nand2tetris/projects/7/VMTranslator/docs/pushTemp.md b/nand2tetris/projects/7/VMTranslator/docs/pushTemp.md new file mode 100644 index 0000000..3cffe5e --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/docs/pushTemp.md @@ -0,0 +1,22 @@ +`push temp i` +---- + +`RAM[SP] <- RAM[temp + i]` + +temp is realized as 5 in the Hack language, so +`RAM[SP] <- RAM[5 + i]` + +In the function `writePushPop` we do this by setting `i = temp + i`: +``` +i += 5 + i +``` + +So the final version must be: + +Output +---- +``` +D = i # D_eq_i.asm +RAM[SP] <- RAM[D] # RAM_SP_eq_RAM_D.asm +SP++ # SPpp.asm +``` \ No newline at end of file diff --git a/nand2tetris/projects/7/VMTranslator/requirements.txt b/nand2tetris/projects/7/VMTranslator/requirements.txt new file mode 100644 index 0000000..e69de29 diff --git a/nand2tetris/projects/7/VMTranslator/setup.py b/nand2tetris/projects/7/VMTranslator/setup.py new file mode 100644 index 0000000..6f95d36 --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/setup.py @@ -0,0 +1,21 @@ +from setuptools import setup, find_packages + +setup( + name='VMTranslator', + version='0.1', + python_requires = '3.12.3', + packages=find_packages('src'), + package_dir={'': 'src'}, + install_requires=[ + # List of Python package dependencies + 'os', + 'sys' + # 'numpy==1.21.0', + # 'pandas==1.3.0', + ], + entry_points={ + 'console_scripts': [ + 'VMTranslator = VMTranslator:main' + ] + } +) \ No newline at end of file diff --git a/nand2tetris/projects/7/VMTranslator/src/Archive/deprecated.py b/nand2tetris/projects/7/VMTranslator/src/Archive/deprecated.py new file mode 100644 index 0000000..83a845a --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/src/Archive/deprecated.py @@ -0,0 +1,72 @@ + + +# https://chat.openai.com/share/40c7964c-0f2f-44d2-81c7-e88d6a1868a1 +def changeExtension(string : str, newExtension : str): + """ + """ + if not isinstance(string, str): + raise TypeError("Input must be a string") + if not isinstance(newExtension, str): + raise TypeError("Input must be a string") + + # Regex pattern to capture the base name and extension + # https://regex101.com/r/SkENd5/1 + regex = r"^(?P[^.].*\.)(?P\w+)$|(?P^.*$)" + + # Replace the extension with the new extension + new_string = re.sub( + regex, + lambda m: ( + m.group('base') + newExtension + if m.group('base') + else m.group('fullname') + ), + string) + + return new_string + + +def writelines(input_data : str | list, filename) -> None: + """ + Write the given string or list of strings to the specified file. + + Args: + input_data (str | list): The string or list of strings to write to the file. + filename (str): The name of the file to write to. + + Raises: + TypeError: If input_data is neither a string nor a list, or if filename is not a string. + ValueError: If filename is an empty string. + IOError: If there is an issue writing to the file. + + Returns: + None + """ + # Check input types + if not isinstance(input_data, (str, list)): + raise TypeError("Input data must be a string or a list of strings") + if not isinstance(filename, str): + raise TypeError("Filename must be a string") + + # Check filename is not empty + if not filename: + raise ValueError("Filename cannot be an empty string") + + # Open the file in write mode + try: + with open(filename, 'w') as file: + # Write data to the file + if isinstance(input_data, str): + file.write(input_data) + else: + file.writelines(input_data) + except IOError as e: + raise IOError("Error writing to file: " + str(e)) + + + + + + + + diff --git a/nand2tetris/projects/7/VMTranslator/src/__init__.py b/nand2tetris/projects/7/VMTranslator/src/__init__.py new file mode 100644 index 0000000..3903534 --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/src/__init__.py @@ -0,0 +1 @@ +## This file exists such that .py scripts from here can be imported. \ No newline at end of file diff --git a/nand2tetris/projects/7/VMTranslator/src/codewriter/__init__.py b/nand2tetris/projects/7/VMTranslator/src/codewriter/__init__.py new file mode 100644 index 0000000..3903534 --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/src/codewriter/__init__.py @@ -0,0 +1 @@ +## This file exists such that .py scripts from here can be imported. \ No newline at end of file diff --git a/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py b/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py new file mode 100644 index 0000000..c901d81 --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py @@ -0,0 +1,210 @@ + +import os +# import importlib + +# sys.path.append("C:/Users/Bruger/OneDrive/Dokumenter/GitHub/BuildComputer/nand2tetris/projects/7/VMTranslator") + + +# parser_module = importlib.import_module("src.parser.parser") +import src.parser.parser as parser_module +# importlib.reload(parser_module) + +# dicts_filepath = "nand2tetris/projects/7/VMTranslator/src/utils/dict/dict" +# dicts = importlib.import_module(dicts_filepath.replace("/", ".")) +import src.utils.dict.dict as dicts + +class codewriter: + """ writes the assembly code that implements the parsed command. """ + + + def __init__(self, file) -> None: + self.file = file ## functions need to be able to grab file. + self.newline = '\n' + + self.Parser = parser_module.parser + + ## Initialize number of times arithmetic has been called + self.arithmeticNo = 0 + + ## Used in some of the .asm templates. + self.segment = None + self.index = None + + def writeArithmetic(self, command : str) -> None: + """ + Arguments + command (string) + Function + Writes to the output file the assembly code that implements the given arithmetic command. + """ + + ## Unary or binary operation: + arity = dicts.arithmetic[command] + + + ## action is used in some of the .asm templates. + ## Contains Hack code for the arithmetic operations + action = dicts.arithmetic_action[command] + if command in ["eq", "lt", "gt"]: + action = dicts.compare_action.format(**locals()) + + with open(f"src/utils/asm/arithmetic_{arity}.asm", 'r') as asm: + parser = self.Parser(asm) + + lines = self.processCommands(parser, action = action) + + self.arithmeticNo += 1 + return lines + + + + def writePushPop(self, command : str, segment : str, index : int, filename : str = ""): + """ + Arguments + ---- + command : ('C_PUSH' or 'C_POP') + segment : constant, local etc. + index : pointer number in the segment + filename: Name of file - used for push/pop static i + Function + ---- + Writes to the output file the assembly code that implements the given command, + where command is either C_PUSH or C_POP. + + If push/pop static i from filename.vm, convert to push/pop filename.i + """ + + segmentPointer = None ## Initialize - passed to self.processCommands but not always used. + lines = [] ## Initialize lines. + + ## Raise errors: + + ## Wrong command + if command not in ["C_PUSH", "C_POP"]: + raise ValueError(f"command: '{command}' not allowed for writePushPop. Only takes 'C_PUSH' or 'C_POP'.") + ## First handle 'pointer' logic translation to THIS/THAT + ## Also handle temp out of bounds. + ## Also raise error if trying to pop constant + + if segment == "temp" and (not 0 <= index < 7): + raise ValueError(f"'temp' segment only valid for index values 0 to 7. Input: {index}.") + elif command == "C_POP" and segment == "constant": + raise ValueError("Cannot pop constant.") + + ## New if-block + if segment == "static": + if filename == "": + raise ValueError(f"No filename supplied for push/pop static.") + file = os.path.splitext(os.path.basename(filename))[0] + index = file + "." + str(index) + + if command == "C_PUSH": + # addr <- filename.index + # D <- RAM[addr] + lines.extend(self.processAsm("D_eq_RAM_i.asm", index = index)) + else: # command == "C_POP" + # addr <- filename.index + lines.extend(self.processAsm("D_eq_i.asm", index = index)) + + elif segment == "constant": + # D <- addr + lines.extend(self.processAsm("D_eq_i.asm", index = index)) + + elif segment == "temp": + # addr <- 5 + index + # D <- addr + index += dicts.segment[segment] + + if command == "C_PUSH": + lines.extend(self.processAsm("D_eq_RAM_i.asm", index = index)) + else: # command == "C_POP" + lines.extend(self.processAsm("D_eq_i.asm", index = index)) + + elif segment == "pointer": + if index not in [0, 1]: + raise ValueError(f"push/pop pointer only valid for value 0 or 1. Input: {index}.") + segment = ["this", "that"][index] ## e.g. 'push pointer 0' means 'push this' + segmentPointer = dicts.segment[segment] + index = 0 ## Silently 'push THIS' means 'push THIS 0' + + if command == "C_PUSH": + lines.extend(self.processAsm("D_eq_RAM_segmentPointer.asm", segmentPointer = segmentPointer)) + else: # command == "C_POP" + lines.extend(self.processAsm("D_eq_segmentPointer.asm", segmentPointer = segmentPointer)) + + elif segment in dicts.segment.keys(): + ## Used in some of the .asm templates. + ## Contains Hack name convention for segments, e.g. local is "LCL" + segmentPointer = dicts.segment[segment] + + if command == "C_PUSH": + # addr <- segmentPointer + index + # D <- RAM[addr] + lines.extend(self.processAsm("D_eq_RAM_segmentPointer_p_i.asm",index = index, segmentPointer = segmentPointer)) + else: #command == "C_POP": + # D <- addr + lines.extend(self.processAsm("D_eq_segmentPointer_p_i.asm", index = index, segmentPointer = segmentPointer)) + else: + raise ValueError(f"Segment = '{segment}' not handled.") + + if command == "C_PUSH": + ## RAM[SP] <- D + ## SP++ + lines.extend(self.processAsm("RAM_SP_eq_D.asm")) + lines.extend(self.processAsm("SPpp.asm")) + elif command == "C_POP": + if segment == "constant": + raise ValueError("Cannot pop constant.") + + ## SP-- + # R13 <- D # R13_eq_D.asm + # RAM[R13] <- RAM[SP] # RAM_R13_eq_RAM_SP.asm + lines.extend(self.processAsm("SPmm.asm")) + lines.extend(self.processAsm("R13_eq_D.asm")) + lines.extend(self.processAsm("RAM_R13_eq_RAM_SP.asm")) + + + + else: + raise KeyError(f"Unexpected command '{command}' in writePushPop (should be 'C_PUSH' or 'C_POP')") + + + + return lines + + + + def processCommands(self, parser, **kwargs): + """ + Arguments + ---- + parser : parser object + Function + ---- + Process commands using the provided parser object until there are no more commands. + """ + lines = [] + while parser.hasMoreCommands(): + parser.advance() + parser.getinstruction() + if not parser.instruction: ## If instruction is blank, skip. Line consisted only of a comment. + continue + + ## Replace any {} in the input file with its value, sent to this function through **kwargs. + ## Important that the arguments are named. E.g. + ## segment = 'local' + ## processCommands(..., segment = segment) + ## processCommands(..., segment = 'local') + line = parser.instruction.format(**kwargs) + + lines.append(line) + + return lines + + def processAsm(self, asm_file : str, **kwargs): + with open(f"src/utils/asm/{asm_file}", 'r') as asm: + parser = self.Parser(asm) + lines = self.processCommands(parser, **kwargs) + return lines + + \ No newline at end of file diff --git a/nand2tetris/projects/7/VMTranslator/src/parser/__init__.py b/nand2tetris/projects/7/VMTranslator/src/parser/__init__.py new file mode 100644 index 0000000..3903534 --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/src/parser/__init__.py @@ -0,0 +1 @@ +## This file exists such that .py scripts from here can be imported. \ No newline at end of file diff --git a/nand2tetris/projects/7/VMTranslator/src/parser/parser.py b/nand2tetris/projects/7/VMTranslator/src/parser/parser.py new file mode 100644 index 0000000..5ef8266 --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/src/parser/parser.py @@ -0,0 +1,165 @@ + +# import importlib + +# dicts_filepath = "src/utils/dict/dict" +#dicts = importlib.import_module(dicts_filepath.replace("/", ".")) +import src.utils.dict.dict as dicts + +class parser: + """ + - Handles the parsing of a single .vm file. + + - Reads a VM command, parses the command into its lexical + components, and provides convenient access to these components. + + - Ignores all white space and comments. + """ + + def __init__(self, file) -> None: + self.line = None ## Initialize line. + self.lineNo = 0 ## Initialize line number. + self.instruction = None ## Initialize instruction. + self.VMinstruction = None ## Initialize VM instruction (list) + self.file = file ## functions need to be able to grab file. + + + def peek(self) -> str: + """ + Peeks at the next line in a filestream without advancing the pointer. + + Parameters + ---- + file - A file object. Should be opened in read mode ('r'). + + If the file is at the end, an empty string is returned. Empty lines return carriage return (e.g. \\n) + """ + pos = self.file.tell() # Get current position in the filestream + line = self.file.readline() # Read line + self.file.seek(pos) # Return to previous line + return line + + + def hasMoreCommands(self) -> bool: + """ + Arguments + ---- + N/A + + Returns + ---- + bool + + Function + ---- + Are there more commands in the input? + """ + return bool(self.peek()) + + def advance(self) -> None: + """ + Arguments + ---- + None + + Returns + ---- + None + Function + ---- + Reads the next command from the input and makes it the current command. + Should be called only if hasMoreCommands() is true. + Initially there is no current command. + """ + self.line = self.file.readline() + self.lineNo += 1 + + def commandType(self) -> str: + """ + Arguments + ---- + N/A + + Returns + ---- + C_ARITHMETIC, C_PUSH, C_POP, C_LABEL, C_GOTO, C_IF, + C_FUNCTION, C_RETURN, C_CALL + + Function + ---- + Returns a constant representing the type of the current command. + C_ARITHMETIC is returned for all the arithmetic/logical commands. + + """ + + try: + return dicts.commandType[self.VMinstruction[0]] + except: + raise KeyError(f"Current command invalid: Cannot parse '{self.line.strip()}' on line {self.lineNo}.") + + def arg1(self) -> str: + """ + Arguments + ---- + None + + Returns + ---- + Str + + Function + ---- + Returns the first argument of the current command. + In the case of C_ARITHMETIC, the command itself (add, sub, etc.) + is returned. + Should not be called if the current command is C_RETURN. + """ + if self.commandType() == "C_ARITHMETIC": + out = self.VMinstruction[0] + elif self.commandType() == "C_RETURN": + return ValueError(f"arg1() called on invalid command type: '{self.commandType()}'") + else: + out = self.VMinstruction[1] + + return out + + def arg2(self) -> int: + """ + Arguments + ---- + None + + Returns + ---- + int + + Function + ---- + Returns the second argument of the current command. + Should be called only if the current command is + C_PUSH, C_POP, C_FUNCTION or C_CALL. + """ + if self.commandType() in ["C_PUSH", "C_POP", "C_FUNCTION", "C_CALL"]: + return int(self.VMinstruction[2]) + else: + return ValueError(f"arg2() called on invalid command type: '{self.commandType()}'") + + def getinstruction(self): + """ + Remove everything after the comment token (//) + """ + self.instruction = self.line.split("//")[0].strip() + + def getVMinstruction(self): + """ + Splits the instruction into a list of objects + """ + + # Call getinstruction to ensure instruction has been retrieved. + self.getinstruction() + self.VMinstruction = self.instruction.split() + + + + + + diff --git a/nand2tetris/projects/7/VMTranslator/src/utils/__init__.py b/nand2tetris/projects/7/VMTranslator/src/utils/__init__.py new file mode 100644 index 0000000..3903534 --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/src/utils/__init__.py @@ -0,0 +1 @@ +## This file exists such that .py scripts from here can be imported. \ No newline at end of file diff --git a/nand2tetris/projects/7/VMTranslator/src/utils/asm/Archive/add.asm b/nand2tetris/projects/7/VMTranslator/src/utils/asm/Archive/add.asm new file mode 100644 index 0000000..d508682 --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/src/utils/asm/Archive/add.asm @@ -0,0 +1,26 @@ +// ---- add ---- +// D = *SP +// SP-- +// *SP = *SP + D +// SP++ + + + + +// D = *SP +@SP +A = M +D = M + +// SP-- +@SP +M = M - 1 + +// *SP = *SP + D +A = M +M = D + M + +// SP++ +@SP +M = M + 1 + diff --git a/nand2tetris/projects/7/VMTranslator/src/utils/asm/Archive/popSegment.asm b/nand2tetris/projects/7/VMTranslator/src/utils/asm/Archive/popSegment.asm new file mode 100644 index 0000000..c277f7c --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/src/utils/asm/Archive/popSegment.asm @@ -0,0 +1,26 @@ +//-- pop {segment} {index} ---- +// addr = segment + i, SP--, *addr = *SP + +// addr <- segmentPointer + i +@{index} +D = A +@{segmentPointer} +D = D + M // D = segment + i + +// SP-- +@SP +M = M - 1 + +// RAM[addr] <- RAM[SP] +// Store D in R13 +// i.e. *R13 = addr +@R13 +M = D +// D = RAM[SP] +@SP +A = M +D = M +// *R13 +@R13 +A = M +M = D \ No newline at end of file diff --git a/nand2tetris/projects/7/VMTranslator/src/utils/asm/Archive/pushConstant.asm b/nand2tetris/projects/7/VMTranslator/src/utils/asm/Archive/pushConstant.asm new file mode 100644 index 0000000..c048c09 --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/src/utils/asm/Archive/pushConstant.asm @@ -0,0 +1,22 @@ +// ---- push constant {index} ---- + +// addr = segment + i, *SP = *addr, SP++ + + +// addr <- SegmentPointer + i +@{index} +D = A + +// Not used if segment is 'constant' +//@{segmentPointer} +//A = D + M +//D = M + +// RAM[SP] = RAM[addr] +@SP +A = M +M = D + +// SP++ +@SP +M = M + 1 \ No newline at end of file diff --git a/nand2tetris/projects/7/VMTranslator/src/utils/asm/Archive/pushSegment.asm b/nand2tetris/projects/7/VMTranslator/src/utils/asm/Archive/pushSegment.asm new file mode 100644 index 0000000..0bd9542 --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/src/utils/asm/Archive/pushSegment.asm @@ -0,0 +1,36 @@ +// ---- push {segment} {index} ---- + +// addr = segment + i, *SP = *addr, SP++ + +// If segment is constant: +// addr <- i +// If segment is static: +// addr <- i +// Else: +// addr <- segmentPointer + i + + +// i or file.i +@{index} + + +// if segment not in ["constant", "static"]: +// D = A +// @{segmentPointer} +// A = D + A + +// if segment == "constant": +// D = A +// else: +// D = RAM[addr] +// D = M + + +// RAM[SP] = RAM[addr] +@SP +A = M +M = D + +// SP++ +@SP +M = M + 1 \ No newline at end of file diff --git a/nand2tetris/projects/7/VMTranslator/src/utils/asm/Archive/pushStatic.asm b/nand2tetris/projects/7/VMTranslator/src/utils/asm/Archive/pushStatic.asm new file mode 100644 index 0000000..aa8501d --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/src/utils/asm/Archive/pushStatic.asm @@ -0,0 +1,29 @@ +// ---- push static {index} ---- + +// addr = Foo.i, *SP = *addr, SP++ + +@{index} +D = A + +@{segmentPointer} +A = D + A +D = A + + +// addr <- SegmentPointer + i +@{index} +D = A + +// Not used if segment is 'constant' +// @{segmentPointer} +// A = D + M +// D = M + +// RAM[SP] = RAM[addr] +@SP +A = M +M = D + +// SP++ +@SP +M = M + 1 \ No newline at end of file diff --git a/nand2tetris/projects/7/VMTranslator/src/utils/asm/D_eq_RAM_i.asm b/nand2tetris/projects/7/VMTranslator/src/utils/asm/D_eq_RAM_i.asm new file mode 100644 index 0000000..1598659 --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/src/utils/asm/D_eq_RAM_i.asm @@ -0,0 +1,4 @@ +// addr <- i +@{index} +// D <- RAM[addr] +D = M \ No newline at end of file diff --git a/nand2tetris/projects/7/VMTranslator/src/utils/asm/D_eq_RAM_segmentPointer.asm b/nand2tetris/projects/7/VMTranslator/src/utils/asm/D_eq_RAM_segmentPointer.asm new file mode 100644 index 0000000..3f1e048 --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/src/utils/asm/D_eq_RAM_segmentPointer.asm @@ -0,0 +1,4 @@ +// addr <- segmentPointer +@{segmentPointer} +// D <- RAM[addr] +D = M \ No newline at end of file diff --git a/nand2tetris/projects/7/VMTranslator/src/utils/asm/D_eq_RAM_segmentPointer_p_i.asm b/nand2tetris/projects/7/VMTranslator/src/utils/asm/D_eq_RAM_segmentPointer_p_i.asm new file mode 100644 index 0000000..6a9518c --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/src/utils/asm/D_eq_RAM_segmentPointer_p_i.asm @@ -0,0 +1,8 @@ +// addr <- segmentPointer + i +@{index} +D = A +@{segmentPointer} +A = M +A = D + A +// D <- RAM[addr] +D = M \ No newline at end of file diff --git a/nand2tetris/projects/7/VMTranslator/src/utils/asm/D_eq_i.asm b/nand2tetris/projects/7/VMTranslator/src/utils/asm/D_eq_i.asm new file mode 100644 index 0000000..0e58833 --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/src/utils/asm/D_eq_i.asm @@ -0,0 +1,6 @@ +// addr <- i + +// D = addr + +@{index} +D = A \ No newline at end of file diff --git a/nand2tetris/projects/7/VMTranslator/src/utils/asm/D_eq_segmentPointer.asm b/nand2tetris/projects/7/VMTranslator/src/utils/asm/D_eq_segmentPointer.asm new file mode 100644 index 0000000..b872787 --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/src/utils/asm/D_eq_segmentPointer.asm @@ -0,0 +1,4 @@ +// addr <- segmentPointer +@{segmentPointer} +// D <- addr +D = A \ No newline at end of file diff --git a/nand2tetris/projects/7/VMTranslator/src/utils/asm/D_eq_segmentPointer_p_i.asm b/nand2tetris/projects/7/VMTranslator/src/utils/asm/D_eq_segmentPointer_p_i.asm new file mode 100644 index 0000000..5d92df0 --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/src/utils/asm/D_eq_segmentPointer_p_i.asm @@ -0,0 +1,7 @@ +// addr <- segmentPointer + i +@{index} +D = A +@{segmentPointer} +// D <- addr +A = M +D = D + A diff --git a/nand2tetris/projects/7/VMTranslator/src/utils/asm/R13_eq_D.asm b/nand2tetris/projects/7/VMTranslator/src/utils/asm/R13_eq_D.asm new file mode 100644 index 0000000..3c4eac3 --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/src/utils/asm/R13_eq_D.asm @@ -0,0 +1,3 @@ +// R13 <- D +@R13 +M = D \ No newline at end of file diff --git a/nand2tetris/projects/7/VMTranslator/src/utils/asm/RAM_D_eq_RAM_SP.asm b/nand2tetris/projects/7/VMTranslator/src/utils/asm/RAM_D_eq_RAM_SP.asm new file mode 100644 index 0000000..6c68d3f --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/src/utils/asm/RAM_D_eq_RAM_SP.asm @@ -0,0 +1,15 @@ +// RAM[D] <- RAM[SP] +// Store D in R13 +// i.e. *R13 = D + + +@R13 +M = D +// D = RAM[SP] +@SP +A = M +D = M +// *R13 +@R13 +A = M +M = D \ No newline at end of file diff --git a/nand2tetris/projects/7/VMTranslator/src/utils/asm/RAM_R13_eq_RAM_SP.asm b/nand2tetris/projects/7/VMTranslator/src/utils/asm/RAM_R13_eq_RAM_SP.asm new file mode 100644 index 0000000..ad65028 --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/src/utils/asm/RAM_R13_eq_RAM_SP.asm @@ -0,0 +1,11 @@ +// RAM[R13] <- RAM[SP] + +// D = RAM[SP] +@SP +A = M +D = M + +// RAM[13] <- D +@R13 +A = M +M = D \ No newline at end of file diff --git a/nand2tetris/projects/7/VMTranslator/src/utils/asm/RAM_SP_eq_D.asm b/nand2tetris/projects/7/VMTranslator/src/utils/asm/RAM_SP_eq_D.asm new file mode 100644 index 0000000..cfd692d --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/src/utils/asm/RAM_SP_eq_D.asm @@ -0,0 +1,5 @@ +// RAM[SP] = D + +@SP +A = M +M = D \ No newline at end of file diff --git a/nand2tetris/projects/7/VMTranslator/src/utils/asm/SPmm.asm b/nand2tetris/projects/7/VMTranslator/src/utils/asm/SPmm.asm new file mode 100644 index 0000000..d3ac680 --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/src/utils/asm/SPmm.asm @@ -0,0 +1,3 @@ +// SP-- +@SP +M = M - 1 \ No newline at end of file diff --git a/nand2tetris/projects/7/VMTranslator/src/utils/asm/SPpp.asm b/nand2tetris/projects/7/VMTranslator/src/utils/asm/SPpp.asm new file mode 100644 index 0000000..9bf79cb --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/src/utils/asm/SPpp.asm @@ -0,0 +1,3 @@ +// SP++ +@SP +M = M + 1 \ No newline at end of file diff --git a/nand2tetris/projects/7/VMTranslator/src/utils/asm/arithmetic_1.asm b/nand2tetris/projects/7/VMTranslator/src/utils/asm/arithmetic_1.asm new file mode 100644 index 0000000..7704f7e --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/src/utils/asm/arithmetic_1.asm @@ -0,0 +1,12 @@ +// ---- unary action ---- +// SP-- +// *SP = {action}*SP +// SP++ +@SP +M = M - 1 + +A = M +M = {action}M + +@SP +M = M + 1 \ No newline at end of file diff --git a/nand2tetris/projects/7/VMTranslator/src/utils/asm/arithmetic_2.asm b/nand2tetris/projects/7/VMTranslator/src/utils/asm/arithmetic_2.asm new file mode 100644 index 0000000..9c2cdbf --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/src/utils/asm/arithmetic_2.asm @@ -0,0 +1,30 @@ +// ---- x {action} y ---- +// SP-- +// D = *SP +// SP-- +// *SP = *SP {action} D +// SP++ + +// SP-- +@SP +M = M - 1 + + +// D = *SP +A = M +D = M + +// SP-- +@SP +M = M - 1 + +// Arithmetic operation +// E.g. add: *SP = *SP + D +// E.g. eq: D = *SP - D, D; JEQ +A = M +{action} + +// SP++ +@SP +M = M + 1 + diff --git a/nand2tetris/projects/7/VMTranslator/src/utils/dict/Archive/dict.py b/nand2tetris/projects/7/VMTranslator/src/utils/dict/Archive/dict.py new file mode 100644 index 0000000..6d8e437 --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/src/utils/dict/Archive/dict.py @@ -0,0 +1,23 @@ +import importlib +import importlib.util + +class dictLoader: + + def __init__(self, module_path : str) -> None: + self.module_path = module_path + self.module = self.load_module() + + def load_module(self): + spec = importlib.util.spec_from_file_location("module", self.module_path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + def get_dict(self, dict_name): + if hasattr(self.module, dict_name): + return getattr(self.module, dict_name) + else: + raise AttributeError(f"Dictionary '{dict_name} not found in module '{self.module_path}'") + +loader = dictLoader("nand2tetris/projects/7/VMTranslator/src/utils/dict/dict_commandType.py") +loader.get_dict("dict_commandType") diff --git a/nand2tetris/projects/7/VMTranslator/src/utils/dict/__init__.py b/nand2tetris/projects/7/VMTranslator/src/utils/dict/__init__.py new file mode 100644 index 0000000..3903534 --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/src/utils/dict/__init__.py @@ -0,0 +1 @@ +## This file exists such that .py scripts from here can be imported. \ No newline at end of file diff --git a/nand2tetris/projects/7/VMTranslator/src/utils/dict/dict.py b/nand2tetris/projects/7/VMTranslator/src/utils/dict/dict.py new file mode 100644 index 0000000..930f176 --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/src/utils/dict/dict.py @@ -0,0 +1,82 @@ +commandType = { + "push" : "C_PUSH", + "pop" : "C_POP", + + "" : "C_LABEL", + "" : "C_GOTO", + "" : "C_IF", + "" : "C_FUNCTION", + "" : "C_RETURN", + "" : "C_CALL", + + "add" : "C_ARITHMETIC", + "sub" : "C_ARITHMETIC", + "neg" : "C_ARITHMETIC", + "eq" : "C_ARITHMETIC", + "gt" : "C_ARITHMETIC", + "lt" : "C_ARITHMETIC", + "and" : "C_ARITHMETIC", + "or" : "C_ARITHMETIC", + "not" : "C_ARITHMETIC" +} + +segment = { + "local" : "LCL", + "argument" : "ARG", + "this" : "THIS", + "that" : "THAT", + + "temp" : 5, +} + +arithmetic = { + "neg" : 1, + "not" : 1, + + "add" : 2, + "sub" : 2, + "eq" : 2, + "gt" : 2, + "lt" : 2, + "and" : 2, + "or" : 2, +} + +arithmetic_action = { + "neg" : "-", # M = -M + "not" : "!", # M = !M + + "add" : "M = M + D", + "sub" : "M = M - D", + "eq" : "JEQ", + "gt" : "JGT", + "lt" : "JLT", + "and" : "M = M & D", + "or" : "M = M | D", +} + +compare_action = """ +D = M - D +@TRUE{self.arithmeticNo} +D; {action} +D = 0 +@SP +A = M +M = D +@CONTINUE{self.arithmeticNo} +0; JMP +(TRUE{self.arithmeticNo}) +D = -1 +@SP +A = M +M = D +(CONTINUE{self.arithmeticNo}) +""".strip() + + + +""" + "R13" : 13, + "R14" : 14, + "constant" : "CONST", +""" \ No newline at end of file diff --git a/nand2tetris/projects/7/VMTranslator/tests/conftest.py b/nand2tetris/projects/7/VMTranslator/tests/conftest.py new file mode 100644 index 0000000..e69de29 diff --git a/nand2tetris/projects/7/VMTranslator/tests/test_arithmetic.py b/nand2tetris/projects/7/VMTranslator/tests/test_arithmetic.py new file mode 100644 index 0000000..5c690e7 --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/tests/test_arithmetic.py @@ -0,0 +1,21 @@ + + + + +# import importlib + +# writer_module = importlib.import_module("src.codewriter.codewriter") +from src.codewriter import codewriter as cw +# importlib.reload(cw) + +# importlib.reload(cw) + +testwriter = cw.codewriter("") + +print("---- eq ----") +print(testwriter.writeArithmetic("lt")) + +# print("---- push local 7 ----") +# testwriter.writePushPop("C_PUSH", "constant", 7) + + diff --git a/nand2tetris/projects/7/VMTranslator/tests/test_path.py b/nand2tetris/projects/7/VMTranslator/tests/test_path.py new file mode 100644 index 0000000..1d92730 --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/tests/test_path.py @@ -0,0 +1,6 @@ + + + +import os + +print(os.getcwd()) \ No newline at end of file diff --git a/nand2tetris/projects/7/VMTranslator/tests/test_writePushPop.py b/nand2tetris/projects/7/VMTranslator/tests/test_writePushPop.py new file mode 100644 index 0000000..dd67e7d --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/tests/test_writePushPop.py @@ -0,0 +1,17 @@ + + + + +# import importlib + +# writer_module = importlib.import_module("src.codewriter.codewriter") +from src.codewriter import codewriter as cw +# importlib.reload(cw) + + +testwriter = cw.codewriter("") + +print("---- push local 7 ----") +print(testwriter.writePushPop("C_PUSH", "static", 7)) + + diff --git a/nand2tetris/projects/7/VMTranslator/tests/unittest_writelines.py b/nand2tetris/projects/7/VMTranslator/tests/unittest_writelines.py new file mode 100644 index 0000000..573f25b --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/tests/unittest_writelines.py @@ -0,0 +1,49 @@ +import unittest +import os + +# Import the function to test +from your_module_name import writelines # Replace 'your_module_name' with the actual name of your module + +class TestWriteLines(unittest.TestCase): + def setUp(self): + # Create a temporary file for testing + self.test_filename = "test_file.txt" + + def tearDown(self): + # Remove the temporary file after each test + if os.path.exists(self.test_filename): + os.remove(self.test_filename) + + def test_write_string_to_file(self): + # Test writing a string to a file + input_data = "This is a test string." + writelines(input_data, self.test_filename) + with open(self.test_filename, 'r') as file: + data_written = file.read() + self.assertEqual(data_written, input_data) + + def test_write_list_to_file(self): + # Test writing a list of strings to a file + input_data = ["Line 1\n", "Line 2\n", "Line 3\n"] + writelines(input_data, self.test_filename) + with open(self.test_filename, 'r') as file: + data_written = file.readlines() + self.assertEqual(data_written, input_data) + + def test_invalid_input_data_type(self): + # Test passing invalid input data type + with self.assertRaises(TypeError): + writelines(123, self.test_filename) + + def test_invalid_filename_type(self): + # Test passing invalid filename type + with self.assertRaises(TypeError): + writelines("Test", 123) + + def test_empty_filename(self): + # Test passing an empty filename + with self.assertRaises(ValueError): + writelines("Test", "") + +if __name__ == '__main__': + unittest.main()