feat: assembler file parsing, parser module beginning
This commit is contained in:
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user