Вдохновившись проектом Google Glass, я подумал, как хорошо было бы сделать крайне простой, но мощный инструмент дополненной реальности специально для таких очков. И почему бы не сделать его на основе такой широко используемой технологии как QR-коды. Так родилась задумка языка QuRava — набора байтов, записанных в QR-коде и интерпретируемого в полноценную программу, реализующую часть возможностей языка Java.
Хочу сразу предупредить, что все нижеизложенное — альфа-версия, сделано за несколько вечеров. Поэтому по поводу маленьких возможностей сильно не ругайтесь и вопросов про отсутствие лямбда-исчисления не задавайте.
1. Предназначение
Как мне кажется, большинство инструментов дополненной реальности напрочь оторваны от реальности. Они красивые, интересные, но бесполезны для практического применения. И мне захотелось сделать технологию, которая была бы крайне проста и удобна для повседневного пользования. Лучшее, что пришло на ум — это встроить интерпретируемый язык в QR-коды. Они уже широко распространены, и не вызывают у людей отторжения. Их можно распечатать и прилепить в любое место, будь то столик кафе или витрина магазина. Они считываются почти мгновенно, и поэтому с помощью Google Glass можно будет отображать программы в реальном времени.
Плюсы такого подхода очевидны:
нужна лишь программа-интерпретатор и сам QR-код, не нужно никакого дополнительного оборудования
стандартизированность интерфейсов; людям не нужно обучаться заново каждый раз
огромные потенциальные возможности: например, можно сделать заказ в кафе, посмотрев на столик, или заказать билеты на рок-концерт, посмотрев на афишу
элементарная простота и дешевизна создания и распространения программок
Но есть и минусы:
маленький размер программы. Решается выкачкой дополнительной логики по сети
возможно легче было бы скачивать из интернета по QR-ссылке код на каком-нибудь скриптовом языке, хоть это и медленнее
вспоминается роман Стивенсона «Лавина», в котором люди, посмотрев на черно-белое изображение, плавили себе мозги
2. Структура языка
В QR-коды информация записывается с помощью последовательности участков по 8 бит каждый. Поэтому язык я решил создать на основе байтового исчисления, то бишь на основе шестнадцатеричных символов от 00 до FF. Сначала в голову пришла идея записи в штрих-код байт-кода Java, но я отказался от нее из-за избыточности данного подхода. Интерпретируемый язык для QR должен быть очень краток, так штрих-код может содержать в себе в лучшем случае лишь 2 килобайта памяти. Да и много возможностей этому языку много не надо.
Пока что в языке используются лишь три вида конструкций: переменные, процедуры и компоненты Андроид. У каждого объекта этих трех типов есть свой подтип (например, для переменных это 00-boolean, 01 — int, 02-float, 03-String), однобайтовое имя и тело. Для удобство парсера перед каждой конструкцией ставится байт длины.
Например в синтаксической конструкции {06,01,AA,00,00,00,03} содержится следующее:
Первый байт — байт длины, равный 6, и парсер считывает 6 байтов после него
Второй байт — байт типа данных. Он равен 01, что означает integer
Третий байт означает имя переменной. Здесь переменная названа AA, и после интерпретатор будет знать ее именно под этим прозвищем
После идет 4 байта тела. Означают, что переменной изначально присвоено значение равное 3
В процедурах же задается список примитивных операций. Например {4,14,АА,АА,ВВ} означает АА = АА + ВВ;
Так как Google Glass еще не вышли, то я написал программу-интерпретатор под Андроид. Поэтому в QuRavе сейчас поддерживаются именно компоненты Андроид. Пока что только Button, TextView и Edit Text. Можно задать их положение на экране, текст на них, а для кнопок добавить указание на процедуру, вызываемую при нажатии. Например, {05,09,05,02,03,08} означает создание кнопки (код 09) с именем 05, положением на экране 02 (вверху по центру, по системе старых телефонных хардварных клавиатур от 1 до 9), с текстом строки по имени 03 и вызовом процедуры по имени 08 при нажатии.
Если честно, сейчас синтаксис выглядит просто ужасно. Он чертовски сложен и имеет очень мало возможностей. Но это же просто прототип. В будущем планирую распихать все примитивные операции по библиотекам, сделав более короткой и удобной их конструкцию. Еще нету возможности вызывать процедуру из другой процедуры, что обусловлено глупо написанным компилятором. Сейчас кстати о нем.
3. Интерпретатор
Основной парсер интерпретатора читает программу, разбивает ее на куски и рассылает их субпарсерам, которые в свою очередь могут иметь свои субпарсеры. На основе результатов работы многочисленных парсеров программа по кусочкам собирается на не менее малочисленных фабриках. Готовые процедуры, переменные и компоненты попадают в класс Main.
В классе Main все содержится в отдельных HashMap'ах. Примером такой HashMap'ы является namesOfVariables:
public Object getVariable(Byte name)
{
return namesOfVariables.get(name);
}
А чтобы доступ к переменным и компонентам был из процедур и примитивных операций, класс Procedure является наследником класса ProgramUnit (на рисунке обозначен как Main), а примитивная операция расширяет класс Procedure.
В классе процедуры переопределен метод взятия переменной. Добавив к нему еще пару строк, можно реализовать механизм локальных переменных.
@Override
public Object getVariable(Byte name)
{
return superiorUnit.getVariable(name);
}
Если честно, то интерпретатор самая скучная часть сделанного, поэтому хватит о нем.
4. Пример использования
А сейчас покажу Вам то, ради чего Вы собственно и начали читать эту статью. Пример действующей программы на языке QuRava.
В данном примере использованы числа типа byte, а не их hex-эквиваленты!
02,03,00 — объявление пустой строки по имени 00 02,01,01 — объявление переменной типа int по имени 01 02,01,02 — объявление переменной типа int по имени 02 06,03,03,80,108,117,115 — объявление строки с текстом «Plus»
04,11,04,01,00 — создание изменяемого текстового поля по имени 04 05,09,05,02,03,08 — создание кнопки по имени 05 вызывающей при нажатии процедуру 08 04,11,06,03,00 — создание изменяемого текстового поля по имени 06 04,13,07,08,00 — создание неизменяемого текстового поля по имени 07
31,04,08 — объявление процедуры по имени 08 ........03,42,00,04 — читаем текст, записанный в поле 04 и кладем его в строку 00 ........03,41,01,00 — переводим строку 00 в int и записываем результат в переменную 01 ........03,42,00,06 — читаем текст, записанный в поле 06 и кладем его в строку 00 ........03,41,02,00 — переводим строку 00 в int и записываем результат в переменную 02 ........04,14,01,01,02 — складываем переменные 01 и 02 и записываем результат в 01 ........03,40,00,01 — переводим значение 01 в строку и записываем результат в 00 ........03,43,07,00 — выводим в текстовое поле 07 строку 00
Ну и собственно демонстрация работы: (извините за качество)
5. Дальнейшее развитие
Сейчас я показал Вам лишь альфа-версию прототипа того, что я хочу сделать. В будущем я бы хотел добавить много интересных вещей в язык QuRava:
разработать простой и мощный синтаксис
добавить регулярные выражения, массивы, циклы и многое другое
распределить все примитивные операции по библиотекам, дублирующим основные классы-библиотеки самой Java (такие как Math или Arrays например.
добавить возможность вызова процедур из других процедур и локальных переменных
добавить работу с сетью, функцию скачивания из сети дополнительного контента к программе или даже логики
написать кучу стандартных библиотек
написать компилятор под desktop и маленькую, но удобную IDE к нему
написать всеобъемлюющую справку к языку
выложить проект на github
К сожалению пока что показывать еще почти нечего, поэтому не выложу никаких ссылок. Если будет интересно, то поработаю над этим и напишу еще одну статью.
А пока что хочу провести опрос.