def test_value_error_on_empty_input():
    txt = "// this is a comment\n\n\n // Another comment //\n // "

    with pytest.raises(ValueError) as e_info:
        Parser(txt)

    assert str(e_info.value) == "The input must contain some code"
def test_assembler_with_max_l_file():
    code = load_file("MaxL.asm")
    parser = Parser(code)
    assembler = Assembler(parser)

    output = assembler.assemble()
    expected = load_file("MaxL.hack").splitlines()

    assert output == expected
def test_assembler_with_add():
    code = "// Compute RAM[0] = 2 + 3\n@2\nD = A\n@3\n\nD = A+ D\n@0\nM=d"
    parser = Parser(code)
    assembler = Assembler(parser)

    output = assembler.assemble()
    expected = [
        "0000000000000010",
        "1110110000010000",
        "0000000000000011",
        "1110000010010000",
        "0000000000000000",
        "1110001100001000",
    ]

    assert output == expected
def test_parser_with_add_asm_file():
    code = load_file("Add.asm")
    parser = Parser(code)

    expected_commands = ["@2", "D=A", "@3", "D=D+A", "@0", "M=D"]
    expected_types = [
        CommandType.A_COMMAND,
        CommandType.C_COMMAND,
        CommandType.A_COMMAND,
        CommandType.C_COMMAND,
        CommandType.A_COMMAND,
        CommandType.C_COMMAND,
    ]
    expected_symbols = ["2", None, "3", None, "0", None]
    expected_dest = [None, "D", None, "D", None, "M"]
    expected_comp = [None, "A", None, "D+A", None, "D"]

    zip_obj = zip(
        expected_commands,
        expected_types,
        expected_symbols,
        expected_dest,
        expected_comp,
    )

    for i, (command, tp, symbol, dest, comp) in enumerate(zip_obj):
        assert parser.has_more_commands()
        assert parser.counter == i
        comm = parser.current_command
        assert comm == command
        assert parser.command_type(comm) == tp
        if symbol is None:
            assert parser.dest == dest
            assert parser.comp == comp
            assert parser.jmp == ""
            with pytest.raises(ValueError):
                _ = parser.symbol
        else:
            assert parser.symbol == symbol
            with pytest.raises(ValueError):
                _ = parser.dest
                _ = parser.comp
                _ = parser.jmp

        parser.advance()

    assert parser.counter == 6
    assert not parser.has_more_commands()
Exemple #5
0
def assemble(
    filepth: Path = Argument(
        ...,
        exists=True,
        file_okay=True,
        dir_okay=True,
        readable=True,
        resolve_path=True,
    ),
    out: Path = Option(None),
):
    if filepth.suffix != ".asm":
        typer.echo("The file name must end with `.asm`")
        raise typer.Exit(code=1)

    if out is None:
        out = filepth.parent.joinpath(f"{filepth.stem}.hack")

    try:
        parser = Parser(filepth.read_text())
    except ValueError as err:
        typer.echo(err)
        raise typer.Exit(1)

    assembler = Assembler(parser)

    try:
        assembly = assembler.assemble()
    except (InvalidCommandException, InvalidMnemonicError, AddressOutOfRange) as err:
        typer.echo(err)
        raise typer.Exit(code=1)

    typer.echo(f"Writing to {out}")
    with out.open("w") as f:
        f.writelines([x + "\n" for x in assembly])

    typer.echo("Done")
def test_valid_l_commands(parser: Parser, command: str, symbol: str):
    assert parser.command_type(command) is CommandType.L_COMMAND
    assert parser.symbol == symbol
def generate_valid_parser():
    txt = "//comment\n\n@23\nA=D\n\n(END)\nM = A + D\n// Another comment \n"
    parser = Parser(txt)

    return parser
def test_parser_process_func_with_(txt: str, expected: str):
    processed = Parser.process(txt)

    assert processed == expected
