Здравствуйте, _Obelisk_, Вы писали:
_O_>Не, специально заточенные языки для описания AST-ов и их трансформаций гораздо удобней, чем любой язык программирования общего назначения.
Любой — это какой?
_O_>Я на С++ такое делал. C#/Java работу сильно не облегчат.
Сомневаюсь.
_O_>Да, вывод типов — это ведь не только для generic-ов. Определить тип выражения "a + b" — это тоже вывод типов. Собственно, даже определить тип литерала (например числа 1) не всегда тривиально. Например, в таком специфическом языке как SDL, тип литерала зависит от его контекста и там в одном месте тип '1' будет Integer, а в другом 'Real' а в треьем — какой-нибудь 'Time'.
Ну дык, для литералов тип заранее неизвестен. А идентификаторы как? Там обязательна аннотация типов? Тогда вывод типов не такой и сложный. Даже в языках с необязательной аннотацией типов используется достаточно простой алгоритм, основанный на унификации, но только в случае системы типов Хиндли-Милнера. А вот если берём другую систему типов (Nemerle) или расширяем её (Haskell) получаются жуткие навороты...
_O_>Вот пример конфликтов в грамматике Шарпа (есть и другие) :
_O_>
_O_>(x) -y
_O_>
_O_>это cast expression или parenthesized expression ?
_O_>или
_O_>
_O_>A < B, C >> D // shift or type arguments ?
_O_>
_O_>Конфликты решаются динамически, с помощью предикатов, которые делают lookahead.
_O_>У тебя генератор поддерживает задание каких-либо condition-ов на правила в грамматике для динамического резолвинга подобных конфликтов ?
Нет. А разве для первого случая возможно что-то определённое сказать, не сделав семантического анализа? Как в данном случае поступают?
_O_>Для описания AST-а, как самостоятельной сущности. Нужно и удобно на практике.
Для меня это общие слова. Можно ссылочку или конкретную ситуацию описать? А то я не догоняю...
_O_>Это позволяет существенно упростить написание семантического анализа. _O_>Понятия синтезируемый атрибут/наследуемый атрибут, атрибутивная грамматика, higher order attribute grammar знакомы ?
Читал в красном драконе. А при чём тут это?
_O_>Ну а у меня всякие генераторы — это почти прямая деятельность, которая поглощает и свободное время А вот времени на разработку всех идей не хватает.
У меня тоже времени на разработку собственных идей не хватает. Работа
Здравствуйте, konsoletyper, Вы писали:
_O_>>Не, специально заточенные языки для описания AST-ов и их трансформаций гораздо удобней, чем любой язык программирования общего назначения.
K>Любой — это какой?
Любой, общего назначения. Просто специализированные языки на то и специализированные, чтоб облегчать реализацию специальных задач.
_O_>>Я на С++ такое делал. C#/Java работу сильно не облегчат.
K>Сомневаюсь.
А я нет Но пусть все останутся при своем мнении.
K>Ну дык, для литералов тип заранее неизвестен. А идентификаторы как? Там обязательна аннотация типов? Тогда вывод типов не такой и сложный. Даже в языках с необязательной аннотацией типов используется достаточно простой алгоритм, основанный на унификации, но только в случае системы типов Хиндли-Милнера. А вот если берём другую систему типов (Nemerle) или расширяем её (Haskell) получаются жуткие навороты...
Тип литералов известен в таких языках, как С++, C#, Java и др. 1 — это целое, 2.3 — вещественное и т.д.
В SDL алгебраическая система типов. Проблема заключается в том, что тип выражения зависит от всего контекста. Т.е. для выражения "a = b + 1" строится множество все видимых в данном месте объектов с именами 'a', 'b', '=', '+', '1'. Дальше это множество редуцируется, если остались единственные объекты для имен 'a', 'b', '=', '+', '1', то они и выбираются. В результате может получиться, что тип '1' будет определяться типом 'b'.
Что мне в такой системе не нравится, так это то, что выражение '1 + 1' выдает ошибку, т.к. существует несколько типов с литералом '1'.
Я делал кодогенератор из модели, в которой используется С++-образная система типов, в этот SDL. Извратные правила с выводом типов и правила видимости доставляли хлопот. С тех пор не люблю алгебраической подход к описанию типов.
_O_>>У тебя генератор поддерживает задание каких-либо condition-ов на правила в грамматике для динамического резолвинга подобных конфликтов ?
K>Нет. А разве для первого случая возможно что-то определённое сказать, не сделав семантического анализа? Как в данном случае поступают?
Ну оставить на этап семантического анализа можно, только в некоторых случаях можно определить тип конструкции на уровне парсера ( скажем, binary expression "(T) / 10" не является преобразованием типа). Для тех случаев, когда можно, делаются предикаты, проверяющие условия. Соотв. правила в спецификации для парсера будут иметь condition-ы, использующие эти предикаты. Там есть еще какие-то фичи, я глубоко не копался
K>Для меня это общие слова. Можно ссылочку или конкретную ситуацию описать? А то я не догоняю...
Есть, например, задача source-to-source transformation из языка А в язык Б. Как делать будем ? Очевидно, что лучше трансформировать одно абстрактное синтаксическое дерево в другое. Затем просто обойти дерево и pretty-printing-ом сгенерить программу на языке Б.
Наличия языка описания AST-ов упрощяет такую деятельность. При описании AST-а мы можем сконцентриоваться именно на самом AST-е и переложить всю рутину на плечи генератора AST-а.
Например, AST для expression-ов из С# выглядит в виде спецификации для Cocktail-я так
задает паттерн, который срабатывает, если expression-ом ему соотвествует.
K>Читал в красном драконе. А при чём тут это?
Ну Cocktail содержит спец. средство для генерации attribute eveluator-ов. У нас на нем весь name resolution & type checking для языка TTCN-3 построен.
K>У меня тоже времени на разработку собственных идей не хватает. Работа
Здравствуйте, _Obelisk_, Вы писали:
K>>Любой — это какой? _O_>Любой, общего назначения. Просто специализированные языки на то и специализированные, чтоб облегчать реализацию специальных задач.
Я знаю это. Ты про какие именно языки говоришь?
K>>Для меня это общие слова. Можно ссылочку или конкретную ситуацию описать? А то я не догоняю...
_O_>Есть, например, задача source-to-source transformation из языка А в язык Б. Как делать будем ? Очевидно, что лучше трансформировать одно абстрактное синтаксическое дерево в другое. Затем просто обойти дерево и pretty-printing-ом сгенерить программу на языке Б. _O_>Наличия языка описания AST-ов упрощяет такую деятельность. При описании AST-а мы можем сконцентриоваться именно на самом AST-е и переложить всю рутину на плечи генератора AST-а.
[код поскипан]
Вот что-то я смотрел на фрагменты кода. В первом увидел что-то вроде алгебраических типов. Во втором — паттерн-матчинг. И не проще ли было бы описать AST языков с помощью type из OCaml, а преобразование — обычная функция, использующая паттерн-матичнг? Или я опять чего-то не догоняю?
Здравствуйте, konsoletyper, Вы писали:
K>Вот что-то я смотрел на фрагменты кода. В первом увидел что-то вроде алгебраических типов. Во втором — паттерн-матчинг. И не проще ли было бы описать AST языков с помощью type из OCaml, а преобразование — обычная функция, использующая паттерн-матичнг? Или я опять чего-то не догоняю?
Мне нужно генерить из AST-а другую модель, которая на С++ написана.
Cocktail генерит кода на С, С++, Java, Modula-2, С# (недавно вроде добавили) и позволяет удобным образом смешивать свои конструкции с кодом на конкретном языке программирования. Поэтому я могу использовать pattern-matching и методы Cocktail-я для задания правил обхода модели и присобачивания трансформаций к узлам AST-а, а собственно порождение новой модели писать на С++.
Вот у меня было правило (мэппит конструкию типа "<expr> as <type>" )
this : as_expr(expression := expression, type := type) :-
RETURN MakeAsExpr(TranslateExpression(expression), TranslateType(type));
.
Здесь
this : as_expr(expression := expression, type := type) :-
есть Cocktail-ое правило.
Это вызов С++-ой функции
MakeAsExpr
А это
TranslateExpression
TranslateType
вызовы Cocktail-ых функций, определенных в другом месте.
Т.е. я спокойно смешиваю формальную спецификацию на Cocktail-е, с вызовом С++-ых функций.
Не думаю, что OCaml мне это позволит. Правда я его не знаю
Проблема с ФЯ в том, что их трудно интегрировать с другими языками и другими API. Особенно, если требуется платформонезависимость. А переписать все систему, где несколько миллионов строк кода на С++ на каком-либо ФЯ нам не дадут
Здравствуйте, _Obelisk_, Вы писали:
_O_>Т.е. я спокойно смешиваю формальную спецификацию на Cocktail-е, с вызовом С++-ых функций. _O_>Не думаю, что OCaml мне это позволит. Правда я его не знаю
Ещё как позволяет. Ну конечно, в одном месте писать сразу на OCaml и на C++ никто не позволит. Но можно из OCaml вызывать функции из С-ных библиотек (по-моему, можно и обратное, но я точно не знаю). В случае с Nemerle или Scala всё ещё, так как .NET и JVM позволяют бесшорвную интеграцию кода на разных ЯП. Так что вполне возможно написать парсер на OCaml, а потом "сшить" полученный код с кодом на C++. Замечу, что OCaml может компилить программу в native-код.
_O_>Проблема с ФЯ в том, что их трудно интегрировать с другими языками и другими API. Особенно, если требуется платформонезависимость. А переписать все систему, где несколько миллионов строк кода на С++ на каком-либо ФЯ нам не дадут
Оно и понятно, что не дадут. Более того, мне вот вообще ничего не дают писать на каком-нибудь F#. Аргументация простая — трудно найти ещё одного программиста, знающего ФЯ, а если и найдём, то ему надо будет больше платить.
Но в рамках ветки мы решаем другой вопрос — о том, насколько C++ подходит для написания компиляторов. И вот что оказывается: даже забывая про проверку типов, формирование промежуточного представления, кодогенерацию, мы упираемся в недостатки C++ уже на стадии парсинга. А увеличивая набор фич генератора, мы рано или поздно придём к тому, что реализуем свой ФЯ, компилирующийся в C++.
Здравствуйте, Varkom, Вы писали:
V>А какова цель разработки нового языка? Создание коммерческого продукта, создание языка под какие-то нужды, или так, в целях самообразования?
Создать язык на котором создавать коммерческие продукты меньшей кровью.
Здравствуйте, Cpphater, Вы писали:
C>Здравствуйте, Varkom, Вы писали:
V>>А какова цель разработки нового языка? Создание коммерческого продукта, создание языка под какие-то нужды, или так, в целях самообразования?
C>Создать язык на котором создавать коммерческие продукты меньшей кровью.
Помоему C# совсем не плох для роли киллера,после C++ писать на нём одно удовольствие.
Вот сделать бы чтонибудь типа C# с постоянно включенным unsafe,без сборщика мусора,компилируемый в нативный код,ну и фреймворк есессно родной(всмысле .NET совместимый по типу Mono).Имхо это самый оптимальный вариант чем создовать очередного Франкенштайна).Ещё одно имхо — писать надо на C++ и всё ручками,ни каких духов мать их перемать.Максимум — STL(возможно свои аллокаторы для контейнеров),сколько говорили о её не всегда хорошей эффективности,а у boost с этим ещё хуже.До момента кодогенерации написать всё ручками не так уж сложно.А вот кодогенерация и уж тем более оптимизация не тривиальная задачка.Вот такое моё имхо)
Здравствуйте, WolfHound, Вы писали:
WH>>>С++ очень плохо подходит для разработки компиляторов. CS>>Чё так? WH>Ни алгебраических типов.
А для программы компилятора они зачем???
WH>Ни сравнения с образцом.
Табличные LL(k) рулят. И не переубедишь.
WH>Ни нормальных макросов.
Для автоматного подхода к лексеру и парсеру имеющихся хватает с головой.
WH>Ни сборщика мусора.
Ну приплыли. И кому же это сборщик понадобился? Лексеру или парсеру? Или древовидным, монотонно растущим словарям?
Если программе-компилятору нужен сборщик мусора, то это какой-то наколенно-склёпанный компилятор.
Здравствуйте, vdimas, Вы писали:
V>А для программы компилятора они зачем???
Нет ничего лучше для работы с AST.
V>Табличные LL(k) рулят. И не переубедишь.
И куда они рулят?
V>Для автоматного подхода к лексеру и парсеру имеющихся хватает с головой.
Кроме того что программу нужно пошенковать на токены для компиляции нужно сделать еще очень много чего.
Скажем в томже AST есть куча тупого кода который прекрасно генерится по структуре этого самого AST.
V>Ну приплыли. И кому же это сборщик понадобился? Лексеру или парсеру? Или древовидным, монотонно растущим словарям? V>Если программе-компилятору нужен сборщик мусора, то это какой-то наколенно-склёпанный компилятор.
Скажи это разработчикам немерле.
Только сначала повтори компилятор немерле на С++.
... << RSDN@Home 1.2.0 alpha rev. 745>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Здравствуйте, WolfHound, Вы писали:
WH>Нет ничего лучше для работы с AST.
А прямое обращение к типизированным данным чем хуже?
V>>Ну приплыли. И кому же это сборщик понадобился? Лексеру или парсеру? Или древовидным, монотонно растущим словарям? V>>Если программе-компилятору нужен сборщик мусора, то это какой-то наколенно-склёпанный компилятор. WH>Скажи это разработчикам немерле. WH>Только сначала повтори компилятор немерле на С++.
Немерле разработан ради работы в составе платформы. Я всё-равно не услышал, зачем конкретно в программе-компиляторе постоянно выделять и затем уничтожать временные куски памяти? Сдаётся мне, что компилятор должен только выделять память, а освобождать в конце своей работы.
Здравствуйте, vdimas, Вы писали:
V>А прямое обращение к типизированным данным чем хуже?
А pattern-matching у тебя по этим самым данным тоже работает?
V>Немерле разработан ради работы в составе платформы.
И?
V>Я всё-равно не услышал, зачем конкретно в программе-компиляторе постоянно выделять и затем уничтожать временные куски памяти?
Например различные трансформации (макросы, оптимизация итп)
Да и вобще нафига сидеть и выдумывать свякие хитрые схемы работы со структурами если можно писать простой код, а GC сам прекрасно разберется что нужно, а что нет.
V>Сдаётся мне, что компилятор должен только выделять память, а освобождать в конце своей работы.
С чего это?
Нафига хранить всякий мусор?
... << RSDN@Home 1.2.0 alpha rev. 745>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Здравствуйте, AndrewVK, Вы писали:
AVK>В AST нет смысла в визиторах? AVK>Или визитор ничем не хуже внятного паттерн-матчинга?
Для работы с AST, где имеем общую базу у объектов-узлов — однофигственно. Паттерн-матчинг там нужен ради определения типа узла AST. Если алгоритмы обработки узлов относительно "большие", то используя синтаксис паттерн-матчинга рискуем получить файлы кода на десятки тыч. строк, ИМХО оформление в виде отдельных методов вкупе с partial поможет не превратить код в кашу.
В принципе, дело вкуса, конечно, на небольших задачах паттернг-матчинг в качестве визитора выглядит вполне эстетично.
Здравствуйте, WolfHound, Вы писали:
V>>Я всё-равно не услышал, зачем конкретно в программе-компиляторе постоянно выделять и затем уничтожать временные куски памяти? WH>Например различные трансформации (макросы, оптимизация итп)
В тех вещах, что мне приходилось писать (ассемблеры и однажды аналог бизона) скомпилированные во внутреннее представление макросы должны были жить до конца компиляции, дабы не повторять эту процедуру перед каждым применением макроса.
WH>Да и вобще нафига сидеть и выдумывать свякие хитрые схемы работы со структурами если можно писать простой код, а GC сам прекрасно разберется что нужно, а что нет.
В общем случае — согласен, ибо сам давно подсел на дотнет. Мы же обсуждаем вполне конкретную задачу. Тем более, что по моему опыту, даже на дотнете во многих случаях нет нужды в постоянном перевыделении памяти.
V>>Сдаётся мне, что компилятор должен только выделять память, а освобождать в конце своей работы. WH>С чего это? WH>Нафига хранить всякий мусор?
А ты думаешь, что в твоей схеме будет хотя бы раз вызван GC до конца работы компилятора?
Сильно заблуждаешься, там загрузка проца будет под 100%, и никакого сбора мусора до конца работы не предвидится, только если память не начнёт заканчиваться.
Вот прямо сейчас вожусь с проектом, где активно обрабатывается звук (конференция аудио-видео), так вот, когда избавились от принципа постоянного выделения блоков памяти и максимально задействовали однажды выделенные структуры, то средняя загрузка проца упала от примерно 30% до практически нуля. Ну и самое главное — прекратились периодические ухудшения кач-ва звука (хрипение), надеюсь, объяснять не надо — почему?
Здравствуйте, vdimas, Вы писали:
V>Для работы с AST, где имеем общую базу у объектов-узлов — однофигственно.
Видишь ли, визитор, при всех его достоинствах, все же довольно громоздкая конструкция. Кроме того, визитор привносит нафик не нужный в данном случае полиморфизм.
V> Паттерн-матчинг там нужен ради определения типа узла AST.
Не ради определения, а ради диспетчеризации вызовов.
V> Если алгоритмы обработки узлов относительно "большие", то используя синтаксис паттерн-матчинга рискуем получить файлы кода на десятки тыч. строк, ИМХО
Не рискуем. При паттерн-матчинге в одной конструкции мы имеем количество вариантов, соответствующее конкретному родителю, а не вообще все ноды AST. В самых запущенных случаях это пара десятков вариантов. А вот визиторы да, обычно делают один на весь AST.
V> оформление в виде отдельных методов вкупе с partial поможет не превратить код в кашу.
Код в кашу превращает исключительно програмимст.
... << RSDN@Home 1.2.0 alpha rev. 725 on Windows Vista 6.0.6000.0>>
Здравствуйте, Cpphater, Вы писали:
C>Имею желание разработать язык следующего, за С++, поколения, являющегося его идейным потомком и его же убийцей. C>Идейную наследственность вижу в кардинальном усилении метапрограммной составляющей. Одновременное, с этим, кардинальное удаление С рудиментов должно породить язык необычайной выразительной мощи.
Зделайте в нем объектный стек вызовов, чтобы исключения можно было по-человечески обрабатывать.
Говорят в питоне это сделано!
Здравствуйте, lexis_t, Вы писали:
_>Зделайте в нем объектный стек вызовов, чтобы исключения можно было по-человечески обрабатывать.
Чего?
Подробней пожалуйсто.
... << RSDN@Home 1.2.0 alpha rev. 745>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Если представить, что каждая функция — это специальный класс, а при вызове функции на стеке создается объект с типом функция-класс...
Таким образом появляется возможность адресовать переменные, лежащие выше по стеку, если имеется указатель на кадр стека и конкретный тип (то есть класс-функция).
Фактически для компилятора ничего не поменяется, в стеке будут лежать все те же локальные переменные (упакованные в класс), но при обработке исключительной ситуации будет теоретическая возможность добраться до их значений (до раскручивания стека) и даже исправить! А потом восстановить прежнюю точку выполнения и продолжить как ни в чем не бывало!
Здравствуйте, lexis_t, Вы писали:
_>Фактически для компилятора ничего не поменяется, в стеке будут лежать все те же локальные переменные (упакованные в класс), но при обработке исключительной ситуации будет теоретическая возможность добраться до их значений (до раскручивания стека) и даже исправить! А потом восстановить прежнюю точку выполнения и продолжить как ни в чем не бывало!
Мягко говоря сомнительная функциональность.
Мало того что она ставит крест на агрессивных оптимизациях но что во много раз хуже данная функциональность превращает код в тАкую лапшу что функция на 10К строк с кучей goto покажется кристально ясной.
... << RSDN@Home 1.2.0 alpha rev. 745>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн