Игрушечный ЯП — Cockroach

в 14:03, , рубрики: antlr, ненормальное программирование

Всем привет.

В школьном и более продвинутом курсе информатики есть учебный язык - Кукарача. Довольно удачный, для обучения детей программированию. Простой, понятный, визуальные результаты с первой строчки.

Авторы курса сделали только exe-шник под Windows. Когда младший сын начал требовать "Папа научи программировать" принял волевое решение - сделаю свою имплементацию. И сделал.

Че это вообще?

Игрушечный ЯП — Cockroach - 1

Есть прямоугольное поле. Жучок и буквочки. Нужно писать программы, что бы жучок правильным образом подвигал буковки.

Поддерживаются простые команды - ВВЕРХ, ВНИЗ, ВПРАВО, ВЛЕВОи их группировка с помощью {}.

После каждого действия известен результат - какую букву толкнул жучок.

Есть циклы - ПОВТОРИ 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 : '//' ~[nr]* -> 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: [ rnt]+ -> 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

А еще у меня есть канал с выступлениями и ссылками на интересные пейперы из мира разработки СУБД.

Автор: Николай Ижиков

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js