feat: class clean up. mneumonics for diff commands. comp, jmp, dest. symbol table start.

This commit is contained in:
2026-02-18 14:58:31 +01:00
parent 7bd67b402b
commit d51a392c1e
6 changed files with 239 additions and 27 deletions

View File

@@ -1,32 +1,86 @@
from pathlib import Path from pathlib import Path
from .parser import Parser from assembler.parser import Parser
from .symbol_table import SymbolTable
def assemble(input_file: Path, output_file: Path): class Assembler:
""" """
Assemble a Hack assembly file into machine code. Main assembler that coordinates the two-pass assembly process.
Args: Attributes:
input_file: Path to the .asm file input_file: Path to the .asm input file
output_file: Path where the .hack file will be written output_file: Path to the .hack output file
parser: Parser instance for reading commands
symbol_table: SymbolTable instance for managing symbols
binary_instructions: List of translated binary instructions
""" """
# Read input file
with open(input_file, "r") as f:
lines = f.readlines()
# Parser def __init__(self, input_file: Path, output_file: Path):
parser = Parser(lines) """Initialize assembler with input/output paths."""
# while parser.has_more_commands(): # Will raise NotImplementedError # File paths
parser.advance() # Does nothing self.input_file: Path = input_file
# cmd_type = parser.command_type() self.output_file: Path = output_file
# For now, just write a placeholder # Components (create now)
# Later we'll implement: self.symbol_table: SymbolTable = SymbolTable()
# 1. First pass: build symbol table
# 2. Second pass: generate machine code
# Output # Data structures (empty, will fill later)
with open(output_file, "w") as f: self.binary_instructions: list[str] = []
f.write(f"// Assembled from {input_file.name}\n")
f.write(f"// {len(lines)} lines processed\n") # Counters
self.next_variable_address = 16
# Will be created later
self.parser: Parser
self.lines: list[str] = []
with open(input_file, "r") as f:
self.lines = f.readlines()
raise NotImplementedError("Not implemented")
def assemble(self) -> None:
"""
Main assembly process - coordinates first and second pass.
Public API method that CLI will call.
"""
def _first_pass(self) -> None:
"""
First pass: Build symbol table with label addresses.
Scans through code to find all (LABEL) declarations.
"""
raise NotImplementedError("Not implemented")
def _second_pass(self) -> None:
"""
Second pass: Translate instructions to binary.
Handles A-commands, C-commands, and resolves symbols.
"""
raise NotImplementedError("Not implemented")
def _translate_a_command(self, symbol: str) -> str:
"""
Translate A-command to 16-bit binary.
Handles: numeric constants, predefined symbols, variables.
"""
raise NotImplementedError("Not implemented")
def _translate_c_command(self) -> str:
"""
Translate C-command to 16-bit binary.
Format: 111 a c1c2c3c4c5c6 d1d2d3 j1j2j3
"""
raise NotImplementedError("Not implemented")
def _write_output(self) -> None:
"""Write binary instructions to output file."""
with open(self.output_file, "w") as f:
f.write(f"// Assembled from {self.input_file.name}\n")
f.write(f"// {len(self.lines)} lines processed\n")
raise NotImplementedError("Not implemented")
def _assembled_success(self) -> None:
"""Success Message"""
print(f"Successfully assembled {self.input_file} -> {self.output_file}")

View File

@@ -2,7 +2,7 @@ import argparse
import sys import sys
from pathlib import Path from pathlib import Path
from .assembler import assemble from .assembler import Assembler
def main(): def main():
@@ -26,8 +26,9 @@ def main():
# Assemble # Assemble
try: try:
assemble(input_path, output_path) # Assemble
print(f"Successfully assembled {input_path} -> {output_path}") assembler = Assembler(input_path, output_path)
assembler.assemble()
except Exception as e: except Exception as e:
print(f"Error during assembly: {e}", file=sys.stderr) print(f"Error during assembly: {e}", file=sys.stderr)
sys.exit(1) sys.exit(1)

View File

@@ -0,0 +1,59 @@
from .code_tables import DEST_TABLE, COMP_TABLE, JUMP_TABLE
class Code:
"""Translates Hack assembly mnemonics into binary codes."""
@staticmethod
def dest(mnemonic: str) -> str:
"""
Returns the 3-bit binary code for the dest mnemonic.
Args:
mnemonic: Destination mnemonic (e.g., "M", "D", "MD", "AMD")
Returns:
3-bit binary string (e.g., "010", "111")
Raises:
KeyError: If mnemonic is invalid
"""
# Use dictionary lookup table
print(mnemonic)
raise NotImplementedError("not implemented")
@staticmethod
def comp(mnemonic: str) -> str:
"""
Returns the 7-bit binary code for the comp mnemonic.
Args:
mnemonic: Computation mnemonic (e.g., "D+1", "M-1", "D&A")
Returns:
7-bit binary string (e.g., "0001111", "1110000")
Raises:
KeyError: If mnemonic is invalid
"""
print(mnemonic)
raise NotImplementedError("not implemented")
# Use dictionary lookup table
@staticmethod
def jump(mnemonic: str) -> str:
"""
Returns the 3-bit binary code for the jump mnemonic.
Args:
mnemonic: Jump mnemonic (e.g., "JGT", "JEQ", "JMP", or "" for no jump)
Returns:
3-bit binary string (e.g., "001", "111")
Raises:
KeyError: If mnemonic is invalid
"""
print(mnemonic)
raise NotImplementedError("not implemented")
# Use dictionary lookup table

View File

@@ -0,0 +1,52 @@
DEST_TABLE = {
"": "000",
"M": "001",
"D": "010",
"MD": "011",
"A": "100",
"AM": "101",
"AD": "110",
"AMD": "111",
}
JUMP_TABLE = {
"": "000",
"JGT": "001",
"JEQ": "010",
"JGE": "011",
"JLT": "100",
"JNE": "101",
"JLE": "110",
"JMP": "111",
}
COMP_TABLE = {
# Constant and register operations (a=0)
"0": "0101010",
"1": "0111111",
"-1": "0111010",
"D": "0001100",
"A": "0110000",
"!D": "0001101",
"!A": "0110001",
"-D": "0001111",
"-A": "0110011",
"D+1": "0011111",
"A+1": "0110111",
"D-1": "0001110",
"A-1": "0110010",
"D+A": "0000010",
"D-A": "0010011",
"A-D": "0000111",
"D&A": "0000000",
"D|A": "0010101",
# Memory-based operations (a=1)
"M": "1110000",
"!M": "1110001",
"-M": "1110011",
"M+1": "1110111",
"M-1": "1110010",
"D+M": "1000010",
"D-M": "1010011",
"M-D": "1000111",
"D&M": "1000000",
"D|M": "1010101",
}

View File

@@ -41,7 +41,7 @@ class Parser:
Raises: Raises:
NotImplementedError: Not yet implemented NotImplementedError: Not yet implemented
""" """
raise NotImplementedError("commandType not yet implemented") raise NotImplementedError("command_type not yet implemented")
def symbol(self) -> str: def symbol(self) -> str:
""" """
@@ -63,7 +63,7 @@ class Parser:
""" """
Returns the dest mnemonic in the current C-command. Returns the dest mnemonic in the current C-command.
Should only be called when commandType() is C_COMMAND. Should only be called when command_type () is C_COMMAND.
Returns: Returns:
str: The dest mnemonic (e.g., "M", "MD", "AMD") str: The dest mnemonic (e.g., "M", "MD", "AMD")

View File

@@ -0,0 +1,46 @@
class SymbolTable:
def __init__(self):
"""
Initialize dictionary with predefined Hack platform symbols
"""
self.symbols = {
"SP": 0,
"LCL": 1,
"ARG": 2,
"THIS": 3,
"THAT": 4,
"R0": 0,
"R1": 1,
"R2": 2,
"R3": 3,
"R4": 4,
"R5": 5,
"R6": 6,
"R7": 7,
"R8": 8,
"R9": 9,
"R10": 10,
"R11": 11,
"R12": 12,
"R13": 13,
"R14": 14,
"R15": 15,
"SCREEN": 16384,
"KBD": 24576,
}
def add_entry(self, symbol: str, address: int) -> None:
print(symbol)
print(address)
# Add symbol-address pair to dictionary
raise NotImplementedError("add_entru not yet implemented")
def contains(self, symbol: str) -> bool:
print(symbol)
raise NotImplementedError("contains not yet implemented")
# Check if symbol exists in dictionary
def get_address(self, symbol: str) -> int:
print(symbol)
raise NotImplementedError("get_address not yet implemented")
# Return address for symbol (raises KeyError if not found)