def test_parser_with_max_asm_file():
    code = load_file("Max.asm")
    parser = Parser(code)

    expected_commands = [
        "@R0",
        "D=M",
        "@R1",
        "D=D-M",
        "@OUTPUT_FIRST",
        "D;JGT",
        "@R1",
        "D=M",
        "@OUTPUT_D",
        "0;JMP",
        "(OUTPUT_FIRST)",
        "@R0",
        "D=M",
        "(OUTPUT_D)",
        "@R2",
        "M=D",
        "(INFINITE_LOOP)",
        "@INFINITE_LOOP",
        "0;JMP",
    ]
    expected_types = [
        CommandType.A_COMMAND,
        CommandType.C_COMMAND,
        CommandType.A_COMMAND,
        CommandType.C_COMMAND,
        CommandType.A_COMMAND,
        CommandType.C_COMMAND,
        CommandType.A_COMMAND,
        CommandType.C_COMMAND,
        CommandType.A_COMMAND,
        CommandType.C_COMMAND,
        CommandType.L_COMMAND,
        CommandType.A_COMMAND,
        CommandType.C_COMMAND,
        CommandType.L_COMMAND,
        CommandType.A_COMMAND,
        CommandType.C_COMMAND,
        CommandType.L_COMMAND,
        CommandType.A_COMMAND,
        CommandType.C_COMMAND,
    ]
    expected_symbols = [
        "R0",
        None,
        "R1",
        None,
        "OUTPUT_FIRST",
        None,
        "R1",
        None,
        "OUTPUT_D",
        None,
        "OUTPUT_FIRST",
        "R0",
        None,
        "OUTPUT_D",
        "R2",
        None,
        "INFINITE_LOOP",
        "INFINITE_LOOP",
        None,
    ]
    expected_dest = [
        None,
        "D",
        None,
        "D",
        None,
        "",
        None,
        "D",
        None,
        "",
        None,
        None,
        "D",
        None,
        None,
        "M",
        None,
        None,
        "",
    ]
    expected_comp = [
        None,
        "M",
        None,
        "D-M",
        None,
        "D",
        None,
        "M",
        None,
        "0",
        None,
        None,
        "M",
        None,
        None,
        "D",
        None,
        None,
        "0",
    ]

    expected_jmp = [
        None,
        "",
        None,
        "",
        None,
        "JGT",
        None,
        "",
        None,
        "JMP",
        None,
        None,
        "",
        None,
        None,
        "",
        None,
        None,
        "JMP",
    ]

    zip_obj = zip(
        expected_commands,
        expected_types,
        expected_symbols,
        expected_dest,
        expected_comp,
        expected_jmp,
    )

    for i, (command, tp, symbol, dest, comp, jmp) in enumerate(zip_obj):
        assert parser.has_more_commands()
        assert parser.counter == i
        comm = parser.current_command
        assert comm == command
        assert parser.command_type(comm) == tp
        if symbol is None:
            assert parser.dest == dest
            assert parser.comp == comp
            assert parser.jmp == jmp
            with pytest.raises(ValueError):
                _ = parser.symbol
        else:
            assert parser.symbol == symbol
            with pytest.raises(ValueError):
                _ = parser.dest
                _ = parser.comp
                _ = parser.jmp

        parser.advance()

    assert parser.counter == 19
    assert not parser.has_more_commands()
