Собери и прочитай кристалл

TL;DR Заметки о том, как создать язык программирования Crystal из исходных текстов.

Пролог

Я работаю над небольшим клиентским инструментом, написанным на Crystal.
Он называется Medup. Как обычно, когда я увижу новую версию Crystal
всего через несколько дней, протестирую мой код на последней версии.
Кристаллы 0.35.0 и 0.35.1 были выпущены практически одновременно.
У меня появилось свободное время для тестирования новых изменений.
У Crystal есть модные существенные различия между версиями, пока
не будет выпущена 1.0.0.
Мне потребовались небольшие изменения кода для запуска crystal build с успехом -
явно добавлен оператор case…else.
Во время сборки появились новые сообщения:

Warning: Deprecated JSON.mapping. use JSON::Serializable instead (the legacy behavior is also available in a shard at github:crystal-lang/json_mapping.cr)

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

Все тесты пройдены. Запустил скомпилированную версию приложения с
новым Кристаллом 0.35.1 — появилась непредвиденная ошибка.
После небольшой отладки обнаружил проблему в коде для разбора аргументов командной строки.
В CHANGELOG от Crystal упоминается новая оптимизация OptionParser.

Мне было очень легко читать PR и код.
Я решил написать свои собственные тесты и проверить мой случай на
текущей версии master (вероятно, исправлено в последних коммитах).

построить кристалл

У меня есть машина, оптимизированная для разработки на Ruby.
Для меня все зависимости уже были предустановлены.
Самое интересное, что Crystal написан на Crystal.
У него есть требования, чтобы каждый следующий выпуск был собран с предыдущей стабильной версией, в моем случае это была 0.35.1. Пробовал проверить совместимость с 0.34.0 — не получилось.
В исходниках есть новые фичи из 0.35.0 типа `case..in`.
Шаги по сборке Crystal доступны на официальном сайте:

$ git clone [email protected]:crystal-lang/crystal.git
$ cd crystal
$ make
$ .build/crystal -v
$ export CRYSTAL_PATH=`pwd`/src
$ .build/crystal <path to src file>

Протестировал свой код с новым бинарным файлом build crystal.
Сборка по-прежнему имеет ту же проблему.

Читать Кристалл

Чтобы ускорить тестирование, я решил добавить тесты для своего случая
в файл spec/std/option_parser_spec.cr. Запускайте только мои изменения с помощью

$ .build/crystal spec spec/std/option_parser_spec.cr

После нескольких экспериментов я нашел ошибку в своем примере.
Оказывается, небольшое столкновение имен обрабатывается по-другому после рефакторинга OptionParser.
Вот пример:

require “option_parser”
opts = %w[-u user other args]
OptionParser.parse(opts) do |parser|
 parser.on(“-u USER”, “ — user=USER”, “Some text”) { |u| puts “user triggered” }
 parser.on(“-u”, “ — update”, “Some text”) { puts “update triggered” }
end
pp opts

Как видите, есть 2 коротких флага -u. В версии до 0.35.0
реализация изменила поведение,
поэтому приоритет имеет последняя спецификация.

Резюме

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

Михаил Никиточкин — ведущий инженер-программист. Подпишитесь на него вLinkedIn или GitHub.

Если вам понравилась эта история, мы рекомендуем прочитать наши последние технические истории и актуальные технические истории.

использованная литература