From a5e88b3cb762430fb42ff984d203e4e92af3396a Mon Sep 17 00:00:00 2001 From: ATherkel Date: Tue, 7 May 2024 12:16:46 +0200 Subject: [PATCH 01/93] Create VMTranslator.py Initial script --- nand2tetris/projects/7/VMTranslator.py | 36 ++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 nand2tetris/projects/7/VMTranslator.py diff --git a/nand2tetris/projects/7/VMTranslator.py b/nand2tetris/projects/7/VMTranslator.py new file mode 100644 index 0000000..3d2a86a --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator.py @@ -0,0 +1,36 @@ +## How program is supposed to be run: +# https://www.coursera.org/learn/nand2tetris2/lecture/qmJl3/unit-1-8-vm-translator-proposed-implementation + + + + + +## Read lines + +filename = '/nand2tetris/projects/7/StackArithmetic/SimpleAdd/SimpleAdd.vm' + +file = open(filename, 'r') +Lines = file.readlines() + + + + + +## ---- VM language: ---- + +## Arithmetic / Logical commands + +# add +# sub +# neg +# eq +# gt +# lt +# and +# or +# not + +## Memory access commands + +# pop +# push \ No newline at end of file From 82cab9efc4a610858febfa082e972ceb1bffa3dd Mon Sep 17 00:00:00 2001 From: ATherkel Date: Tue, 7 May 2024 15:52:46 +0200 Subject: [PATCH 02/93] Test stuff for VMTranslator --- nand2tetris/projects/7/VMTranslator.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/nand2tetris/projects/7/VMTranslator.py b/nand2tetris/projects/7/VMTranslator.py index 3d2a86a..64ab62e 100644 --- a/nand2tetris/projects/7/VMTranslator.py +++ b/nand2tetris/projects/7/VMTranslator.py @@ -1,6 +1,12 @@ ## How program is supposed to be run: # https://www.coursera.org/learn/nand2tetris2/lecture/qmJl3/unit-1-8-vm-translator-proposed-implementation +## Test stuff START + +import os + +os.getcwd() +## Test stuff END From e909c250c43217f8045f3d8cae9a2f7f7c1c17c6 Mon Sep 17 00:00:00 2001 From: Therkel Date: Tue, 7 May 2024 19:30:50 +0200 Subject: [PATCH 03/93] readlines function (method) created --- nand2tetris/projects/7/VMTranslator.py | 45 ++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/nand2tetris/projects/7/VMTranslator.py b/nand2tetris/projects/7/VMTranslator.py index 64ab62e..0646d52 100644 --- a/nand2tetris/projects/7/VMTranslator.py +++ b/nand2tetris/projects/7/VMTranslator.py @@ -1,22 +1,53 @@ ## How 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 + +# Test packages +import os ## Testing purposes + + ## Test stuff START -import os +# os.getcwd() -os.getcwd() -## Test stuff END + +# filename = 'nand2tetris/projects/7/StackArithmetic/SimpleAdd/SimpleAdd.vm' +## Test stuff END -## Read lines -filename = '/nand2tetris/projects/7/StackArithmetic/SimpleAdd/SimpleAdd.vm' -file = open(filename, 'r') -Lines = file.readlines() +## Read lines method +def readlines(filename : str, comment = "//"): + """ +Returns a list of the lines from an input filename. Removes comments and blank lines. + +Parameters +---- +filename : str + File path and file name of file to be read. +comment : str + Comment identifier. + """ + import re + file = open(filename, 'r') + lines = file.readlines() + + ## Remove comments + re_comment = re.escape(comment) + # print(re_comment) + lines_nocomment = [re.sub(r'(\s*'+re_comment+'.*)','',line) for line in lines] + # print(lines_nocomment) + ## Remove empty lines + lines_filtered = [line for line in lines_nocomment if line.strip()] + # print(lines_filtered) + return lines_filtered From 0d95b17e2c767cb12ca7e78dad1c58dd96bdc33f Mon Sep 17 00:00:00 2001 From: Therkel Date: Tue, 7 May 2024 20:58:19 +0200 Subject: [PATCH 04/93] Prepare classes --- nand2tetris/projects/7/VMTranslator.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/nand2tetris/projects/7/VMTranslator.py b/nand2tetris/projects/7/VMTranslator.py index 0646d52..8b7beba 100644 --- a/nand2tetris/projects/7/VMTranslator.py +++ b/nand2tetris/projects/7/VMTranslator.py @@ -1,4 +1,4 @@ -## How program is supposed to be run: +## How the program is supposed to be run: # https://www.coursera.org/learn/nand2tetris2/lecture/qmJl3/unit-1-8-vm-translator-proposed-implementation #### ---- import ---- @@ -21,8 +21,6 @@ ## Test stuff END - - ## Read lines method def readlines(filename : str, comment = "//"): """ @@ -50,6 +48,16 @@ def readlines(filename : str, comment = "//"): return lines_filtered +class parser: + """ parses each VM command into its lexical elements. """ + +class codewriter: + """ writes the assembly code that + implements the parsed command """ + + +class main: + """ drives the VMTranslator process. """ From a9a9d33b9409a5e4b7a1cd95c7a82d5fd54ecafe Mon Sep 17 00:00:00 2001 From: Therkel Date: Tue, 7 May 2024 21:02:06 +0200 Subject: [PATCH 05/93] Create Resources.md --- nand2tetris/projects/7/Resources.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 nand2tetris/projects/7/Resources.md diff --git a/nand2tetris/projects/7/Resources.md b/nand2tetris/projects/7/Resources.md new file mode 100644 index 0000000..b8aca14 --- /dev/null +++ b/nand2tetris/projects/7/Resources.md @@ -0,0 +1 @@ + \ No newline at end of file From d86107080c6c23cd6e0a3e122dab3144f22abfb7 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Tue, 7 May 2024 22:19:21 +0200 Subject: [PATCH 06/93] Update Resources.md Add documentation on code from gpt --- nand2tetris/projects/7/Resources.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/nand2tetris/projects/7/Resources.md b/nand2tetris/projects/7/Resources.md index b8aca14..d02ffab 100644 --- a/nand2tetris/projects/7/Resources.md +++ b/nand2tetris/projects/7/Resources.md @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file From 0a56b2b38ada1ac0797cef4749f39bb59d10a32d Mon Sep 17 00:00:00 2001 From: ATherkel Date: Tue, 7 May 2024 23:05:29 +0200 Subject: [PATCH 07/93] Create read/write functions. 1. readlines 2. writelines 3. removeComments the three above should be finished with this commit. Minor work to `main`. --- nand2tetris/projects/7/VMTranslator.py | 155 +++++++++++++++++++------ 1 file changed, 121 insertions(+), 34 deletions(-) diff --git a/nand2tetris/projects/7/VMTranslator.py b/nand2tetris/projects/7/VMTranslator.py index 8b7beba..712324b 100644 --- a/nand2tetris/projects/7/VMTranslator.py +++ b/nand2tetris/projects/7/VMTranslator.py @@ -5,6 +5,7 @@ # https://stackoverflow.com/a/37867717/3560695 import re +import sys # Test packages import os ## Testing purposes @@ -22,60 +23,146 @@ ## Read lines method -def readlines(filename : str, comment = "//"): +def readlines(filename : str): """ -Returns a list of the lines from an input filename. Removes comments and blank lines. +Returns a list of the lines from an input filename. Parameters ---- filename : str File path and file name of file to be read. -comment : str - Comment identifier. """ - import re file = open(filename, 'r') lines = file.readlines() - - ## Remove comments + # print(lines) + return lines + +def removeComments(input_data : str|list, comment : str = "//") -> str|list: + """ Returns the input stripped for comments. + If the first argument is a list, iterate over all elements. + Does not handle multiline comments. + + Parameters + ---- + input_data : str or list of str + Input string or list of strings. + comment : str, optional + Comment token. Defaults to "//". Everything after this token is removed. + """ + # Compile the regular expression pattern re_comment = re.escape(comment) - # print(re_comment) - lines_nocomment = [re.sub(r'(\s*'+re_comment+'.*)','',line) for line in lines] - # print(lines_nocomment) - ## Remove empty lines - lines_filtered = [line for line in lines_nocomment if line.strip()] - # print(lines_filtered) - return lines_filtered + regex = r'\s*' + re_comment + '.*' + + # Initialize output variable + output = None + + if isinstance(input_data, str): + output = re.sub(regex, '', input_data) + elif isinstance(input_data, list): + data_nocomment = [re.sub(regex, '', line) for line in input_data] + ## Remove empty lines + output = [line for line in data_nocomment if line.strip()] + else: + raise ValueError("input_data must be a string or list of strings.") + return output + +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)) -class parser: + +def parser(): """ parses each VM command into its lexical elements. """ -class codewriter: - """ writes the assembly code that - implements the parsed command """ + ## ---- VM language: ---- + + ## Arithmetic / Logical commands + + # add + # sub + # neg + # eq + # gt + # lt + # and + # or + # not + + ## Memory access commands + + # pop + # push + + +def codewriter(): + """ writes the assembly code that implements the parsed command. """ + + + + + +def main(filename : str = None): + """ + +""" + # Check if the correct number of command-line arguments are provided + if filename is None: + if len(sys.argv) != 2: + print("Usage: py VMTranslator.py ") + return + filename = sys.argv[1] + # Read file + lines = readlines(filename) + # Remove comments + lines_nocomment = removeComments(lines) -class main: - """ drives the VMTranslator process. """ + # Call functions from different modules to perform the main tasks + parsed_data = parser(filename) + translated_data = codewriter(parsed_data) + ## This should write to filename.asm + writelines(translated_data, ) + return translated_data -## ---- VM language: ---- -## Arithmetic / Logical commands +if __name__ == "__main__": + main() -# add -# sub -# neg -# eq -# gt -# lt -# and -# or -# not -## Memory access commands -# pop -# push \ No newline at end of file From d70c71674ae8c69863c0510cbbcffcf25ab16858 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Tue, 7 May 2024 23:09:01 +0200 Subject: [PATCH 08/93] Unittest for writelines --- nand2tetris/projects/7/unittest_writelines.py | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 nand2tetris/projects/7/unittest_writelines.py diff --git a/nand2tetris/projects/7/unittest_writelines.py b/nand2tetris/projects/7/unittest_writelines.py new file mode 100644 index 0000000..573f25b --- /dev/null +++ b/nand2tetris/projects/7/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() From eb11ea4c9f296de60fadbe14dfdc37577d406160 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Tue, 7 May 2024 23:10:32 +0200 Subject: [PATCH 09/93] Add regex101 explanation --- nand2tetris/projects/7/VMTranslator.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nand2tetris/projects/7/VMTranslator.py b/nand2tetris/projects/7/VMTranslator.py index 712324b..e2f97a4 100644 --- a/nand2tetris/projects/7/VMTranslator.py +++ b/nand2tetris/projects/7/VMTranslator.py @@ -51,6 +51,7 @@ def removeComments(input_data : str|list, comment : str = "//") -> str|list: """ # Compile the regular expression pattern re_comment = re.escape(comment) + # https://regex101.com/r/BH3D67/1 regex = r'\s*' + re_comment + '.*' # Initialize output variable From 1a3232463cc15754133b11e716a80f56a00f1c11 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Wed, 8 May 2024 12:09:32 +0200 Subject: [PATCH 10/93] Create changeExtension method --- nand2tetris/projects/7/VMTranslator.py | 33 ++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/nand2tetris/projects/7/VMTranslator.py b/nand2tetris/projects/7/VMTranslator.py index e2f97a4..038dbae 100644 --- a/nand2tetris/projects/7/VMTranslator.py +++ b/nand2tetris/projects/7/VMTranslator.py @@ -37,7 +37,7 @@ def readlines(filename : str): # print(lines) return lines -def removeComments(input_data : str|list, comment : str = "//") -> str|list: +def removeComments(input_data : str | list, comment : str = "//") -> str | list: """ Returns the input stripped for comments. If the first argument is a list, iterate over all elements. Does not handle multiline comments. @@ -67,6 +67,32 @@ def removeComments(input_data : str|list, comment : str = "//") -> str|list: raise ValueError("input_data must be a string or list of strings.") return output +# 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. @@ -134,8 +160,6 @@ def codewriter(): - - def main(filename : str = None): """ @@ -156,7 +180,8 @@ def main(filename : str = None): parsed_data = parser(filename) translated_data = codewriter(parsed_data) - ## This should write to filename.asm + # https://regex101.com/r/SkENd5/1 + writelines(translated_data, ) return translated_data From f633ce99f06deefb148a4a717d423b53695aa575 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Wed, 8 May 2024 12:16:11 +0200 Subject: [PATCH 11/93] Development on `main` --- nand2tetris/projects/7/VMTranslator.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/nand2tetris/projects/7/VMTranslator.py b/nand2tetris/projects/7/VMTranslator.py index 038dbae..9c16867 100644 --- a/nand2tetris/projects/7/VMTranslator.py +++ b/nand2tetris/projects/7/VMTranslator.py @@ -181,8 +181,9 @@ def main(filename : str = None): translated_data = codewriter(parsed_data) # https://regex101.com/r/SkENd5/1 - - writelines(translated_data, ) + + out_filename = changeExtension(filename, "asm") + writelines(input_data = translated_data, filename = out_filename) return translated_data From fdcf5c1a9506d3aedf376b70a69e106c7d18ca34 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Wed, 8 May 2024 12:44:45 +0200 Subject: [PATCH 12/93] Following unit 1.8 for parser --- nand2tetris/projects/7/VMTranslator.py | 67 ++++++++++++++++++++++---- 1 file changed, 58 insertions(+), 9 deletions(-) diff --git a/nand2tetris/projects/7/VMTranslator.py b/nand2tetris/projects/7/VMTranslator.py index 9c16867..44d4f77 100644 --- a/nand2tetris/projects/7/VMTranslator.py +++ b/nand2tetris/projects/7/VMTranslator.py @@ -16,8 +16,6 @@ # os.getcwd() -# filename = 'nand2tetris/projects/7/StackArithmetic/SimpleAdd/SimpleAdd.vm' - ## Test stuff END @@ -132,9 +130,40 @@ def writelines(input_data : str | list, filename) -> None: -def parser(): + +def parser(a): """ parses each VM command into its lexical elements. """ + ## ---- Constructor ---- + # Read file + lines = readlines(filename) + # Remove comments + lines_nocomment = removeComments(lines) + + + def commandType() -> str: + """ + Returns a constant representing the type of the current command. + C_ARITHMETIC is returned for all the arithmetic/logical commands. + + Returns C_ARITHMETIC, C_PUSH, C_POP, + C_LABEL, C_GOTO, C_IF, C_FUNCTION, + C_RETURN, C_CALL + """ + ... + + def arg1() -> str: + """ + + Returns string + """ + ... + def arg2() -> int: + """ + + """ + ... + ## ---- VM language: ---- ## Arithmetic / Logical commands @@ -155,11 +184,32 @@ def parser(): # push -def codewriter(): +def codewriter(a): """ writes the assembly code that implements the parsed command. """ + + + + + + + + + + + + + + + + + + + + + def main(filename : str = None): """ @@ -171,15 +221,13 @@ def main(filename : str = None): return filename = sys.argv[1] - # Read file - lines = readlines(filename) - # Remove comments - lines_nocomment = removeComments(lines) + # Call functions from different modules to perform the main tasks parsed_data = parser(filename) translated_data = codewriter(parsed_data) + translated_data = lines_nocomment # https://regex101.com/r/SkENd5/1 out_filename = changeExtension(filename, "asm") @@ -192,4 +240,5 @@ def main(filename : str = None): main() - +filename = 'nand2tetris/projects/7/StackArithmetic/SimpleAdd/SimpleAdd.vm' +main(filename) From 481ea3246dd26ad3f79e568ef500cbdd1ae17e9a Mon Sep 17 00:00:00 2001 From: ATherkel Date: Thu, 9 May 2024 10:11:34 +0200 Subject: [PATCH 13/93] Prepare codeWriter --- nand2tetris/projects/7/VMTranslator.py | 35 +++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/nand2tetris/projects/7/VMTranslator.py b/nand2tetris/projects/7/VMTranslator.py index 44d4f77..6353c7d 100644 --- a/nand2tetris/projects/7/VMTranslator.py +++ b/nand2tetris/projects/7/VMTranslator.py @@ -160,7 +160,7 @@ def arg1() -> str: ... def arg2() -> int: """ - + """ ... @@ -186,7 +186,40 @@ def arg2() -> int: def codewriter(a): """ writes the assembly code that implements the parsed command. """ + def constructor(): + """ + Arguments + Output file / stream + Function + Opens the output file / stream and gets ready to write into it. + """ + def writeArithmetic(): + """ + Arguments + command (string) + Function + Writes to the output file the assembly code that implements the given arithmetic command. + """ + def writePushPop(command, segment : str, index : int): + """ + Arguments + ---- + command (C_PUSH or C_POP) + segment : str + index : int + Function + ---- + Writes to the output file the assembly code that implements the given command, + where command is either C_PUSH or C_POP. + """ + def close(): + """ + Function + ---- + Closes the output file. + Likely redundant for my method. + """ From ea7410393dbfb33bc7cbaf7de96630ff32775a10 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Sun, 12 May 2024 15:20:51 +0200 Subject: [PATCH 14/93] Create translation scripts. These files contain the VM to hack translation that from these files just need to be prepared by the Python script. --- nand2tetris/projects/7/add.asm | 26 ++++++++++++++++++++++++++ nand2tetris/projects/7/popSegment.asm | 25 +++++++++++++++++++++++++ nand2tetris/projects/7/pushStatic.asm | 2 ++ 3 files changed, 53 insertions(+) create mode 100644 nand2tetris/projects/7/add.asm create mode 100644 nand2tetris/projects/7/popSegment.asm create mode 100644 nand2tetris/projects/7/pushStatic.asm diff --git a/nand2tetris/projects/7/add.asm b/nand2tetris/projects/7/add.asm new file mode 100644 index 0000000..d508682 --- /dev/null +++ b/nand2tetris/projects/7/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/popSegment.asm b/nand2tetris/projects/7/popSegment.asm new file mode 100644 index 0000000..7502441 --- /dev/null +++ b/nand2tetris/projects/7/popSegment.asm @@ -0,0 +1,25 @@ +// ---- pop segment i +// addr = segment + i, SP--, *addr = *SP +// addr <- segmentPointer + i +@i +D = A +@segment +D = D + M // D = segment + i + +// SP-- +@SP +M = M - 1 + +// RAM[addr] <- RAM[SP] +// Store D in THIS +// i.e. *THIS = addr +@THIS +M = D +// D = RAM[SP] +@SP +A = M +D = M +// *THIS +@THIS +A = M +M = D \ No newline at end of file diff --git a/nand2tetris/projects/7/pushStatic.asm b/nand2tetris/projects/7/pushStatic.asm new file mode 100644 index 0000000..e446c63 --- /dev/null +++ b/nand2tetris/projects/7/pushStatic.asm @@ -0,0 +1,2 @@ +// push static i + From cc3db87e3cdce9d9d6c8c6bc79afbe95ed1ad65f Mon Sep 17 00:00:00 2001 From: ATherkel Date: Sun, 12 May 2024 15:24:01 +0200 Subject: [PATCH 15/93] Minor work --- nand2tetris/projects/7/VMTranslator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nand2tetris/projects/7/VMTranslator.py b/nand2tetris/projects/7/VMTranslator.py index 6353c7d..efcbb0d 100644 --- a/nand2tetris/projects/7/VMTranslator.py +++ b/nand2tetris/projects/7/VMTranslator.py @@ -260,7 +260,7 @@ def main(filename : str = None): parsed_data = parser(filename) translated_data = codewriter(parsed_data) - translated_data = lines_nocomment + translated_data = parsed_data # https://regex101.com/r/SkENd5/1 out_filename = changeExtension(filename, "asm") From 63fba166296e6e4d99b66388ab55d0fff35ca0a2 Mon Sep 17 00:00:00 2001 From: Therkel Date: Sun, 12 May 2024 20:25:07 +0200 Subject: [PATCH 16/93] pop newline --- nand2tetris/projects/7/popSegment.asm | 1 + 1 file changed, 1 insertion(+) diff --git a/nand2tetris/projects/7/popSegment.asm b/nand2tetris/projects/7/popSegment.asm index 7502441..4b0fbd3 100644 --- a/nand2tetris/projects/7/popSegment.asm +++ b/nand2tetris/projects/7/popSegment.asm @@ -1,5 +1,6 @@ // ---- pop segment i // addr = segment + i, SP--, *addr = *SP + // addr <- segmentPointer + i @i D = A From 0b8dd80770f0fbeedbf53a177821378020885225 Mon Sep 17 00:00:00 2001 From: Therkel Date: Sun, 12 May 2024 20:25:19 +0200 Subject: [PATCH 17/93] Create pushSegment.asm --- nand2tetris/projects/7/pushSegment.asm | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 nand2tetris/projects/7/pushSegment.asm diff --git a/nand2tetris/projects/7/pushSegment.asm b/nand2tetris/projects/7/pushSegment.asm new file mode 100644 index 0000000..e1e31eb --- /dev/null +++ b/nand2tetris/projects/7/pushSegment.asm @@ -0,0 +1,19 @@ +// ---- push segment i +// addr = segment + i, *SP = *addr, SP++ + + + +// addr <- SegmentPointer + i +@i +D = A +@segment +D = D + M + +// RAM[SP] = RAM[addr] +@SP +A = M +M = D + +// SP++ +@SP +M = M + 1 \ No newline at end of file From cd66f4910e4c590ec77a310ebf9adb6e644cee1e Mon Sep 17 00:00:00 2001 From: Therkel Date: Sun, 12 May 2024 20:25:56 +0200 Subject: [PATCH 18/93] Move functions to module --- nand2tetris/projects/7/Functions/__init__.py | 1 + .../projects/7/Functions/codewriter.py | 37 ++++ nand2tetris/projects/7/Functions/parser.py | 182 ++++++++++++++++++ nand2tetris/projects/7/VMTranslator.py | 143 +------------- 4 files changed, 226 insertions(+), 137 deletions(-) create mode 100644 nand2tetris/projects/7/Functions/__init__.py create mode 100644 nand2tetris/projects/7/Functions/codewriter.py create mode 100644 nand2tetris/projects/7/Functions/parser.py diff --git a/nand2tetris/projects/7/Functions/__init__.py b/nand2tetris/projects/7/Functions/__init__.py new file mode 100644 index 0000000..3903534 --- /dev/null +++ b/nand2tetris/projects/7/Functions/__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/Functions/codewriter.py b/nand2tetris/projects/7/Functions/codewriter.py new file mode 100644 index 0000000..663ebf7 --- /dev/null +++ b/nand2tetris/projects/7/Functions/codewriter.py @@ -0,0 +1,37 @@ + +def codewriter(a): + """ writes the assembly code that implements the parsed command. """ + def constructor(): + """ + Arguments + Output file / stream + Function + Opens the output file / stream and gets ready to write into it. + """ + def writeArithmetic(): + """ + Arguments + command (string) + Function + Writes to the output file the assembly code that implements the given arithmetic command. + """ + def writePushPop(command, segment : str, index : int): + """ + Arguments + ---- + command (C_PUSH or C_POP) + segment : str + index : int + Function + ---- + Writes to the output file the assembly code that implements the given command, + where command is either C_PUSH or C_POP. + """ + def close(): + """ + Function + ---- + Closes the output file. + + Likely redundant for my method. + """ diff --git a/nand2tetris/projects/7/Functions/parser.py b/nand2tetris/projects/7/Functions/parser.py new file mode 100644 index 0000000..d32b495 --- /dev/null +++ b/nand2tetris/projects/7/Functions/parser.py @@ -0,0 +1,182 @@ +import re + + + +def parser(filename : str): + """ + Parses each VM command into its lexical elements. + + Arguments + ---- + filename : str - A string containing the path to the VM file. """ + + ## ---- Constructor ---- + # Read file + lines = readlines(filename) + # Remove comments + lines_nocomment = removeComments(lines) + + + def Constructor(filename : str) -> None: + """ + Arguments + ---- + Input file / stream + + Returns + ---- + N/A + + Function + ---- + Opens the input file/stream and gets ready to parse it. + """ + ... + + + def hasMoreCommands() -> bool: + """ + Arguments + ---- + N/A + + Returns + ---- + bool + + 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. + """ + + + def commandType() -> 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. + + """ + ... + + def arg1() -> str: + """ + Arguments + ---- + N/A + + 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. + """ + ... + + def arg2() -> int: + """ + Arguments + ---- + N/A + + 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. + """ + ... + + ## ---- VM language: ---- + + ## Arithmetic / Logical commands + + # add + # sub + # neg + # eq + # gt + # lt + # and + # or + # not + + ## Memory access commands + + # pop + # push + + + +## ---- readlines ---- + +## Read lines method +def readlines(filename : str): + """ +Returns a list of the lines from an input filename. + +Parameters +---- +filename : str + File path and file name of file to be read. + """ + file = open(filename, 'r') + lines = file.readlines() + # print(lines) + return lines + + + + +## ---- removeComments ---- + +def removeComments(input_data : str | list, comment : str = "//") -> str | list: + """ Returns the input stripped for comments. + If the first argument is a list, iterate over all elements. + Does not handle multiline comments. + + Parameters + ---- + input_data : str or list of str + Input string or list of strings. + comment : str, optional + Comment token. Defaults to "//". Everything after this token is removed. + """ + # Compile the regular expression pattern + re_comment = re.escape(comment) + # https://regex101.com/r/BH3D67/1 + regex = r'\s*' + re_comment + '.*' + + # Initialize output variable + output = None + + if isinstance(input_data, str): + output = re.sub(regex, '', input_data) + elif isinstance(input_data, list): + data_nocomment = [re.sub(regex, '', line) for line in input_data] + ## Remove empty lines + output = [line for line in data_nocomment if line.strip()] + else: + raise ValueError("input_data must be a string or list of strings.") + return output \ No newline at end of file diff --git a/nand2tetris/projects/7/VMTranslator.py b/nand2tetris/projects/7/VMTranslator.py index efcbb0d..9dbfa05 100644 --- a/nand2tetris/projects/7/VMTranslator.py +++ b/nand2tetris/projects/7/VMTranslator.py @@ -7,6 +7,10 @@ import re import sys +## Functions +import Functions as f + + # Test packages import os ## Testing purposes @@ -20,50 +24,6 @@ ## Test stuff END -## Read lines method -def readlines(filename : str): - """ -Returns a list of the lines from an input filename. - -Parameters ----- -filename : str - File path and file name of file to be read. - """ - file = open(filename, 'r') - lines = file.readlines() - # print(lines) - return lines - -def removeComments(input_data : str | list, comment : str = "//") -> str | list: - """ Returns the input stripped for comments. - If the first argument is a list, iterate over all elements. - Does not handle multiline comments. - - Parameters - ---- - input_data : str or list of str - Input string or list of strings. - comment : str, optional - Comment token. Defaults to "//". Everything after this token is removed. - """ - # Compile the regular expression pattern - re_comment = re.escape(comment) - # https://regex101.com/r/BH3D67/1 - regex = r'\s*' + re_comment + '.*' - - # Initialize output variable - output = None - - if isinstance(input_data, str): - output = re.sub(regex, '', input_data) - elif isinstance(input_data, list): - data_nocomment = [re.sub(regex, '', line) for line in input_data] - ## Remove empty lines - output = [line for line in data_nocomment if line.strip()] - else: - raise ValueError("input_data must be a string or list of strings.") - return output # https://chat.openai.com/share/40c7964c-0f2f-44d2-81c7-e88d6a1868a1 def changeExtension(string : str, newExtension : str): @@ -131,96 +91,6 @@ def writelines(input_data : str | list, filename) -> None: -def parser(a): - """ parses each VM command into its lexical elements. """ - - ## ---- Constructor ---- - # Read file - lines = readlines(filename) - # Remove comments - lines_nocomment = removeComments(lines) - - - def commandType() -> str: - """ - Returns a constant representing the type of the current command. - C_ARITHMETIC is returned for all the arithmetic/logical commands. - - Returns C_ARITHMETIC, C_PUSH, C_POP, - C_LABEL, C_GOTO, C_IF, C_FUNCTION, - C_RETURN, C_CALL - """ - ... - - def arg1() -> str: - """ - - Returns string - """ - ... - def arg2() -> int: - """ - - """ - ... - - ## ---- VM language: ---- - - ## Arithmetic / Logical commands - - # add - # sub - # neg - # eq - # gt - # lt - # and - # or - # not - - ## Memory access commands - - # pop - # push - - -def codewriter(a): - """ writes the assembly code that implements the parsed command. """ - def constructor(): - """ - Arguments - Output file / stream - Function - Opens the output file / stream and gets ready to write into it. - """ - def writeArithmetic(): - """ - Arguments - command (string) - Function - Writes to the output file the assembly code that implements the given arithmetic command. - """ - def writePushPop(command, segment : str, index : int): - """ - Arguments - ---- - command (C_PUSH or C_POP) - segment : str - index : int - Function - ---- - Writes to the output file the assembly code that implements the given command, - where command is either C_PUSH or C_POP. - """ - def close(): - """ - Function - ---- - Closes the output file. - - Likely redundant for my method. - """ - @@ -257,10 +127,9 @@ def main(filename : str = None): # Call functions from different modules to perform the main tasks - parsed_data = parser(filename) - translated_data = codewriter(parsed_data) + parsed_data = f.parser(filename) + translated_data = f.codewriter(parsed_data) - translated_data = parsed_data # https://regex101.com/r/SkENd5/1 out_filename = changeExtension(filename, "asm") From 758df9fe15c1a51cfdbb9b4937b5a225cc932648 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Sun, 12 May 2024 23:10:48 +0200 Subject: [PATCH 19/93] Create dictionary module --- nand2tetris/projects/7/dict/__init__.py | 1 + nand2tetris/projects/7/dict/dict.py | 7 +++++++ 2 files changed, 8 insertions(+) create mode 100644 nand2tetris/projects/7/dict/__init__.py create mode 100644 nand2tetris/projects/7/dict/dict.py diff --git a/nand2tetris/projects/7/dict/__init__.py b/nand2tetris/projects/7/dict/__init__.py new file mode 100644 index 0000000..3903534 --- /dev/null +++ b/nand2tetris/projects/7/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/dict/dict.py b/nand2tetris/projects/7/dict/dict.py new file mode 100644 index 0000000..ef86870 --- /dev/null +++ b/nand2tetris/projects/7/dict/dict.py @@ -0,0 +1,7 @@ +segment = dict = { + "local" : "LCL", + "argument" : "ARG", + "THIS" : "THIS", + "THAT" : "THAT", + +} \ No newline at end of file From 00c507a3949250403e7bc54f2f4e8a92dbf04a80 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Sun, 12 May 2024 23:11:29 +0200 Subject: [PATCH 20/93] Function and dictionary prep --- nand2tetris/projects/7/Functions/parser.py | 1 + nand2tetris/projects/7/VMTranslator.py | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/nand2tetris/projects/7/Functions/parser.py b/nand2tetris/projects/7/Functions/parser.py index d32b495..1063337 100644 --- a/nand2tetris/projects/7/Functions/parser.py +++ b/nand2tetris/projects/7/Functions/parser.py @@ -1,5 +1,6 @@ import re +from dict import dict def parser(filename : str): diff --git a/nand2tetris/projects/7/VMTranslator.py b/nand2tetris/projects/7/VMTranslator.py index 9dbfa05..5e36018 100644 --- a/nand2tetris/projects/7/VMTranslator.py +++ b/nand2tetris/projects/7/VMTranslator.py @@ -1,6 +1,11 @@ ## How the program is supposed to be run: # https://www.coursera.org/learn/nand2tetris2/lecture/qmJl3/unit-1-8-vm-translator-proposed-implementation + +## Add this project's location to path +## for module imports. +sys.path.append("nand2tetris/projects/7") + #### ---- import ---- # https://stackoverflow.com/a/37867717/3560695 @@ -12,7 +17,6 @@ # Test packages -import os ## Testing purposes ## Test stuff START From 5a353bc4bcbc02364d2e47de73fb8dde8478c9f2 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Sun, 12 May 2024 23:12:10 +0200 Subject: [PATCH 21/93] Prepare f string translation --- nand2tetris/projects/7/popSegment.asm | 16 ++++++++-------- nand2tetris/projects/7/pushSegment.asm | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/nand2tetris/projects/7/popSegment.asm b/nand2tetris/projects/7/popSegment.asm index 4b0fbd3..c277f7c 100644 --- a/nand2tetris/projects/7/popSegment.asm +++ b/nand2tetris/projects/7/popSegment.asm @@ -1,10 +1,10 @@ -// ---- pop segment i +//-- pop {segment} {index} ---- // addr = segment + i, SP--, *addr = *SP // addr <- segmentPointer + i -@i +@{index} D = A -@segment +@{segmentPointer} D = D + M // D = segment + i // SP-- @@ -12,15 +12,15 @@ D = D + M // D = segment + i M = M - 1 // RAM[addr] <- RAM[SP] -// Store D in THIS -// i.e. *THIS = addr -@THIS +// Store D in R13 +// i.e. *R13 = addr +@R13 M = D // D = RAM[SP] @SP A = M D = M -// *THIS -@THIS +// *R13 +@R13 A = M M = D \ No newline at end of file diff --git a/nand2tetris/projects/7/pushSegment.asm b/nand2tetris/projects/7/pushSegment.asm index e1e31eb..58a418f 100644 --- a/nand2tetris/projects/7/pushSegment.asm +++ b/nand2tetris/projects/7/pushSegment.asm @@ -1,12 +1,12 @@ -// ---- push segment i -// addr = segment + i, *SP = *addr, SP++ +// ---- push {segment} {index} ---- +// addr = segment + i, *SP = *addr, SP++ // addr <- SegmentPointer + i -@i +@{index} D = A -@segment +@{segmentPointer} D = D + M // RAM[SP] = RAM[addr] From 08d1416f701f77b92c867dad743cd197972488f7 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Sun, 12 May 2024 23:12:36 +0200 Subject: [PATCH 22/93] pycache files --- .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 198 bytes .../7/dict/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 193 bytes .../7/dict/__pycache__/dict.cpython-312.pyc | Bin 0 -> 296 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 nand2tetris/projects/7/Functions/__pycache__/__init__.cpython-312.pyc create mode 100644 nand2tetris/projects/7/dict/__pycache__/__init__.cpython-312.pyc create mode 100644 nand2tetris/projects/7/dict/__pycache__/dict.cpython-312.pyc diff --git a/nand2tetris/projects/7/Functions/__pycache__/__init__.cpython-312.pyc b/nand2tetris/projects/7/Functions/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a82836273ee5a7d3ac53c2d2c5ecfd95fea081ec GIT binary patch literal 198 zcmX@j%ge<81Tp-MX|6!}F^Gc>KC=KtrZZGBXfpb(WGG?+@;-yq{7QAUiU}=FEh>(2 zDk@D+EsF8aOLZyAEK7}X$KC=KtrZZGBXfpb(WGG?+@;-yq{7P`PiU}=FEh>(2 zDk@D+EsF8aOLZyAEK7}X$ zH60{+izOv9xnw27XOLaLVw|mFLW@(2iesFLO4Cz|V*K+`U5YZxQe#~5v%#hS#oRMX zJW7*doJuouQk?U13rb6Xf_aH~DMlr!B}JLV`UOS#S*gh-#rozkAj83gUP0wA4x8Nk zl+v73yCPnoWsE>vEDt0;Ff%eTKHw9+At-i(R~U#sa Date: Sun, 12 May 2024 23:16:04 +0200 Subject: [PATCH 23/93] Move `.asm` to subfolder --- nand2tetris/projects/7/{ => asm}/add.asm | 0 nand2tetris/projects/7/{ => asm}/popSegment.asm | 0 nand2tetris/projects/7/{ => asm}/pushSegment.asm | 0 nand2tetris/projects/7/{ => asm}/pushStatic.asm | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename nand2tetris/projects/7/{ => asm}/add.asm (100%) rename nand2tetris/projects/7/{ => asm}/popSegment.asm (100%) rename nand2tetris/projects/7/{ => asm}/pushSegment.asm (100%) rename nand2tetris/projects/7/{ => asm}/pushStatic.asm (100%) diff --git a/nand2tetris/projects/7/add.asm b/nand2tetris/projects/7/asm/add.asm similarity index 100% rename from nand2tetris/projects/7/add.asm rename to nand2tetris/projects/7/asm/add.asm diff --git a/nand2tetris/projects/7/popSegment.asm b/nand2tetris/projects/7/asm/popSegment.asm similarity index 100% rename from nand2tetris/projects/7/popSegment.asm rename to nand2tetris/projects/7/asm/popSegment.asm diff --git a/nand2tetris/projects/7/pushSegment.asm b/nand2tetris/projects/7/asm/pushSegment.asm similarity index 100% rename from nand2tetris/projects/7/pushSegment.asm rename to nand2tetris/projects/7/asm/pushSegment.asm diff --git a/nand2tetris/projects/7/pushStatic.asm b/nand2tetris/projects/7/asm/pushStatic.asm similarity index 100% rename from nand2tetris/projects/7/pushStatic.asm rename to nand2tetris/projects/7/asm/pushStatic.asm From 122b947efa54c807d6b81ad7a5375723f2f3e4be Mon Sep 17 00:00:00 2001 From: ATherkel Date: Mon, 13 May 2024 12:11:09 +0200 Subject: [PATCH 24/93] Restructure project --- .../projects/7/VMTranslator/.gitignore | 0 nand2tetris/projects/7/VMTranslator/README.md | 0 .../7/{ => VMTranslator}/VMTranslator.py | 0 .../7/{ => VMTranslator/docs}/Resources.md | 0 .../projects/7/VMTranslator/requirements.txt | 0 nand2tetris/projects/7/VMTranslator/setup.py | 20 ++++++++++++++++++ .../7/{dict => VMTranslator/src}/__init__.py | 0 .../src}/codewriter.py | 0 .../7/VMTranslator/src/codewriter/__init__.py | 1 + .../{Functions => VMTranslator/src}/parser.py | 0 .../7/VMTranslator/src/parser/__init__.py | 1 + .../7/VMTranslator/src/utils/__init__.py | 1 + .../{ => VMTranslator/src/utils}/asm/add.asm | 0 .../src/utils}/asm/popSegment.asm | 0 .../src/utils}/asm/pushSegment.asm | 0 .../src/utils}/asm/pushStatic.asm | 0 .../src/utils/segment.py} | 0 .../dict/__pycache__/__init__.cpython-312.pyc | Bin 193 -> 0 bytes .../7/dict/__pycache__/dict.cpython-312.pyc | Bin 296 -> 0 bytes 19 files changed, 23 insertions(+) create mode 100644 nand2tetris/projects/7/VMTranslator/.gitignore create mode 100644 nand2tetris/projects/7/VMTranslator/README.md rename nand2tetris/projects/7/{ => VMTranslator}/VMTranslator.py (100%) rename nand2tetris/projects/7/{ => VMTranslator/docs}/Resources.md (100%) create mode 100644 nand2tetris/projects/7/VMTranslator/requirements.txt create mode 100644 nand2tetris/projects/7/VMTranslator/setup.py rename nand2tetris/projects/7/{dict => VMTranslator/src}/__init__.py (100%) rename nand2tetris/projects/7/{Functions => VMTranslator/src}/codewriter.py (100%) create mode 100644 nand2tetris/projects/7/VMTranslator/src/codewriter/__init__.py rename nand2tetris/projects/7/{Functions => VMTranslator/src}/parser.py (100%) create mode 100644 nand2tetris/projects/7/VMTranslator/src/parser/__init__.py create mode 100644 nand2tetris/projects/7/VMTranslator/src/utils/__init__.py rename nand2tetris/projects/7/{ => VMTranslator/src/utils}/asm/add.asm (100%) rename nand2tetris/projects/7/{ => VMTranslator/src/utils}/asm/popSegment.asm (100%) rename nand2tetris/projects/7/{ => VMTranslator/src/utils}/asm/pushSegment.asm (100%) rename nand2tetris/projects/7/{ => VMTranslator/src/utils}/asm/pushStatic.asm (100%) rename nand2tetris/projects/7/{dict/dict.py => VMTranslator/src/utils/segment.py} (100%) delete mode 100644 nand2tetris/projects/7/dict/__pycache__/__init__.cpython-312.pyc delete mode 100644 nand2tetris/projects/7/dict/__pycache__/dict.cpython-312.pyc diff --git a/nand2tetris/projects/7/VMTranslator/.gitignore b/nand2tetris/projects/7/VMTranslator/.gitignore new file mode 100644 index 0000000..e69de29 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.py b/nand2tetris/projects/7/VMTranslator/VMTranslator.py similarity index 100% rename from nand2tetris/projects/7/VMTranslator.py rename to nand2tetris/projects/7/VMTranslator/VMTranslator.py diff --git a/nand2tetris/projects/7/Resources.md b/nand2tetris/projects/7/VMTranslator/docs/Resources.md similarity index 100% rename from nand2tetris/projects/7/Resources.md rename to nand2tetris/projects/7/VMTranslator/docs/Resources.md 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..ad144e7 --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/setup.py @@ -0,0 +1,20 @@ +from setuptools import setup, find_packages + +setup( + name='VMTranslator', + version='0.1', + 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/dict/__init__.py b/nand2tetris/projects/7/VMTranslator/src/__init__.py similarity index 100% rename from nand2tetris/projects/7/dict/__init__.py rename to nand2tetris/projects/7/VMTranslator/src/__init__.py diff --git a/nand2tetris/projects/7/Functions/codewriter.py b/nand2tetris/projects/7/VMTranslator/src/codewriter.py similarity index 100% rename from nand2tetris/projects/7/Functions/codewriter.py rename to nand2tetris/projects/7/VMTranslator/src/codewriter.py 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/Functions/parser.py b/nand2tetris/projects/7/VMTranslator/src/parser.py similarity index 100% rename from nand2tetris/projects/7/Functions/parser.py rename to nand2tetris/projects/7/VMTranslator/src/parser.py 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/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/asm/add.asm b/nand2tetris/projects/7/VMTranslator/src/utils/asm/add.asm similarity index 100% rename from nand2tetris/projects/7/asm/add.asm rename to nand2tetris/projects/7/VMTranslator/src/utils/asm/add.asm diff --git a/nand2tetris/projects/7/asm/popSegment.asm b/nand2tetris/projects/7/VMTranslator/src/utils/asm/popSegment.asm similarity index 100% rename from nand2tetris/projects/7/asm/popSegment.asm rename to nand2tetris/projects/7/VMTranslator/src/utils/asm/popSegment.asm diff --git a/nand2tetris/projects/7/asm/pushSegment.asm b/nand2tetris/projects/7/VMTranslator/src/utils/asm/pushSegment.asm similarity index 100% rename from nand2tetris/projects/7/asm/pushSegment.asm rename to nand2tetris/projects/7/VMTranslator/src/utils/asm/pushSegment.asm diff --git a/nand2tetris/projects/7/asm/pushStatic.asm b/nand2tetris/projects/7/VMTranslator/src/utils/asm/pushStatic.asm similarity index 100% rename from nand2tetris/projects/7/asm/pushStatic.asm rename to nand2tetris/projects/7/VMTranslator/src/utils/asm/pushStatic.asm diff --git a/nand2tetris/projects/7/dict/dict.py b/nand2tetris/projects/7/VMTranslator/src/utils/segment.py similarity index 100% rename from nand2tetris/projects/7/dict/dict.py rename to nand2tetris/projects/7/VMTranslator/src/utils/segment.py diff --git a/nand2tetris/projects/7/dict/__pycache__/__init__.cpython-312.pyc b/nand2tetris/projects/7/dict/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index d5a6020db6422523b86a1c561c32b6a3dcfae4d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 193 zcmX@j%ge<81Tp-MX|6!}F^Gc>KC=KtrZZGBXfpb(WGG?+@;-yq{7P`PiU}=FEh>(2 zDk@D+EsF8aOLZyAEK7}X$ zH60{+izOv9xnw27XOLaLVw|mFLW@(2iesFLO4Cz|V*K+`U5YZxQe#~5v%#hS#oRMX zJW7*doJuouQk?U13rb6Xf_aH~DMlr!B}JLV`UOS#S*gh-#rozkAj83gUP0wA4x8Nk zl+v73yCPnoWsE>vEDt0;Ff%eTKHw9+At-i(R~U#sa Date: Mon, 13 May 2024 12:15:03 +0200 Subject: [PATCH 25/93] Remove redundant files --- nand2tetris/projects/7/Functions/__init__.py | 1 - .../__pycache__/__init__.cpython-312.pyc | Bin 198 -> 0 bytes 2 files changed, 1 deletion(-) delete mode 100644 nand2tetris/projects/7/Functions/__init__.py delete mode 100644 nand2tetris/projects/7/Functions/__pycache__/__init__.cpython-312.pyc diff --git a/nand2tetris/projects/7/Functions/__init__.py b/nand2tetris/projects/7/Functions/__init__.py deleted file mode 100644 index 3903534..0000000 --- a/nand2tetris/projects/7/Functions/__init__.py +++ /dev/null @@ -1 +0,0 @@ -## This file exists such that .py scripts from here can be imported. \ No newline at end of file diff --git a/nand2tetris/projects/7/Functions/__pycache__/__init__.cpython-312.pyc b/nand2tetris/projects/7/Functions/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index a82836273ee5a7d3ac53c2d2c5ecfd95fea081ec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 198 zcmX@j%ge<81Tp-MX|6!}F^Gc>KC=KtrZZGBXfpb(WGG?+@;-yq{7QAUiU}=FEh>(2 zDk@D+EsF8aOLZyAEK7}X$ Date: Mon, 13 May 2024 12:23:24 +0200 Subject: [PATCH 26/93] Restructure part 2 --- nand2tetris/projects/7/VMTranslator/.gitignore | 5 +++++ .../projects/7/VMTranslator/VMTranslator.py | 16 +++++++++------- .../tests}/unittest_writelines.py | 0 3 files changed, 14 insertions(+), 7 deletions(-) rename nand2tetris/projects/7/{ => VMTranslator/tests}/unittest_writelines.py (100%) diff --git a/nand2tetris/projects/7/VMTranslator/.gitignore b/nand2tetris/projects/7/VMTranslator/.gitignore index e69de29..22872b8 100644 --- a/nand2tetris/projects/7/VMTranslator/.gitignore +++ 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/VMTranslator.py b/nand2tetris/projects/7/VMTranslator/VMTranslator.py index 5e36018..b40a047 100644 --- a/nand2tetris/projects/7/VMTranslator/VMTranslator.py +++ b/nand2tetris/projects/7/VMTranslator/VMTranslator.py @@ -2,18 +2,20 @@ # https://www.coursera.org/learn/nand2tetris2/lecture/qmJl3/unit-1-8-vm-translator-proposed-implementation -## Add this project's location to path -## for module imports. -sys.path.append("nand2tetris/projects/7") - #### ---- import ---- # https://stackoverflow.com/a/37867717/3560695 import re import sys + +## Add this project's location to path +## for module imports. +sys.path.append("nand2tetris\projects\7\VMTranslator") + ## Functions -import Functions as f +from src.codewriter import codewriter +from src.parser import parser # Test packages @@ -131,8 +133,8 @@ def main(filename : str = None): # Call functions from different modules to perform the main tasks - parsed_data = f.parser(filename) - translated_data = f.codewriter(parsed_data) + parsed_data = parser(filename) + translated_data = codewriter(parsed_data) # https://regex101.com/r/SkENd5/1 diff --git a/nand2tetris/projects/7/unittest_writelines.py b/nand2tetris/projects/7/VMTranslator/tests/unittest_writelines.py similarity index 100% rename from nand2tetris/projects/7/unittest_writelines.py rename to nand2tetris/projects/7/VMTranslator/tests/unittest_writelines.py From f8596c617c2dbdec0eab8da096379fb267991072 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Mon, 13 May 2024 13:03:25 +0200 Subject: [PATCH 27/93] Rename and parser work --- .../src/{ => codewriter}/codewriter.py | 0 .../7/VMTranslator/src/{ => parser}/parser.py | 57 +++++++++++++------ .../src/{utils => parser}/segment.py | 2 +- 3 files changed, 40 insertions(+), 19 deletions(-) rename nand2tetris/projects/7/VMTranslator/src/{ => codewriter}/codewriter.py (100%) rename nand2tetris/projects/7/VMTranslator/src/{ => parser}/parser.py (72%) rename nand2tetris/projects/7/VMTranslator/src/{utils => parser}/segment.py (83%) diff --git a/nand2tetris/projects/7/VMTranslator/src/codewriter.py b/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py similarity index 100% rename from nand2tetris/projects/7/VMTranslator/src/codewriter.py rename to nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py diff --git a/nand2tetris/projects/7/VMTranslator/src/parser.py b/nand2tetris/projects/7/VMTranslator/src/parser/parser.py similarity index 72% rename from nand2tetris/projects/7/VMTranslator/src/parser.py rename to nand2tetris/projects/7/VMTranslator/src/parser/parser.py index 1063337..5cf9f88 100644 --- a/nand2tetris/projects/7/VMTranslator/src/parser.py +++ b/nand2tetris/projects/7/VMTranslator/src/parser/parser.py @@ -1,9 +1,12 @@ import re -from dict import dict +## Import segment dictionary +from src.parser.segment import * -def parser(filename : str): + + +class parser: """ Parses each VM command into its lexical elements. @@ -11,14 +14,8 @@ def parser(filename : str): ---- filename : str - A string containing the path to the VM file. """ - ## ---- Constructor ---- - # Read file - lines = readlines(filename) - # Remove comments - lines_nocomment = removeComments(lines) - - - def Constructor(filename : str) -> None: + + def __init__(self, filename : str) -> None: """ Arguments ---- @@ -32,10 +29,11 @@ def Constructor(filename : str) -> None: ---- Opens the input file/stream and gets ready to parse it. """ - ... + self.file = open(filename, 'r') + self.current_command = None - def hasMoreCommands() -> bool: + def hasMoreCommands(self) -> bool: """ Arguments ---- @@ -51,9 +49,10 @@ def hasMoreCommands() -> bool: Should be called only if hasMoreCommands() is true. Initially there is no current command. """ + return bool(self.file.peek()) - def commandType() -> str: + def commandType(self) -> str: """ Arguments ---- @@ -70,9 +69,20 @@ def commandType() -> str: C_ARITHMETIC is returned for all the arithmetic/logical commands. """ - ... + if self.current_command.startswith("push"): + return "C_PUSH" + elif self.current_command.startswith("pop"): + return "C_POP" + elif self.current_command in [ + "add", "sub", "neg", + "eq", "gt", "lt", + "and", "or", "not"]: + return "C_ARITHMETIC" + + else: + raise ValueError(f"Current command invalid: Cannot parse '{self.current_command}'") - def arg1() -> str: + def arg1(self) -> str: """ Arguments ---- @@ -89,9 +99,14 @@ def arg1() -> str: is returned. Should not be called if the current command is C_RETURN. """ - ... + if self.commandType() == "C_ARITHMETIC": + out = self.current_command + else: + out = self.current_command.split()[1] - def arg2() -> int: + return out + + def arg2(self) -> int: """ Arguments ---- @@ -107,8 +122,14 @@ def arg2() -> int: 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.current_command.split()[2]) + else: + return ValueError(f"arg2() called on invalid command type: '{self.current_command}'") + def close(self) -> None: + self.file.close() + ## ---- VM language: ---- ## Arithmetic / Logical commands diff --git a/nand2tetris/projects/7/VMTranslator/src/utils/segment.py b/nand2tetris/projects/7/VMTranslator/src/parser/segment.py similarity index 83% rename from nand2tetris/projects/7/VMTranslator/src/utils/segment.py rename to nand2tetris/projects/7/VMTranslator/src/parser/segment.py index ef86870..81ee6be 100644 --- a/nand2tetris/projects/7/VMTranslator/src/utils/segment.py +++ b/nand2tetris/projects/7/VMTranslator/src/parser/segment.py @@ -1,4 +1,4 @@ -segment = dict = { +segment = { "local" : "LCL", "argument" : "ARG", "THIS" : "THIS", From 43645ea94302f60b4c25961ed23abfcccc477ea6 Mon Sep 17 00:00:00 2001 From: Therkel Date: Mon, 13 May 2024 19:15:41 +0200 Subject: [PATCH 28/93] Add Python version requirement --- nand2tetris/projects/7/VMTranslator/setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nand2tetris/projects/7/VMTranslator/setup.py b/nand2tetris/projects/7/VMTranslator/setup.py index ad144e7..6f95d36 100644 --- a/nand2tetris/projects/7/VMTranslator/setup.py +++ b/nand2tetris/projects/7/VMTranslator/setup.py @@ -3,6 +3,7 @@ setup( name='VMTranslator', version='0.1', + python_requires = '3.12.3', packages=find_packages('src'), package_dir={'': 'src'}, install_requires=[ From fdd407289993a841372a1b65cd30bc214ec1b20c Mon Sep 17 00:00:00 2001 From: Therkel Date: Mon, 13 May 2024 19:16:00 +0200 Subject: [PATCH 29/93] Remove dead stuff --- .../projects/7/VMTranslator/VMTranslator.py | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/nand2tetris/projects/7/VMTranslator/VMTranslator.py b/nand2tetris/projects/7/VMTranslator/VMTranslator.py index b40a047..dd9883f 100644 --- a/nand2tetris/projects/7/VMTranslator/VMTranslator.py +++ b/nand2tetris/projects/7/VMTranslator/VMTranslator.py @@ -5,29 +5,35 @@ #### ---- import ---- # https://stackoverflow.com/a/37867717/3560695 -import re -import sys +import importlib.util + +## Import parser +## We have to use import_module since the path contains an illegal directory name (7). +parser_module = importlib.import_module("nand2tetris.projects.7.VMTranslator.src.parser.parser") +Parser = parser_module.parser + + + + +#### ---- main ---- + +#def main(): +parser = Parser("nand2tetris/projects/7/StackArithmetic/SimpleAdd/SimpleAdd.vm") + +parser.hasMoreCommands() + + -## Add this project's location to path -## for module imports. -sys.path.append("nand2tetris\projects\7\VMTranslator") -## Functions -from src.codewriter import codewriter -from src.parser import parser -# Test packages -## Test stuff START -# os.getcwd() -## Test stuff END @@ -119,7 +125,7 @@ def writelines(input_data : str | list, filename) -> None: -def main(filename : str = None): +def main_old(filename : str = None): """ """ From f53b5cdf4b77fc3e3044add1446054171cfa1d66 Mon Sep 17 00:00:00 2001 From: Therkel Date: Mon, 13 May 2024 19:16:32 +0200 Subject: [PATCH 30/93] Add fns to parser class --- .../7/VMTranslator/src/parser/parser.py | 113 ++++++++++-------- 1 file changed, 62 insertions(+), 51 deletions(-) diff --git a/nand2tetris/projects/7/VMTranslator/src/parser/parser.py b/nand2tetris/projects/7/VMTranslator/src/parser/parser.py index 5cf9f88..a888096 100644 --- a/nand2tetris/projects/7/VMTranslator/src/parser/parser.py +++ b/nand2tetris/projects/7/VMTranslator/src/parser/parser.py @@ -1,8 +1,10 @@ import re - +import importlib.util ## Import segment dictionary -from src.parser.segment import * +## We have to use import_module since the path contains an illegal directory name (7). +segment_module = importlib.import_module("nand2tetris.projects.7.VMTranslator.src.parser.segment") +segment = segment_module.segment @@ -14,22 +16,31 @@ class parser: ---- filename : str - A string containing the path to the VM file. """ - def __init__(self, filename : str) -> None: """ Arguments ---- - Input file / stream + filename : str + Input file / stream Returns ---- - N/A + None Function ---- Opens the input file/stream and gets ready to parse it. + + Raises + ---- + FileNotFoundError + If the specified file does not exist. """ - self.file = open(filename, 'r') + try: + file = open(filename, 'r') + self.lines = file.readlines() + except FileNotFoundError: + raise FileNotFoundError(f"File does not exist. Input file string: '{filename}") self.current_command = None @@ -86,7 +97,7 @@ def arg1(self) -> str: """ Arguments ---- - N/A + None Returns ---- @@ -110,7 +121,7 @@ def arg2(self) -> int: """ Arguments ---- - N/A + None Returns ---- @@ -151,54 +162,54 @@ def close(self) -> None: -## ---- readlines ---- + ## ---- readlines ---- -## Read lines method -def readlines(filename : str): - """ -Returns a list of the lines from an input filename. + ## Read lines method + def readlines(filename : str): + """ + Returns a list of the lines from an input filename. -Parameters ----- -filename : str - File path and file name of file to be read. - """ - file = open(filename, 'r') - lines = file.readlines() - # print(lines) - return lines + Parameters + ---- + filename : str + File path and file name of file to be read. + """ + file = open(filename, 'r') + lines = file.readlines() + # print(lines) + return lines -## ---- removeComments ---- + ## ---- removeComments ---- -def removeComments(input_data : str | list, comment : str = "//") -> str | list: - """ Returns the input stripped for comments. - If the first argument is a list, iterate over all elements. - Does not handle multiline comments. + def removeComments(input_data : str | list, comment : str = "//") -> (str | list): + """ Returns the input stripped for comments. + If the first argument is a list, iterate over all elements. + Does not handle multiline comments. - Parameters - ---- - input_data : str or list of str - Input string or list of strings. - comment : str, optional - Comment token. Defaults to "//". Everything after this token is removed. - """ - # Compile the regular expression pattern - re_comment = re.escape(comment) - # https://regex101.com/r/BH3D67/1 - regex = r'\s*' + re_comment + '.*' - - # Initialize output variable - output = None - - if isinstance(input_data, str): - output = re.sub(regex, '', input_data) - elif isinstance(input_data, list): - data_nocomment = [re.sub(regex, '', line) for line in input_data] - ## Remove empty lines - output = [line for line in data_nocomment if line.strip()] - else: - raise ValueError("input_data must be a string or list of strings.") - return output \ No newline at end of file + Arguments + ---- + input_data : str or list of str + Input string or list of strings. + comment : str, optional + Comment token. Defaults to "//". Everything after this token is removed. + """ + # Compile the regular expression pattern + re_comment = re.escape(comment) + # https://regex101.com/r/BH3D67/1 + regex = r'\s*' + re_comment + '.*' + + # Initialize output variable + output = None + + if isinstance(input_data, str): + output = re.sub(regex, '', input_data) + elif isinstance(input_data, list): + data_nocomment = [re.sub(regex, '', line) for line in input_data] + ## Remove empty lines + output = [line for line in data_nocomment if line.strip()] + else: + raise ValueError("input_data must be a string or list of strings.") + return output \ No newline at end of file From 94572de16eb8282fa4b5aabae01e3373c848d981 Mon Sep 17 00:00:00 2001 From: Therkel Date: Mon, 13 May 2024 23:08:42 +0200 Subject: [PATCH 31/93] More parser trying --- .../projects/7/VMTranslator/VMTranslator.py | 1 + .../7/VMTranslator/src/parser/parser.py | 40 +++++++++++++++---- .../7/VMTranslator/src/parser/parser2.py | 37 +++++++++++++++++ 3 files changed, 70 insertions(+), 8 deletions(-) create mode 100644 nand2tetris/projects/7/VMTranslator/src/parser/parser2.py diff --git a/nand2tetris/projects/7/VMTranslator/VMTranslator.py b/nand2tetris/projects/7/VMTranslator/VMTranslator.py index dd9883f..9e02128 100644 --- a/nand2tetris/projects/7/VMTranslator/VMTranslator.py +++ b/nand2tetris/projects/7/VMTranslator/VMTranslator.py @@ -156,3 +156,4 @@ def main_old(filename : str = None): filename = 'nand2tetris/projects/7/StackArithmetic/SimpleAdd/SimpleAdd.vm' main(filename) + diff --git a/nand2tetris/projects/7/VMTranslator/src/parser/parser.py b/nand2tetris/projects/7/VMTranslator/src/parser/parser.py index a888096..00ffeb6 100644 --- a/nand2tetris/projects/7/VMTranslator/src/parser/parser.py +++ b/nand2tetris/projects/7/VMTranslator/src/parser/parser.py @@ -36,14 +36,18 @@ def __init__(self, filename : str) -> None: FileNotFoundError If the specified file does not exist. """ - try: - file = open(filename, 'r') - self.lines = file.readlines() - except FileNotFoundError: - raise FileNotFoundError(f"File does not exist. Input file string: '{filename}") - self.current_command = None + + # We need the filename to write to a similarly named file. + self.filename = filename + # Open the file for reading the lines. + self.file = open(filename, 'r') + + ## Initialize current line + self.current_line = self.file.readline().strip() + self.next_line = self.file.readline().strip() + @property def hasMoreCommands(self) -> bool: """ Arguments @@ -54,14 +58,34 @@ def hasMoreCommands(self) -> bool: ---- bool + Function + ---- + Are there more commands in the input? + """ + return bool(self.instruction) + + def instruction(self, line = None) -> None: + line = line if line is not None else self.filename.readline().strip() + self.instruction = line.split("//")[0].strip().split() + + 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. """ - return bool(self.file.peek()) - + self.current_line = self.next_line + self.next_line = self.file.readline().strip() def commandType(self) -> str: """ diff --git a/nand2tetris/projects/7/VMTranslator/src/parser/parser2.py b/nand2tetris/projects/7/VMTranslator/src/parser/parser2.py new file mode 100644 index 0000000..58be902 --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/src/parser/parser2.py @@ -0,0 +1,37 @@ + +class Parser: + + def __init__(self, filename : str = "") -> None: + """ + Arguments + ---- + filename : str + Input file / stream + + Returns + ---- + None + + Function + ---- + Opens the input file/stream and gets ready to parse it. + + Raises + ---- + FileNotFoundError + If the specified file does not exist. + """ + self.filename = filename + self.file = open(self.filename, 'r') + + def instruction(self, line = None) -> None: + line = line if line is not None else self.filename.readline().strip() + self.instruction = line.split("//")[0].strip().split() + + + +filename = 'nand2tetris/projects/7/StackArithmetic/SimpleAdd/SimpleAdd.vm' +parser = Parser(filename) + +parser.filename[:2] + From 2f22b635caaaf6cd3d19094df0a9666500439d03 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Tue, 14 May 2024 22:32:21 +0200 Subject: [PATCH 32/93] New dictionary handling --- .../7/VMTranslator/src/parser/parser2.py | 6 ++++ .../7/VMTranslator/src/parser/segment.py | 7 ----- .../7/VMTranslator/src/utils/dict/dict.py | 29 +++++++++++++++++++ 3 files changed, 35 insertions(+), 7 deletions(-) delete mode 100644 nand2tetris/projects/7/VMTranslator/src/parser/segment.py create mode 100644 nand2tetris/projects/7/VMTranslator/src/utils/dict/dict.py diff --git a/nand2tetris/projects/7/VMTranslator/src/parser/parser2.py b/nand2tetris/projects/7/VMTranslator/src/parser/parser2.py index 58be902..be14983 100644 --- a/nand2tetris/projects/7/VMTranslator/src/parser/parser2.py +++ b/nand2tetris/projects/7/VMTranslator/src/parser/parser2.py @@ -1,4 +1,10 @@ +import importlib + +dict_filepath = "nand2tetris/projects/7/VMTranslator/src/utils/dict/dict" +dict = importlib.import_module(dict_filepath.replace("/", ".")) + + class Parser: def __init__(self, filename : str = "") -> None: diff --git a/nand2tetris/projects/7/VMTranslator/src/parser/segment.py b/nand2tetris/projects/7/VMTranslator/src/parser/segment.py deleted file mode 100644 index 81ee6be..0000000 --- a/nand2tetris/projects/7/VMTranslator/src/parser/segment.py +++ /dev/null @@ -1,7 +0,0 @@ -segment = { - "local" : "LCL", - "argument" : "ARG", - "THIS" : "THIS", - "THAT" : "THAT", - -} \ 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..6407e95 --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/src/utils/dict/dict.py @@ -0,0 +1,29 @@ +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", + +} From f4e8283e6b4093cc9ba842c9195b846dd50b80c9 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Tue, 14 May 2024 22:32:34 +0200 Subject: [PATCH 33/93] test .asm file with error --- .../7/StackArithmetic/SimpleAdd/SimpleAdd - Kopi.vm | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 nand2tetris/projects/7/StackArithmetic/SimpleAdd/SimpleAdd - Kopi.vm 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 From b98cdff4a64f4a9a401beb105118229e9484165d Mon Sep 17 00:00:00 2001 From: ATherkel Date: Tue, 14 May 2024 22:33:15 +0200 Subject: [PATCH 34/93] New main - and testing --- .../projects/7/VMTranslator/VMTranslator.py | 41 ++++++++++++++++++- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/nand2tetris/projects/7/VMTranslator/VMTranslator.py b/nand2tetris/projects/7/VMTranslator/VMTranslator.py index 9e02128..a5a261b 100644 --- a/nand2tetris/projects/7/VMTranslator/VMTranslator.py +++ b/nand2tetris/projects/7/VMTranslator/VMTranslator.py @@ -6,6 +6,8 @@ # https://stackoverflow.com/a/37867717/3560695 import importlib.util +import sys +import re ## Import parser ## We have to use import_module since the path contains an illegal directory name (7). @@ -18,18 +20,53 @@ #### ---- main ---- #def main(): -parser = Parser("nand2tetris/projects/7/StackArithmetic/SimpleAdd/SimpleAdd.vm") -parser.hasMoreCommands() + +def main(filename): + + ## 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") + + + with open(filename, 'r') as file, open(filename_write, "w"): + parser = Parser(file) + + while parser.hasMoreCommands(): ## As long as file has more lines, do: + parser.advance() ## Go to the next line in the file. + parser.getinstruction() ## sets parser.instruction to current instruction + if not parser.instruction: ## If instruction is blank, skip. Line consisted only of a comment. + continue + print(f"---- Line {parser.lineNo} ----") + print(f"instruction = {parser.instruction}") + print(f"commandType() = {parser.commandType()}") + print(f"arg1() = {parser.arg1()}") + print(f"arg2() = {parser.arg2()}") + + if parser.commandType() == "C_ARITHMETIC": + # Write arithmetic from arithmetic.asm + ... + elif parser.commandType() in ["C_PUSH", "C_POP"]: + # Write push and pop commands from push.asm and pop.asm + ... + else: + ... + +## Testing +filename = '/nand2tetris/projects/7/StackArithmetic/SimpleAdd/SimpleAdd.vm' +main(filename[1:]) +## Below is probably redundant From 128febe8a7096cb49c7ff2b3c6fe432e7b46dd5f Mon Sep 17 00:00:00 2001 From: ATherkel Date: Tue, 14 May 2024 22:33:32 +0200 Subject: [PATCH 35/93] dict __init__ --- nand2tetris/projects/7/VMTranslator/src/utils/dict/__init__.py | 1 + 1 file changed, 1 insertion(+) create mode 100644 nand2tetris/projects/7/VMTranslator/src/utils/dict/__init__.py 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 From cca5e599490f9795f47dff454144b2cee0309e40 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Tue, 14 May 2024 22:34:16 +0200 Subject: [PATCH 36/93] Parser class work --- .../7/VMTranslator/src/parser/parser2.py | 147 ++++++++++++++++-- 1 file changed, 138 insertions(+), 9 deletions(-) diff --git a/nand2tetris/projects/7/VMTranslator/src/parser/parser2.py b/nand2tetris/projects/7/VMTranslator/src/parser/parser2.py index be14983..f5c24ca 100644 --- a/nand2tetris/projects/7/VMTranslator/src/parser/parser2.py +++ b/nand2tetris/projects/7/VMTranslator/src/parser/parser2.py @@ -6,8 +6,16 @@ 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. - def __init__(self, filename : str = "") -> None: + - Ignores all white space and comments. + """ + + def __init__(self, file) -> None: """ Arguments ---- @@ -27,17 +35,138 @@ def __init__(self, filename : str = "") -> None: FileNotFoundError If the specified file does not exist. """ - self.filename = filename - self.file = open(self.filename, 'r') + self.line = None ## Initialize line. + self.lineNo = 0 ## Initialize line number. + self.instruction = None ## Initialize instruction. + 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 dict.commandType[self.instruction[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.instruction[0] + elif self.commandType() == "C_RETURN": + return ValueError(f"arg1() called on invalid command type: '{self.commandType()}'") + else: + out = self.instruction[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.instruction[2]) + else: + return ValueError(f"arg2() called on invalid command type: '{self.commandType()}'") + + def getinstruction(self): + self.instruction = self.line.split("//")[0].strip().split() - def instruction(self, line = None) -> None: - line = line if line is not None else self.filename.readline().strip() - self.instruction = line.split("//")[0].strip().split() -filename = 'nand2tetris/projects/7/StackArithmetic/SimpleAdd/SimpleAdd.vm' -parser = Parser(filename) -parser.filename[:2] From 271881e65127ac3ef74b53ac0701f8fd1cf956e5 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Tue, 14 May 2024 22:34:52 +0200 Subject: [PATCH 37/93] Archived dict class, not used --- .../src/utils/dict/Archive/dict.py | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 nand2tetris/projects/7/VMTranslator/src/utils/dict/Archive/dict.py 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") From 997557c8a565e5b1e66dc59c7a7633fd5a00d724 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Tue, 14 May 2024 22:35:49 +0200 Subject: [PATCH 38/93] Garbage cleanup --- .../projects/7/VMTranslator/VMTranslator.py | 34 ------------------- 1 file changed, 34 deletions(-) diff --git a/nand2tetris/projects/7/VMTranslator/VMTranslator.py b/nand2tetris/projects/7/VMTranslator/VMTranslator.py index a5a261b..ef706f5 100644 --- a/nand2tetris/projects/7/VMTranslator/VMTranslator.py +++ b/nand2tetris/projects/7/VMTranslator/VMTranslator.py @@ -159,38 +159,4 @@ def writelines(input_data : str | list, filename) -> None: - - - -def main_old(filename : str = None): - """ - -""" - # Check if the correct number of command-line arguments are provided - if filename is None: - if len(sys.argv) != 2: - print("Usage: py VMTranslator.py ") - return - filename = sys.argv[1] - - - - # Call functions from different modules to perform the main tasks - parsed_data = parser(filename) - translated_data = codewriter(parsed_data) - - # https://regex101.com/r/SkENd5/1 - - out_filename = changeExtension(filename, "asm") - writelines(input_data = translated_data, filename = out_filename) - return translated_data - - - -if __name__ == "__main__": - main() - - -filename = 'nand2tetris/projects/7/StackArithmetic/SimpleAdd/SimpleAdd.vm' -main(filename) From 50fc0141ea02d10a404e9ea5916fbb2283ad7d4f Mon Sep 17 00:00:00 2001 From: ATherkel Date: Tue, 14 May 2024 22:36:44 +0200 Subject: [PATCH 39/93] Delete old version --- .../7/VMTranslator/src/parser/parser.py | 239 ------------------ 1 file changed, 239 deletions(-) delete mode 100644 nand2tetris/projects/7/VMTranslator/src/parser/parser.py diff --git a/nand2tetris/projects/7/VMTranslator/src/parser/parser.py b/nand2tetris/projects/7/VMTranslator/src/parser/parser.py deleted file mode 100644 index 00ffeb6..0000000 --- a/nand2tetris/projects/7/VMTranslator/src/parser/parser.py +++ /dev/null @@ -1,239 +0,0 @@ -import re -import importlib.util - -## Import segment dictionary -## We have to use import_module since the path contains an illegal directory name (7). -segment_module = importlib.import_module("nand2tetris.projects.7.VMTranslator.src.parser.segment") -segment = segment_module.segment - - - -class parser: - """ - Parses each VM command into its lexical elements. - - Arguments - ---- - filename : str - A string containing the path to the VM file. """ - - def __init__(self, filename : str) -> None: - """ - Arguments - ---- - filename : str - Input file / stream - - Returns - ---- - None - - Function - ---- - Opens the input file/stream and gets ready to parse it. - - Raises - ---- - FileNotFoundError - If the specified file does not exist. - """ - - # We need the filename to write to a similarly named file. - self.filename = filename - # Open the file for reading the lines. - self.file = open(filename, 'r') - - ## Initialize current line - self.current_line = self.file.readline().strip() - self.next_line = self.file.readline().strip() - - - @property - def hasMoreCommands(self) -> bool: - """ - Arguments - ---- - N/A - - Returns - ---- - bool - - Function - ---- - Are there more commands in the input? - """ - return bool(self.instruction) - - def instruction(self, line = None) -> None: - line = line if line is not None else self.filename.readline().strip() - self.instruction = line.split("//")[0].strip().split() - - 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.current_line = self.next_line - self.next_line = self.file.readline().strip() - - 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. - - """ - if self.current_command.startswith("push"): - return "C_PUSH" - elif self.current_command.startswith("pop"): - return "C_POP" - elif self.current_command in [ - "add", "sub", "neg", - "eq", "gt", "lt", - "and", "or", "not"]: - return "C_ARITHMETIC" - - else: - raise ValueError(f"Current command invalid: Cannot parse '{self.current_command}'") - - 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.current_command - else: - out = self.current_command.split()[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.current_command.split()[2]) - else: - return ValueError(f"arg2() called on invalid command type: '{self.current_command}'") - - def close(self) -> None: - self.file.close() - - ## ---- VM language: ---- - - ## Arithmetic / Logical commands - - # add - # sub - # neg - # eq - # gt - # lt - # and - # or - # not - - ## Memory access commands - - # pop - # push - - - - ## ---- readlines ---- - - ## Read lines method - def readlines(filename : str): - """ - Returns a list of the lines from an input filename. - - Parameters - ---- - filename : str - File path and file name of file to be read. - """ - file = open(filename, 'r') - lines = file.readlines() - # print(lines) - return lines - - - - - ## ---- removeComments ---- - - def removeComments(input_data : str | list, comment : str = "//") -> (str | list): - """ Returns the input stripped for comments. - If the first argument is a list, iterate over all elements. - Does not handle multiline comments. - - Arguments - ---- - input_data : str or list of str - Input string or list of strings. - comment : str, optional - Comment token. Defaults to "//". Everything after this token is removed. - """ - # Compile the regular expression pattern - re_comment = re.escape(comment) - # https://regex101.com/r/BH3D67/1 - regex = r'\s*' + re_comment + '.*' - - # Initialize output variable - output = None - - if isinstance(input_data, str): - output = re.sub(regex, '', input_data) - elif isinstance(input_data, list): - data_nocomment = [re.sub(regex, '', line) for line in input_data] - ## Remove empty lines - output = [line for line in data_nocomment if line.strip()] - else: - raise ValueError("input_data must be a string or list of strings.") - return output \ No newline at end of file From 9a47139a0fb44fb1abaa60c2ce27538e585a31fe Mon Sep 17 00:00:00 2001 From: ATherkel Date: Tue, 14 May 2024 22:37:19 +0200 Subject: [PATCH 40/93] Rename parser2 to parser --- .../projects/7/VMTranslator/src/parser/{parser2.py => parser.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename nand2tetris/projects/7/VMTranslator/src/parser/{parser2.py => parser.py} (100%) diff --git a/nand2tetris/projects/7/VMTranslator/src/parser/parser2.py b/nand2tetris/projects/7/VMTranslator/src/parser/parser.py similarity index 100% rename from nand2tetris/projects/7/VMTranslator/src/parser/parser2.py rename to nand2tetris/projects/7/VMTranslator/src/parser/parser.py From 1462d132bfdcefca3da0f4161a02f8ac2dc2b5cf Mon Sep 17 00:00:00 2001 From: ATherkel Date: Tue, 14 May 2024 22:37:43 +0200 Subject: [PATCH 41/93] Remove constructor, handling in main --- .../7/VMTranslator/src/codewriter/codewriter.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py b/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py index 663ebf7..f2f0ef0 100644 --- a/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py +++ b/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py @@ -1,13 +1,7 @@ -def codewriter(a): +class codewriter: """ writes the assembly code that implements the parsed command. """ - def constructor(): - """ - Arguments - Output file / stream - Function - Opens the output file / stream and gets ready to write into it. - """ + def writeArithmetic(): """ Arguments From bd6aea7c5a9047aada709b3ad376a57071bd671c Mon Sep 17 00:00:00 2001 From: ATherkel Date: Tue, 14 May 2024 22:54:08 +0200 Subject: [PATCH 42/93] codewriter first work --- .../projects/7/VMTranslator/VMTranslator.py | 12 ++++++++---- .../VMTranslator/src/codewriter/codewriter.py | 19 +++++++++++++++++-- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/nand2tetris/projects/7/VMTranslator/VMTranslator.py b/nand2tetris/projects/7/VMTranslator/VMTranslator.py index ef706f5..30a18d0 100644 --- a/nand2tetris/projects/7/VMTranslator/VMTranslator.py +++ b/nand2tetris/projects/7/VMTranslator/VMTranslator.py @@ -14,6 +14,10 @@ parser_module = importlib.import_module("nand2tetris.projects.7.VMTranslator.src.parser.parser") Parser = parser_module.parser +writer_module = importlib.import_module("nand2tetris.projects.7.VMTranslator.src.codewriter.codewriter") +codewriter = writer_module.codewriter + + @@ -28,11 +32,12 @@ def main(filename): ## https://stackoverflow.com/a/59696768/3560695 regex = r'^(?P.*[\\\/])?(?P\.*.*?)(?P\.[^.]+?|)$' - filename_write = re.sub(regex, r"\1\2.asm") + filename_write = re.sub(regex, r"\1\2.asm", filename) - with open(filename, 'r') as file, open(filename_write, "w"): + with open(filename, 'r') as file, open(filename_write, "w") as file_write: parser = Parser(file) + writer = codewriter(file_write) while parser.hasMoreCommands(): ## As long as file has more lines, do: parser.advance() ## Go to the next line in the file. @@ -49,8 +54,7 @@ def main(filename): # Write arithmetic from arithmetic.asm ... elif parser.commandType() in ["C_PUSH", "C_POP"]: - # Write push and pop commands from push.asm and pop.asm - ... + writer.writePushPop(parser.commandType(), parser.arg1(), parser.arg2()) else: ... diff --git a/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py b/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py index f2f0ef0..d215886 100644 --- a/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py +++ b/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py @@ -2,14 +2,20 @@ class codewriter: """ writes the assembly code that implements the parsed command. """ - def writeArithmetic(): + + def __init__(self, file) -> None: + self.file = file ## functions need to be able to grab file. + + + def writeArithmetic(command : str) -> None: """ Arguments command (string) Function Writes to the output file the assembly code that implements the given arithmetic command. """ - def writePushPop(command, segment : str, index : int): + + def writePushPop(command : str, segment : str, index : int): """ Arguments ---- @@ -21,6 +27,15 @@ def writePushPop(command, segment : str, index : int): Writes to the output file the assembly code that implements the given command, where command is either C_PUSH or C_POP. """ + # Write push and pop commands from push.asm and pop.asm + if parser.arg1().lower() == "constant": + print("constant.") + elif parser.arg1().lower == "static": + print("static.") + else: + print(parser.arg1()) + + def close(): """ Function From 6b8a6b0086642abb159bb13f571937c6d679ce93 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Tue, 14 May 2024 22:54:19 +0200 Subject: [PATCH 43/93] Remove wrong docstring --- .../7/VMTranslator/src/parser/parser.py | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/nand2tetris/projects/7/VMTranslator/src/parser/parser.py b/nand2tetris/projects/7/VMTranslator/src/parser/parser.py index f5c24ca..f879ded 100644 --- a/nand2tetris/projects/7/VMTranslator/src/parser/parser.py +++ b/nand2tetris/projects/7/VMTranslator/src/parser/parser.py @@ -16,25 +16,6 @@ class Parser: """ def __init__(self, file) -> None: - """ - Arguments - ---- - filename : str - Input file / stream - - Returns - ---- - None - - Function - ---- - Opens the input file/stream and gets ready to parse it. - - Raises - ---- - FileNotFoundError - If the specified file does not exist. - """ self.line = None ## Initialize line. self.lineNo = 0 ## Initialize line number. self.instruction = None ## Initialize instruction. From 59d33d53e80c58bb519826e8ab9bec636b313710 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Tue, 14 May 2024 23:39:34 +0200 Subject: [PATCH 44/93] Rename to avoid overload --- nand2tetris/projects/7/VMTranslator/src/parser/parser.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/nand2tetris/projects/7/VMTranslator/src/parser/parser.py b/nand2tetris/projects/7/VMTranslator/src/parser/parser.py index f879ded..53ed6f7 100644 --- a/nand2tetris/projects/7/VMTranslator/src/parser/parser.py +++ b/nand2tetris/projects/7/VMTranslator/src/parser/parser.py @@ -1,11 +1,11 @@ import importlib -dict_filepath = "nand2tetris/projects/7/VMTranslator/src/utils/dict/dict" -dict = importlib.import_module(dict_filepath.replace("/", ".")) +dicts_filepath = "nand2tetris/projects/7/VMTranslator/src/utils/dict/dict" +dicts = importlib.import_module(dicts_filepath.replace("/", ".")) -class Parser: +class parser: """ - Handles the parsing of a single .vm file. @@ -63,7 +63,6 @@ def advance(self) -> None: Returns ---- None - Function ---- Reads the next command from the input and makes it the current command. @@ -92,7 +91,7 @@ def commandType(self) -> str: """ try: - return dict.commandType[self.instruction[0]] + return dicts.commandType[self.instruction[0]] except: raise KeyError(f"Current command invalid: Cannot parse '{self.line.strip()}' on line {self.lineNo}.") From 56929d40846e586e6c38b7ce3de335716baf035a Mon Sep 17 00:00:00 2001 From: ATherkel Date: Tue, 14 May 2024 23:39:45 +0200 Subject: [PATCH 45/93] Initial codewriter work --- .../projects/7/VMTranslator/VMTranslator.py | 28 +++++++++++-------- .../VMTranslator/src/codewriter/codewriter.py | 10 ++----- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/nand2tetris/projects/7/VMTranslator/VMTranslator.py b/nand2tetris/projects/7/VMTranslator/VMTranslator.py index 30a18d0..6cc9b70 100644 --- a/nand2tetris/projects/7/VMTranslator/VMTranslator.py +++ b/nand2tetris/projects/7/VMTranslator/VMTranslator.py @@ -5,22 +5,20 @@ #### ---- import ---- # https://stackoverflow.com/a/37867717/3560695 -import importlib.util -import sys +import importlib import re ## Import parser ## We have to use import_module since the path contains an illegal directory name (7). parser_module = importlib.import_module("nand2tetris.projects.7.VMTranslator.src.parser.parser") +importlib.reload(parser_module) Parser = parser_module.parser writer_module = importlib.import_module("nand2tetris.projects.7.VMTranslator.src.codewriter.codewriter") +importlib.reload(writer_module) codewriter = writer_module.codewriter - - - #### ---- main ---- #def main(): @@ -45,16 +43,22 @@ def main(filename): if not parser.instruction: ## If instruction is blank, skip. Line consisted only of a comment. continue print(f"---- Line {parser.lineNo} ----") - print(f"instruction = {parser.instruction}") - print(f"commandType() = {parser.commandType()}") - print(f"arg1() = {parser.arg1()}") - print(f"arg2() = {parser.arg2()}") + # print(f"instruction = {parser.instruction}") + # print(f"commandType() = {parser.commandType()}, type = {type(parser.commandType())}") + # print(f"arg1() = {parser.arg1()}") + # print(f"arg2() = {parser.arg2()}") + if parser.commandType() == "C_ARITHMETIC": # Write arithmetic from arithmetic.asm ... elif parser.commandType() in ["C_PUSH", "C_POP"]: - writer.writePushPop(parser.commandType(), parser.arg1(), parser.arg2()) + commandType = parser.commandType() + arg1 = parser.arg1() + arg2 = parser.arg2() +# return commandType + writer.writePushPop(commandType, arg1, arg2) + ... else: ... @@ -63,10 +67,10 @@ def main(filename): ## Testing filename = '/nand2tetris/projects/7/StackArithmetic/SimpleAdd/SimpleAdd.vm' - main(filename[1:]) - +1+1 +dict.commandType diff --git a/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py b/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py index d215886..b34ea1d 100644 --- a/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py +++ b/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py @@ -14,8 +14,9 @@ def writeArithmetic(command : str) -> None: Function Writes to the output file the assembly code that implements the given arithmetic command. """ + print(command) - def writePushPop(command : str, segment : str, index : int): + def writePushPop(command : str, segment : str, index : int, wtf): """ Arguments ---- @@ -28,12 +29,7 @@ def writePushPop(command : str, segment : str, index : int): where command is either C_PUSH or C_POP. """ # Write push and pop commands from push.asm and pop.asm - if parser.arg1().lower() == "constant": - print("constant.") - elif parser.arg1().lower == "static": - print("static.") - else: - print(parser.arg1()) + print(f"command = '{command}', segment = {segment}, index = {index}, wtf = {wtf}") def close(): From 6b7d18db1aad72ee8c6f590e8aab46e7a7063e42 Mon Sep 17 00:00:00 2001 From: Therkel Date: Tue, 14 May 2024 23:59:15 +0200 Subject: [PATCH 46/93] Update parser.py Attempt to fix object return --- nand2tetris/projects/7/VMTranslator/src/parser/parser.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nand2tetris/projects/7/VMTranslator/src/parser/parser.py b/nand2tetris/projects/7/VMTranslator/src/parser/parser.py index 53ed6f7..3549d94 100644 --- a/nand2tetris/projects/7/VMTranslator/src/parser/parser.py +++ b/nand2tetris/projects/7/VMTranslator/src/parser/parser.py @@ -89,9 +89,9 @@ def commandType(self) -> str: C_ARITHMETIC is returned for all the arithmetic/logical commands. """ - + command = self.instruction[0] try: - return dicts.commandType[self.instruction[0]] + return dicts.commandType[command] except: raise KeyError(f"Current command invalid: Cannot parse '{self.line.strip()}' on line {self.lineNo}.") From a919093408b7d568438ed00a77f4c863885ea465 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Wed, 15 May 2024 10:28:06 +0200 Subject: [PATCH 47/93] Clean up --- .../projects/7/VMTranslator/VMTranslator.py | 80 +------------------ 1 file changed, 1 insertion(+), 79 deletions(-) diff --git a/nand2tetris/projects/7/VMTranslator/VMTranslator.py b/nand2tetris/projects/7/VMTranslator/VMTranslator.py index 6cc9b70..aff37c4 100644 --- a/nand2tetris/projects/7/VMTranslator/VMTranslator.py +++ b/nand2tetris/projects/7/VMTranslator/VMTranslator.py @@ -56,8 +56,7 @@ def main(filename): commandType = parser.commandType() arg1 = parser.arg1() arg2 = parser.arg2() -# return commandType - writer.writePushPop(commandType, arg1, arg2) + print(writer.writePushPop(commandType, arg1, arg2)) ... else: ... @@ -69,83 +68,6 @@ def main(filename): filename = '/nand2tetris/projects/7/StackArithmetic/SimpleAdd/SimpleAdd.vm' main(filename[1:]) -1+1 -dict.commandType - - - -## Below is probably redundant - - - - - - - -# 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)) - - From db10a76c175dab82c31a3f9a7d4d133eaa8273cc Mon Sep 17 00:00:00 2001 From: ATherkel Date: Wed, 15 May 2024 10:28:17 +0200 Subject: [PATCH 48/93] Clean up --- .../7/VMTranslator/src/Archive/deprecated.py | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 nand2tetris/projects/7/VMTranslator/src/Archive/deprecated.py 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)) + + + + + + + + From df7ff277067e4a669a7d67e5c80735d0aed90c89 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Wed, 15 May 2024 10:28:29 +0200 Subject: [PATCH 49/93] Test --- nand2tetris/projects/7/VMTranslator/tests/test_path.py | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 nand2tetris/projects/7/VMTranslator/tests/test_path.py 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 From 9e1f74f05d6350804c4bb7273c4a49e8143e50c3 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Wed, 15 May 2024 10:28:42 +0200 Subject: [PATCH 50/93] Corrected pointer --- .../projects/7/VMTranslator/src/utils/asm/pushSegment.asm | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/nand2tetris/projects/7/VMTranslator/src/utils/asm/pushSegment.asm b/nand2tetris/projects/7/VMTranslator/src/utils/asm/pushSegment.asm index 58a418f..7caa1a2 100644 --- a/nand2tetris/projects/7/VMTranslator/src/utils/asm/pushSegment.asm +++ b/nand2tetris/projects/7/VMTranslator/src/utils/asm/pushSegment.asm @@ -6,8 +6,11 @@ // addr <- SegmentPointer + i @{index} D = A + +// Not used if segment is 'constant' @{segmentPointer} -D = D + M +A = D + M +D = M // RAM[SP] = RAM[addr] @SP From db4e6ed9c79410886f2a8eb8154a9c0ab4528bad Mon Sep 17 00:00:00 2001 From: ATherkel Date: Wed, 15 May 2024 10:28:56 +0200 Subject: [PATCH 51/93] Simplify --- nand2tetris/projects/7/VMTranslator/src/parser/parser.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nand2tetris/projects/7/VMTranslator/src/parser/parser.py b/nand2tetris/projects/7/VMTranslator/src/parser/parser.py index 3549d94..53ed6f7 100644 --- a/nand2tetris/projects/7/VMTranslator/src/parser/parser.py +++ b/nand2tetris/projects/7/VMTranslator/src/parser/parser.py @@ -89,9 +89,9 @@ def commandType(self) -> str: C_ARITHMETIC is returned for all the arithmetic/logical commands. """ - command = self.instruction[0] + try: - return dicts.commandType[command] + return dicts.commandType[self.instruction[0]] except: raise KeyError(f"Current command invalid: Cannot parse '{self.line.strip()}' on line {self.lineNo}.") From 028a81ab40e931ee52f799bd373b41157fd5baac Mon Sep 17 00:00:00 2001 From: ATherkel Date: Wed, 15 May 2024 10:29:11 +0200 Subject: [PATCH 52/93] writePushPop work --- .../VMTranslator/src/codewriter/codewriter.py | 41 ++++++++++++------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py b/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py index b34ea1d..7a0d9fb 100644 --- a/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py +++ b/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py @@ -1,4 +1,14 @@ +import importlib + +parser_module = importlib.import_module("nand2tetris.projects.7.VMTranslator.src.parser.parser") +importlib.reload(parser_module) + +dicts_filepath = "nand2tetris/projects/7/VMTranslator/src/utils/dict/dict" +dicts = importlib.import_module(dicts_filepath.replace("/", ".")) + + + class codewriter: """ writes the assembly code that implements the parsed command. """ @@ -6,7 +16,7 @@ class codewriter: def __init__(self, file) -> None: self.file = file ## functions need to be able to grab file. - + @staticmethod def writeArithmetic(command : str) -> None: """ Arguments @@ -16,27 +26,30 @@ def writeArithmetic(command : str) -> None: """ print(command) - def writePushPop(command : str, segment : str, index : int, wtf): + @staticmethod + def writePushPop(command : str, segment : str, index : int): """ Arguments ---- - command (C_PUSH or C_POP) - segment : str - index : int + command : ('C_PUSH' or 'C_POP') + segment : constant, local etc. + index : pointer number in the segment Function ---- Writes to the output file the assembly code that implements the given command, where command is either C_PUSH or C_POP. """ + newline = '\n' + + segmentPointer = dicts.segment[segment] + # Write push and pop commands from push.asm and pop.asm - print(f"command = '{command}', segment = {segment}, index = {index}, wtf = {wtf}") - - def close(): - """ - Function - ---- - Closes the output file. + if command == "C_PUSH": + with open("nand2tetris/projects/7/VMTranslator/src/utils/asm/pushSegment.asm", 'r') as asm: + lines = f"{asm.read().replace(newline, '')}".format(**locals()) + print(lines) - Likely redundant for my method. - """ + + + \ No newline at end of file From 7a9707b59025135bf822a55a6b553573176f3306 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Thu, 16 May 2024 10:05:35 +0200 Subject: [PATCH 53/93] Ignore created asm files --- nand2tetris/projects/7/StackArithmetic/.gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 nand2tetris/projects/7/StackArithmetic/.gitignore diff --git a/nand2tetris/projects/7/StackArithmetic/.gitignore b/nand2tetris/projects/7/StackArithmetic/.gitignore new file mode 100644 index 0000000..7bdbb1b --- /dev/null +++ b/nand2tetris/projects/7/StackArithmetic/.gitignore @@ -0,0 +1 @@ +*.asm \ No newline at end of file From 08d80b0cb68d3f9ea07992b953478a1d6b06b4bd Mon Sep 17 00:00:00 2001 From: ATherkel Date: Thu, 16 May 2024 10:06:33 +0200 Subject: [PATCH 54/93] Test with constant --- .../projects/7/VMTranslator/src/utils/asm/pushSegment.asm | 6 +++--- nand2tetris/projects/7/VMTranslator/src/utils/dict/dict.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/nand2tetris/projects/7/VMTranslator/src/utils/asm/pushSegment.asm b/nand2tetris/projects/7/VMTranslator/src/utils/asm/pushSegment.asm index 7caa1a2..a1eda4a 100644 --- a/nand2tetris/projects/7/VMTranslator/src/utils/asm/pushSegment.asm +++ b/nand2tetris/projects/7/VMTranslator/src/utils/asm/pushSegment.asm @@ -8,9 +8,9 @@ D = A // Not used if segment is 'constant' -@{segmentPointer} -A = D + M -D = M +//@{segmentPointer} +//A = D + M +//D = M // RAM[SP] = RAM[addr] @SP diff --git a/nand2tetris/projects/7/VMTranslator/src/utils/dict/dict.py b/nand2tetris/projects/7/VMTranslator/src/utils/dict/dict.py index 6407e95..b9951e5 100644 --- a/nand2tetris/projects/7/VMTranslator/src/utils/dict/dict.py +++ b/nand2tetris/projects/7/VMTranslator/src/utils/dict/dict.py @@ -25,5 +25,5 @@ "argument" : "ARG", "THIS" : "THIS", "THAT" : "THAT", - + "constant" : "CONST", } From e8e44c9800e65b50566fd5c80823232bdbf54405 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Thu, 16 May 2024 10:24:15 +0200 Subject: [PATCH 55/93] Create pushConstant.asm --- .../src/utils/asm/pushConstant.asm | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 nand2tetris/projects/7/VMTranslator/src/utils/asm/pushConstant.asm diff --git a/nand2tetris/projects/7/VMTranslator/src/utils/asm/pushConstant.asm b/nand2tetris/projects/7/VMTranslator/src/utils/asm/pushConstant.asm new file mode 100644 index 0000000..a1eda4a --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/src/utils/asm/pushConstant.asm @@ -0,0 +1,22 @@ +// ---- push {segment} {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 From d33c54dc0a3c92687f3a16c0d00ddabd73909407 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Thu, 16 May 2024 10:24:32 +0200 Subject: [PATCH 56/93] Correct pushSegment.asm --- .../projects/7/VMTranslator/src/utils/asm/pushSegment.asm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nand2tetris/projects/7/VMTranslator/src/utils/asm/pushSegment.asm b/nand2tetris/projects/7/VMTranslator/src/utils/asm/pushSegment.asm index a1eda4a..7caa1a2 100644 --- a/nand2tetris/projects/7/VMTranslator/src/utils/asm/pushSegment.asm +++ b/nand2tetris/projects/7/VMTranslator/src/utils/asm/pushSegment.asm @@ -8,9 +8,9 @@ D = A // Not used if segment is 'constant' -//@{segmentPointer} -//A = D + M -//D = M +@{segmentPointer} +A = D + M +D = M // RAM[SP] = RAM[addr] @SP From 413199202fd1f28f9a247650993e3800f24211b5 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Thu, 16 May 2024 10:25:22 +0200 Subject: [PATCH 57/93] Update segment in dict.py --- .../projects/7/VMTranslator/src/utils/dict/dict.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/nand2tetris/projects/7/VMTranslator/src/utils/dict/dict.py b/nand2tetris/projects/7/VMTranslator/src/utils/dict/dict.py index b9951e5..332e804 100644 --- a/nand2tetris/projects/7/VMTranslator/src/utils/dict/dict.py +++ b/nand2tetris/projects/7/VMTranslator/src/utils/dict/dict.py @@ -23,7 +23,14 @@ segment = { "local" : "LCL", "argument" : "ARG", - "THIS" : "THIS", - "THAT" : "THAT", - "constant" : "CONST", + "this" : "THIS", + "that" : "THAT", + + "temp" : 5, } + +""" + "R13" : 13, + "R14" : 14, + "constant" : "CONST", +""" \ No newline at end of file From e9fc652f35c37a38aaf8f4c4a2a0b3403838aeb4 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Thu, 16 May 2024 10:26:12 +0200 Subject: [PATCH 58/93] Move class call inside `main` --- .../projects/7/VMTranslator/VMTranslator.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/nand2tetris/projects/7/VMTranslator/VMTranslator.py b/nand2tetris/projects/7/VMTranslator/VMTranslator.py index aff37c4..da78fbd 100644 --- a/nand2tetris/projects/7/VMTranslator/VMTranslator.py +++ b/nand2tetris/projects/7/VMTranslator/VMTranslator.py @@ -11,12 +11,10 @@ ## Import parser ## We have to use import_module since the path contains an illegal directory name (7). parser_module = importlib.import_module("nand2tetris.projects.7.VMTranslator.src.parser.parser") -importlib.reload(parser_module) -Parser = parser_module.parser + writer_module = importlib.import_module("nand2tetris.projects.7.VMTranslator.src.codewriter.codewriter") -importlib.reload(writer_module) -codewriter = writer_module.codewriter + #### ---- main ---- @@ -33,13 +31,16 @@ def main(filename): filename_write = re.sub(regex, r"\1\2.asm", filename) + Parser = parser_module.parser + codewriter = writer_module.codewriter + with open(filename, 'r') as file, open(filename_write, "w") as file_write: parser = Parser(file) writer = codewriter(file_write) while parser.hasMoreCommands(): ## As long as file has more lines, do: parser.advance() ## Go to the next line in the file. - parser.getinstruction() ## sets parser.instruction to current instruction + parser.getVMinstruction() ## sets parser.instruction to current instruction if not parser.instruction: ## If instruction is blank, skip. Line consisted only of a comment. continue print(f"---- Line {parser.lineNo} ----") @@ -63,6 +64,8 @@ def main(filename): +importlib.reload(parser_module) +importlib.reload(writer_module) ## Testing filename = '/nand2tetris/projects/7/StackArithmetic/SimpleAdd/SimpleAdd.vm' @@ -87,6 +90,5 @@ def main(filename): - From 2d67dda9b0315513fc2c2ce4c59dec9a3d0fd7dc Mon Sep 17 00:00:00 2001 From: ATherkel Date: Thu, 16 May 2024 10:26:57 +0200 Subject: [PATCH 59/93] Update codewriter.writePushPop --- .../VMTranslator/src/codewriter/codewriter.py | 32 ++++++++++++++++--- .../7/VMTranslator/src/parser/parser.py | 22 ++++++++++--- 2 files changed, 45 insertions(+), 9 deletions(-) diff --git a/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py b/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py index 7a0d9fb..2eee5b3 100644 --- a/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py +++ b/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py @@ -41,15 +41,39 @@ def writePushPop(command : str, segment : str, index : int): """ newline = '\n' + Parser = parser_module.parser + + ## Used in some of the .asm templates. + ## Contains Hack name convention for segments, e.g. local is "LCL" segmentPointer = dicts.segment[segment] # Write push and pop commands from push.asm and pop.asm + + ## First handle 'pointer' logic translation to THIS/THAT + if segment == "pointer": + segment = ["THIS", "THAT"][index] if command == "C_PUSH": - with open("nand2tetris/projects/7/VMTranslator/src/utils/asm/pushSegment.asm", 'r') as asm: - lines = f"{asm.read().replace(newline, '')}".format(**locals()) - print(lines) - + if segment == "constant": + asm_location = "pushConstant.asm" + elif segment in ["local", "argument", "this", "that"]: + asm_location = "pushSegment.asm" + elif segment == "static": + asm_location = "pushStatic.asm" + elif segment == "temp": + ... + elif segment == "pointer": + ... + with open("nand2tetris/projects/7/VMTranslator/src/utils/asm/pushSegment.asm", 'r') as asm: + parser = Parser(asm) + + while parser.hasMoreCommands(): + parser.advance() + parser.getinstruction() + if not parser.instruction: ## If instruction is blank, skip. Line consisted only of a comment. + continue + return lines + \ 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 index 53ed6f7..d80cd9d 100644 --- a/nand2tetris/projects/7/VMTranslator/src/parser/parser.py +++ b/nand2tetris/projects/7/VMTranslator/src/parser/parser.py @@ -16,10 +16,11 @@ class parser: """ def __init__(self, file) -> None: - self.line = None ## Initialize line. - self.lineNo = 0 ## Initialize line number. - self.instruction = None ## Initialize instruction. - self.file = file ## functions need to be able to grab file. + 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: @@ -143,7 +144,18 @@ def arg2(self) -> int: return ValueError(f"arg2() called on invalid command type: '{self.commandType()}'") def getinstruction(self): - self.instruction = self.line.split("//")[0].strip().split() + """ + Remove everything after the comment token (//) + """ + self.instruction = self.line.split("//")[0].strip() + + def getVMinstruction(self): + """ + Splits the instruction into a list of objects + """ + self.instruction = self.getinstruction() + print(self.instruction) + self.VMinstruction = self.instruction.split() From 40ed9bc17cd58fdb2311972c1b85712773dc290a Mon Sep 17 00:00:00 2001 From: ATherkel Date: Thu, 16 May 2024 10:48:27 +0200 Subject: [PATCH 60/93] Segment handling --- .../7/VMTranslator/src/codewriter/codewriter.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py b/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py index 2eee5b3..9c856ca 100644 --- a/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py +++ b/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py @@ -51,19 +51,20 @@ def writePushPop(command : str, segment : str, index : int): ## First handle 'pointer' logic translation to THIS/THAT if segment == "pointer": - segment = ["THIS", "THAT"][index] + segment = ["this", "that"][index] ## e.g. 'push pointer 0' means 'push this' + index = 0 ## Silently 'push THIS' means 'push THIS 0' if command == "C_PUSH": if segment == "constant": asm_location = "pushConstant.asm" - elif segment in ["local", "argument", "this", "that"]: + elif segment in ["local", "argument", "this", "that", "temp"]: asm_location = "pushSegment.asm" elif segment == "static": asm_location = "pushStatic.asm" - elif segment == "temp": - ... - elif segment == "pointer": - ... + # elif segment == "temp": + # ... + # elif segment == "pointer": ## Handled above + # ... with open("nand2tetris/projects/7/VMTranslator/src/utils/asm/pushSegment.asm", 'r') as asm: parser = Parser(asm) From 195655bde2b9eddd79787c19a4953702a0f75781 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Thu, 16 May 2024 11:57:36 +0200 Subject: [PATCH 61/93] VMinstruction correction. --- .../projects/7/VMTranslator/src/parser/parser.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/nand2tetris/projects/7/VMTranslator/src/parser/parser.py b/nand2tetris/projects/7/VMTranslator/src/parser/parser.py index d80cd9d..65fd7bf 100644 --- a/nand2tetris/projects/7/VMTranslator/src/parser/parser.py +++ b/nand2tetris/projects/7/VMTranslator/src/parser/parser.py @@ -92,7 +92,7 @@ def commandType(self) -> str: """ try: - return dicts.commandType[self.instruction[0]] + return dicts.commandType[self.VMinstruction[0]] except: raise KeyError(f"Current command invalid: Cannot parse '{self.line.strip()}' on line {self.lineNo}.") @@ -114,11 +114,11 @@ def arg1(self) -> str: Should not be called if the current command is C_RETURN. """ if self.commandType() == "C_ARITHMETIC": - out = self.instruction[0] + out = self.VMinstruction[0] elif self.commandType() == "C_RETURN": return ValueError(f"arg1() called on invalid command type: '{self.commandType()}'") else: - out = self.instruction[1] + out = self.VMinstruction[1] return out @@ -139,7 +139,7 @@ def arg2(self) -> int: C_PUSH, C_POP, C_FUNCTION or C_CALL. """ if self.commandType() in ["C_PUSH", "C_POP", "C_FUNCTION", "C_CALL"]: - return int(self.instruction[2]) + return int(self.VMinstruction[2]) else: return ValueError(f"arg2() called on invalid command type: '{self.commandType()}'") @@ -153,8 +153,9 @@ def getVMinstruction(self): """ Splits the instruction into a list of objects """ - self.instruction = self.getinstruction() - print(self.instruction) + + # Call getinstruction to ensure instruction has been retrieved. + self.getinstruction() self.VMinstruction = self.instruction.split() From 10ed3142775c6524204c4a0c3d1e321f061d1cc0 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Thu, 16 May 2024 11:58:15 +0200 Subject: [PATCH 62/93] VMinstruction correction --- nand2tetris/projects/7/VMTranslator/VMTranslator.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/nand2tetris/projects/7/VMTranslator/VMTranslator.py b/nand2tetris/projects/7/VMTranslator/VMTranslator.py index da78fbd..fc525c2 100644 --- a/nand2tetris/projects/7/VMTranslator/VMTranslator.py +++ b/nand2tetris/projects/7/VMTranslator/VMTranslator.py @@ -40,9 +40,11 @@ def main(filename): while parser.hasMoreCommands(): ## As long as file has more lines, do: parser.advance() ## Go to the next line in the file. - parser.getVMinstruction() ## sets parser.instruction to current instruction + 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())}") From 5a5d3160a685a75f1e8d91880fca8bca03c894a3 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Thu, 16 May 2024 13:04:42 +0200 Subject: [PATCH 63/93] Update writePushPop --- .../VMTranslator/src/codewriter/codewriter.py | 45 ++++++++++++------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py b/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py index 9c856ca..ef420aa 100644 --- a/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py +++ b/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py @@ -15,19 +15,30 @@ class codewriter: def __init__(self, file) -> None: self.file = file ## functions need to be able to grab file. + self.newline = '\n' + + self.Parser = parser_module.parser @staticmethod - def writeArithmetic(command : str) -> None: + def writeArithmetic(command : str, ) -> None: """ Arguments command (string) Function Writes to the output file the assembly code that implements the given arithmetic command. """ - print(command) - @staticmethod - def writePushPop(command : str, segment : str, index : int): + ## Unary or binary operation: + arity = dicts.arithmetic[command] + + with open(f"nand2tetris/projects/7/VMTranslator/src/utils/asm/arithmetic_{arity}", 'r') as asm: + ... + + + + + + def writePushPop(self, command : str, segment : str, index : int): """ Arguments ---- @@ -39,15 +50,6 @@ def writePushPop(command : str, segment : str, index : int): Writes to the output file the assembly code that implements the given command, where command is either C_PUSH or C_POP. """ - newline = '\n' - - Parser = parser_module.parser - - ## Used in some of the .asm templates. - ## Contains Hack name convention for segments, e.g. local is "LCL" - segmentPointer = dicts.segment[segment] - - # Write push and pop commands from push.asm and pop.asm ## First handle 'pointer' logic translation to THIS/THAT if segment == "pointer": @@ -58,6 +60,10 @@ def writePushPop(command : str, segment : str, index : int): if segment == "constant": asm_location = "pushConstant.asm" elif segment in ["local", "argument", "this", "that", "temp"]: + ## Used in some of the .asm templates. + ## Contains Hack name convention for segments, e.g. local is "LCL" + segmentPointer = dicts.segment[segment] + asm_location = "pushSegment.asm" elif segment == "static": asm_location = "pushStatic.asm" @@ -66,15 +72,24 @@ def writePushPop(command : str, segment : str, index : int): # elif segment == "pointer": ## Handled above # ... - with open("nand2tetris/projects/7/VMTranslator/src/utils/asm/pushSegment.asm", 'r') as asm: - parser = Parser(asm) + lines = [] ## Initialize lines. + + + with open(f"nand2tetris/projects/7/VMTranslator/src/utils/asm/{asm_location}", 'r') as asm: + parser = self.Parser(asm) while parser.hasMoreCommands(): parser.advance() parser.getinstruction() if not parser.instruction: ## If instruction is blank, skip. Line consisted only of a comment. continue + + line = parser.instruction.format(**locals()) + lines.append(line) + # if not parser.hasMoreCommands(): + # lines.append(self.newline) return lines + \ No newline at end of file From e43f12e6b434ba4f74bb054839999d5f4a939f85 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Thu, 16 May 2024 13:43:30 +0200 Subject: [PATCH 64/93] Create arithmetic asm files --- .../src/utils/asm/arithmetic_1.asm | 8 ++++++ .../src/utils/asm/arithmetic_2.asm | 28 +++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 nand2tetris/projects/7/VMTranslator/src/utils/asm/arithmetic_1.asm create mode 100644 nand2tetris/projects/7/VMTranslator/src/utils/asm/arithmetic_2.asm 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..e05404c --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/src/utils/asm/arithmetic_1.asm @@ -0,0 +1,8 @@ +// ---- unary action ---- +// *SP = {unary}*SP + +@SP +A = M + +M = {unary}M + 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..b2a9751 --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/src/utils/asm/arithmetic_2.asm @@ -0,0 +1,28 @@ +// ---- x {action} y ---- +// D = *SP +// SP-- +// *SP = *SP {action} D +// SP++ + + + + +// D = *SP +@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 + From bfe12df3b5b784ab5ea27cbf45cf63fded87abd0 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Thu, 16 May 2024 13:44:00 +0200 Subject: [PATCH 65/93] Update VMTranslator --- .../projects/7/VMTranslator/VMTranslator.py | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/nand2tetris/projects/7/VMTranslator/VMTranslator.py b/nand2tetris/projects/7/VMTranslator/VMTranslator.py index fc525c2..d6b0503 100644 --- a/nand2tetris/projects/7/VMTranslator/VMTranslator.py +++ b/nand2tetris/projects/7/VMTranslator/VMTranslator.py @@ -45,32 +45,42 @@ def main(filename): continue parser.getVMinstruction() ## sets parser.VMinstruction to current instruction - print(f"---- Line {parser.lineNo} ----") + # 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(commandType, arg1) + elif parser.commandType() in ["C_PUSH", "C_POP"]: - commandType = parser.commandType() arg1 = parser.arg1() arg2 = parser.arg2() - print(writer.writePushPop(commandType, arg1, arg2)) - ... + lines = writer.writePushPop(commandType, arg1, arg2) 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') + + -importlib.reload(parser_module) -importlib.reload(writer_module) ## Testing filename = '/nand2tetris/projects/7/StackArithmetic/SimpleAdd/SimpleAdd.vm' + +importlib.reload(parser_module) +importlib.reload(writer_module) + main(filename[1:]) From 3a484e7edd9125bc34744c32362b8b8a450bd70f Mon Sep 17 00:00:00 2001 From: ATherkel Date: Thu, 16 May 2024 13:46:13 +0200 Subject: [PATCH 66/93] Update VMTranslator --- nand2tetris/projects/7/VMTranslator/VMTranslator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nand2tetris/projects/7/VMTranslator/VMTranslator.py b/nand2tetris/projects/7/VMTranslator/VMTranslator.py index d6b0503..74543d8 100644 --- a/nand2tetris/projects/7/VMTranslator/VMTranslator.py +++ b/nand2tetris/projects/7/VMTranslator/VMTranslator.py @@ -56,7 +56,7 @@ def main(filename): if parser.commandType() == "C_ARITHMETIC": # Write arithmetic from arithmetic.asm arg1 = parser.arg1() - lines = writer.writeArithmetic(commandType, arg1) + lines = writer.writeArithmetic(arg1) elif parser.commandType() in ["C_PUSH", "C_POP"]: arg1 = parser.arg1() From 9377149073234ef5bd16f1abc3e71c3e864b49e1 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Thu, 16 May 2024 13:46:42 +0200 Subject: [PATCH 67/93] Update codewriter, create processCommands --- .../VMTranslator/src/codewriter/codewriter.py | 55 ++++++++++++++----- 1 file changed, 40 insertions(+), 15 deletions(-) diff --git a/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py b/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py index ef420aa..c019d68 100644 --- a/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py +++ b/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py @@ -19,8 +19,11 @@ def __init__(self, file) -> None: self.Parser = parser_module.parser - @staticmethod - def writeArithmetic(command : str, ) -> None: + ## Used in some of the .asm templates. + self.action = None + self.segmentPointer = None + + def writeArithmetic(self, command : str) -> None: """ Arguments command (string) @@ -30,9 +33,17 @@ def writeArithmetic(command : str, ) -> None: ## Unary or binary operation: arity = dicts.arithmetic[command] + print(arity) + + ## action is used in some of the .asm templates. + ## Contains Hack code for the arithmetic operations + self.action = dicts.arithmetic_action[command] - with open(f"nand2tetris/projects/7/VMTranslator/src/utils/asm/arithmetic_{arity}", 'r') as asm: - ... + with open(f"nand2tetris/projects/7/VMTranslator/src/utils/asm/arithmetic_{arity}.asm", 'r') as asm: + parser = self.Parser(asm) + + lines = self.processCommands(parser) + return lines @@ -62,7 +73,7 @@ def writePushPop(self, command : str, segment : str, index : int): elif segment in ["local", "argument", "this", "that", "temp"]: ## Used in some of the .asm templates. ## Contains Hack name convention for segments, e.g. local is "LCL" - segmentPointer = dicts.segment[segment] + self.segmentPointer = dicts.segment[segment] asm_location = "pushSegment.asm" elif segment == "static": @@ -78,18 +89,32 @@ def writePushPop(self, command : str, segment : str, index : int): with open(f"nand2tetris/projects/7/VMTranslator/src/utils/asm/{asm_location}", 'r') as asm: parser = self.Parser(asm) - while parser.hasMoreCommands(): - parser.advance() - parser.getinstruction() - if not parser.instruction: ## If instruction is blank, skip. Line consisted only of a comment. - continue - - line = parser.instruction.format(**locals()) - lines.append(line) - # if not parser.hasMoreCommands(): - # lines.append(self.newline) + lines = self.processCommands(parser) return lines + def processCommands(self, parser): + """ + 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 + + print(parser.instruction) + + line = parser.instruction.format(**locals()) + lines.append(line) + + return lines + \ No newline at end of file From ff10599ba12605e1540d7282d59014a35fa019ae Mon Sep 17 00:00:00 2001 From: ATherkel Date: Thu, 16 May 2024 13:46:53 +0200 Subject: [PATCH 68/93] Update dict.py --- .../7/VMTranslator/src/utils/dict/dict.py | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/nand2tetris/projects/7/VMTranslator/src/utils/dict/dict.py b/nand2tetris/projects/7/VMTranslator/src/utils/dict/dict.py index 332e804..8bb6128 100644 --- a/nand2tetris/projects/7/VMTranslator/src/utils/dict/dict.py +++ b/nand2tetris/projects/7/VMTranslator/src/utils/dict/dict.py @@ -29,6 +29,33 @@ "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" : "D = M - D \\n D; JEQ", + "gt" : "D = M - D \\n D; JGT", + "lt" : "D = M - D \\n D; JLT", + "and" : "M = M & D", + "or" : "M = M | D", +} + + """ "R13" : 13, "R14" : 14, From c239eec5936ad4cf91e5d521a77858c61609e621 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Thu, 16 May 2024 13:47:15 +0200 Subject: [PATCH 69/93] Create test_arithmetic.py --- .../7/VMTranslator/tests/test_arithmetic.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 nand2tetris/projects/7/VMTranslator/tests/test_arithmetic.py 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..b88eef3 --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/tests/test_arithmetic.py @@ -0,0 +1,15 @@ + + +import importlib + + + +writer_module = importlib.import_module("nand2tetris.projects.7.VMTranslator.src.codewriter.codewriter") + +importlib.reload(writer_module) + + +testwriter = writer_module.codewriter("") + + +testwriter.writeArithmetic("add") From 250356d6f0e713f8ce61e15fa4e58afaf4378b07 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Thu, 16 May 2024 13:47:29 +0200 Subject: [PATCH 70/93] Update test_arithmetic.py --- nand2tetris/projects/7/VMTranslator/tests/test_arithmetic.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/nand2tetris/projects/7/VMTranslator/tests/test_arithmetic.py b/nand2tetris/projects/7/VMTranslator/tests/test_arithmetic.py index b88eef3..b0708c9 100644 --- a/nand2tetris/projects/7/VMTranslator/tests/test_arithmetic.py +++ b/nand2tetris/projects/7/VMTranslator/tests/test_arithmetic.py @@ -13,3 +13,7 @@ testwriter.writeArithmetic("add") + +testwriter.writePushPop("push", "constant", 7) + + From bea7c019eac77affcf6e0e43b3654442466be19a Mon Sep 17 00:00:00 2001 From: ATherkel Date: Fri, 17 May 2024 22:15:29 +0200 Subject: [PATCH 71/93] Change for Python debugger --- .../projects/7/VMTranslator/VMTranslator.py | 21 ++++++++++--------- .../projects/7/VMTranslator/__init__.py | 1 + .../VMTranslator/src/codewriter/codewriter.py | 16 ++++++++------ .../7/VMTranslator/src/parser/parser.py | 8 +++---- 4 files changed, 26 insertions(+), 20 deletions(-) create mode 100644 nand2tetris/projects/7/VMTranslator/__init__.py diff --git a/nand2tetris/projects/7/VMTranslator/VMTranslator.py b/nand2tetris/projects/7/VMTranslator/VMTranslator.py index 74543d8..4ec9a24 100644 --- a/nand2tetris/projects/7/VMTranslator/VMTranslator.py +++ b/nand2tetris/projects/7/VMTranslator/VMTranslator.py @@ -5,16 +5,15 @@ #### ---- import ---- # https://stackoverflow.com/a/37867717/3560695 -import importlib import re ## Import parser ## We have to use import_module since the path contains an illegal directory name (7). -parser_module = importlib.import_module("nand2tetris.projects.7.VMTranslator.src.parser.parser") - - -writer_module = importlib.import_module("nand2tetris.projects.7.VMTranslator.src.codewriter.codewriter") +# 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 ---- @@ -31,8 +30,8 @@ def main(filename): filename_write = re.sub(regex, r"\1\2.asm", filename) - Parser = parser_module.parser - codewriter = writer_module.codewriter + Parser = p.parser + codewriter = cw.codewriter with open(filename, 'r') as file, open(filename_write, "w") as file_write: parser = Parser(file) @@ -74,12 +73,14 @@ def main(filename): +import importlib ## Testing -filename = '/nand2tetris/projects/7/StackArithmetic/SimpleAdd/SimpleAdd.vm' +filename = '.../StackArithmetic/SimpleAdd/SimpleAdd.vm' +filename = '.../StackArithmetic/Stacktest/Stacktest.vm' -importlib.reload(parser_module) -importlib.reload(writer_module) +importlib.reload(p) +importlib.reload(cw) main(filename[1:]) 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/src/codewriter/codewriter.py b/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py index c019d68..39577b7 100644 --- a/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py +++ b/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py @@ -1,13 +1,17 @@ -import importlib +import sys +# import importlib -parser_module = importlib.import_module("nand2tetris.projects.7.VMTranslator.src.parser.parser") -importlib.reload(parser_module) +# sys.path.append("C:/Users/Bruger/OneDrive/Dokumenter/GitHub/BuildComputer/nand2tetris/projects/7/VMTranslator") -dicts_filepath = "nand2tetris/projects/7/VMTranslator/src/utils/dict/dict" -dicts = importlib.import_module(dicts_filepath.replace("/", ".")) +# 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. """ @@ -86,7 +90,7 @@ def writePushPop(self, command : str, segment : str, index : int): lines = [] ## Initialize lines. - with open(f"nand2tetris/projects/7/VMTranslator/src/utils/asm/{asm_location}", 'r') as asm: + with open(f"src/utils/asm/{asm_location}", 'r') as asm: parser = self.Parser(asm) lines = self.processCommands(parser) diff --git a/nand2tetris/projects/7/VMTranslator/src/parser/parser.py b/nand2tetris/projects/7/VMTranslator/src/parser/parser.py index 65fd7bf..5ef8266 100644 --- a/nand2tetris/projects/7/VMTranslator/src/parser/parser.py +++ b/nand2tetris/projects/7/VMTranslator/src/parser/parser.py @@ -1,9 +1,9 @@ -import importlib - -dicts_filepath = "nand2tetris/projects/7/VMTranslator/src/utils/dict/dict" -dicts = importlib.import_module(dicts_filepath.replace("/", ".")) +# import importlib +# dicts_filepath = "src/utils/dict/dict" +#dicts = importlib.import_module(dicts_filepath.replace("/", ".")) +import src.utils.dict.dict as dicts class parser: """ From 4ae24d8176705ce869644e11e598f9cf26d134bf Mon Sep 17 00:00:00 2001 From: ATherkel Date: Fri, 17 May 2024 22:15:48 +0200 Subject: [PATCH 72/93] Python debugger --- .vscode/launch.json | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .vscode/launch.json 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 From d3a9feb60938f69a2d5009a53d79dfa0ff975916 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Fri, 17 May 2024 22:16:52 +0200 Subject: [PATCH 73/93] Fix vwriteArithmetic --- .../VMTranslator/src/codewriter/codewriter.py | 42 +++++++++++++------ .../src/utils/asm/arithmetic_1.asm | 12 ++++-- .../src/utils/asm/arithmetic_2.asm | 6 ++- .../7/VMTranslator/src/utils/dict/dict.py | 29 ++++++++++--- 4 files changed, 65 insertions(+), 24 deletions(-) diff --git a/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py b/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py index 39577b7..0d983fb 100644 --- a/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py +++ b/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py @@ -22,10 +22,13 @@ def __init__(self, file) -> None: 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.action = None - self.segmentPointer = None + self.segment = None + self.index = None def writeArithmetic(self, command : str) -> None: """ @@ -37,16 +40,20 @@ def writeArithmetic(self, command : str) -> None: ## Unary or binary operation: arity = dicts.arithmetic[command] - print(arity) + ## action is used in some of the .asm templates. ## Contains Hack code for the arithmetic operations - self.action = dicts.arithmetic_action[command] + action = dicts.arithmetic_action[command] + if command in ["eq", "lt", "gt"]: + action = dicts.compare_action.format(**locals()) - with open(f"nand2tetris/projects/7/VMTranslator/src/utils/asm/arithmetic_{arity}.asm", 'r') as asm: + with open(f"src/utils/asm/arithmetic_{arity}.asm", 'r') as asm: parser = self.Parser(asm) - lines = self.processCommands(parser) + lines = self.processCommands(parser, action = action) + + self.arithmeticNo += 1 return lines @@ -66,6 +73,8 @@ def writePushPop(self, command : str, segment : str, index : int): where command is either C_PUSH or C_POP. """ + segmentPointer = None ## Initialize - passed to self.processCommands but not always used. + ## First handle 'pointer' logic translation to THIS/THAT if segment == "pointer": segment = ["this", "that"][index] ## e.g. 'push pointer 0' means 'push this' @@ -77,8 +86,7 @@ def writePushPop(self, command : str, segment : str, index : int): elif segment in ["local", "argument", "this", "that", "temp"]: ## Used in some of the .asm templates. ## Contains Hack name convention for segments, e.g. local is "LCL" - self.segmentPointer = dicts.segment[segment] - + segmentPointer = dicts.segment[segment] asm_location = "pushSegment.asm" elif segment == "static": asm_location = "pushStatic.asm" @@ -86,6 +94,10 @@ def writePushPop(self, command : str, segment : str, index : int): # ... # elif segment == "pointer": ## Handled above # ... + # elif command == "C_POP": + # ... + else: + raise KeyError(f"Unexpected command '{command}' in writePushPop (should be 'C_PUSH' or 'C_POP')") lines = [] ## Initialize lines. @@ -93,10 +105,10 @@ def writePushPop(self, command : str, segment : str, index : int): with open(f"src/utils/asm/{asm_location}", 'r') as asm: parser = self.Parser(asm) - lines = self.processCommands(parser) + lines = self.processCommands(parser, segment = segment, segmentPointer = segmentPointer, index = index) return lines - def processCommands(self, parser): + def processCommands(self, parser, **kwargs): """ Arguments ---- @@ -112,9 +124,13 @@ def processCommands(self, parser): if not parser.instruction: ## If instruction is blank, skip. Line consisted only of a comment. continue - print(parser.instruction) - - line = parser.instruction.format(**locals()) + ## 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 diff --git a/nand2tetris/projects/7/VMTranslator/src/utils/asm/arithmetic_1.asm b/nand2tetris/projects/7/VMTranslator/src/utils/asm/arithmetic_1.asm index e05404c..7704f7e 100644 --- a/nand2tetris/projects/7/VMTranslator/src/utils/asm/arithmetic_1.asm +++ b/nand2tetris/projects/7/VMTranslator/src/utils/asm/arithmetic_1.asm @@ -1,8 +1,12 @@ // ---- unary action ---- -// *SP = {unary}*SP - +// SP-- +// *SP = {action}*SP +// SP++ @SP -A = M +M = M - 1 -M = {unary}M +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 index b2a9751..9c2cdbf 100644 --- a/nand2tetris/projects/7/VMTranslator/src/utils/asm/arithmetic_2.asm +++ b/nand2tetris/projects/7/VMTranslator/src/utils/asm/arithmetic_2.asm @@ -1,14 +1,16 @@ // ---- x {action} y ---- +// SP-- // D = *SP // SP-- // *SP = *SP {action} D // SP++ - +// SP-- +@SP +M = M - 1 // D = *SP -@SP A = M D = M diff --git a/nand2tetris/projects/7/VMTranslator/src/utils/dict/dict.py b/nand2tetris/projects/7/VMTranslator/src/utils/dict/dict.py index 8bb6128..930f176 100644 --- a/nand2tetris/projects/7/VMTranslator/src/utils/dict/dict.py +++ b/nand2tetris/projects/7/VMTranslator/src/utils/dict/dict.py @@ -43,18 +43,37 @@ } arithmetic_action = { - "neg" : "M = -M", - "not" : "M = !M", + "neg" : "-", # M = -M + "not" : "!", # M = !M "add" : "M = M + D", "sub" : "M = M - D", - "eq" : "D = M - D \\n D; JEQ", - "gt" : "D = M - D \\n D; JGT", - "lt" : "D = M - D \\n D; JLT", + "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, From 582e054fd1897fb0e82816ea1384348f95179f91 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Fri, 17 May 2024 22:17:12 +0200 Subject: [PATCH 74/93] Create test scripts --- .../projects/7/VMTranslator/tests/conftest.py | 0 .../7/VMTranslator/tests/test_arithmetic.py | 18 ++++++++++-------- .../7/VMTranslator/tests/test_writePushPop.py | 17 +++++++++++++++++ 3 files changed, 27 insertions(+), 8 deletions(-) create mode 100644 nand2tetris/projects/7/VMTranslator/tests/conftest.py create mode 100644 nand2tetris/projects/7/VMTranslator/tests/test_writePushPop.py 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 index b0708c9..5c690e7 100644 --- a/nand2tetris/projects/7/VMTranslator/tests/test_arithmetic.py +++ b/nand2tetris/projects/7/VMTranslator/tests/test_arithmetic.py @@ -1,19 +1,21 @@ -import importlib +# import importlib -writer_module = importlib.import_module("nand2tetris.projects.7.VMTranslator.src.codewriter.codewriter") +# writer_module = importlib.import_module("src.codewriter.codewriter") +from src.codewriter import codewriter as cw +# importlib.reload(cw) -importlib.reload(writer_module) +# importlib.reload(cw) +testwriter = cw.codewriter("") -testwriter = writer_module.codewriter("") +print("---- eq ----") +print(testwriter.writeArithmetic("lt")) - -testwriter.writeArithmetic("add") - -testwriter.writePushPop("push", "constant", 7) +# print("---- push local 7 ----") +# testwriter.writePushPop("C_PUSH", "constant", 7) 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)) + + From 3881e029692ca4e287688f2451efa74cdfe9b07e Mon Sep 17 00:00:00 2001 From: ATherkel Date: Fri, 17 May 2024 22:33:56 +0200 Subject: [PATCH 75/93] Create .gitignore --- nand2tetris/.gitignore | 1 + nand2tetris/projects/7/MemoryAccess/.gitignore | 2 ++ nand2tetris/projects/7/StackArithmetic/.gitignore | 3 ++- 3 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 nand2tetris/.gitignore create mode 100644 nand2tetris/projects/7/MemoryAccess/.gitignore 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/StackArithmetic/.gitignore b/nand2tetris/projects/7/StackArithmetic/.gitignore index 7bdbb1b..bf82470 100644 --- a/nand2tetris/projects/7/StackArithmetic/.gitignore +++ b/nand2tetris/projects/7/StackArithmetic/.gitignore @@ -1 +1,2 @@ -*.asm \ No newline at end of file +*.asm +*.out \ No newline at end of file From 3104b093c0e7fa2c530efcb43ba12f6873f53600 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Fri, 17 May 2024 22:43:20 +0200 Subject: [PATCH 76/93] Run from command line --- .../projects/7/VMTranslator/VMTranslator.py | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/nand2tetris/projects/7/VMTranslator/VMTranslator.py b/nand2tetris/projects/7/VMTranslator/VMTranslator.py index 4ec9a24..9bb567c 100644 --- a/nand2tetris/projects/7/VMTranslator/VMTranslator.py +++ b/nand2tetris/projects/7/VMTranslator/VMTranslator.py @@ -6,6 +6,7 @@ # 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). @@ -21,7 +22,14 @@ #def main(): -def main(filename): +def main(filename = None): + + 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 @@ -75,16 +83,18 @@ def main(filename): import importlib -## Testing -filename = '.../StackArithmetic/SimpleAdd/SimpleAdd.vm' -filename = '.../StackArithmetic/Stacktest/Stacktest.vm' - -importlib.reload(p) -importlib.reload(cw) +# ## Testing +# filename = '.../StackArithmetic/SimpleAdd/SimpleAdd.vm' +# filename = '.../StackArithmetic/Stacktest/Stacktest.vm' +# filename = '.../StackArithmetic/Stacktest/Stacktest.vm' -main(filename[1:]) +# importlib.reload(p) +# importlib.reload(cw) +# main(filename[1:]) +if __name__ == "main": + main() From 90261fb45d990cdf2e6c9d05529b0e2297f444ad Mon Sep 17 00:00:00 2001 From: ATherkel Date: Sat, 18 May 2024 13:50:07 +0200 Subject: [PATCH 77/93] Create extra test `.vm` scripts --- nand2tetris/projects/7/Mytests/testPush.vm | 9 +++++++++ nand2tetris/projects/7/Mytests/testStatic.vm | 6 ++++++ 2 files changed, 15 insertions(+) create mode 100644 nand2tetris/projects/7/Mytests/testPush.vm create mode 100644 nand2tetris/projects/7/Mytests/testStatic.vm 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/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 From ebb1fa0885cc75329fcf6aa8e99fe3571ee2e8fc Mon Sep 17 00:00:00 2001 From: ATherkel Date: Sat, 18 May 2024 13:52:00 +0200 Subject: [PATCH 78/93] Archive previous .asm versions --- .../src/utils/asm/{ => Archive}/add.asm | 0 .../utils/asm/{ => Archive}/popSegment.asm | 0 .../utils/asm/{ => Archive}/pushConstant.asm | 2 +- .../src/utils/asm/Archive/pushSegment.asm | 36 +++++++++++++++++++ .../pushStatic.asm} | 17 ++++++--- .../VMTranslator/src/utils/asm/pushStatic.asm | 2 -- 6 files changed, 49 insertions(+), 8 deletions(-) rename nand2tetris/projects/7/VMTranslator/src/utils/asm/{ => Archive}/add.asm (100%) rename nand2tetris/projects/7/VMTranslator/src/utils/asm/{ => Archive}/popSegment.asm (100%) rename nand2tetris/projects/7/VMTranslator/src/utils/asm/{ => Archive}/pushConstant.asm (86%) create mode 100644 nand2tetris/projects/7/VMTranslator/src/utils/asm/Archive/pushSegment.asm rename nand2tetris/projects/7/VMTranslator/src/utils/asm/{pushSegment.asm => Archive/pushStatic.asm} (53%) delete mode 100644 nand2tetris/projects/7/VMTranslator/src/utils/asm/pushStatic.asm diff --git a/nand2tetris/projects/7/VMTranslator/src/utils/asm/add.asm b/nand2tetris/projects/7/VMTranslator/src/utils/asm/Archive/add.asm similarity index 100% rename from nand2tetris/projects/7/VMTranslator/src/utils/asm/add.asm rename to nand2tetris/projects/7/VMTranslator/src/utils/asm/Archive/add.asm diff --git a/nand2tetris/projects/7/VMTranslator/src/utils/asm/popSegment.asm b/nand2tetris/projects/7/VMTranslator/src/utils/asm/Archive/popSegment.asm similarity index 100% rename from nand2tetris/projects/7/VMTranslator/src/utils/asm/popSegment.asm rename to nand2tetris/projects/7/VMTranslator/src/utils/asm/Archive/popSegment.asm diff --git a/nand2tetris/projects/7/VMTranslator/src/utils/asm/pushConstant.asm b/nand2tetris/projects/7/VMTranslator/src/utils/asm/Archive/pushConstant.asm similarity index 86% rename from nand2tetris/projects/7/VMTranslator/src/utils/asm/pushConstant.asm rename to nand2tetris/projects/7/VMTranslator/src/utils/asm/Archive/pushConstant.asm index a1eda4a..c048c09 100644 --- a/nand2tetris/projects/7/VMTranslator/src/utils/asm/pushConstant.asm +++ b/nand2tetris/projects/7/VMTranslator/src/utils/asm/Archive/pushConstant.asm @@ -1,4 +1,4 @@ -// ---- push {segment} {index} ---- +// ---- push constant {index} ---- // addr = segment + i, *SP = *addr, SP++ 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/pushSegment.asm b/nand2tetris/projects/7/VMTranslator/src/utils/asm/Archive/pushStatic.asm similarity index 53% rename from nand2tetris/projects/7/VMTranslator/src/utils/asm/pushSegment.asm rename to nand2tetris/projects/7/VMTranslator/src/utils/asm/Archive/pushStatic.asm index 7caa1a2..aa8501d 100644 --- a/nand2tetris/projects/7/VMTranslator/src/utils/asm/pushSegment.asm +++ b/nand2tetris/projects/7/VMTranslator/src/utils/asm/Archive/pushStatic.asm @@ -1,6 +1,13 @@ -// ---- push {segment} {index} ---- +// ---- push static {index} ---- -// addr = segment + i, *SP = *addr, SP++ +// addr = Foo.i, *SP = *addr, SP++ + +@{index} +D = A + +@{segmentPointer} +A = D + A +D = A // addr <- SegmentPointer + i @@ -8,9 +15,9 @@ D = A // Not used if segment is 'constant' -@{segmentPointer} -A = D + M -D = M +// @{segmentPointer} +// A = D + M +// D = M // RAM[SP] = RAM[addr] @SP diff --git a/nand2tetris/projects/7/VMTranslator/src/utils/asm/pushStatic.asm b/nand2tetris/projects/7/VMTranslator/src/utils/asm/pushStatic.asm deleted file mode 100644 index e446c63..0000000 --- a/nand2tetris/projects/7/VMTranslator/src/utils/asm/pushStatic.asm +++ /dev/null @@ -1,2 +0,0 @@ -// push static i - From c4fb87a14496f11c985bc030f0a36ccaa5387a14 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Sat, 18 May 2024 14:06:33 +0200 Subject: [PATCH 79/93] Create `.asm` elements --- .../7/VMTranslator/src/utils/asm/D_eq_i.asm | 6 ++++++ .../src/utils/asm/D_eq_segmentPointer_p_i.asm | 5 +++++ .../src/utils/asm/RAM_D_eq_RAM_SP.asm | 15 +++++++++++++++ .../7/VMTranslator/src/utils/asm/RAM_SP_eq_D.asm | 5 +++++ .../7/VMTranslator/src/utils/asm/SPmm.asm | 3 +++ .../7/VMTranslator/src/utils/asm/SPpp.asm | 3 +++ 6 files changed, 37 insertions(+) create mode 100644 nand2tetris/projects/7/VMTranslator/src/utils/asm/D_eq_i.asm create mode 100644 nand2tetris/projects/7/VMTranslator/src/utils/asm/D_eq_segmentPointer_p_i.asm create mode 100644 nand2tetris/projects/7/VMTranslator/src/utils/asm/RAM_D_eq_RAM_SP.asm create mode 100644 nand2tetris/projects/7/VMTranslator/src/utils/asm/RAM_SP_eq_D.asm create mode 100644 nand2tetris/projects/7/VMTranslator/src/utils/asm/SPmm.asm create mode 100644 nand2tetris/projects/7/VMTranslator/src/utils/asm/SPpp.asm 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_p_i.asm b/nand2tetris/projects/7/VMTranslator/src/utils/asm/D_eq_segmentPointer_p_i.asm new file mode 100644 index 0000000..18217dc --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/src/utils/asm/D_eq_segmentPointer_p_i.asm @@ -0,0 +1,5 @@ +// addr <- segmentPointer + i +@{index} +D = A +@{segmentPointer} +D = D + A // D = segment + i \ 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_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 From 1746c04ca7a64172d9e45d4bf00beb3d54a061c7 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Sat, 18 May 2024 16:36:55 +0200 Subject: [PATCH 80/93] Update codewriter --- .../VMTranslator/src/codewriter/codewriter.py | 82 +++++++++++++------ 1 file changed, 59 insertions(+), 23 deletions(-) diff --git a/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py b/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py index 0d983fb..48cff1a 100644 --- a/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py +++ b/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py @@ -1,5 +1,5 @@ -import sys +import os # import importlib # sys.path.append("C:/Users/Bruger/OneDrive/Dokumenter/GitHub/BuildComputer/nand2tetris/projects/7/VMTranslator") @@ -60,53 +60,85 @@ def writeArithmetic(self, command : str) -> None: - def writePushPop(self, command : str, segment : str, index : int): + 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. ## First handle 'pointer' logic translation to THIS/THAT if 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' index = 0 ## Silently 'push THIS' means 'push THIS 0' + + elif segment == "temp" and (not 0 <= index < 7): + raise ValueError(f"'temp' segment only valid for index values 0 to 7. Input: {index}.") + + elif 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) + print(index) + + 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] + # elif segment == "temp": + # ... + # elif segment == "pointer": ## Handled above + # ... + + + if segment != "static": + lines.extend([ + f"@{index}", + "D = A" + ]) + if command == "C_PUSH": + # if segment == "constant": + # asm_location = "pushConstant.asm" + # else: + # asm_location = "pushSegment.asm" + + if segment != "constant": + # RAM[SP] = index + # SP++ + lines.extend(self.processCommands(parser, segment = segment, segmentPointer = segmentPointer, index = index)) + asm_location = "pushSegment.asm" + + elif command == "C_POP": + asm_location = "popSegment.asm" if segment == "constant": - asm_location = "pushConstant.asm" - elif segment in ["local", "argument", "this", "that", "temp"]: - ## Used in some of the .asm templates. - ## Contains Hack name convention for segments, e.g. local is "LCL" - segmentPointer = dicts.segment[segment] - asm_location = "pushSegment.asm" - elif segment == "static": - asm_location = "pushStatic.asm" - # elif segment == "temp": - # ... - # elif segment == "pointer": ## Handled above - # ... - # elif command == "C_POP": - # ... + raise ValueError("Cannot pop constant.") + + else: raise KeyError(f"Unexpected command '{command}' in writePushPop (should be 'C_PUSH' or 'C_POP')") - lines = [] ## Initialize lines. - with open(f"src/utils/asm/{asm_location}", 'r') as asm: - parser = self.Parser(asm) - - lines = self.processCommands(parser, segment = segment, segmentPointer = segmentPointer, index = index) - return lines + return self.processAsm(segment, index, segmentPointer, lines, asm_location) + + def processCommands(self, parser, **kwargs): """ @@ -135,6 +167,10 @@ def processCommands(self, parser, **kwargs): return lines - + def processAsm(self, segment : str, index : str|int, segmentPointer, lines, asm_location): + with open(f"src/utils/asm/{asm_location}", 'r') as asm: + parser = self.Parser(asm) + lines = self.processCommands(parser, segment = segment, segmentPointer = segmentPointer, index = index) + return lines \ No newline at end of file From e75baede1da1f9a8b80319de205b4436c1e5d0ab Mon Sep 17 00:00:00 2001 From: ATherkel Date: Sat, 18 May 2024 17:22:07 +0200 Subject: [PATCH 81/93] Push commands fixed --- .../VMTranslator/src/codewriter/codewriter.py | 48 ++++++++----------- ..._i.asm => D_eq_RAM_segmentPointer_p_i.asm} | 4 +- 2 files changed, 23 insertions(+), 29 deletions(-) rename nand2tetris/projects/7/VMTranslator/src/utils/asm/{D_eq_segmentPointer_p_i.asm => D_eq_RAM_segmentPointer_p_i.asm} (65%) diff --git a/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py b/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py index 48cff1a..9089f23 100644 --- a/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py +++ b/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py @@ -57,8 +57,6 @@ def writeArithmetic(self, command : str) -> None: return lines - - def writePushPop(self, command : str, segment : str, index : int, filename : str = ""): """ @@ -85,7 +83,6 @@ def writePushPop(self, command : str, segment : str, index : int, filename : str 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' index = 0 ## Silently 'push THIS' means 'push THIS 0' - elif segment == "temp" and (not 0 <= index < 7): raise ValueError(f"'temp' segment only valid for index values 0 to 7. Input: {index}.") @@ -94,8 +91,6 @@ def writePushPop(self, command : str, segment : str, index : int, filename : str raise ValueError(f"No filename supplied for push/pop static.") file = os.path.splitext(os.path.basename(filename))[0] index = file + "." + str(index) - print(index) - elif segment in dicts.segment.keys(): ## Used in some of the .asm templates. ## Contains Hack name convention for segments, e.g. local is "LCL" @@ -105,30 +100,27 @@ def writePushPop(self, command : str, segment : str, index : int, filename : str # elif segment == "pointer": ## Handled above # ... - - if segment != "static": - lines.extend([ - f"@{index}", - "D = A" - ]) - - if command == "C_PUSH": - # if segment == "constant": - # asm_location = "pushConstant.asm" - # else: - # asm_location = "pushSegment.asm" - - if segment != "constant": - # RAM[SP] = index - # SP++ - lines.extend(self.processCommands(parser, segment = segment, segmentPointer = segmentPointer, index = index)) - asm_location = "pushSegment.asm" + if segment in ["constant", "static"]: + # D <- index + lines.extend(self.processAsm("D_eq_i.asm", index = index)) + elif segment in dicts.segment.keys(): # local, argument, this, that, temp + # addr <- segmentPointer + index + # D <- RAM[addr] + lines.extend(self.processAsm("D_eq_RAM_segmentPointer_p_i.asm",index = index, segmentPointer = segmentPointer)) + else: + raise ValueError(f"Segment = '{segment}' not handled.") + ## RAM[SP] <- D + ## SP++ + lines.extend(self.processAsm("RAM_SP_eq_D.asm")) + lines.extend(self.processAsm("SPpp.asm")) + elif command == "C_POP": - asm_location = "popSegment.asm" if segment == "constant": raise ValueError("Cannot pop constant.") + elif segment in dicts.segment.keys(): + ... else: @@ -136,7 +128,7 @@ def writePushPop(self, command : str, segment : str, index : int, filename : str - return self.processAsm(segment, index, segmentPointer, lines, asm_location) + return lines @@ -167,10 +159,10 @@ def processCommands(self, parser, **kwargs): return lines - def processAsm(self, segment : str, index : str|int, segmentPointer, lines, asm_location): - with open(f"src/utils/asm/{asm_location}", 'r') as asm: + 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, segment = segment, segmentPointer = segmentPointer, index = index) + lines = self.processCommands(parser, **kwargs) return lines \ 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_RAM_segmentPointer_p_i.asm similarity index 65% rename from nand2tetris/projects/7/VMTranslator/src/utils/asm/D_eq_segmentPointer_p_i.asm rename to nand2tetris/projects/7/VMTranslator/src/utils/asm/D_eq_RAM_segmentPointer_p_i.asm index 18217dc..4b64500 100644 --- a/nand2tetris/projects/7/VMTranslator/src/utils/asm/D_eq_segmentPointer_p_i.asm +++ b/nand2tetris/projects/7/VMTranslator/src/utils/asm/D_eq_RAM_segmentPointer_p_i.asm @@ -2,4 +2,6 @@ @{index} D = A @{segmentPointer} -D = D + A // D = segment + i \ No newline at end of file +A = D + A +// D <- RAM[addr] +D = M \ No newline at end of file From e5407ed9844c814cb0cbe48cf21707d054a78244 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Sat, 18 May 2024 18:54:11 +0200 Subject: [PATCH 82/93] Create pushTemp.md --- .../projects/7/VMTranslator/docs/pushTemp.md | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 nand2tetris/projects/7/VMTranslator/docs/pushTemp.md 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 From af6434e231bada56afe0fdb2e579081c0221ec7b Mon Sep 17 00:00:00 2001 From: ATherkel Date: Sat, 18 May 2024 18:54:16 +0200 Subject: [PATCH 83/93] Create pushSegment.md --- .../projects/7/VMTranslator/docs/pushSegment.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 nand2tetris/projects/7/VMTranslator/docs/pushSegment.md 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 From 3bfaec254a27fa9b752857e5f74cd0cd2c59f0c4 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Sat, 18 May 2024 18:54:20 +0200 Subject: [PATCH 84/93] Create pushConstant.md --- .../projects/7/VMTranslator/docs/pushConstant.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 nand2tetris/projects/7/VMTranslator/docs/pushConstant.md 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 From 112e05ff64fe71c2809358fe03e05175d390267d Mon Sep 17 00:00:00 2001 From: ATherkel Date: Sat, 18 May 2024 18:55:21 +0200 Subject: [PATCH 85/93] Create pushStatic.md --- .../projects/7/VMTranslator/docs/pushStatic.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 nand2tetris/projects/7/VMTranslator/docs/pushStatic.md diff --git a/nand2tetris/projects/7/VMTranslator/docs/pushStatic.md b/nand2tetris/projects/7/VMTranslator/docs/pushStatic.md new file mode 100644 index 0000000..4042d21 --- /dev/null +++ b/nand2tetris/projects/7/VMTranslator/docs/pushStatic.md @@ -0,0 +1,15 @@ +`push static i` +---- + +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). + +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 From 42d6cd1191a4673a8e698a604b83570febc47ca2 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Sat, 18 May 2024 22:18:54 +0200 Subject: [PATCH 86/93] Pop command work --- .../VMTranslator/src/codewriter/codewriter.py | 64 +++++++++++++------ .../VMTranslator/src/utils/asm/D_eq_RAM_i.asm | 4 ++ .../utils/asm/D_eq_RAM_segmentPointer_p_i.asm | 1 + .../src/utils/asm/D_eq_segmentPointer_p_i.asm | 7 ++ .../7/VMTranslator/src/utils/asm/R13_eq_D.asm | 3 + .../src/utils/asm/RAM_R13_eq_RAM_SP.asm | 11 ++++ 6 files changed, 71 insertions(+), 19 deletions(-) create mode 100644 nand2tetris/projects/7/VMTranslator/src/utils/asm/D_eq_RAM_i.asm create mode 100644 nand2tetris/projects/7/VMTranslator/src/utils/asm/D_eq_segmentPointer_p_i.asm create mode 100644 nand2tetris/projects/7/VMTranslator/src/utils/asm/R13_eq_D.asm create mode 100644 nand2tetris/projects/7/VMTranslator/src/utils/asm/RAM_R13_eq_RAM_SP.asm diff --git a/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py b/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py index 9089f23..b6d69e7 100644 --- a/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py +++ b/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py @@ -77,50 +77,76 @@ def writePushPop(self, command : str, segment : str, index : int, filename : str segmentPointer = None ## Initialize - passed to self.processCommands but not always used. lines = [] ## Initialize lines. - ## First handle 'pointer' logic translation to THIS/THAT + ## 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 == "pointer": if index not in [0, 1]: - raise ValueError(f"push/pop pointer only valid for value 0 or 1. Input: {index}") + 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' index = 0 ## Silently 'push THIS' means 'push THIS 0' + + elif segment == "temp" and (not 0 <= index < 7): raise ValueError(f"'temp' segment only valid for index values 0 to 7. Input: {index}.") - - elif segment == "static": + 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 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] - # elif segment == "temp": - # ... - # elif segment == "pointer": ## Handled above - # ... - if command == "C_PUSH": - if segment in ["constant", "static"]: - # D <- index - lines.extend(self.processAsm("D_eq_i.asm", index = index)) - elif segment in dicts.segment.keys(): # local, argument, this, that, temp + 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: - raise ValueError(f"Segment = '{segment}' not handled.") + 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.") - elif segment in dicts.segment.keys(): - ... + + ## 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: 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_p_i.asm b/nand2tetris/projects/7/VMTranslator/src/utils/asm/D_eq_RAM_segmentPointer_p_i.asm index 4b64500..6a9518c 100644 --- 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 @@ -2,6 +2,7 @@ @{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_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_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 From 2ea980adc3e120768f1d7284484c3892c3cc32b6 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Sat, 18 May 2024 23:42:50 +0200 Subject: [PATCH 87/93] Passes BasicTest --- .../VMTranslator/src/codewriter/codewriter.py | 27 ++++++++++++++----- .../src/utils/asm/D_eq_segmentPointer.asm | 4 +++ 2 files changed, 24 insertions(+), 7 deletions(-) create mode 100644 nand2tetris/projects/7/VMTranslator/src/utils/asm/D_eq_segmentPointer.asm diff --git a/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py b/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py index b6d69e7..a04b1c9 100644 --- a/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py +++ b/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py @@ -85,14 +85,8 @@ def writePushPop(self, command : str, segment : str, index : int, filename : str ## First handle 'pointer' logic translation to THIS/THAT ## Also handle temp out of bounds. ## Also raise error if trying to pop constant - if 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' - index = 0 ## Silently 'push THIS' means 'push THIS 0' - - elif segment == "temp" and (not 0 <= index < 7): + 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.") @@ -116,6 +110,25 @@ def writePushPop(self, command : str, segment : str, index : int, filename : str # 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' + + 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" 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 From 3cc56edecd30049285f5707e01dcc421dfb9d753 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Sun, 19 May 2024 11:54:42 +0200 Subject: [PATCH 88/93] Passes PointerTest --- .../projects/7/VMTranslator/src/codewriter/codewriter.py | 5 ++++- .../7/VMTranslator/src/utils/asm/D_eq_RAM_segmentPointer.asm | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 nand2tetris/projects/7/VMTranslator/src/utils/asm/D_eq_RAM_segmentPointer.asm diff --git a/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py b/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py index a04b1c9..c901d81 100644 --- a/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py +++ b/nand2tetris/projects/7/VMTranslator/src/codewriter/codewriter.py @@ -127,7 +127,10 @@ def writePushPop(self, command : str, segment : str, index : int, filename : str segmentPointer = dicts.segment[segment] index = 0 ## Silently 'push THIS' means 'push THIS 0' - lines.extend(self.processAsm("D_eq_segmentPointer.asm", segmentPointer = segmentPointer)) + 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. 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 From a3a53b880100b0c25678a2c17a1c00158adf9077 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Sun, 19 May 2024 12:21:21 +0200 Subject: [PATCH 89/93] Create pushPointer.md --- .../7/VMTranslator/docs/pushPointer.md | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 nand2tetris/projects/7/VMTranslator/docs/pushPointer.md 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 From ea0de73e6bb77b71bc311e52df2c1c3cfd1ad735 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Sun, 19 May 2024 12:23:04 +0200 Subject: [PATCH 90/93] Update pushStatic.md --- .../projects/7/VMTranslator/docs/pushStatic.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/nand2tetris/projects/7/VMTranslator/docs/pushStatic.md b/nand2tetris/projects/7/VMTranslator/docs/pushStatic.md index 4042d21..658ca4b 100644 --- a/nand2tetris/projects/7/VMTranslator/docs/pushStatic.md +++ b/nand2tetris/projects/7/VMTranslator/docs/pushStatic.md @@ -1,15 +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 From 105079aeef8c8f3a5a41132b5593774c4c3dd5dd Mon Sep 17 00:00:00 2001 From: ATherkel Date: Sun, 19 May 2024 12:25:13 +0200 Subject: [PATCH 91/93] Create test cases --- nand2tetris/projects/7/Mytests/popLocal.asm | 23 ++++++ nand2tetris/projects/7/Mytests/popLocal.tst | 14 ++++ nand2tetris/projects/7/Mytests/popLocal.vm | 2 + .../projects/7/Mytests/popLocalVME.tst | 11 +++ .../projects/7/Mytests/testPointer.asm | 37 ++++++++++ .../projects/7/Mytests/testPointer.tst | 8 +++ nand2tetris/projects/7/Mytests/testPointer.vm | 4 ++ .../projects/7/Mytests/testPointerVME.tst | 11 +++ nand2tetris/projects/7/Mytests/testPush.asm | 71 +++++++++++++++++++ .../projects/7/Mytests/testPushVME.tst | 11 +++ 10 files changed, 192 insertions(+) create mode 100644 nand2tetris/projects/7/Mytests/popLocal.asm create mode 100644 nand2tetris/projects/7/Mytests/popLocal.tst create mode 100644 nand2tetris/projects/7/Mytests/popLocal.vm create mode 100644 nand2tetris/projects/7/Mytests/popLocalVME.tst create mode 100644 nand2tetris/projects/7/Mytests/testPointer.asm create mode 100644 nand2tetris/projects/7/Mytests/testPointer.tst create mode 100644 nand2tetris/projects/7/Mytests/testPointer.vm create mode 100644 nand2tetris/projects/7/Mytests/testPointerVME.tst create mode 100644 nand2tetris/projects/7/Mytests/testPush.asm create mode 100644 nand2tetris/projects/7/Mytests/testPushVME.tst 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/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; +} From 885e29d7bd41024fb5b096f62386bf5b16ef1225 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Sun, 19 May 2024 12:26:59 +0200 Subject: [PATCH 92/93] Update VMTranslator.py Print and properly call VMTranslator from --- nand2tetris/projects/7/VMTranslator/VMTranslator.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/nand2tetris/projects/7/VMTranslator/VMTranslator.py b/nand2tetris/projects/7/VMTranslator/VMTranslator.py index 9bb567c..eaf556e 100644 --- a/nand2tetris/projects/7/VMTranslator/VMTranslator.py +++ b/nand2tetris/projects/7/VMTranslator/VMTranslator.py @@ -23,7 +23,7 @@ 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: @@ -45,6 +45,8 @@ def main(filename = None): 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() @@ -76,6 +78,7 @@ def main(filename = None): file_write.write(f'// {parser.instruction}\n') for line in lines: file_write.write(f'{line}\n') + print("VMTranslator finished!") @@ -93,7 +96,7 @@ def main(filename = None): # main(filename[1:]) -if __name__ == "main": +if __name__ == "__main__": main() From 1507f00f6286dd2cf0ae90eda2b6a14292f19d59 Mon Sep 17 00:00:00 2001 From: ATherkel Date: Sun, 19 May 2024 12:27:35 +0200 Subject: [PATCH 93/93] Update VMTranslator.py Send `filename` to `writer.writePushPop`. --- nand2tetris/projects/7/VMTranslator/VMTranslator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nand2tetris/projects/7/VMTranslator/VMTranslator.py b/nand2tetris/projects/7/VMTranslator/VMTranslator.py index eaf556e..b7dda95 100644 --- a/nand2tetris/projects/7/VMTranslator/VMTranslator.py +++ b/nand2tetris/projects/7/VMTranslator/VMTranslator.py @@ -70,7 +70,7 @@ def main(filename = None): elif parser.commandType() in ["C_PUSH", "C_POP"]: arg1 = parser.arg1() arg2 = parser.arg2() - lines = writer.writePushPop(commandType, arg1, arg2) + lines = writer.writePushPop(commandType, arg1, arg2, filename = filename) else: ...