def test_parser_with_max_L_asm_file():
    code = load_file("MaxL.asm")
    parser = Parser(code)

    expected_commands = [
        "@0",
        "D=M",
        "@1",
        "D=D-M",
        "@10",
        "D;JGT",
        "@1",
        "D=M",
        "@12",
        "0;JMP",
        "@0",
        "D=M",
        "@2",
        "M=D",
        "@14",
        "0;JMP",
    ]
    expected_types = [
        CommandType.A_COMMAND,
        CommandType.C_COMMAND,
        CommandType.A_COMMAND,
        CommandType.C_COMMAND,
        CommandType.A_COMMAND,
        CommandType.C_COMMAND,
        CommandType.A_COMMAND,
        CommandType.C_COMMAND,
        CommandType.A_COMMAND,
        CommandType.C_COMMAND,
        CommandType.A_COMMAND,
        CommandType.C_COMMAND,
        CommandType.A_COMMAND,
        CommandType.C_COMMAND,
        CommandType.A_COMMAND,
        CommandType.C_COMMAND,
    ]
    expected_symbols = [
        "0",
        None,
        "1",
        None,
        "10",
        None,
        "1",
        None,
        "12",
        None,
        "0",
        None,
        "2",
        None,
        "14",
        None,
    ]
    expected_dest = expected_dest = [
        None,
        "D",
        None,
        "D",
        None,
        "",
        None,
        "D",
        None,
        "",
        None,
        "D",
        None,
        "M",
        None,
        "",
    ]

    expected_comp = [
        None,
        "M",
        None,
        "D-M",
        None,
        "D",
        None,
        "M",
        None,
        "0",
        None,
        "M",
        None,
        "D",
        None,
        "0",
    ]

    expected_jmp = [
        None,
        "",
        None,
        "",
        None,
        "JGT",
        None,
        "",
        None,
        "JMP",
        None,
        "",
        None,
        "",
        None,
        "JMP",
    ]

    zip_obj = zip(
        expected_commands,
        expected_types,
        expected_symbols,
        expected_dest,
        expected_comp,
        expected_jmp,
    )

    for i, (command, tp, symbol, dest, comp, jmp) in enumerate(zip_obj):
        assert parser.has_more_commands()
        assert parser.counter == i
        comm = parser.current_command
        assert comm == command
        assert parser.command_type(comm) == tp
        if symbol is None:
            assert parser.dest == dest
            assert parser.comp == comp
            assert parser.jmp == jmp
            with pytest.raises(ValueError):
                _ = parser.symbol
        else:
            assert parser.symbol == symbol
            with pytest.raises(ValueError):
                _ = parser.dest
                _ = parser.comp
                _ = parser.jmp

        parser.advance()

    assert parser.counter == 16
    assert not parser.has_more_commands()
def test_parser_with_proper_code():
    code = (
        "// this is a comment\n\n\n // Another comment //  \n"
        " @value // comment\nA = A + D \n(LOOP)\n//comm\n\nD;jle\n"
    )

    parser = Parser(code)

    # Raise error if we try to access anything right now
    for attrib in ["symbol", "dest", "comp", "jmp"]:
        with pytest.raises(ValueError):
            getattr(parser, attrib)

    assert parser.counter == 0
    assert parser.line_idx == 0
    assert parser.has_more_commands()
    command = parser.current_command
    assert parser.command_type(command) == CommandType.A_COMMAND
    assert parser.symbol == "value"
    with pytest.raises(ValueError):
        _ = parser.jmp
        _ = parser.dest
        _ = parser.comp

    parser.advance()

    assert parser.counter == 1
    assert parser.line_idx == 1
    assert parser.has_more_commands()
    command = parser.current_command
    assert parser.command_type(command) == CommandType.C_COMMAND
    assert parser.dest == "A"
    assert parser.comp == "A+D"
    assert parser.jmp == ""
    with pytest.raises(ValueError):
        _ = parser.symbol

    parser.advance()

    assert parser.counter == 2
    assert parser.line_idx == 2
    assert parser.has_more_commands()
    command = parser.current_command
    assert parser.command_type(command) == CommandType.L_COMMAND
    assert parser.symbol == "LOOP"
    with pytest.raises(ValueError):
        _ = parser.jmp
        _ = parser.dest
        _ = parser.comp

    parser.advance()

    assert parser.counter == 3
    assert parser.line_idx == 2
    assert parser.has_more_commands()
    command = parser.current_command
    assert parser.command_type(command) == CommandType.C_COMMAND
    assert parser.dest == ""
    assert parser.comp == "D"
    assert parser.jmp == "JLE"
    with pytest.raises(ValueError):
        _ = parser.symbol

    parser.advance()
    assert parser.counter == 4
    assert parser.line_idx == 3
    assert not parser.has_more_commands()
    with pytest.raises(ValueError):
        _ = parser.current_command
def test_invalid_commands(parser: Parser, command: str):
    with pytest.raises(InvalidCommandException):
        parser.command_type(command)
def test_valid_c_commands(parser: Parser):
    for command, dest, comp, jmp in possible_c_commands:
        assert parser.command_type(command) is CommandType.C_COMMAND
        assert parser.comp == comp
        assert parser.dest == dest
        assert parser.jmp == jmp
def test_addr_out_of_range():
    parser = Parser("@24579")
    assembler = Assembler(parser)

    with pytest.raises(AddressOutOfRange):
        _ = assembler.assemble()