Некоторое время назад я написал интерпретатор лиспоподобного языка, который назвал Liscript. Опубликовал несколько статей на Хабре, посвященных особенностям реализации ядра, TCO, GUI, REPL-ботов и т.п. Недавно добавил web-интерфейс REPL-у (ссылка в конце статьи).
При чем здесь поцелуи и экскаваторы? Думаю, большинству известны такие аббревиатуры, как KISS (keep it simple stupid — делай это проще, дурачок), YAGNI (You ain't gonna need it — Вам это не понадобится), а также высказывания людей разной степени великости про архитектурных астронавтов, «все должно быть сделано так просто, насколько возможно, но не проще», и т.п.
Допустим, перед вами стоит задача — выкопать яму. Какие есть варианты решения? Взять лопату и выкопать самому — дешево и сердито, но долго и возможно неоптимально (зависит от вашего уровня владения лопатой и размеров ямы). Отдать на аутсорс таджикам (не будем рассматривать здесь этот вариант, хотя я должен был его упомянуть). Взять экскаватор — быстро и эффективно, но затратно: бензин/аренда, плюс не факт, что он проедет в вашу садовую калитку, значит надо сносить/восстанавливать забор и т.д. Также, необходимо определиться с моделью (порой из 100500 вариантов), а если вы будете управлять им самостоятельно, надо разобраться во всех его рычагах и педалях.
Разумеется, если вы — профессиональный экскаваторщик, копаете по 200 ям за день, или вы стремитесь им стать, а изначальная задача (вырыть яму) нужна вам не сама по себе, а как тренировка или демонстрация ваших умений, тогда выбор очевиден (остается разве что вопрос модели). Но даже профессионал возьмет лопату, сажая цветы.
В общем, про выбор инструментов под задачи, и конкретные (подозреваю, что спорные) решения, которые я выбирал в процессе реализации проекта, под катом.
Реализация односвязного списка
В Liscript отсутствуют точечные пары (cons-ячейки), так что основная структура как кода так и данных в нем — список. В Java-версии интерпретатора я написал собственный простейший класс с полями car и cdr. Можно было взять стандартные коллекции Java типа LinkedList или ArrayList — скорее всего они лучше оптимизированы в плане аллокаций и дружбы со сборщиком мусора. Но я выбрал свой собственный велосипед, хотя это несложно отрефакторить ко второму варианту.
Токенизатор/Лексер/Парсер
Это пример поинтереснее. Нужно читать входной текст/строку и преобразовывать ее в готовое AST (абстрактное синтаксическое дерево), выделяя из текста токены и организуя их в иерархическую структуру. Задача более чем известная, существует целая обширная теория синтаксического анализа и разбора, и множество библиотечных реализаций различных парсеров, например www.antlr.org Но я принял решение написать свой простой велосипед на конечном автомате — у Лиспа очень простая грамматика и синтаксис, хотя конечно пришлось учитывать многострочные строковые литералы и комментарии. Более того — таких велосипедов у меня 3: в Haskell-реализации (несмотря на множество готовых решений: parsec, attoparsec и т.д.), в ядре Java-реализации и в GUI на Swing для синтаксической подсветки текста кода.
WEB backend
История болезни: это мой первый web-проект, в качестве
WEB frontend
Здесь вообще просто море вариантов выбора самых различных технологий, ибо область бурно развивается уже много лет. И апологеты разных фреймворков доказывают всему миру (и друг другу), что их выбор лучше/эффективнее/перспективнее и только так надо «писать WEB в 2017 году» (С) Каждый из них по-своему прав, но какой выбор был у меня? Для решения сугубо утилитарной задачи — вырыть яму написать web-моринтерфейс своего REPL-а.
- Использовать Java-фреймворки, умеющие генерировать вэб-интерфейс: Vaadin, FX и т.п. Тяжелые мощные экскаваторы, поддерживающие парадигму «написано однажды — копает везде».
- Использовать многочисленные WEB-фреймворки/библиотеки, имя им легион: Angular, React,… Каждый со своим богатым
внутренним миромблек-джекомфункционалом и свистелками. - Написать все самому на голом ванильном Javascript / HTML / CSS.
Как вы наверное догадываетесь, я конечно же выбрал третий вариант :). Даже без использования jQuerry, потому что этот станок пришлось бы тоже изучать. Без Babel / Webpack и еще 100500 модных на фронте слов. Впервые в жизни ощутив радость отладки под различные браузеры, включая IE, я был вынужден захардкодить в CSS константы, вместо объявления их в начале файла, что само по себе убого, но IE у меня не хотел видеть эти объявления, а технологии генерирования CSS из других форматов, как предлагают различные модные фреймворки, подразумевали использование упомянутых фреймворков.
Но конечно я не настолько упорот, как вам могло показаться велосипедист, а не экскаваторщик. В паре задач вэб-интерфейса я выбрал готовые библиотеки:
Сплит-панели
Мне для REPL-а они были очень нужны. Мощные фреймворки для вэб-GUI реализуют их в своем составе, но, исключив тяжелую технику, я рассмотрел более мелкие готовые реализации, и, попробовав 3-4, таки выбрал одну. Хотя она тянет за собой jQuerry, что мне не очень нравится (больше мне пока ни для чего он не нужен), но гораздо лучше реагирует в плане отзывчивости интерфейса. Если будет время/желание, может перепишу на свой велосипед, без jQuerry.
Подсветка кода
Здесь я считаю, что мне повезло — для решения этой задачи я сразу нашел великолепную библиотеку codemirror.net, разобрался в ее возможностях, написал плагин для синтаксиса своего языка и тему оформления. Не писал свои велосипедные парсеры/лексеры, потому что эта библиотека проста, легковесна и хорошо справляется со своей работой — как в статических текстовых областях, так и в динамике при редактировании текста кода пользователем.
Заключение
Разумеется, я в курсе, что вышеописанные принятые мной решения как минимум спорны и неоднозначны. Что можно (и с каких-то позиций даже лучше) было применить готовые технологические инструменты. Что я приобрел, написав свои парсеры / лексеры? Опыт в написании подобных велосипедов на конечных автоматах, полный контроль происходящего, возможность легкой кастомизации. Что потерял? Не разобрался с готовыми существующими парсерами, не освоил их, и если придется применять в будущем — я буду вынужден осваивать их с нуля. И такой же вердикт по каждому из приведенных вариантов отдельных локальных задач. Но в любом подобном случае надо делать выбор, поэтому приходится взвешивать все плюсы и минусы, и принимать последствия. А что по этому поводу думаете вы?
главная страница сайта Liscript — liscript.herokuapp.com
онлайн REPL — liscript.herokuapp.com/repl
Автор: Андрей