From 7bd67b402b4893594623c465a7b64b5e43bbf699 Mon Sep 17 00:00:00 2001 From: Jonas Date: Tue, 17 Feb 2026 14:35:21 +0100 Subject: [PATCH] feat: assembler file parsing, parser module beginning --- .../assembler/provided-asm-files/Add.hack | 2 + .../assembler/provided-asm-files/Add.js | 13 +++ .../assembler/src/assembler/assembler.py | 32 ++++++ .../assembler/src/assembler/cli.py | 33 +++++- .../assembler/src/assembler/parser.py | 102 ++++++++++++++++++ 5 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 project-6-assembler/assembler/provided-asm-files/Add.hack create mode 100644 project-6-assembler/assembler/provided-asm-files/Add.js diff --git a/project-6-assembler/assembler/provided-asm-files/Add.hack b/project-6-assembler/assembler/provided-asm-files/Add.hack new file mode 100644 index 0000000..0d17344 --- /dev/null +++ b/project-6-assembler/assembler/provided-asm-files/Add.hack @@ -0,0 +1,2 @@ +// Assembled from Add.asm +// 12 lines processed diff --git a/project-6-assembler/assembler/provided-asm-files/Add.js b/project-6-assembler/assembler/provided-asm-files/Add.js new file mode 100644 index 0000000..8b920a3 --- /dev/null +++ b/project-6-assembler/assembler/provided-asm-files/Add.js @@ -0,0 +1,13 @@ +// this file is for resting purposes only +// This file is part of www.nand2tetris.org +// and the book "The Elements of Computing Systems" +// by Nisan and Schocken, MIT Press. + +// Computes R0 = 2 + 3 (R0 refers to RAM[0]) + +@2 +D=A +@3 +D=D+A +@0 +M=D diff --git a/project-6-assembler/assembler/src/assembler/assembler.py b/project-6-assembler/assembler/src/assembler/assembler.py index e69de29..190af97 100644 --- a/project-6-assembler/assembler/src/assembler/assembler.py +++ b/project-6-assembler/assembler/src/assembler/assembler.py @@ -0,0 +1,32 @@ +from pathlib import Path + +from .parser import Parser + + +def assemble(input_file: Path, output_file: Path): + """ + Assemble a Hack assembly file into machine code. + + Args: + input_file: Path to the .asm file + output_file: Path where the .hack file will be written + """ + # Read input file + with open(input_file, "r") as f: + lines = f.readlines() + + # Parser + parser = Parser(lines) + # while parser.has_more_commands(): # Will raise NotImplementedError + parser.advance() # Does nothing + # cmd_type = parser.command_type() + + # For now, just write a placeholder + # Later we'll implement: + # 1. First pass: build symbol table + # 2. Second pass: generate machine code + + # Output + with open(output_file, "w") as f: + f.write(f"// Assembled from {input_file.name}\n") + f.write(f"// {len(lines)} lines processed\n") diff --git a/project-6-assembler/assembler/src/assembler/cli.py b/project-6-assembler/assembler/src/assembler/cli.py index b577ac2..ee95d84 100644 --- a/project-6-assembler/assembler/src/assembler/cli.py +++ b/project-6-assembler/assembler/src/assembler/cli.py @@ -1,2 +1,33 @@ +import argparse +import sys +from pathlib import Path + +from .assembler import assemble + + def main(): - print("HELLO CAMPIONI") + parser = argparse.ArgumentParser(description="Hack Assembly Language Assembler") + parser.add_argument("input_file", help="Path to .asm file to assemble") + + args = parser.parse_args() + input_path = Path(args.input_file) + + # Validation + if not input_path.exists(): + print(f"Error: File '{input_path}' does not exist", file=sys.stderr) + sys.exit(1) + + if input_path.suffix != ".asm": + print("Error: File must have .asm extension", file=sys.stderr) + sys.exit(1) + + # Generate output path + output_path = input_path.with_suffix(".hack") + + # Assemble + try: + assemble(input_path, output_path) + print(f"Successfully assembled {input_path} -> {output_path}") + except Exception as e: + print(f"Error during assembly: {e}", file=sys.stderr) + sys.exit(1) diff --git a/project-6-assembler/assembler/src/assembler/parser.py b/project-6-assembler/assembler/src/assembler/parser.py index e69de29..bac421a 100644 --- a/project-6-assembler/assembler/src/assembler/parser.py +++ b/project-6-assembler/assembler/src/assembler/parser.py @@ -0,0 +1,102 @@ +class Parser: + def __init__(self, file_lines): + """ + Constructor that takes loaded file content. + + Args: + file_lines: List of strings (lines from the .asm file) + """ + self.lines = file_lines + # Store the file lines + # Initialize current_command index to -1 (before first command) + # No processing happens in constructor for now + + def has_more_commands(self) -> bool: + """ + Check if there are more commands in the input. + + Returns: + bool: True if more commands exist, False otherwise + """ + raise NotImplementedError("hasMoreCommands not yet implemented") + + def advance(self): + """ + Reads the next command and makes it the current command. + Should only be called if hasMoreCommands() is true. + + For now: does nothing (stub) + Later: will skip whitespace/comments and advance to next valid command + """ + print(self.lines) + pass # Does nothing for now + + def command_type(self) -> str: + """ + Returns the type of the current command. + + Returns: + str: "A_COMMAND", "C_COMMAND", or "L_COMMAND" + + Raises: + NotImplementedError: Not yet implemented + """ + raise NotImplementedError("commandType not yet implemented") + + def symbol(self) -> str: + """ + Returns the symbol or decimal value of the current command. + + Should only be called when commandType() is A_COMMAND or L_COMMAND. + - For @Xxx, returns "Xxx" + - For (Xxx), returns "Xxx" + + Returns: + str: The symbol/decimal value + + Raises: + NotImplementedError: Not yet implemented + """ + raise NotImplementedError("symbol not yet implemented") + + def dest(self) -> str: + """ + Returns the dest mnemonic in the current C-command. + + Should only be called when commandType() is C_COMMAND. + + Returns: + str: The dest mnemonic (e.g., "M", "MD", "AMD") + + Raises: + NotImplementedError: Not yet implemented + """ + raise NotImplementedError("dest not yet implemented") + + def comp(self) -> str: + """ + Returns the comp mnemonic in the current C-command. + + Should only be called when commandType() is C_COMMAND. + + Returns: + str: The comp mnemonic (e.g., "D+1", "M-1", "D&A") + + Raises: + NotImplementedError: Not yet implemented + """ + raise NotImplementedError("comp not yet implemented") + + def jump(self) -> str: + """ + Returns the jump mnemonic in the current C-command. + + Should only be called when commandType() is C_COMMAND. + + Returns: + str: The jump mnemonic (e.g., "JGT", "JEQ", "JMP") or empty string + + Raises: + NotImplementedError: Not yet implemented + """ + raise NotImplementedError("jump not yet implemented")