コンテンツにスキップ

027: 「ラベルは番地を忘れさせる」の解答

問題 / ヒント

難易度: ☆☆☆

方針

ラベルは、後ろにある番地を前から参照できるようにする仕組みです。 そのため、先にすべてのラベルと番地を集めます。 2回目の走査で、命令やDataを実際の整数に変換します。

実装

def clean_line(line):
    return line.split(";", 1)[0].strip()


def split_label(line):
    if ":" not in line:
        return (None, line)
    label, rest = line.split(":", 1)
    return (label.strip(), rest.strip())


def operand_value(text, labels):
    try:
        return int(text)
    except ValueError:
        return labels[text]


def assemble_program(lines):
    labels = {}
    address = 0
    cleaned_lines = []

    for raw_line in lines:
        line = clean_line(raw_line)
        if line == "":
            continue
        label, body = split_label(line)
        if label is not None:
            if label in labels:
                raise ValueError("duplicate label: " + label)
            labels[label] = address
        if body != "":
            cleaned_lines.append(body)
            address += 1

    program = []
    for body in cleaned_lines:
        if body == "Stop":
            program.append(encode_instruction("Jump", 0))
            continue

        parts = body.split()
        if parts[0] == "Data":
            program.append(int(parts[1]))
        else:
            name = parts[0]
            operand = operand_value(parts[1], labels)
            program.append(encode_instruction(name, operand))

    return program

確認

program = assemble_program([
    "Load value",
    "Shift 1",
    "Store value",
    "Write value",
    "Stop",
    "value: Data 21",
])
assert run(program) == [42]

発展

ラベル名が未定義の場合は、現在の実装では辞書アクセスによる KeyError が起こります。 利用者向けのエラーにしたい場合は、未定義ラベルを明示して ValueError を送出します。