Что проще - найти или написать самому транслятор небольшого языка?
Мне понадобился транслятор с какого-нибудь языка типа Small-C или Pascal-S на FASM. Требования:
- Есть исходник на C с комментами.
- Генерирует код на FASM32 не ассоциированный с какой-либо платформой.
- Может транслировать сам себя (в переводе) - т. е. в дальнейшем понадобиться раскрутка.
- Транслирует за один проход.
- Без malloc и прочей библиотечной ерунды (или, по крайней мере, malloc можно убрать).
- Ввод-вывод кода через консоль.
- Ок. 1000 строк.
С таким списком проще, конечно самому написать.
Но может кто знает? Хотя бы чтобы подглядывать, когда буду писать свой.
Ответ на этот вопрос, даст приблизительно ответ на ваш вопрос.
А ещё лучше вот это: http://progbook.ru/technologiya-programmirovaniya/798-sverdlov-yazyki-programmirovaniya-i-metody.html
Я когда-то, начиная интересоваться трансляторами, много всякого читал по теории формальных языков и мне казалось, что разобраться во всей этой клинописи нормальному человеку совершенно невозможно. Когда же мне попалась эта книга, (у меня бумажный вариант) я её прочитал, а потом сел и за вечер написал целочисленный Бейсик.
Мне понадобился транслятор с какого-нибудь языка типа Small-C или Pascal-S на FASM. Требования:
- Есть исходник на C с комментами.
- Генерирует код на FASM32 не ассоциированный с какой-либо платформой.
- Может транслировать сам себя (в переводе) - т. е. в дальнейшем понадобиться раскрутка.
- Транслирует за один проход.
- Без malloc и прочей библиотечной ерунды (или, по крайней мере, malloc можно убрать).
- Ввод-вывод кода через консоль.
- Ок. 1000 строк.
С таким списком проще, конечно самому написать.
Но может кто знает? Хотя бы чтобы подглядывать, когда буду писать свой.
1) Использование Си в деле написания трансляторов - верный способ так и не завершить проект.
2) Прямая гненерация кода для стеково-регистровой машины - верный способ взорвать себе мозг и не завершить проект (особенно при условии пункта 1).
3) Зачем вам раскрутка?
4) Однопроходная трансляция - утопия. В нормальных компиляторах используется множество проходов, на вскидку: разбор исходников и построение AST, типизация полученного AST (связывание, вывод типов), трансляция типизированного AST в абстрактный код машины стековой архитуектуры (тоже своего рода дерево), трансляция полученного кода в требуемую архитектуру. В целом же работает правило: чем сложнее язык - тем больше проходов.
5) Неясное требование про маллоки :)
6) С таким количеством кода нужно смотреть на ФП-шные трансляторы.
Предлагаю начать отсюда.
Кстати, я по циклу этих статей учился :) Подход описанный в ней сильно устарел.
4) Однопроходная трансляция - утопия. В нормальных компиляторах используется множество проходов, на вскидку: разбор исходников и построение AST, типизация полученного AST (связывание, вывод типов), трансляция типизированного AST в абстрактный код машины стековой архитуектуры (тоже своего рода дерево), трансляция полученного кода в требуемую архитектуру. В целом же работает правило: чем сложнее язык - тем больше проходов.
Думаю, что ты заблуждаешься. Поясню на примере. Да, мы даже ассемблер в машинный код не переведём за один проход, так как есть goto с меткой, адрес которой не известен, так как она появится позже. Нужно хотя бы два прохода.
Но почему же трансляция должна производиться в машинный код? Мы же можем транслировать код одного диалекта ассемблера в код другого. Тут проблемы с goto может не быть и будет возможен один проход.
А если посмотреть на C, то видно, что он не так уж и далеко ушел от ассемблера. Так что не очевидно, что не удастся его транслировать в ассемблер за один проход. Могут возникнуть проблемы с оптимизацией, но это тонкости. В общем, надо смотреть что во что транслируем перед тем как про утопии говорить.
Ассемблер транслируется в машкод (соответствующий ему) в один проход - при достижении метки просто возвращаемся назад и вставляем адрес. ;)
Надобы конкретизировать что понимается под проходом. Проход компиляции - это обход AST компилируемой программы.
Си транслировать в машкод в один проход можно. Именно для этого в Си есть идиотизм с #include-ами и форвард объявлениями функций. Просто язык проектировался в то время, когда "один проход" считался комильфо.
А сейчас, комильфо, это вприсядку и с комбинаторным взрывом грамматики?
PS: 418ImATeapot, используйте существующие решения, возможно, с модификацией.
Так не будет никакого выигрыша, если у нас спагетти-код. Придется искать все переходы в пройденном коде для каждой метки. Два прохода проще и надёжнее.
Если взять грубое определение, то при однопроходной трансляции каждую строку входного кода можем сразу транслировать в выходной код (при том, что помним данные предыдущих строк). То есть возвращаться назад не надо. В многопроходной трансляции надо несколько раз анализировать программу (модуль) целиком, получая в каждом проходе промежуточный код. Как-то так.
Так это и требовалось автору темы.
Вприсядку - это если парсер руками и весь анализ в одном проходе на языке без сопоставления с образцом :) Ааа, ты про рукопашный недо-PEG Из цикла D.Mon-а? Такие вещи элементарно решаются мемоизацией (что в статье и было сделано), которую в той или иной форме применяют все нормальные PEG генераторы парсеров (лично написал PEG-грамматику C# 4.0, по которой сгенерировался вполне шустрый парсер).
Решил использовать два прохода:
Code->Inv. Polish->FASM
Уже начал писать, всем спасибо, кто ответил.
А сейчас, комильфо, это вприсядку и с комбинаторным взрывом грамматики?
Вприсядку - это если парсер руками и весь анализ в одном проходе на языке без сопоставления с образцом Ааа, ты про рукопашный недо-PEG Из цикла D.Mon-а? Такие вещи элементарно решаются мемоизацией (что в статье и было сделано), которую в той или иной форме применяют все нормальные PEG генераторы парсеров (лично написал PEG-грамматику C# 4.0, по которой сгенерировался вполне шустрый парсер).