Комментарии 28
Есть такой язык – Haxe. И там практически все это есть из коробки. И макро апи для работы с AST, и Printer который прямо в консольку напечатает кусок AST в виде уже готового кода. И таргетов намного больше. И сурсмапы из коробки работают.
Вот так, например, выглядит макрос, который в любой, отмеченный метой, класс
– докинет интовое поле
– если есть метод main(), то докинет в него вывод приветствия
– выведет все поля класса в виде кода в консоль во время компиляции.
public static function macroTrace() {
var fields = Context.getBuildFields();
for (f in fields) {
switch f {
case {name:"main", kind:FFun({ expr:{expr:EBlock(exprs)}})}:
exprs.push(macro trace("hi"));
case _:
}
}
fields.push(
{name:"genVar",
kind:FVar(macro:Int, macro 5),
pos:Context.currentPos()
}
);
var p = new Printer();
for (f in fields) {
trace(p.printField(f));
}
return fields;
}
Поиграться можно здесь. Код макроса на вкладке Source 2, вывод полей – Compiler output.
Haxe, конечно, классный язык, но в качестве DSL он слабо подходит. Для обычного пользователя он слишком сложен. Для анализируемых конфигураций он слишком привязан к Haxe. А возможности AST ограничены лишь моделью Haxe.
По крайней мере, под определение «пайплайна здорового человека» он подходит больше, чем все конкуренты из статьи, поэтому быть упомянутым в качестве альтернатив он достоин.
Если нужен «экстремально» простой и гибкий язык программирования, то даже в качестве DSL — Domain Specific Language применяют Forth (Форт).
При программировании микроконтроллеров и зачастую как основной инструментарий.
Была и статья на Хабр Универсальный DSL. Возможно ли это?
где Форт рассматривался в качестве DSL языка C# проекта.
Вот и ещё пример — EFrt проект полной реализации Форт на C#
P.S. Конечно же, «поверх» JavaScript JeForth.3we — Форт тоже делают
И на Github достаточно ещё проектов по связке Форт и JS
![image](https://habrastorage.org/getpro/habr/comment_images/2ea/85a/382/2ea85a382ecdeeefc89768271331e612.png)
Mako.JS — Демо игры проекта запускаемые в браузере, с Форт в качестве DSL
Mako on Github
![image](https://habrastorage.org/getpro/habr/comment_images/040/e1a/1b4/040e1a1b4257594e6ecef54b15a57754.png)
Forth имеет постфиксную нотацию, что для людей крайне не удобно. Но её легко превратить в префиксную, которую воспринимать уже гораздо легче.
Например, возьмём эту программу на форте:
25 10 MULT 50 SUM LOG2
И напишем эквивалентную в префиксной нотации:
LOG2 SUM
MULT
25
10
50
P.S. Кстати, страшилки по поводу неудобства «формата» записи Форт программ немного преувеличена в отношении к нему и эта «страшилка Forth» не на первом месте по степени грамотного понимания и использования Форт языка, есть и другие, но серое вещество мозга достаточно гибкая система при её «переформатировании». Форт, как «искуственный» компьютерный язык, гораздо проще в освоении чем, например, иностранный естественный язык по поводу которого нет «предвзятых» взглядов.
В России Форт начался и так же быстро сощёл со сцены в мутные 90-е годы безвременья.
Баранов С.Н. «Язык Форт в СССР и России»
Да, надо ещё очень внимательно следить за содержимым стека. Ибо одно неверное движение, и программа ведёт себя непредсказуемым образом. С этим людям ещё сложнее управляться. Поэтому для тог же WASM кроме постфиксной нотации с ручным управлением стеком, есть и префиксная, где стеком управляет компилятор, а программист управляет лишь виртуальными регистрами.
Обычно происходят мелкие перестановки 2-3 элементов стэка и они не сложно понимаемы. Для других — «особо тяжёлых случаев» никак не ложащихся в алгоритм в Форт реализации можно использовать, например, локальные переменные появившееся в Форт стандарте 94года.
И, конечно, Форт, как язык нацеленный на использование метапрограмирования и DSL наиболее значимо себя проявляет именно при использовании таких техник его использования неплохо описанных во 2-й книге за авторством Броуди «Мышление Форт».
P.S. И, локально оттестировать поведение слова не сложно в Форт.
А стат типизация там есть, чтобы гарантировать, что к моменту применения слова на стеке уже лежит всё необходимое, а после применения не лежит ничего лишнего? В васме, например, ошибки со стеком приводят к падению в рантайме.
Если, программист, по определению не даун, то это им легко должно легко пониматься, а если что пошло не так, то последствия его действий не заставят себя ждать (Форт упадёт и/или выведет информацию об эксепшене)
Для чисел плавающей арифметики есть отдельный стэк со словами подобными F+, F-…
P.S. Конечно, в более серьёзном Форт инструментарии может в процессе трансляции программы выводится и проверяться соответствие типов данных и операций применённых к ним, но обычно на это все использующие Форт обычно кладут «болт» и особо не парятся :)
Хотя есть варианты Форт -типа StrongForth где разработчики его стали контролировать типизацию в их Форт.
Если интересен более продвинутый «Форт», то есть например Factor — язык программирования. где на стэк помещаются уже более комплексные сущности чем числа.
Что то мало решённых задач с ресурса rosettacode.org на Haxe (всего 52-а решения)
А вы не могли бы продолжить мысль? Это по каким-то критериям должно влиять на принятие решения о выборе инструмента?
Если нужен «экстремально» простой и гибкий язык программирования, то даже в качестве DSL — Domain Specific Language применяют Forth (Форт).
При программировании микроконтроллеров и зачастую как основной инструментарий.
Применять-то применяют совершенно разное. И выбирают по совершенно разным критериям, в зависимости от ситуации. Я бы предположил, что если бы была возможность оценить количество всего, что можно посчитать DSL, то в «наколенных» условиях, коих большинство, победили бы JSON/XML, так как для них вообще не надо задумываться о лексерах-парсерах-синтаксических деревьях, все инструменты для разбора доступны с любого языка/платфомы.
Была и статья на Хабр Универсальный DSL. Возможно ли это?
где Форт рассматривался в качестве DSL языка C# проекта.
Посыл автора я не совсем понял. Особенно вот эту сентенцию
Из-за отсутствия избыточных конструкций всего несколько строк на DSL могут реализовать довольно сложный функционал
Я не понимаю, как ограничение доступных средств может обеспечить возможность более короткой записи логики. На самом деле, здесь происходит подмена понятий, а «выразительность» достигается комплектными командами. При предоставлении такого же апи, запись этой логики на «родительском» языке вряд ли будет сильно длиннее.
Кроме того, предлагается самостоятельно велосипедить с нуля интерпретатор. Это может оказаться хорошим решением в некоторых ситуациях, но вряд ли во всех. На месте пользователя DSL из той статьи я бы предпочел что-то на основе XML или хотя бы JSON.
Вообще, когда речь заходит про «запилить по-быстрому язык для своей системы с ноля», то в первую очередь в голову придет лисп из-за простоты и надежности. Хотя он далеко не самый юзер-фрэндли для пользователя.
В других ситуациях хорошим решением будет встроить в систему каую-нибудь lua, выствив для нее нужный апи. Это уже и не совсем DSL, но области применимости могут пересекаться.
А вы не могли бы продолжить мысль? Это по каким-то критериям должно влиять на принятие решения о выборе инструмента?
Например для типовых примеров уже готовых решений в рамках использования нового языка в чём разница Hexe с другими языками на «типовом» примере задачи.
Я не понимаю, как ограничение доступных средств может обеспечить возможность более короткой записи логики. На самом деле, здесь происходит подмена понятий, а «выразительность» достигается комплектными командами. При предоставлении такого же апи, запись этой логики на «родительском» языке вряд ли будет сильно длиннее.
Сентенция с Форт, в общем понимании его как языка программирования приводит к возможности записи каких то «макросов» близко по форме к естественным языкам.
Для кого то это хорошо, а кто то без явно синтаксическо-семантических ограничений присутствующих в современных языках даже не сможет понять для использования Форт.
т.е., наши привычки и освоенные взгляды — наши же «оковы» из «недоступности» понимания, что пути решения задач этим не ограничивается.
Вообще, когда речь заходит про «запилить по-быстрому язык для своей системы с ноля», то в первую очередь в голову придет лисп из-за простоты и надежности. Хотя он далеко не самый юзер-фрэндли для пользователя.
Форт ещё проще т.к. в нём нет скобок в Лисп понимании и он прямолинеен по последовательности выполнения слов при трансляции кода программы в исполнение.
На уровне трансляции Форт программ в базисе Форт нет даже понятия синтаксиса языка, кроме того, что мы имеем дело со словами — самодостаточными сущностями во взаимодействии с другими словами и создающими свой контекст при их использовании для других слов.
Уф… :)
Если серьезно, то не вижу, какая разница 10 или 20 сортировок написано на языке для примера, они используют одни и те же конструкции. Гораздо эффективнее заглянуть на сайт языка, часто на главной странице есть выразительный пример. У Haxe кроме документации есть еще и весьма интересный кукбук.
и если есть кукбук, то не должно (или должно) возникнуть сложностей с их размещением и в рамках этого ресурса и возможно со ссылками и в самой «Книге рецепов для IT повара на Hexe»
(или должно)
Не думаю, что кто-то будет возражать, если вы перенесете примеры из кукбука на розетту или куда-либо еще.
P.S. Не являясь пользователем Haxe мне сложно перенести примеры из кукбоок на ресурс rosettacode.org.
Или это «сложно/нецелесобразно»?
А как у него с, собственно, сорсмаппингом при трансляции в другие языки?
HX_LOCAL_STACK_FRAME(_hx_pos_cf91c14e89d01160_8_writeInt8Attribute,"MeshUtils","writeInt8Attribute",0xc120e75e,"MeshUtils.writeInt8Attribute","MeshUtils.hx",8,0xfe3ad45a)
HX_LOCAL_STACK_FRAME(_hx_pos_cf91c14e89d01160_24_writeFloatAttribute,"MeshUtils","writeFloatAttribute",0xf3619395,"MeshUtils.writeFloatAttribute","MeshUtils.hx",24,0xfe3ad45a)
void MeshUtils_obj::__construct() { }
Dynamic MeshUtils_obj::__CreateEmpty() { return new MeshUtils_obj; }
void *MeshUtils_obj::_hx_vtable = 0;
Dynamic MeshUtils_obj::__Create(::hx::DynamicArray inArgs)
{
::hx::ObjectPtr< MeshUtils_obj > _hx_result = new MeshUtils_obj();
_hx_result->__construct();
return _hx_result;
}
bool MeshUtils_obj::_hx_isInstanceOf(int inClassId) {
return inClassId==(int)0x00000001 || inClassId==(int)0x48150a24;
}
void MeshUtils_obj::writeInt8Attribute( ::data::AttribSet attrs, ::haxe::io::Bytes bytes,::String attName,int firstVert,int vertCount, ::Dynamic provider){
HX_STACKFRAME(&_hx_pos_cf91c14e89d01160_8_writeInt8Attribute)
HX_STACK_ARG(attrs,"attrs")
HX_STACK_ARG(bytes,"bytes")
HX_STACK_ARG(attName,"attName")
HX_STACK_ARG(firstVert,"firstVert")
HX_STACK_ARG(vertCount,"vertCount")
HX_STACK_ARG(provider,"provider")
HXLINE( 8)
HXLINE( 9) HX_VARI( ::Dynamic,descr) = attrs->getDescr(attName);
HXLINE( 10) if (::hx::IsNotEq( descr->__Field(HX_("type",ba,f2,08,4d),::hx::paccDynamic),1 )) {
HXLINE( 11) HX_STACK_DO_THROW(::haxe::Exception_obj::thrown(HX_("wrong type",8d,aa,d4,e5)));
}
HXLINE( 12) HX_VAR( int,size);
HXDLIN( 12) switch((int)(( (int)(descr->__Field(HX_("type",ba,f2,08,4d),::hx::paccDynamic)) ))){
case (int)0: {
HXLINE( 12) size = 4;
}
break;
case (int)1: {
HXLINE( 12) size = 1;
}
break;
case (int)2: {
HXLINE( 12) size = 2;
}
break;
case (int)3: {
HXLINE( 12) size = 4;
}
break;
}
Если речь идет об отладке прикладного кода, то великолепно работает связка flash+idea. Быстрая компиляция и очень удобный дебаггер от JetBrains. Когда я работал над юнити проектами, то стектрейсы из редактора тоже нормально мапились (там c#).
В качестве предупреждения: если хотите маппить стек трейс ошибок (например, из логов приложения) на исходники - забудьте по Safari как про страшный сон. Ну или помните - и игнорируйте.
Ибо Safari генерит трейсы как бог на душу положит, и после применения сорс мапов в лучшем случае что-то просто не смаппится, а в худшем - смаппится не туда, и долго разбираешься, что же пошло не так.
Насчет js.tree. А как его расширять доп метаданными?
К примеру, для генерации сорсмапов нам для каждого узла AST надо знать позицию в исходном файле. Babel это записывает в поле range
каждого узла. Как это будет выглядеть в js.tree?
Все tree-ноды имеют поле span, через которое можно получить координаты. Чтобы их увидеть в AST можно их заимпринтить, как тут. Это полезно при передаче между процессами, чтобы при сериализации не терялись ссылки на исходники и сами исходники.
В общем же случае, в любой узел можно подкладывать кастомный узел с метаданными. Например, добавим аннотации типов:
const
foo @ [,] any
[,]
@ [,] |
number
string
123 @ number
bar @ string
Просто нужно не забыть эти узлы вырезать перед трансляцией в JS.
Как вводная часть статьи выглядит для ничего не подозревающего человека:
Сейчас вы видите законченное приложение на $shmol:
$my_app
Оно состоит из панельки, внутри которой расположен чекбокс. И вместе они связаны двусторонней связью по заданным свойствам. В этой 1 строчке кода есть даже поддержка локализации. Эквивалентный код на JavaScript занимает в 25 раз больше места.
@nin-jin, у вас есть подробное описание того, как работает hack()? Из примеров и ссылок на tree.hyoo.ru не совсем понятно как интерпретируются наборы скобочек со знаками препинания и как происходит поиск и подмена узлов в исходном дереве. Можно ли описать трансформацию нескольких узлов в дереве по шаблону, например поменять местами узлы сохраняя все их листья как есть.
Проще, наверно, просто код глянуть. Хак проходится по всем дочерним узлам, смотря на их типы. Если для типа задан хендлер, то заменяет узел на тот список узлов, что что он вернёт. Дефолтный хендлер возвращает клон узла, применяя хак к каждому ребёнку. Таким образом, если не задать никаких хендлеров, то хак просто склонирует всё дерево. Ну а в кастомном хендлере можно делать что хочешь. Можно вернуть поддерево как есть без какого-либо процессинга, создавать новые деревья, хачить сторонние деревья и тд.
Поиска по шаблону пока что нет. У меня есть реализация сверки шаблонов, но она специфична для конкретного языка. В общем же случае, нужно проработать язык шаблонов и сделать их компилятор, чтобы выбирать шаблон, на который сматчиться, за один проход, а не в цикле по шаблонам.
Что не так с сорсмапами и как с ними не связываться?