Описание платформы Gide — различия между версиями
(→Подключение внешних модулей) |
|||
Строка 19: | Строка 19: | ||
Она может быть записана как на отдельной строке, так и на строке с инструкцией. | Она может быть записана как на отдельной строке, так и на строке с инструкцией. | ||
− | + | If I were a Teenage Mutant Ninja Turtle, now I'd say "Kowbagnua, dude!" | |
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
=== Объявление классов === | === Объявление классов === |
Версия 08:24, 9 марта 2012
Содержание |
Синтаксис языка gide
Синтаксис языка Gide максимально упрощен - с рассчетом на то, что поверх него будут писаться реализации языков более высокого уровня.
Каждая непустая строка исходного кода gide - это некоторая инструкция, записанная в следующей форме:
ключевое_слово аргумент1, аргумент2, ..., аргументN
Некоторые инструкции допустимы только за пределами тела функции, некоторые, наоборот, допустимы только внутри тела функции.
Аргументы для инструкции - это произвольные текстовые строки. Если значение аргумента содержит управляющие символы (пробел, табуляция, перевод строки, запятая, символы # или ") - его необходимо заключить в двойные кавычки. Внутри таких кавычек также допустимы escape-последовательности \r, \n, \t, \" и \\.
ЗАМЕЧАНИЕ: вставка произвольных символов \xNN на данный момент не поддерживается, но запланирована на будущее.
Весь текст от символа # и до конца строки - комментарий, он игнорируется.
Внутри тела функции также возможно вставлять т.н. метки. Метка - это некоторая уникальная в пределах данной функции ссылка на инструкцию. Метка выглядит следующим образом:
имя_метки:
Она может быть записана как на отдельной строке, так и на строке с инструкцией.
If I were a Teenage Mutant Ninja Turtle, now I'd say "Kowbagnua, dude!"
Объявление классов
Для того, чтобы объявить класс, нужно написать следующую инструкцию:
class имя_класса
Имя класса - это произвольная строка. Данная инструкция объявляет класс, в котором еще нет ни одного метода.
Инструкция
inherit имя_класса, имя_родителя
добавляет класс имя_родителя в качестве родительского класса. Она должна быть записана после объявления класса. В этой инструкции, и класс имя_класса, и класс имя_родителя могут быть объявлены во внешнем модуле.
Существует также понятие родителя по-умолчанию. Они могут быть заданы только в неуправляемых модулях. Такие родители добавляются автоматически, если не указаны другие родительские классы.
Объявление глобальных переменных
Для того, чтобы объявить глобальную переменную - т.е. переменную, доступную из любой функции - нужно написать инструкцию
global имя_переменной
В качестве имени переменной, опять же, может выступать любая строка.
Глобальные переменные доступны только в пределах данного модуля. Для получения их значений из других модулей, следует использовать функции.
По-умолчанию все глобальные переменные инициализируются нулем.
Объявление функции
Функция объявляется следующим образом:
function имя_функции, аргумент1, аргумент2, ..., аргументN тело функции end
Имя функции - это произвольная текстовая строка. Если имя имеет вид
имя_класса:имя_метода
, метод имя_метода добавляется в класс имя_класса (этот класс должен быть предварительно объявлен, но не обязательно в текущем модуле). В противном случае, объявляется функция, а не метод.
Аргументы - это формальные параметры функции, они доступны в теле как локальные переменные.
Список аргументов в какой-то мере условен. При вызове функции всегда можно указывать произвольное число параметров, это нигде не проверяется. Если фактических параметров меньше, чем формальных - оставшиеся аргументы будут инициализированы нулем. Если фактических больше - некоторые из них не будут доступны напрямую, однако стандартная библиотека может предоставлять функции, открывающие к ним доступ.
Методы класса могут быть публичными (public), защищенными (protected) или частными (private). Публичные методы можно вызывать без ограничений, защищенные - только из данного класса и его потомков, частные - только из данного класса. Эти проверки делаются во время исполнения.
По-умолчанию, метод является публичным. Для объявления защищенных и частных методов, нужно заменить ключевое слово function в объявлении метода на func_protected и func_private соответственно.
Если метод/функция уже был объявлен ранее, он переобъявляется - т.е. данная реализация перекрывает предыдущую. Вызвать предыдущую реализацию можно при помощи оператора recall (см. ниже).
Функция со специальным именем @@module_init является конструктором модуля; она вызывается в момент загрузки данного модуля. В ней, в частности, можно инициализировать глобальные переменные модуля.
Метод @@init - конструктор класса. Он вызывается в момент создания объекта данного класса. Порядок вызова конструкторов таков: сначала рекурсивно вызываются конструкторы родительских классов в порядке объявления родителей, затем - конструктор данного класса. В конструкторе, в частности, можно инициализировать поля объекта.
Специальные переменные
Существует 6 зарезервированных имен переменных:
- 0 (число ноль) - означает отсутствие объекта;
- @true - указывает на специальный объект "истина". Этот объект не имеет полей и методов; он бывает полезен для написания логических конструкций;
- @false - то же, что и 0;
- @this - внутри функции-метода, указывает на текущий объект; внутри обычной функции равен 0;
- @result - результат последней вызванной функции или оператора access;
- @exception - объект исключения, которое требуется обработать.
Операторы функции
Операторы - это управляющие инструкции, составляющие тело функции.
Поскольку единственный существующий тип переменной - это указатель на объект, значительная часть операторов не требуется. Все операторы реализуются на уровне самих объектов в виде методов.
Существуют следующие операторы:
new переменная, имя_класса, строка
- создает новый объект класса имя_класса и записывает результат в указанную переменную. Если переменная была объявлена ранее - результат записывается в нее; если нет - создается новая локальная переменная (т.е. переменная, доступная только в пределах данной функции). Третий параметр - это некоторые данные для инициализации объекта. Они имеют значение только для некоторых классов в неуправляемых модулях. Кроме того, любой класс может быть инициализирован пустой строкой.
bless переменная, имя_класса
- эквивалент new без последнего параметра.
mov переменная, источник
- копирует в переменную переменная ссылку на существующий объект источник (аналогично, если переменная не была объявлена ранее - создается новая локальная переменная). Важно подчеркнуть, что при этой операции не создается нового объекта; две переменные будут указывать на один и тот же объект.
goto имя_метки
- осуществляет безусловный переход к оператору, помеченному меткой имя_метки.
if переменная, имя_метки
- осуществляет условный переход к оператору имя_метки: если управляющая переменная (первый параметр) имеет отличное от нуля значение, осуществляется переход; в противном случае этот оператор игнорируется и выполнение продолжается со следующей инструкции.
call переменная, имя_функции, аргумент1, ..., аргументN
- если переменная - 0, вызывает функцию имя_функции; в противном случае вызывает метод объекта, на который ссылается переменная. Как уже говорилось ранее, количество аргументов может быть любым вне зависимости от количества формальных параметров. Результат выполнения функции записывается в специальную переменную @result.
- имя_функции может быть задано в форме имя_класса:имя_метода. В этом случае осуществляется вызов метода конкретного класса, а не текущего (имя_класса должно либо совпадать с классом объекта, либо быть его родительским классом). Это позволяет вызывать методы родительских классов, несмотря на перекрытие в дочерних классах.
return переменная
- осуществляет выход из текущей функции с возвратом объекта, на который ссылается переменная. Если выполнение функции доходит до конца, функция возвращает 0.
access переменная, имя_поля
- получает значение поля имя_поля объекта, на который ссылается переменная. Значение записывается в специальную переменную @result. Если указанного поля в классе нет, в @result записывается 0.
mutate переменная, имя_поля, значение
- записывает объект, на который ссылается значение, в поле имя_поля объекта, на который ссылается переменная. Если такого поля ранее не существовало, оно создается.
recall
- вызывает замещенную функцию с теми же параметрами, которые были переданы данной функции; осуществляет выход из текущей функции с результатом, который вернула замещенная функция.
upgrade переменная, имя_класса
- расширяет объект, на который ссылается переменная, до класса имя_класса. Это делается путем вызова конструкторов классов, которые в дереве иерархии находятся между классом переменной и указанным классом. Очевидно, класс объекта должен быть родительским для класса имя_класса. После выполнения данной инструкции, класс объекта станет равным имя_класса, т.е. для него будут доступны все методы этого класса.
use_locals имя_функции
- открывает данной функции доступ к локальным переменным функции имя_функции. Этот оператор может быть указан только в самом начале тела функции и только единожды. имя_функции записывается как при объявлении: для обычной функции - просто ее имя, для метода класса - имя класса и имя метода, разделенные двоеточием. Для того, чтобы локальные переменные реально стали доступны, функция имя_функции должна вызвать текущую функцию специальным образом.
- ПРИМЕЧАНИЕ: такой специальный вызов на данный момент невозможно сделать напрямую. Класс Closure стандартной библиотеки позволяет создать указатель на метод, при вызове которого (через этот указатель) происходит требуемый тип вызова. В будущем планируется добавить оператор вызова ccall, выполняющий эту операцию.
- данная инструкция требуется для реализации замыканий.
Кроме перечисленных, существуют еще операторы для работы с исключениями; они рассмотрены в следующем подразделе.
Обработка исключений
Исключения - это специальный механизм, предназначенный для передачи информации об ошибках. Он заключается в следующем.
Любая функция может бросить исключение. Само исключение - это некоторый объект произвольного класса. Когда такое происходит, виртуальная машина начинает искать ближайший обработчик исключений. Если сама функция, бросившая исключение, может его обработать - исполнение передается соответствующей инструкции. В противном случае, начинается раскрутка стека вызовов до первой функции, которая может обработать исключение. Если ни одна функция этого сделать не может, выполнение программы прекращается, а вызвавшему модулю сообщается о необработанном исключении.
В функции может быть установлено несколько обработчиков исключений, которые формируют стек. Обработчик - это некоторая метка в пределах функции. Когда функции требуется обработать исключение, управление передается оператору, на который ссылается эта метка, а сама эта метка извлекается из стека обработчиков. Другими словами, в функции может быть установлено несколько обработчиков, которые, при необходимости, будут вызываться в порядке, обратном их установке. Если же возникает исключение, а в функции не установлено ни одного обработчика - происходит выход из функции, а исключение передается функции, которая вызвала данную функцию, и т.д.
Для работы с исключением существуют следующие четыре оператора:
throw переменная
- кидает исключение, передав в качестве параметра объект, на который ссылается переменная.
except_push имя_метки
- добавляет метку имя_метки в стек обработчиков исключений для данной функции. Когда возникает исключение, управление передается указанной метке, а объект исключения записывается в специальную переменную @exception.
except_pop
- изымает из стека обработчиков метку, находящуюся на вершине стека.
except_clr
- очищает стек обработчиков исключений для данной функции.
Кроме оператора throw, исключения могут кидаться неуправляемыми модулями. Кроме того, существует механизм, при помощи которого ошибки, возникающие на уровне самой виртуальной машины, передаются в стандартную библиотеку, которая кидает исключение.
Формат байт-кода gide
ПРИМЕЧАНИЕ: в данном разделе приведена устаревшая информация, которая не соответствует действительности. Раздел оставлен для ознакомления с общей концепцией хранения байт-кода. Через некоторое время он будет переписан.
Байт-код gide представляет собой популярный BER-формат: весь файл разбит на секции, каждая из которых начинается с описания ее типа и размера.
Следует сразу оговориться, что все числа в gide представляются в формате big endian (т.е. наименее значимый байт впереди).
Заголовок BER-секции представляет собой последовательность кода секции (1 байт) и ее размер в BER-формате.
Все целые числа в байт-коде, если это не оговорено специально, представляются в BER-формате: для записи числа требуется столько байт, сколько нужно; в каждом байте записывается 7 битов числа. Если самый старший бит равен 1, требуется прочитать/записать следующий байт. Если старший бит равен 0 - это последний байт, описывающий число.
Секция имен
Тип секции - код символа N.
Для большей компактности кода используется секция имен. В ней последовательно записываются все текстовые строки (имена переменных/функций, данные конструкторов и пр.), которые встречаются в скрипте. Одинаковые текстовые строки записываются только один раз. Таким образом, каждая текстовая строка получает свой уникальный номер, и в байт-коде вместо строк используются эти номера.
Поскольку в скрипте часто встречаются одинаковые строки (например, работа с одной и той же переменной), это сильно уменьшает размер кода.
Все строки записываются следующим образом:
- длина строки (в BER формате, см. выше);
- текст.
Нумерация строк начинается не с нуля, а с 16. Меньшие номера зарезервированы для стандартных имен:
- Переменная @null (она же 0) имеет номер 0;
- Переменная @this имеет номер 1;
- Переменная @false имеет номер 2;
- Переменная @true имеет номер 3.
В случае обращения к полю объекта, в байт-код записывается все составное имя объекта (вместе с разделяющими точками). Виртуальная машина сама разбирает составные имена.
Секция экспорта
Тип секции - ASCII-код символа E
Данная секция предназначена для упрощения навигации по секции кода.
Для каждой функции, описанной в модуле, записывается:
- индекс имени функции;
- смещение функции относительно начала секции кода.
Секция кода
Тип секции кода - ASCII-код символа C.
В эту секцию записываются тела функций Gide.
Для каждой функции записывается:
- индекс имени функции (он должен быть смещен от начала секции кода на количество байт, указанных в секции экспорта);
- количество аргументов функции;
- индексы имен аргументов функции;
- размер блока операторов;
- для каждого оператора:
- индекс имени оператора;
- количество передаваемых параметров функции (только для оператора call);
- индексы имен всех параметров.
Индексы операторов:
- Простые операторы имеют номера от 1 до 9 в порядке их перечисления в п.2.1;
- Оператор создания объекта имеет номер 10;
- Оператор throw имеет номер 13;
- Оператор except_push имеет номер 16;
- Оператор except_pop имеет номер 17;
- Оператор создания переменной пользовательского класса имеет номер 11;
- Оператор расширения наследования имеет номер 15;
- Оператор use имеет номер 12;
- Оператор external номер 14;
Секция импорта
Тип секции импорта - ASCII-код символа I
В эту секцию записываются подключаемые операторами use и external библиотеки.
Для каждой библиотеки записывается:
- число 1 в случае оператора external, 0 - в случае use;
- индекс пути к библиотеке.
Секция наследования классов
Тип секции - код символа H.
В эту секцию записывается статическая таблица наследований классов.
Для каждого оператора inherit записывается:
- индекс наследующего класса;
- индекс наследуемого класса.
Секция номеров строк
Тип секции - код символа D.
Эта секция не является обязательной. В ней записываются соответствия операторов и номеров строк в исходных файлах, на которых указан соответствующий оператор.
В секцию записывается массив следующих структур:
- индекс имени файла;
- размер блока функций;
- для каждой функции:
- индекс имени функции;
- размер блока операторов;
- для каждого оператора:
- номер оператора в теле функции;
- номер строки кода, на которой объявлен оператор.