-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Resolves #6 implement asm translator with documentation #7
Changes from 2 commits
6d323b0
18bb62b
48fe030
4d4d984
f694ab7
cb30fa4
20b4098
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,73 @@ | ||||||||||||||||||||||||||||||||||||||||||
# Язык программирования Asm | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
Синтаксис в расширенной БНФ. | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
- `[ ... ]` -- вхождение 0 или 1 раз | ||||||||||||||||||||||||||||||||||||||||||
- `{ ... }` -- повторение 0 или несколько раз | ||||||||||||||||||||||||||||||||||||||||||
- `{ ... }-` -- повторение 1 или несколько раз | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
``` ebnf | ||||||||||||||||||||||||||||||||||||||||||
program ::= { line } | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
line ::= [ label ] [ instr ] [ comment ] "\n" | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
label ::= label_name ":" | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
instr ::= "increment" | ||||||||||||||||||||||||||||||||||||||||||
| "decrement" | ||||||||||||||||||||||||||||||||||||||||||
| "right" | ||||||||||||||||||||||||||||||||||||||||||
| "left" | ||||||||||||||||||||||||||||||||||||||||||
| "print" | ||||||||||||||||||||||||||||||||||||||||||
| "input" | ||||||||||||||||||||||||||||||||||||||||||
| "jmp" arg | ||||||||||||||||||||||||||||||||||||||||||
| "jz" arg | ||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
arg ::= integer | ||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we really need |
||||||||||||||||||||||||||||||||||||||||||
| label_name | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
integer ::= [ "-" ] { <any of "0-9"> }- | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
label_name ::= <any of "a-z A-Z _"> { <any of "a-z A-Z 0-9 _"> } | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
comment ::= ";" <any symbols except "\n"> | ||||||||||||||||||||||||||||||||||||||||||
``` | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
Поддерживаются однострочные комментарии, начинающиеся с `;`. | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
Операции: | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
- `increment` -- увеличить значение в текущей ячейке на 1 | ||||||||||||||||||||||||||||||||||||||||||
- `decrement` -- уменьшить значение в текущей ячейке на 1 | ||||||||||||||||||||||||||||||||||||||||||
- `right` -- перейти к следующей ячейке | ||||||||||||||||||||||||||||||||||||||||||
- `left` -- перейти к предыдущей ячейке | ||||||||||||||||||||||||||||||||||||||||||
- `print` -- напечатать значение из текущей ячейки (символ) | ||||||||||||||||||||||||||||||||||||||||||
- `input` -- ввести извне значение и сохранить в текущей ячейке (символ) | ||||||||||||||||||||||||||||||||||||||||||
- `jmp addr` -- безусловный переход по заданному адресу или метке | ||||||||||||||||||||||||||||||||||||||||||
- `jz addr` -- условный переход по заданному адресу или метке, если значение текущей ячейки ноль | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
Перед операцией можно определить метку: | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
``` asm | ||||||||||||||||||||||||||||||||||||||||||
label: increment | ||||||||||||||||||||||||||||||||||||||||||
``` | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
И в другом месте (неважно, до или после определения) сослаться на эту метку: | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
``` asm | ||||||||||||||||||||||||||||||||||||||||||
jmp label ; --> `jmp 123`, где 123 - номер инструкции после объявления метки | ||||||||||||||||||||||||||||||||||||||||||
``` | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
Транслятор поставит на место использования метки адрес той инструкции, перед которой она определена. | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
В программе не может быть дублирующихся меток, определенных в разных местах с одним именем. | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
Пример кода на Asm см. в файле [./examples/cat.asm](./examples/cat.asm). | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
## Транслятор | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
Интерфейс командной строки: `translator_asm.py <input_file> <target_file>` | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
Реализован в модуле [translator_asm](./translator_asm.py) | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
Ассемблерные мнемоники один в один транслируются в машинные команды. | ||||||||||||||||||||||||||||||||||||||||||
Метки из программы исчезают, а на место обращений к ним подставляются конкретные адреса. | ||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We need golden tests for the translator. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Two paragraphs? |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
; Данный пример генерирует идентичный машинный код, что и программа на brainfuck: | ||
; | ||
; ,[.,] | ||
; | ||
; Каждый символ brainfuck соответствует одной инструкции на Asm. | ||
input ; , | ||
loop: jz break ; [ | ||
print ; . | ||
input ; , | ||
jmp loop ; ] | ||
break: halt ; конец файла |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
#!/usr/bin/python3 | ||
"""Транслятор Asm в машинный код. | ||
""" | ||
|
||
import re | ||
import sys | ||
|
||
from isa import Opcode, Term, write_code | ||
|
||
|
||
def translate(text): | ||
"""Трансляция текста программы на Asm в машинный код. | ||
|
||
Выполняется в два этапа: | ||
|
||
1. Разбор текста на метки и инструкции. | ||
|
||
2. Подстановка адресов меток в инструкции. | ||
""" | ||
labels, instrs = parse(text) | ||
|
||
for instr in instrs: | ||
if "arg" in instr and isinstance(instr["arg"], str): | ||
# у инструкции есть аргумент, и этот аргумент - строка (название метки) | ||
label = instr["arg"] | ||
assert label in labels, "Undefined label: {}".format(label) | ||
# подставляем адрес | ||
instr["arg"] = labels[label] | ||
|
||
return instrs | ||
|
||
|
||
def parse(text): | ||
"""Разбор текста на словарь с определением меток и список машинных инструкций.""" | ||
pc = 0 | ||
labels = {} | ||
instrs = [] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rename to |
||
|
||
for line_num, raw_line in enumerate(text.splitlines(), 1): | ||
line = remove_comment(raw_line) | ||
|
||
line, label = parse_label(line) | ||
if label is not None: | ||
assert label not in labels, "Redefinition of label: {}".format(label) | ||
# адрес метки - адрес следующей инструкции | ||
labels[label] = pc | ||
|
||
if not line: | ||
# на строке нет инструкции | ||
continue | ||
|
||
# вычисляем номер столбца, с которого начинается инструкция | ||
col = raw_line.find(line) + 1 | ||
term = Term(line_num, col, line) | ||
|
||
instr = parse_instr(line, pc, term) | ||
instrs.append(instr) | ||
pc += 1 | ||
|
||
return labels, instrs | ||
|
||
|
||
def remove_comment(line): | ||
"""Удаляет комментарий, начинающийся с `;` из строки.""" | ||
return line.split(";", 1)[0].strip() | ||
|
||
|
||
def is_valid_label_name(name): | ||
"""Проверяет название метки на корректность синтаксиса.""" | ||
return re.match(r"^[a-zA-Z_][a-zA-Z0-9_]*$", name) | ||
|
||
|
||
def parse_label(line): | ||
"""Достает из строки метку, если она присутствует, и проверяет ее на корректность. | ||
|
||
Возвращает остаток строки и название метки (или `None`, если метки нет)""" | ||
parts = line.split(":", 1) | ||
if len(parts) == 1: | ||
return line, None | ||
|
||
label = parts[0].strip() | ||
rest = parts[1].strip() | ||
|
||
assert is_valid_label_name(label), "Invalid label name: {}".format(label) | ||
|
||
return rest, label | ||
|
||
|
||
def parse_instr(line, index, term): | ||
"""Разбирает инструкцию из строки. Конвертирует мнемонику в опкод, а аргумент - в число.""" | ||
parts = line.split(None) | ||
|
||
mnemonic = parts[0] | ||
opcode = Opcode(mnemonic) | ||
|
||
arg = parts[1] if len(parts) > 1 else None | ||
if arg is not None: | ||
assert opcode == Opcode.JZ or opcode == Opcode.JMP, "Only `jz` and `jnz` instructions take an argument" | ||
assert len(parts) == 2, "Trailing characters" | ||
|
||
if not is_valid_label_name(arg): | ||
arg = int(arg) | ||
|
||
# порядок полей такой же, как в трансляторе brainfuck | ||
return {"index": index, "opcode": opcode, "arg": arg, "term": term} | ||
|
||
return {"index": index, "opcode": opcode, "term": term} | ||
|
||
|
||
def main(source, target): | ||
"""Функция запуска транслятора. Параметры -- исходный и целевой файлы.""" | ||
with open(source, encoding="utf-8") as f: | ||
source = f.read() | ||
|
||
code = translate(source) | ||
|
||
write_code(target, code) | ||
print("source LoC:", len(source.split("\n")), "code instr:", len(code)) | ||
|
||
|
||
if __name__ == "__main__": | ||
assert len(sys.argv) == 3, "Wrong arguments: translator_asm.py <input_file> <target_file>" | ||
_, source, target = sys.argv | ||
main(source, target) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be more simple: