LINUX.ORG.RU

A Nanopass Framework for Commercial Compiler Development

 , ,


2

3

/cast true_admin; анонимус, толкавший nanopass народу; другой умный анонимус

Мне одному кажется, что оригинальной реализации сабжа от Эндрю Кипа и компании для commercial compiler development недостаёт хоть чего-то для обработки ошибок и реализации макросов?

Для сообщений об ошибках надо писать свой парсер и подпиливать грамматику языка, чтобы прямо в программе сохранять информацию о положении конструкций в исходнике. Для макросов надо вообще городить свои pattern matcher и code walker, потому что всё оригинальное добро на клёвом compile-time syntax-case и — сюрприз! — не позволяет доопределять языки в рантайме.

Итого что в осадке остаётся? Разве что идеи о том, 1) что маленькие специализированные проходы удобнее комбинировать и дорабатывать, чем большие монолитные, 2) что eDSL лучше сраной кодогенерации, 3) что pattern matching удобен для написания трансляторов. Ну, блин, это вроде и так очевидные вещи.

Есть, правда, ещё идея о постоянном сохранении синтаксической корректности преобразований, но у меня есть некоторые сомнения в её радикальной полезности, ведь с понижением уровня языка и сокращением количества конструкций становится всё легче выдавать синтаксически корректную околесицу.

В общем, правы были те ребята из ICFP о том, что nanopass годится только как наглядное пособие для обучения. Даже во второй инкарнации. Ну, или как, э-э-э... pattern matcher для бек-энда, если закрыть глаза на обработку ошибок.

Я уж думал, появилось хоть что-то, уменьшающее количество ad-hoc решений при реализации языков. Но, похоже, создание подходящих утилит всё ещё остаётся искусством.

/discuss

★★★

не позволяет доопределять языки в рантайме

А зачем? Кто и как будет писать на языке, который не определен в, условно говоря, «сообщении о языке $WHATEVER»?

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

В рантайме = во время исполнения компилятора. Не программы на языке с макросами. Я о том, что во время компиляции компилятора его нельзя научить раскрывать макросы в конкретной программе, которую он будет компилировать в рантайме.

ilammy ★★★
() автор топика
Ответ на: комментарий от ilammy

В рантайме = во время исполнения компилятора

Без разницы.

Не программы на языке с макросами.

?

Я о том, что во время компиляции компилятора его нельзя научить раскрывать макросы в конкретной программе

В языке, написанном на этом nanopass, не может быть конструкции определения макроса?

tailgunner ★★★★★
()

Я уж думал, появилось хоть что-то

A preliminary version of this article was presented at the 2004

2004
2004
2004

anonymous
()

анонимус, толкавший nanopass народу

Спали этот тред, пожалуйста.

Kuzy ★★★
()
Ответ на: комментарий от tailgunner

В языке, написанном на этом nanopass, не может быть конструкции определения макроса?

Может, но их нельзя обрабатывать напрямую с помощью того же nanopass, по крайней мере в его официальной реализации. Это набор макросов, которые раскрываются в парсер исходного языка и пачку трансформеров. Чтобы с помощью того же кода обрабатывать макросы, нужен какой-то nanopass внутри nanopass, чтобы сгенерирорвать парсеры и трансформеры для того языка, которые определяется макросами в компилируемой программе.

ilammy ★★★
() автор топика
Ответ на: комментарий от anonymous

Извини, криокамера барахлит. Появилось что-то новенькое?

ilammy ★★★
() автор топика

Зачем оно нужно, если основные трудности в компиляторах начинаются как раз после синтаксического анализа?

for Commercial Compiler Development

Что именно делает его непригодным для некоммерческого применения?

rand
()

Nanopass папсеру эквипенисуален. Генери AST исходный любым парсером, способным записывать положение в потоке, а дальше с этой инфой делай что хочешь. Макросы опять же делаются элементарным пассом - и не надо привязывать это к макросам Схемы.

Насколько мне известно, самый крупный проект на nanopass, это Harlan. Посмотри на него.

anonymous
()

ЗЫ: смотрел на mbase/pfront? Вроде тот же подход что и nanopass.

anonymous
()
Ответ на: комментарий от rand

Зачем оно нужно, если основные трудности в компиляторах начинаются как раз после синтаксического анализа?

Вот внимательно на него посмотрев, я и задумался, а от каких же трудностей эта тулза может избавить? Парсер к нему писать самому, экспандер писать самому, пассы писать, естественно, тоже самому. Всё, что он предлагает,— это R6RS-only (и то только для четырёх реализаций) генератор матчеров для S-выражений и обходилка дерева. А если я захочу не S-выражения, а какие-нибудь объекты, то и матчер пиши сам. «Фреймворк»...

ilammy ★★★
() автор топика
Ответ на: комментарий от anonymous

Да я думал, можно хоть поиск макровызовов на него свалить. Но не, он поможет только найти и немного распарсить define-syntax, а дальше компиль-ищи-проверяй-раскрывай сам, разве что по формочкам тебя рекурсивно проведут.

ilammy ★★★
() автор топика
Ответ на: комментарий от anonymous

Генери AST исходный любым парсером, способным записывать положение в потоке, а дальше с этой инфой делай что хочешь.

И где я это положение буду хранить? Разве что в каждый нетерминал добавить ещё один элемент для этого, и бережно его перекладывать от языка к языку.

ilammy ★★★
() автор топика
Ответ на: комментарий от ilammy

Там как раз ни фига не S-выражения. И это не просто матчер, он строит тебе рекурсивный visitor из каждого pass-а. Аналогично тому, что умеет scrap your boilerplate.

anonymous
()
Ответ на: комментарий от ilammy

Ну да. В каждый терминал. А ты как хотел? Кто, кроме тебя, может знать семантику положения в коде?

anonymous
()
Ответ на: комментарий от ilammy

А чего их искать? Пишешь pass, который все, что похоже на приложение функции, проверяет на наличие макроса.

anonymous
()

что eDSL лучше сраной кодогенерации

Как ты собираешься реализовывать eDSL без кодогенерации? Если тебе надо вводить новый синтаксис в язык? Про какой eDSL Ты говоришь?

anonymous
()
Ответ на: комментарий от ilammy

В рантайме = во время исполнения компилятора

Это что-то новенькое. рантайтм=компилтайм???

anonymous
()
Ответ на: комментарий от anonymous

Ну да, я знаю, что там не S-выражения, а структурки, просто для их описания используются S-выражения, что (как мне кажется) накладывает ограничения на то, что можно выразить. Но, похоже, единственная особенность, которая из-за этого проявляется — это отсутствие скрытых параметров. Я не могу где-то спрятать информацию о том же положении в исходнике, её надо таскать как отдельный терм. То же самое касается и всего остального. В общем, спасибо, моя убеждённость в бесполезности Nanopass поутихла, пойду думать, как туда можно прикрутить скрытые параметры.

ilammy ★★★
() автор топика
Ответ на: комментарий от anonymous

Так это embedded DSL, он использует родной синтаксис языка. Просто в лиспоподобных языках есть как бы два уровня синтаксиса: 1) скобочки и атомы; 2) синтаксис отдельных форм. Второй может изменяться макросами как угодно. Поэтому тут очень удобно писать скобчатые DSL, а если есть макросистема с гигиеной, то и eDSL, избегая кодогенерации.

Под кодогенерацией я понимаю любое создание программ в виде сырого кода, а не синтаксических конструкций. Макросы Common Lisp и тупое распечатывание в файлик — вот это примеры кодогенерации. Макросы Scheme же манипулируют синтаксическими объектами, а просто S-выражениями; они понимают, что символы могут иметь разную область видимости и прочее.

У подхода с кодогенерацией есть некоторые трудности со взаимодействием с исходным языком, потому что всю гигиену надо разруливать самому, утыкивая код этими мерзкими генсимами; все импорты-экспорты тоже выяснять самому или прописывать отдельно; наконец, модуль с особенным синтаксисом «внешнего» DSL надо ещё предварительно обработать спецтулзой — то есть встроить это как-то в билд-систему.

ilammy ★★★
() автор топика
Ответ на: комментарий от anonymous

Нет. Просто тут три программы: пользовательская программа, компилятор, и компилятор компилятора. Естественно, что время исполнения компилятора совпадает со временем компиляции программы, обрабатываемой компилятором.

ilammy ★★★
() автор топика
Ответ на: комментарий от ilammy

Да не надо их скрывать. Nanopass и так позволяет от boilerplate избавляться, если какое переписывание не затрагивает позиции в коде, то ее явно тащить и не надо, само приползет. А если же семантика меняется, то тебе по любому руками их тащить.

anonymous
()
Ответ на: комментарий от ilammy

Естественно, что время исполнения компилятора совпадает со временем компиляции программы, обрабатываемой компилятором.

А в каких случаях компиляции (в общем смысле) это не так? Процесс компиляции — это всегда рантайм компилятора. Однако же не принято говорить, что компиляция=рантайм, посаны не поймут. Я хочу сказать, что любой компилятор — это всего лишь программа, и она имеет свой рантайм. Вообще, это все идиотизм и эскалация сложности. Ни к чему хорошему это не приведет, в конечном итоге.

Вообще, эта ваша компиляция компиляторов — старая, и изжившая себя идея. Было уже это. Турчин, Футамура, etc. Ну и где все это, как проявилось? «улушение конпелятора жавы»? Жава-way?

То что Вы делаете — это АНТИлисп. Что-то с мозгами людей (и лисперов) случилось, начиная с 70-х. Наверное моль съела.

anonymous
()
Ответ на: комментарий от anonymous

И чо gcc? С него создатели и отцы-основатели уже блюют. Просто сделать уже ничего невозможно, легаси. Где то я читал в рассылках как Столлман плевался на него, все хотел переписать. Gcc сложен и уродлив.

anonymous
()
Ответ на: комментарий от anonymous

Не крути жопой. Факт - все самые популярные компиляторы построены на цепочках простых преобразований.

anonymous
()
Ответ на: комментарий от ilammy

Нет. Просто тут три программы

Я извиняюсь за нубский вопрос, но: откуда у вас там 3 программы? Имхо, там 2 программы, а не 3. Эта суперкомпиляция может быть разложена очень просто, по-моему. Пусть у нас есть текст из единственного символа x. Вот наш «суперкомпилятор»:

(lambda(rule) (lambda(x) (rule x)))
Вы подаете на вход правило. Компилятор специализирует функцию — происходит редукция — он вам возвращает готовый компилятор. Этому компилятору осталось подать на вход слово языка. Поэтому, у вашего компилятора нет рантайма, рантайм есть только у суперкомпилятора.

То есть, да, тут, казалось-бы, есть 3 фазы: supercompil->compil->word, но на самом деле, работает только supercompil, который принимает в качестве 2 аргументов текст Вашего компилятора и слово языка.

anonimous
()
Ответ на: комментарий от anonymous

М-м-м... И правда. Так что единственное, что я бы подправил — это синтаксис для implicit begin: добавить конструкцию для одного и более повторений, а не только ... для ноля или более.

ilammy ★★★
() автор топика
Ответ на: комментарий от anonymous

Ну, я положительно уверен, что это в общем полезная штука. Просто надо иметь и механизм отхода от гигиены, когда она не нужна, а не совать её абсолютно везде.

ilammy ★★★
() автор топика
Ответ на: комментарий от anonymous

Вообще, эта ваша компиляция компиляторов — старая, и изжившая себя идея. Было уже это. Турчин, Футамура, etc. Ну и где все это, как проявилось? «улушение конпелятора жавы»? Жава-way?

Не особо понял, к чему это. Под компиляцией компилятора я понимаю просто компиляцию программы компилятора, не более того. Никаких интеллектуальных специализаций, суперкомпиляций, и прочей фигни.

ilammy ★★★
() автор топика
Ответ на: комментарий от ilammy

Тогда зачем вам этот ваш commercial компилоклепатель? берите любой язык, пишите на нем компилятор, тогда не возникнет вопросов, где там рантайм компилятора(т.е. непосредственно компиляция), а где рантайм программы. А то у Вас 3 уровня откуда то вылезло.

anonymous
()
Ответ на: комментарий от ilammy

Хорошо, может я Вас не правильно понял. Можно это вот так,условно изобразить

1) процесс компиляции программы_компилятора:
(компилятор_компиляторов<--программа_компилятор)-->программа_компилятор
2)процесс компиляции конечной программы:
(программа_компилятор<--текст_программы)-->текст_выходной_программы
3)процесс выполнения выходной_программы:
(процессор<--текст_выходной_программы)-->результат_работы_вых_программы
Эти три стадии Вы имели в виду? И тут 3 рантайма, вы имели в виду 1 процесс перезаписи, так?

anonymous
()
Ответ на: комментарий от ilammy

Пардон, 1-й процесс следует читать

1) процесс компиляции программы_компилятора:
(компилятор_компиляторов<--ТЕКСТ_программы_компилятора)-->программа_компилятор

anonymous
()
Ответ на: комментарий от anonymous

Да. И я имел в виду очевидную вещь, что выполняя первый шаг, нельзя проанализировать и наклепать готовых трансформеров для макросов, определяемых в конечной программе из второго шага.

ilammy ★★★
() автор топика

Я практически ничего не понимаю в лиспах и nanopass, но я примерно понимаю о чём ты. По этой причине у меня аж три этапа превращения кода в AST. Короче, я правильно понимаю что у тебя возникло желание влезть в кишки nanopass?

true_admin ★★★★★
()
Ответ на: комментарий от true_admin

Типа того. Я не в восторге от R6RS и прочих Racket, поэтому мне хочется его портировать на более рабоче-крестьянский R7RS. Подвожу вот для себя списочек того, что следует в нём исправить, доработать и т. д.

ilammy ★★★
() автор топика
Ответ на: комментарий от ilammy

Although the nanopass framework defines just two primary syntactic forms, the macros that implement them are complex, with approximately 4600 lines of code.

4600 звучат пугающе.

Кстати, ты как к ООП относишься?

true_admin ★★★★★
()
Ответ на: комментарий от ilammy

Мне вот интересно одна вещь. Видел в одном из гайдов по хаскелю сравнение типов в хаскеле с обычным ооп. Ну и там, как всегда, ООП похоронили потому что оно не удовлетворяет Liskov Substitution Principle (LSP) (http://okmij.org/ftp/Computation/Subtyping/#Problem). Как это в хаскеле-то решили? Строгой типизацией? Т.е., нельзя смешивать дочерние (derived?) и родительские типы?

true_admin ★★★★★
()
Ответ на: комментарий от true_admin

Проблема-то именно в том, что классы — это не типы, хотя традиционные объектно-ориентированные языки навязывают это соответствие. Видимо потому, что там по-другому типы ввести нельзя. Принцип подстановки Лисков фактически говорит: «Пишите классы так, как будто это типы, а наследование — это субтипизация». Классы обычно наследуют друг от друга с целью совместного использования деталей реализации. Тогда как субтипизация — это, грубо говоря, «наследование интерфейса».

Disclaimer: я Haskell не знаю, так что пишу, пользуясь общим образованием и Гуглом.

В Haskell нет классов объектов, как в ООП. Есть классы типов, которые схожи с интерфейсами. Они задают протокол — набор функций, которые обязаны быть реализованы для значений типа, который хочет быть экземпляром какого-то класса типов. Классы типов могут наследоваться, но это приводит только к наследованию протокола, а не конкретных его реализаций.

Однако, наследование классов типов — это тоже не подтипизация, поэтому Haskell не обеспечивает соблюдение LSP автомагически и проблему по сути не решает. Требование класса Eq реализовать функцию (==) не запрещает её реализовывать совершенно разнообразными долбанутыми способами для различных типов. Например, числа и единицы измерения — для обеих сущностей можно определить понятие равенства, но точный смысл этого понятия несколько отличается. Поэтому функции, требующие абстрактные сравнимые величины, не всегда будут в равной мере адекватно обрабатывать килограммы, человеко-часы, и тысячи.

Подтипизация и LSP несут в себе идею того, что подтипы/подклассы должны являться семантическими эквивалентами своих надтипов/надклассов. И, боюсь, данное соответствие невозможно статически проверить для невырожденных случаев в силу его невычислимости. Поэтому в любом языке программирования эта проблема будет существовать в том или ином виде.

В качестве примера тотального разносa «традиционного» Simula-like ООП лучше использовать expression problem. Которую Haskell всё же решает тем, что тип может реализовать класс типов в любом месте программы.

ilammy ★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.