Всем привет.
В школьном и более продвинутом курсе информатики есть учебный язык - Кукарача. Довольно удачный, для обучения детей программированию. Простой, понятный, визуальные результаты с первой строчки.
Авторы курса сделали только exe-шник под Windows. Когда младший сын начал требовать "Папа научи программировать" принял волевое решение - сделаю свою имплементацию. И сделал.
Че это вообще?

Есть прямоугольное поле. Жучок и буквочки. Нужно писать программы, что бы жучок правильным образом подвигал буковки.
Поддерживаются простые команды - ВВЕРХ, ВНИЗ, ВПРАВО, ВЛЕВОи их группировка с помощью {}.
После каждого действия известен результат - какую букву толкнул жучок.
Есть циклы - ПОВТОРИ x , ПОКА y и условие ЕСЛИ z ТО ... ИНАЧЕ ...
И даже процедуры - ЭТО proc_name ... КОНЕЦ
Поэтому, в процессе обучения ребенок учится довольно сложных концепциям включая рекурсию и процедурное программирование.
Поле
Поле работает совсем очевидно - массивчик char'ов и специальный символ для жучка. Процедуры, которые передвигают жучка изменяя состояние. Запоминаем последнюю букву, которую толкнули. Осталось добавить загрузку/выгрузку в строку примерно такого вида:
А_А__ _1_1_ _____ ____~
а также колбек на изменение, что бы отрисовывать и симпатичный компонент готов.
Как сделать интерпретатор?
Оказалось, что сделать интерпретатор не так уж и сложно. Надо понимать что такое синтаксис и грамматика, почитать немного примеров и воспользоваться готовыми либами.
Я взял antlr - т.к мой основной язык java и там этот генератор парсеров на слуху. В итоге получилась такая грамматика
грамматика
grammar Cockroach; @header { package ru.nizhikov.cockroach.antlr; } prog : exprs EOF ; exprs : expr+ ; expr : statement | repeat | while | if | proc | id | LINE_COMMENT ; statement : UP | DOWN | LEFT | RIGHT | STAY | group ; repeat : REPEAT NUM expr ; while : WHILE condition expr ; group : OPEN_BRACKET exprs CLOSE_BRACKET ; if : IF condition THEN statement (ELSE statement)? ; proc : THIS id exprs END ; condition : NOT? id | NOT? EMPTY | NOT? NUMBER ; id : ID ; LINE_COMMENT : '//' ~[\n\r]* -> skip; UP: 'ВВЕРХ'; DOWN: 'ВНИЗ'; LEFT: 'ВЛЕВО'; RIGHT: 'ВПРАВО'; STAY: 'СТОЯТЬ'; NOT: 'НЕ'; EMPTY: 'ПУСТО'; NUMBER: 'ЦИФРА'; REPEAT: 'ПОВТОРИ'; WHILE: 'ПОКА'; CHAR: 'БУКВА'; OPEN_BRACKET: '{'; CLOSE_BRACKET: '}'; IF: 'ЕСЛИ'; THEN: 'ТО'; ELSE: 'ИНАЧЕ'; THIS: 'ЭТО'; END: 'КОНЕЦ'; ID: LETTER (LETTER | DIGIT)*; LETTER: [a-zA-Zа-яА-Я]; NUM: DIGIT+; DIGIT: [0-9]; SPACE: [ \r\n\t]+ -> skip;
Грамматика задает правила для парсера что бы из текста сформировать синтаксическое дерево. Пример на картинке.

Интерпретатору всего-лишь надо обойти это дерево верным образом и выполнить необходимые действия. Действия следуют из их смысла:
Определение процедуры - запоминаем имя процедуры в специальной мапке. Ключ - название, значение - поддерево команд.
Вызов процедуры (токен ID) - ищем определение процедуры и вызываем соответствующее поддерево команд.
Цикл
ПОВТОРИ x- выполняем поддеревоxраз.Цикл
ПОКА у- проверяем условиеyи выполняем поддерево пока оно выполняется.ЕСЛИ z ТО ... ИНАЧЕ ...- проверяем условиеzи выполняем то или иное поддерево.Обычные команды - изменяем состояние поля.
Интерфейс
Последний раз я делал UI еще во времена когда jquery и extjs были модными :), поэтому погуглив как сейчас делается интерфейс слегка ох^Wудивился обилию возможностей. В итоге собрал из туториала который первым заработал рабочее one page application и запилил с помощью того что знаю - bootstrap и jquery.
Очень хотелось сделать подсветку синтаксиса, а нагугленный компонент поддерживал другой генератор парсеров. Нет проблем - записать грамматику немного другим синтаксисом проще простого.
Несмотря на то, что в CodeMirror довольно подробная документация разобраться в API оказалось не так просто. Возможно, я отвык от уровня документирования js компонент. Но, в итоге, подсветка синтаксиса и текущей команды во время дебага и запуска работает.
Сохранение файликов сделал через localStorage - удобненько.
Сложности
Первая реализация интерпретатора написана на java и работает через консоль. Пошаговое выполнение (отладку) легко сделать через ожидание ввода с консоли.
А вот в javascript простой возможности останавить выполнение в рандомном месте нет. Поэтому, пришлось заморочиться и сделать, что бы интерпретация приложения работала на promise'ах. Команда кукарачи выполняется при выполнении Promise.resolve.
Публикация
Вкратце - github прекрасен)
Оказалось, что в github есть бесплатный, автоматизированный, удобный функционал, что бы опубликовать one page application. Называется github pages. Обалденно удобно. Собираешь свое приложение, указываешь папочку, жмешь кнопку и вуаля - приложение готово и работает.
Ну вот и все pet project готов и вроде как работает. Ребенок два раза позанимался программированием и получил массу удовольствия. Я тоже доволен и пописал интересный код.
Иходники - https://github.com/nizhikov/cockroach
А еще у меня есть канал с выступлениями и ссылками на интересные пейперы из мира разработки СУБД.
