Здравствуйте, Sinclair, Вы писали:
S> Я так понял, что штатный способ прямого порождения байткода Java — это библиотека AST. S> А есть ли аналог этой библиотеки на тайпскрипте?
А у тайпскрипта разве есть байткод? wasm что-ли?
Здравствуйте, ·, Вы писали:
·>А у тайпскрипта разве есть байткод? wasm что-ли?
Я почему-то подумал, что коллега хочет именно Java-байткод на TS.
Под определение странного это вписывается вполне.
Здравствуйте, Sinclair, Вы писали:
S>Я так понял, что штатный способ прямого порождения байткода Java — это библиотека AST. S>А есть ли аналог этой библиотеки на тайпскрипте?
Наверное есть на С++, а оттуда уже можно сделать wasm и к нему интерфейс на TS.
Здравствуйте, ·, Вы писали: ·>А у тайпскрипта разве есть байткод? wasm что-ли?
Нет. Байткод нужен джавный. Просто писать компиляторщину на Java — это боль.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>·>А у тайпскрипта разве есть байткод? wasm что-ли? S>Нет. Байткод нужен джавный. Просто писать компиляторщину на Java — это боль.
А, понял вопрос. Да, сабж, очень сильно. А задача то какая? Зачем генерить именно байт-код? Может джаву лучше? Ещё кстати scala или kotlin если языка не хватает.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ·, Вы писали: ·>А, понял вопрос. Да, сабж, очень сильно. А задача то какая? Зачем генерить именно байт-код? Может джаву лучше? Ещё кстати scala или kotlin если языка не хватает.
Задача — сделать компилятор воображаемого языка.
В JVM — потому, что это проще, чем в натив (и даже в LLVM).
В принципе, можно и Java-код порождать, но это не даст студентам понимания устройства байт-кода
Вот, кстати, смотрю в сторону kotlin. Похоже, на нём тоже неплохо пишется компилятор. Но для него, я так понял, в качестве IDE можно использовать только IntelliJ, т.к. интеграция с VS Code у него так себе.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>Задача — сделать компилятор воображаемого языка. S>В JVM — потому, что это проще, чем в натив (и даже в LLVM).
GraalJs? Ну и "typescript for jvm" находит какие-то проекты, в большинстве случаев архивные.
S>В принципе, можно и Java-код порождать, но это не даст студентам понимания устройства байт-кода S>Вот, кстати, смотрю в сторону kotlin. Похоже, на нём тоже неплохо пишется компилятор. Но для него, я так понял, в качестве IDE можно использовать только IntelliJ, т.к. интеграция с VS Code у него так себе.
Ну Kotlin с IntelliJ (есть community версия) всяко лучше будет, чем натягивать Ts на Jvm
Здравствуйте, amironov79, Вы писали:
A>GraalJs? Ну и "typescript for jvm" находит какие-то проекты, в большинстве случаев архивные.
Большинство этих проектов — про исполнение typescript в JVM. А мне как раз это не очень надо, typescript пускай себе в node.js работает.
A>Ну Kotlin с IntelliJ (есть community версия)
Ладно, попробуем. A>всяко лучше будет, чем натягивать Ts на Jvm
Натягивать TS на JVM нужды нет. Всё, что нужно — это порождать .class. Это можно делать из примерно любого языка — С++, Pascal, Java, you name it.
Вопрос только в доступности инструментальных средств.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, ·, Вы писали: S>·>А, понял вопрос. Да, сабж, очень сильно. А задача то какая? Зачем генерить именно байт-код? Может джаву лучше? Ещё кстати scala или kotlin если языка не хватает. S>Задача — сделать компилятор воображаемого языка.
О! Это какой-то курс по трансляторам?
А можете поделиться — вы планируете чтобы студенты и фронт (анализ) и бэк (генерацию) писали врукопашную?
Я в свое время на такое не решился (но там был курс для заочников — они бы просто не успели вообще всё).
И выбрал вариант:
— используем генератор анализаторов (я брал ANTLR)
— для генерации использовал выход в виде IL-файла
Мне такой вариант понравился тем, что:
— всё наглядно: сразу видно, что получилось
— можно использовать текстовые шаблоны генерации — это актуально если делать DSL, который порождает не сразу байткод, а код на другом языке.
Этот подход очень развит в том же ANTLR, с его глубокой интеграцией в текстовые шаблоны
Я делал по практике пару "методичек". Какраз по ANTLR и немного по IL.
Если вдруг заинтересует, можете посмотреть https://mihailromanov.github.io/docs/tutorials/
Но ... сейчас я бы всё сделал иначе + это всё очень устарело, но вдруг натолкнет на интересные мысли...
S>В принципе, можно и Java-код порождать, но это не даст студентам понимания устройства байт-кода
А если рассмотреть вариант с .Net?
Т.е. фронт на C# (или даже F#!) а для генерации, что-то типа https://www.nuget.org/packages/Fluent.IL
Или есть какие-то ограничения и .Net нельзя ни в коем случае?
Здравствуйте, Михаил Романов, Вы писали: МР>О! Это какой-то курс по трансляторам?
Да. "Методы трансляции и компиляции". МР>А можете поделиться — вы планируете чтобы студенты и фронт (анализ) и бэк (генерацию) писали врукопашную?
Нет, пусть они пользуются готовыми инструментами. Но им нужно научиться самим писать грамматики и трансформировать код.
У нас этот курс уже есть, но к нему есть претензии. Основная претензия — чрезмерная воображаемость языков.
Код практики пишется на языке flow9, которым пользуется примерно столько же народу, сколько у нас студентов .
При этом компиляция и верификация пишется для недетерминированного языка, что сильно ломает мозг студентам.
Вот, думаем над тем, чтобы
а) убрать реализацию своей VM — там всё равно VM получается ущербной, и совершенно непохожей на настоящие VM.
б) заменить недетерминированный воображаемый язык на что-то больше похожее на обычные императивные языки
в) использовать в качестве основного языка программирования что-то более промышленно-стандартное, то есть хорошо документированное и стабильно работающее.
МР>- используем генератор анализаторов (я брал ANTLR)
У нас используется PEG. У PEG-грамматик есть ряд преимуществ перед традиционными LL(*), LR(k) и прочимию LALR(1)/LR/IELR(1)/GLR МР>- для генерации использовал выход в виде IL-файла
Ну, я думал над CIL в качестве целевого языка. Но по ряду причин лучше было бы взять Java. В частности, у меня есть желание научить их сразу генерировать отладочные символы, что даст возможность интерактивной отладки программок на их воображаемом языке. На майкрософтовской стороне забора это требует разбираться с PDB, который вообще ортогонален кодогенерации; а в жаве всё лежит в тех же .class файлах.
МР>- можно использовать текстовые шаблоны генерации — это актуально если делать DSL, который порождает не сразу байткод, а код на другом языке.
Порождать текстовый язык мы их и так будем учить — там одна из задач сводится к генерации системы уравнений для Z3 на его языке SMTLIB. МР>Этот подход очень развит в том же ANTLR, с его глубокой интеграцией в текстовые шаблоны МР>Я делал по практике пару "методичек". Какраз по ANTLR и немного по IL. МР>Если вдруг заинтересует, можете посмотреть https://mihailromanov.github.io/docs/tutorials/
Да, спасибо, очень интересно. МР>Но ... сейчас я бы всё сделал иначе + это всё очень устарело, но вдруг натолкнет на интересные мысли...
МР>А если рассмотреть вариант с .Net? МР>Т.е. фронт на C# (или даже F#!) а для генерации, что-то типа https://www.nuget.org/packages/Fluent.IL
Если честно, C# — не лучший язык для таких вещей.
Идеально — строготипизированный язык с автовыводом типов. В частности, это позволяет очень сильно упростить описания грамматик в PEG, потому что не надо выписывать result type для каждого семантического действия.
Из промышленных языков больше всего похож на то, что надо, собственно typescript. Генерация кода — да, в дотнете она сделана сильно более понятно, чем в Java.
Для C# есть библиотека PEG-разбора, но грамматики на ней.... обнять и плакать. На F# не смотрел.
Никакого сравнения с грамматиками Lingo, которая встроена в flow9. МР>Или есть какие-то ограничения и .Net нельзя ни в коем случае?
Да в целом-то ограничений нет; если бы просматривалась возможность покрыть все аспекты курса, оставаясь в рамках дотнета — то можно было бы наверное и его протащить. Но пока выглядит так, что Java в среднем встретит больше понимания (и органичнее впишется в другие курсы, которые читаются тем же студентам).
МР>P.S. Мне еще в качестве генераторов парсеров очень понравился Irony https://www.nuget.org/packages/Irony
Посмотрим. МР>Минимум зависимостей и не нужны специальные инструменты. По сути DSL для описания языка прямо на C#.
Документации маловато. Из сэмплов выглядит так, что он генерирует LR парсеры?
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>Код практики пишется на языке flow9, которым пользуется примерно столько же народу, сколько у нас студентов .
Ну да, я впервые услышал https://github.com/area9innovation/flow9
Прикольно, что контрибьютеры в подавляющем большинстве из России или с Украины
S>У нас используется PEG. У PEG-грамматик есть ряд преимуществ перед традиционными LL(*), LR(k) и прочимию LALR(1)/LR/IELR(1)/GLR
У меня тут мало опыта, поэтому ничего сказать не могу. Наверное так и есть.
S>Ну, я думал над CIL в качестве целевого языка. Но по ряду причин лучше было бы взять Java. В частности, у меня есть желание научить их сразу генерировать отладочные символы, что даст возможность интерактивной отладки программок на их воображаемом языке. На майкрософтовской стороне забора это требует разбираться с PDB, который вообще ортогонален кодогенерации; а в жаве всё лежит в тех же .class файлах.
Опять же не разбирался, не могу ничего сказать.
Вроде как есть такие вещи как как ILGenerator.MarkSequencePointCore, но они тянут за собой пространство System.Diagnostics.SymbolStore, так что, похоже, всё не так просто как мне казалось.
S>Если честно, C# — не лучший язык для таких вещей. S>Идеально — строготипизированный язык с автовыводом типов. В частности, это позволяет очень сильно упростить описания грамматик в PEG, потому что не надо выписывать result type для каждого семантического действия. S>Из промышленных языков больше всего похож на то, что надо, собственно typescript.
Понятно. Я, увы, забросил этот вопрос весьма давно — хочется вернуться и посмотреть на что-то еще помимо, простых генераторов и как работается с ними в простых императивных языках. Но времени как всегда нет ни на что.
По поводу генерации — я немного ввел вас в заблуждение (хотя, может вы и были в курсе).
В .Net после Framework и вплоть до 9 версии, была недоступна функциональность выгрузки сгенерированной сборки вовне.
В 9 появился PersistedAssemblyBuilder, но всякие обертки над Emit его пока не подхватили.
А если взглянуть на использование его напрямую (на базовом Emit) то, конечно, делается печально https://learn.microsoft.com/en-us/dotnet/fundamentals/runtime-libraries/system-reflection-emit-persistedassemblybuilder.
МР>>Минимум зависимостей и не нужны специальные инструменты. По сути DSL для описания языка прямо на C#. S>Документации маловато. Из сэмплов выглядит так, что он генерирует LR парсеры?
Это правда. Я смотрел на примеры и через утилиту (GrammarExplorer вроде)
Ну и я подумал, что вас он вряд ли впечатлит — там нормальной типизации узлов разбора не будет из коробки, а значит всё равно в рукопашную резвись с тем, что он там наразбирал...
Здравствуйте, Sinclair, Вы писали:
S>Задача — сделать компилятор воображаемого языка. S>В JVM — потому, что это проще, чем в натив (и даже в LLVM).
А ты в курсе, что есть древний, как UNIX System 7, паттерн делать такие компиляторы, используя Си в качестве промежуточного представления? А потом уже штатным компилятором Си — в нативный код.
Здравствуйте, Pzz, Вы писали: Pzz>А ты в курсе, что есть древний, как UNIX System 7, паттерн делать такие компиляторы, используя Си в качестве промежуточного представления? А потом уже штатным компилятором Си — в нативный код.
В курсе. А ещё можно их фортрану обучать на перфокартах
Мы-то хотим более-менее современному их обучать. В частности, нет никакого желания тиранить их лексерами — ни рукопашными, ни автопорождёнными.
Ну и с представлениями тоже — никто в здравом уме не будет порождать код для JVM через Си как промежуточный язык.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Михаил Романов, Вы писали: МР>Ну да, я впервые услышал https://github.com/area9innovation/flow9 МР>Прикольно, что контрибьютеры в подавляющем большинстве из России или с Украины
Отож.
МР>У меня тут мало опыта, поэтому ничего сказать не могу. Наверное так и есть.
Главное — он безлексерный и unambiguous. Нет никаких конфликтов, которые нужно как-то разрешать.
Плюс — очень естественно обобщается на инкрементальность.
МР>Вроде как есть такие вещи как как ILGenerator.MarkSequencePointCore, но они тянут за собой пространство System.Diagnostics.SymbolStore, так что, похоже, всё не так просто как мне казалось.
Вот и я не разбирался .
МР>В .Net после Framework и вплоть до 9 версии, была недоступна функциональность выгрузки сгенерированной сборки вовне.
Это вообще не проблема. Я когда переезжал с фреймворка на Core, взял https://github.com/Lokad/ILPack и поехал. МР>В 9 появился PersistedAssemblyBuilder, но всякие обертки над Emit его пока не подхватили. МР>А если взглянуть на использование его напрямую (на базовом Emit) то, конечно, делается печально https://learn.microsoft.com/en-us/dotnet/fundamentals/runtime-libraries/system-reflection-emit-persistedassemblybuilder.
МР>По грамматике, вроде как LALR https://www.codeproject.com/Articles/22650/Irony-NET-Compiler-Construction-Kit?msg=2381770
Спасибо за внятный пример.
Вот по нему уже видно, за что можно не любить все эти тяжеловесные парсер-генераторы.
Для сравнения — полный код PEG-грамматики, которая разбирает арифметические выражения и строит типизированное AST на Lingo:
МР>Ну и я подумал, что вас он вряд ли впечатлит — там нормальной типизации узлов разбора не будет из коробки, а значит всё равно в рукопашную резвись с тем, что он там наразбирал...
Именно. Гораздо удобнее работать с типизированным AST.
Как раз автовывод типов этим и прекрасен — ведь любое правило в PEG-грамматике описывается семантическим действием; если тип этого действия известен, то и тип правила известен.
Остаётся правильно вывести тип для правил с альтернативами — и мы получаем то, что нужно.
По идее это должно ездить и на typescript; но пока что я не нашёл идеальной ts-реализации PEG. PEGjs выглядит заброшенным и он всё же JS, а не TS; Ohm неплох (и умеет инкрементальность из коробки), но у них очень странные идеи про то, как привязывать семантические действия к грамматике.
В общем, нету серебряной пули
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>Я так понял, что штатный способ прямого порождения байткода Java — это библиотека AST. S>А есть ли аналог этой библиотеки на тайпскрипте?
Каждый раз, когда мне необходимо проделать что-либо с AST, я первым делом открываю astexplorer.net, а затем начинаю действовать. Этот инструмент позволяет проанализировать AST, созданное различными парсерами, в том числе babel/parser и парсером компилятора TypeScript. С помощью astexplorer.net можно визуализировать структуры данных, с которыми вам предстоит работать, и ознакомиться с типами узлов AST каждого парсера.
Взгляните на пример файла исходных данных и AST, созданное на его основе с помощью babel-parser:
example.ts
import { protos } from 'my_company_protos'
export type User = protos.user.User;
Здравствуйте, Serginio1, Вы писали: S> Не там есть пример получения ast.json
Там пример получения AST программы, написанной на typescript.
Предсказуемо, для этого применяется компилятор TS
У нас задача совсем другая — нужно получить AST своего воображаемого языка, а потом сгенерировать для него байткод.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>Я так понял, что штатный способ прямого порождения байткода Java — это библиотека AST.
Есть еще JEP 466: Class-File API (Second Preview)
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, ·, Вы писали: S>·>А у тайпскрипта разве есть байткод? wasm что-ли? S>Нет. Байткод нужен джавный. Просто писать компиляторщину на Java — это боль.
Scala? Cotlin? Clojure? https://github.com/caoccao/Javet/?
Здравствуйте, novitk, Вы писали: N>Scala? Cotlin? Clojure? https://github.com/caoccao/Javet/?
Clojure недостаточно типизирована. Учебный компилятор — не то место, которое хочется в шесть слоёв покрывать тестами.
На Kotlin смотрю пока. Вот, вчера поставил IntelliJ.
Есть для него библиотека PEG-парсинга?
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S> N>Scala? Cotlin? Clojure? https://github.com/caoccao/Javet/?
S> Clojure недостаточно типизирована. Учебный компилятор — не то место, которое хочется в шесть слоёв покрывать тестами. S> На Kotlin смотрю пока. Вот, вчера поставил IntelliJ. S> Есть для него библиотека PEG-парсинга?
Для всяких CS-изысков обычно всё-таки Скалу юзают. Если я правильно понял, тут вроде всё что надо: https://github.com/sirthias/parboiled2
Здравствуйте, Sinclair, Вы писали:
S>Clojure недостаточно типизирована. Учебный компилятор — не то место, которое хочется в шесть слоёв покрывать тестами. S>На Kotlin смотрю пока. Вот, вчера поставил IntelliJ.
я бывший скалист, Котлином не пользовался. ИМХО Скалка самый удобный и продвинытый язык из немаргинальных. Для компилятора сам бог велел.
S>Есть для него библиотека PEG-парсинга?
Для скалки в компиляторо-писание все есть: https://github.com/lauris/awesome-scala?tab=readme-ov-file#parsing
Здравствуйте, Sinclair, Вы писали:
S>Я так понял, что штатный способ прямого порождения байткода Java — это библиотека AST. S>А есть ли аналог этой библиотеки на тайпскрипте?
Можно попробовать запустить AST в среде JS через TeaVM. Берем AST, прогоняем через TeaVM, получаем AST, которую можно запускать в среде JS. Вроде бы то, что хочется получить.
Здравствуйте, Sinclair, Вы писали:
S>Задача — сделать компилятор воображаемого языка. S>В JVM — потому, что это проще, чем в натив (и даже в LLVM). S>В принципе, можно и Java-код порождать, но это не даст студентам понимания устройства байт-кода
А зачем какие то библиотеки? Почему нельзя тупо писать байтики напрямую? Одно дело когда ты байткод сам анализируешь, тут библиотека поможет и снимет куча геморроя. Но если задача байткод генерить, да еще в учебных целях — ИМХО лучше тупо в файлик сразу байты писать да и все. Если не ставится задача вообще все покрыть, а чтоб было покрытие ограниченного подмножества — достаточно просто быстро и интересно делается.
Здравствуйте, elmal, Вы писали:
E>Здравствуйте, Sinclair, Вы писали:
S>>Задача — сделать компилятор воображаемого языка. S>>В JVM — потому, что это проще, чем в натив (и даже в LLVM). S>>В принципе, можно и Java-код порождать, но это не даст студентам понимания устройства байт-кода E>А зачем какие то библиотеки? Почему нельзя тупо писать байтики напрямую? Одно дело когда ты байткод сам анализируешь, тут библиотека поможет и снимет куча геморроя. Но если задача байткод генерить, да еще в учебных целях — ИМХО лучше тупо в файлик сразу байты писать да и все. Если не ставится задача вообще все покрыть, а чтоб было покрытие ограниченного подмножества — достаточно просто быстро и интересно делается.
Чтобы генерить байтики, нужно знать, какие байтики генерить.
Я не очень разбираюсь в JVM-ном байткоде, но из опыта дотнетного байткода уже понятно, что
а) мнемоники команд. Банальная штука типа "положи в стек целую константу" требует енкодинга команды и енкодинга константы. Делать это каждый раз руками? Код становится невозможно даже прочитать, не то что поддерживать. Интуитивно хочется иметь готовую функцию типа EmitLoadConstant(x byteCodeStream, int value) или byteCodeStream.EmitLoadConstant(value).
б) goto. Не хочется вручную вычислять байтовые смещения с риском промахнуться. Удобнее иметь функции вида MarkLabel() c возможностью использовать результат этой функции в эмите jump-инструкций
в) метаданные. Банальный Call использует в аргументах method token, который весьма нетривиально устроен. Вычислять его вручную — крайне громоздко.
г) предсказание будущего. Каждый метод указывает в заголовке размер байт-кода, количество переменных, а также максимальную глубину стека. При кодогенерации это довольно-таки тяжело оценить заранее — так что либо делать два прохода по внутреннему представлению, либо, всё-таки, инкапсулировать это всё в некий класс, отвечающий за порождение байтиков, который всё это рассчитает самостоятельно с гарантиями корректности и без замусоривания пользовательского кода.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>а) мнемоники команд. Банальная штука типа "положи в стек целую константу" требует енкодинга команды и енкодинга константы. Делать это каждый раз руками? Код становится невозможно даже прочитать, не то что поддерживать. Интуитивно хочется иметь готовую функцию типа EmitLoadConstant(x byteCodeStream, int value) или byteCodeStream.EmitLoadConstant(value).
А что, сейчас такую вещь как словари, студенты не знают? Причем не знают в джаваскрипте, где это основная структура данных? Я вот считаю, что прежде чем рассказывать студентам как компилятор работает, нужно их с базовыми структурами данных познакомить и приучить их использовать. А так да, я в курсе что сейчас и выпускают специалистов без этого знания.
Относительно готовых функция и того, как ты считаешь они должны выглядеть. То, что ты привел — это нарушение Single Responsibility Principle в чистом виде. Я бы делал проще, функция encode, на вход енам команды и опциональные параметры (если язык понавороченнее, то можно и поинтереснее фичи использовать вроде Sealed Class и тому подобному), на выходе массив байт. Внутри логика преобразования, причем весьма тривиальная. И отдельно уже идет запись в стрим или еще куда, уже стандартными средствами. Базовый принцип — нельзя смешивать логику и ввод вывод, это всегда должно быть отделено. В идеале сначала ввод без логики, затем операции, затем вывод без логики, тривиальный — если так делать, то код прекрасно становится читаемый и поддерживаемый.
Кстати рекомендую прочитать: https://habr.com/ru/companies/jugru/articles/858418/
Принцип блин в 1977 сформулирован!
S>б) goto. Не хочется вручную вычислять байтовые смещения с риском промахнуться. Удобнее иметь функции вида MarkLabel() c возможностью использовать результат этой функции в эмите jump-инструкций
Тоже самое, эту функцию написать самому тривиально, там кода 3 строчки. Если студент не в состоянии это написать сам — рано ему компиляторами заниматься.
S>г) предсказание будущего. Каждый метод указывает в заголовке размер байт-кода, количество переменных, а также максимальную глубину стека. При кодогенерации это довольно-таки тяжело оценить заранее — так что либо делать два прохода по внутреннему представлению, либо, всё-таки, инкапсулировать это всё в некий класс, отвечающий за порождение байтиков, который всё это рассчитает самостоятельно с гарантиями корректности и без замусоривания пользовательского кода.
Да, нужно инкапсулировать. И там не будет ничего сложного и громоздкого. Можно через ленивые вычисления и все такое, совершенно не обязательно в несколько проходов. А на деле — а хоть бы и в несколько проходов, что тут такого? Ну медленнее, но зато код будет вообще шикарный и простой, прекрасно тестируемый. Хоть 10 проходов, лишь бы почище сам код был.
Вот студенты и должны научиться это делать. Написать компилятор учебный — это именно требуется разобраться как подобные вещи делаются с нуля. Без библиотек кодогенерации, только через стандартные структуры данных, средства языка и все такое. Для начала следует сделать интерпретатор, крайне б желательно было осилить книжку SICP, чтоб мозги на место поставить, и там собственно пример как делается интерпретатор вполне есть. Изменений как из интерпретатора сделать компилятор, кстати, будет вообще минимум! Тупо бежишь по AST, но не непосредственно выполняешь, а делаешь кодогенерарацию.
Если же пользоваться готовыми библиотеками — ни хрена студенты не поймут! И придется еще тратить время на то, чтоб с библиотекой разобраться. И далеко не факт что API библиотеки будет сделано достаточно хорошо. В результате что был курс, что его не было — в памяти не останется вообще ни черта! Особенно если упор делать не на реализацию и программу, а на отчет и его защиту с презентациями, как у нас тут некоторые делают, да еще и ЕСПД привлекают начиная с первого курса для даже лабораторных.
На деле — прекрасная учебная задача, на которой студенты могут набить руку и попробовать не городить спагетти говнокод, а написать прекрасный компактный и поддерживаемый код, пусть и не идеально шустрый, однопроходный и все такое. Однопроходный тоже кстати можно написать достаточно компактно и поддерживаемо, но напрягаться умственно придется сложнее, все эти ленивости и все такое, контекст держать — если нет цели сделать максимально шустро, лучше сделать просто максимально понятно. Нужно именно приучить студентов не говнокодить копипастить, а бороться со сложностью и запутанностью, походя самим писать вспомогательные функции для удобства на скорость, причем чтоб тратить на это меньше времени, чем если говнокодить все в лоб! А то ведь большинство такому не научится даже на работе реальной, тупо никто не покажет как можно, так и будут блин ждать пока добрый буржуй напишет удобную функцию, а если функции нет, так и будут копипастить блин один и тот же код и даже мысли блин не возникнет написать так, как удобно. Собственно большинство библиотек тоже ни фига не удобны в использовании.
А вообще, я блин в шоке насколько мало программистов с профильным блин образованием в курсе как работают интерпретаторы, компиляторы и т.д. Не на детальном уровне, а на базовом и общем!!!! Мне в обычной работе, если что, неоднократно приходилось это писать. Чаще всего интерпретаторы, именно компилятор не было смысла, но кодогенератор тоже приходилось писать далеко не один раз, и напишу еще не раз. Вроде не совсем ненужный навык.
PS Хотел еще ссылку на охрененный курс по компиляторам на ютубе привести, но блин не могу найти. Даже университет не помню какой, толь MIT, толь Беркли. Там несколько, более ранний был довольно стандартный, профессора звали что то вроде Кубианович, начинается точно на Куби, а вот более поздний там преподавали поинтереснее и был фокус на написании DSL и еще там в названии было Hack Your Language. Очень мне б блин самому не помешало пересмотреть, а то как понадобилось однопроходный парсер с приоритетами операций писать — я блин нормально хрен написал, тупо забыл как это все элегантно делается.
Здравствуйте, elmal, Вы писали:
E>А что, сейчас такую вещь как словари, студенты не знают? Причем не знают в джаваскрипте, где это основная структура данных? Я вот считаю, что прежде чем рассказывать студентам как компилятор работает, нужно их с базовыми структурами данных познакомить и приучить их использовать. А так да, я в курсе что сейчас и выпускают специалистов без этого знания.
Непонятно, при чём тут словари.
E>Относительно готовых функция и того, как ты считаешь они должны выглядеть. То, что ты привел — это нарушение Single Responsibility Principle в чистом виде.
С чего бы это вдруг? E>Я бы делал проще, функция encode, на вход енам команды и опциональные параметры (если язык понавороченнее, то можно и поинтереснее фичи использовать вроде Sealed Class и тому подобному), на выходе массив байт.
В принципе, можно и так. Хотя не очень понятно, как будет выглядеть сигнатура этой функции. И как потом этой функцией пользоваться. E>Внутри логика преобразования, причем весьма тривиальная. И отдельно уже идет запись в стрим или еще куда, уже стандартными средствами.
Вы так говорите, как будто я предлагал что-то другое. E>Базовый принцип — нельзя смешивать логику и ввод вывод, это всегда должно быть отделено. В идеале сначала ввод без логики, затем операции, затем вывод без логики, тривиальный — если так делать, то код прекрасно становится читаемый и поддерживаемый.
E>Кстати рекомендую прочитать: E>https://habr.com/ru/companies/jugru/articles/858418/ E>Принцип блин в 1977 сформулирован!
Спасибо за ссылку на общеизвестное.
По-прежнему непонятно, как то, что вы предлагаете, противоречит идее библиотеки.
E>Да, нужно инкапсулировать. И там не будет ничего сложного и громоздкого. Можно через ленивые вычисления и все такое, совершенно не обязательно в несколько проходов. А на деле — а хоть бы и в несколько проходов, что тут такого? Ну медленнее, но зато код будет вообще шикарный и простой, прекрасно тестируемый. Хоть 10 проходов, лишь бы почище сам код был. E>Вот студенты и должны научиться это делать. Написать компилятор учебный — это именно требуется разобраться как подобные вещи делаются с нуля. Без библиотек кодогенерации, только через стандартные структуры данных, средства языка и все такое. Для начала следует сделать интерпретатор, крайне б желательно было осилить книжку SICP, чтоб мозги на место поставить, и там собственно пример как делается интерпретатор вполне есть. Изменений как из интерпретатора сделать компилятор, кстати, будет вообще минимум! Тупо бежишь по AST, но не непосредственно выполняешь, а делаешь кодогенерарацию.
В итоге 3/4 курса студенты будут заниматься нудным выпиливанием лобзиком по вазелину, и только 1/4 — собственно компиляцией.
E>Если же пользоваться готовыми библиотеками — ни хрена студенты не поймут! И придется еще тратить время на то, чтоб с библиотекой разобраться. И далеко не факт что API библиотеки будет сделано достаточно хорошо. В результате что был курс, что его не было — в памяти не останется вообще ни черта! Особенно если упор делать не на реализацию и программу, а на отчет и его защиту с презентациями, как у нас тут некоторые делают, да еще и ЕСПД привлекают начиная с первого курса для даже лабораторных.
Если бы у нас было всё время мира — да, можно всё пилить с нуля. Но у нас — 1 семестр, и это не единственный предмет в семестре. Положа руку на сердце: вы напишете с нуля компилятор небольшого языка с кодогенерацией и статической верификацией корректности за 32 часа?
E>А вообще, я блин в шоке насколько мало программистов с профильным блин образованием в курсе как работают интерпретаторы, компиляторы и т.д. Не на детальном уровне, а на базовом и общем!!!! Мне в обычной работе, если что, неоднократно приходилось это писать. Чаще всего интерпретаторы, именно компилятор не было смысла, но кодогенератор тоже приходилось писать далеко не один раз, и напишу еще не раз. Вроде не совсем ненужный навык.
Ну, вот мы их и обучаем. Причём в реальном производстве никогда они не будут с нуля ни токенайзер, ни парсер, ни кодогенератор писать. Бессмысленная затея. Значит, и навыки им нужны больше похожие на то, что в жизни понадобится.
E>PS Хотел еще ссылку на охрененный курс по компиляторам на ютубе привести, но блин не могу найти. Даже университет не помню какой, толь MIT, толь Беркли. Там несколько, более ранний был довольно стандартный, профессора звали что то вроде Кубианович, начинается точно на Куби, а вот более поздний там преподавали поинтереснее и был фокус на написании DSL и еще там в названии было Hack Your Language. Очень мне б блин самому не помешало пересмотреть, а то как понадобилось однопроходный парсер с приоритетами операций писать — я блин нормально хрен написал, тупо забыл как это все элегантно делается.
Вот у нас студенты как раз учатся делать это элегантно — на PEG-грамматиках.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>Непонятно, при чём тут словари.
Для сопоставления инструкции на ее код и тому подобное. Я не очень понимаю смысл жестко хардкодить кучу методов вроде writeLoadInstructionToStream и т.д. Но это ладно, вкусовщина.
S>В итоге 3/4 курса студенты будут заниматься нудным выпиливанием лобзиком по вазелину, и только 1/4 — собственно компиляцией.
Да не будет там выпиливания лобзиком по вазелину.
S>Если бы у нас было всё время мира — да, можно всё пилить с нуля. Но у нас — 1 семестр, и это не единственный предмет в семестре. Положа руку на сердце: вы напишете с нуля компилятор небольшого языка с кодогенерацией и статической верификацией корректности за 32 часа?
Да, напишу. Правда без статической верификации корректности, этим никогда не занимался. Мне когда на практике такие задачи попадаются, естественно никто время на это не выделяет и не ставит задачу написания DSL. Но приходилось этим заниматься в трех разных конторах, навык оказался полезным.
S>Ну, вот мы их и обучаем. Причём в реальном производстве никогда они не будут с нуля ни токенайзер, ни парсер, ни кодогенератор писать. Бессмысленная затея. Значит, и навыки им нужны больше похожие на то, что в жизни понадобится.
Что интересно — мне это реально понадобилось в реальном проекте, и я писал и токенайзер (примитивный, тупо сплитом строки по регэкспу), и парсер, и кодогенератор. Причем на время и именно что с нуля. Язык, который пришлось парсить — 1С, естественно очень ограниченное подмножество . Некоторые таланты блин в базу зафигачили 1С выражение как строку. Тупо логическое выражение с приоритетом, но там тысячи переменных со скобками и сотни функций, и вот мне требовалось повторить логику, но в более чем тысячу раз быстрее, ибо если реюзать функцию на 1С оригинальную, расчет займет много лет при полной загрузке всех серверов . Как пример просто. Так еще доводилось DSL писать уже на текущем языке, чтоб можно было описывать уравнения на миллионы переменных в виде близкой к математической записи и затем из этого интерпретировать в различные решатели систем уравнений. Проект правда через года 3 всеж помер, из за смены языка программирования на питон, но тем не менее какое то время доводилось писать и классический интерпретатор, хоть и без токенайзера и парсера, ибо DSL была на том же языке сделана, тупо переопределены операторы были и был набор классов определенных и функций их конструирования, далее тупо обход AST и интерпретация, причем еще и с оптимизациями некоторыми. Написать компилятор тоже не составило б труда, но не было необходимости.
Навык написания как минимум интерпретаторов и кодогенератора, наколенный — позволяет сэкономить кучу времени, и если задачи типовые — реюзать и ОЧЕНЬ быстро реагировать на изменения требований. Тупо добавить потом пару строчек, нажать билд и деплой — и все. А не хреначить один и тот же копипастный код из проекта в проект.
S>Вот у нас студенты как раз учатся делать это элегантно — на PEG-грамматиках.
Нашел этот курс охрененный! Блин, его с ютуба удалили, потому и потерял. На всякий случай еще и сюда — http://www.infocobuild.com/education/audio-video-courses/computer-science/CS164-Spring2012-Berkeley/lecture-01.html. Ключевые слова CS 164 Ras Bodik Spring 2012. Здесь именно упор на написание DSL, кажется всеж этот, надо будет пересмотреть, а то я забыл все нафик. Или посмотреть что поновее было б неплохо, но именно от этого курса я был в свое время в восторге и этот курс много заставил переосмыслить. Надо б блин сохранить как нибудь, а то ведь удалят нафик снова ...
Здравствуйте, elmal, Вы писали: S>>Непонятно, при чём тут словари. E>Для сопоставления инструкции на ее код и тому подобное. Я не очень понимаю смысл жестко хардкодить кучу методов вроде writeLoadInstructionToStream и т.д. Но это ладно, вкусовщина.
В байткоде инструкция и есть код. Я не понимаю, что вы предлагаете — возможно, потому, что вы пишете словами, а не кодом. Какими типами будет параметризован ваш словарь?
E>Да не будет там выпиливания лобзиком по вазелину.
Только оно и будет. Рукопашное порождение каждого байта — это вот оно и есть. E>Да, напишу. Правда без статической верификации корректности, этим никогда не занимался.
Ну вот это, конечно, не суперсложная задача, но время на неё тоже надо. E>Мне когда на практике такие задачи попадаются, естественно никто время на это не выделяет и не ставит задачу написания DSL. Но приходилось этим заниматься в трех разных конторах, навык оказался полезным.
Ну, может быть вы и напишете. Я может быть тоже напишу — но у меня за плечами не один год опыта. А у студентов что? Им только что отчитали курс дискретки с конечными автоматами; а сейчас быстро-быстро запихивают формальные грамматики вместе с тройками Хоара и алгоритмами прямого и обратного прослеживания. E>Что интересно — мне это реально понадобилось в реальном проекте, и я писал и токенайзер (примитивный, тупо сплитом строки по регэкспу), и парсер, и кодогенератор. Причем на время и именно что с нуля.
Непонятно, зачем вы этим занимались. В такой задаче можно сэкономить примерно три четверти усилий, если взять готовую реализацию парсера на том же PEG. E>Язык, который пришлось парсить — 1С, естественно очень ограниченное подмножество . Некоторые таланты блин в базу зафигачили 1С выражение как строку. Тупо логическое выражение с приоритетом, но там тысячи переменных со скобками и сотни функций
Вот примерно всё, что вы рассказываете — это лабораторная номер 7 (из 15), она же вторая в серии "про грамматики". Предыдущие работы — чисто на освоение языка программирования и его стандартной библиотеки; они не понадобятся, если брать менее экзотический язык.
E>Навык написания как минимум интерпретаторов и кодогенератора, наколенный — позволяет сэкономить кучу времени, и если задачи типовые — реюзать и ОЧЕНЬ быстро реагировать на изменения требований. Тупо добавить потом пару строчек, нажать билд и деплой — и все. А не хреначить один и тот же копипастный код из проекта в проект.\
Ну так вы и предлагаете хреначить копипастный код, вместо того, чтобы взять готовую реализацию и поехать
E>Нашел этот курс охрененный! Блин, его с ютуба удалили, потому и потерял. На всякий случай еще и сюда — http://www.infocobuild.com/education/audio-video-courses/computer-science/CS164-Spring2012-Berkeley/lecture-01.html. Ключевые слова CS 164 Ras Bodik Spring 2012.
Посмотрим, спасибо.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>·>А, понял вопрос. Да, сабж, очень сильно. А задача то какая? Зачем генерить именно байт-код? Может джаву лучше? Ещё кстати scala или kotlin если языка не хватает. S>Задача — сделать компилятор воображаемого языка. S>В JVM — потому, что это проще, чем в натив (и даже в LLVM). S>В принципе, можно и Java-код порождать, но это не даст студентам понимания устройства байт-кода S>Вот, кстати, смотрю в сторону kotlin. Похоже, на нём тоже неплохо пишется компилятор. Но для него, я так понял, в качестве IDE можно использовать только IntelliJ, т.к. интеграция с VS Code у него так себе.
Я бы посоветовал генерировать байткод wasm.
Во-первых он реально очень простой, гораздо проще байткода JVM.
Во-вторых в нём будто всё сделано для того, чтобы его было просто генерировать.
В-третьих у него есть шикарный официальный лиспо-подобный синтаксис, который ещё проще генерировать, а уж из него в байты переконвертировать код есть.
JVM байткод не такой уж простой и там хватает нюансов...
Что такое библиотека AST я не знаю, никогда не слышал. Есть библиотека ASM. Это не официальный, но самый популярный способ генерировать байткод. Но там всё достаточно низкоуровневое, никакого компилятора в ней нет. Да и API у неё специфическое... https://www.baeldung.com/java-asm тут можно ознакомиться немного.
Здравствуйте, vsb, Вы писали: S>>Задача — сделать компилятор воображаемого языка. S>>В JVM — потому, что это проще, чем в натив (и даже в LLVM). S>>В принципе, можно и Java-код порождать, но это не даст студентам понимания устройства байт-кода S>>Вот, кстати, смотрю в сторону kotlin. Похоже, на нём тоже неплохо пишется компилятор. Но для него, я так понял, в качестве IDE можно использовать только IntelliJ, т.к. интеграция с VS Code у него так себе. vsb>Я бы посоветовал генерировать байткод wasm. vsb>Во-первых он реально очень простой, гораздо проще байткода JVM.
А вот это интересная идея. vsb>Во-вторых в нём будто всё сделано для того, чтобы его было просто генерировать.
Посмотим. vsb>В-третьих у него есть шикарный официальный лиспо-подобный синтаксис, который ещё проще генерировать, а уж из него в байты переконвертировать код есть. vsb>JVM байткод не такой уж простой и там хватает нюансов...
Отож. vsb>Что такое библиотека AST я не знаю, никогда не слышал. Есть библиотека ASM. Это не официальный, но самый популярный способ генерировать байткод. Но там всё достаточно низкоуровневое, никакого компилятора в ней нет. Да и API у неё специфическое... https://www.baeldung.com/java-asm тут можно ознакомиться немного.
Да, я как раз про неё — опечатался в названии.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.