ObjectScript API, интеграция с C++. Часть 1: работа со стеком, вызов функций OS из C++

в 12:54, , рубрики: api, javascript, objectscript api, Программирование, метки:

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


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


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