ObjectScript — новый объектно-ориентированный язык программирования с открытым исходным кодом. Его презентация прошла на хабре некоторое время назад в этой статье и вызвала у читателей интерес и бурное обсуждение. Поэтому я решил не останавливаться на презентации и описать ObjectScript API. Постараюсь делать статьи как можно короче, побив описание API на несколько частей.
Часть 1: работа со стеком, вызов функций OS из C++
Минимальная программа, использующая OS (ObjectScript) выглядит следующим образом:
#include "objectscript.h"
using namespace ObjectScript;
int main()
{
OS * os = OS::create();
// TODO: main code here
os->release();
return 0;
}
Т.е. создаем экземпляр OS, работаем с ним и корректно удаляем, но только не с помощью оператора delete
, а вызовом метода release
.
Экземпляров OS может быть создано одновременно сколько угодно, если нужно, работать они будут полностью независимо друг от друга.
Далее я буду приводить пример кода, который должен быть расположен вместо TODO: main code here.
Итак, давайте средствами C++ просимулируем следующий код на OS:
print("10 * (3+2) = ", 10 * (3+2))
Что у нас тут есть: вызов глобальной функции с двумя параметрами, первый — константная строка, второй — результат математических операций.
Первое, что нужно сделать — подготовить вызов функции. Для этого в стек нужно поместить два значения, первый — сама функция, второй — this
для данной функции. Если функция не использует this, ну например статическая функция, то в качестве this
нужно поместить null
. Делаем это так:
os->getGlobal("print"); // #1 - количество значений в стеке
os->pushNull(); // #2
Теперь добавляем в стек параметры, с которыми функция будет вызвана:
os->pushString("10 * (3+2) = "); // #3 - первый параметр
Теперь имитируем матетические операции для второго параметра:
os->pushNumber(10); // #4
os->pushNumber(3); // #5
os->pushNumber(2); // #6
os->runOp(OP_ADD); // #5 - 3+2
os->runOp(OP_MUL); // #4 - 10 * (3+2)
Готово! метод runOp
может выполнять математические, логические и побитовые операторы над значениями в стеке средствами ядра OS. Иначе говоря, при необходимости произойдут конвертации типов и т.п… OP_ADD
выполняет оператор сложения над двумя значениями на вершине стека (т.е. то, что в стек поместили в последние два раза). При этом результат заменит их в стеке (т.е. два значения будут убраны из стека, а результат добавлен). OP_MUL
— аналогично для умножения.
На данный момент мы будем иметь в стеке 4 значения: 1 — функция, 2 — null, 3 — строка, 4 — число. Отлично! можно вызывать:
os->call(2); // вызвать функцию с 2 параметрами
Все, смотрим консоль (print
выводить результат в консоль), должно быть так:
10 * (3+2) = 50
При этом стек будет полностью пустой, т.к. 4 используемых при вызове функции значения будут убраны из стека.
Пример 2
Просимулируем следующий код на OS:
bar = {firsname="James", lastname="Bond"}
bar.profession = "actor"
print bar
Создадим в стеке новый объект:
os->newObject(); // #1
Установим первое свойство firsname="James"
:
os->pushStackValue(-1); // #2
-1 — это относительный указатель на вершину стека, т.е. добавлем в стек объект, для которого будем устанавливать свойство (объект добавляется в стек по ссылке).
os->pushString("firsname"); // #3 - имя свойства
os->pushString("James"); // #4 - будущее значение свойста
os->setProperty(); // #1
Метод setProperty
устанавливает свойство и вынимает используемые значения из стека (в данном случае на вершине стека используются три значения: объект, имя свойства и значение).
Проделаем со вторым свойством то же самое, но более коротким путем:
os->pushString("Bond"); // #2 - значение
os->setProperty(-2, "lastname"); // #1
-2 — это относительный указатель на второе значение от вершины стека (это наш объект), а на вершине стека сейчас находится строка "Bond"
.
Теперь сохраним наш объект в глобальную переменную bar:
os->setGlobal("bar"); // #0
На данный момент в стеке нет значений. Теперь выполняем код bar.profession = "actor"
:
os->getGlobal("bar"); // #1 - помещаем в стек значение переменной bar
os->pushString("actor"); // #2
os->setProperty(-2, "profession"); // #0
Готово, теперь делаем print bar
:
os->getGlobal("print"); // #1
os->pushNull(); // #2
os->getGlobal("bar"); // #3
os->call(1); // #0
и смотрим в консоль, должно быть так:
{"firsname":"James","lastname":"Bond","profession":"actor"}
Пример 3
Просимулируем следующий код на OS:
print(concat(5, " big differences"))
Начинаем как обычно:
os->getGlobal("print"); // #1 - добавляем в стек глобальную функцию print
os->pushNull(); // #2 - добавляем this для функции print
os->getGlobal("concat"); // #3 - добавляем в стек глобальную функцию concat
os->pushNull(); // #4 - добавляем this для функции concat
os->pushNumber(5); // #5 - первый параметр для concat
os->pushString(" big differences"); // #6 - второй параметр для concat
os->call(2, 1); // #3 - вызываем функцию concat
На данном этупе мы вызвали функцию с 2 параметрами и затребовали на выходе 1 результат (concat возвращает 1 результат по умолчанию, если мы запросим 0 результатов, то в стеке после вызова функции не будет значений с результатами, если мы затребуем 2 и больше значений, то первый результат будет от функции concat, а остальные дополнятся null-ами).
Теперь вызываем print
:
os->call(1); // #0
в консоли должно быть так:
5 big differences
Полный текст программы:
#include "objectscript.h"
using namespace ObjectScript;
int main()
{
OS * os = OS::create();
os->getGlobal("print"); // #1 - stack values, it's print function from standart library
os->pushNull(); // #2 - null, it's function this, each call of function must have this
// push the first argument
os->pushString("10 * (3+2) = "); // #3 - we have 3 stack values here
// prepare second argument
os->pushNumber(10); // #4
os->pushNumber(3); // #5
os->pushNumber(2); // #6
os->runOp(OP_ADD); // #5 - 3+2
os->runOp(OP_MUL); // #4 - 10 * (3+2)
os->call(2); // call function with 2 arguments
/*
bar = {firsname="James", lastname="Bond"}
bar.profession = "actor"
print bar
*/
os->newObject(); // #1 - new object
os->pushStackValue(-1); // #2 - the same object, -1 - is relative pointer to the top stack value
os->pushString("firsname"); // #3 - property key
os->pushString("James"); // #4 - property value
os->setProperty(); // #1 - setProperty uses 3 stack values and pop them
// second way of same functionality
os->pushString("Bond"); // #2 - property value
os->setProperty(-2, "lastname"); // #1
os->setGlobal("bar"); // #0 - assign object value to global bar variable, pop value
// let's do bar.profession = "actor"
os->getGlobal("bar"); // #1 - our global a variable
os->pushString("actor"); // #2 - property value
os->setProperty(-2, "profession"); // #0
// let's do print bar
os->getGlobal("print"); // #1
os->pushNull(); // #2
os->getGlobal("bar"); // #3
os->call(1); // #0
/*
print(concat(5, " big differences"))
*/
os->getGlobal("print"); // #1 - print function
os->pushNull(); // #2 - this for print
os->getGlobal("concat"); // #3 - concat function
os->pushNull(); // #4 - this for concat
os->pushNumber(5); // #5
os->pushString(" big differences"); // #6
os->call(2, 1); // #3 - result is already at the top of stack
os->call(1); // #0
os->release();
return 0;
}
Вы можете скачать исходники ObjectScript и пример из данной статьи по этой ссылке, открыть proj.win32examples.sln, проект stack_usage.
Автор: evgeniyup