Skip to content

artem8086/ArtSoftScriptEngine

Repository files navigation

ArtSoft Scripter - скриптовый встраиваемый язык, для программ на платформе Java,

похож на JavaScript но с несколькими отличиями.

Разрабатывался как встраиваемый поэтому имеет ограниченный базовый

функционал, но при этом имеется средства для расширения.

При этом для оптимизации перед исполнением текст компилируется

в специальное объектное дерево команд с оптимизацией, которая

заключается в том, что выражения, которые можно непосредственно вычислить,

будут вычислены в процессе компиляции. Например:

(2 + 2) * 2 будет представлено как 8;

"Hello " + "world!" - как "Hello world!".

Комментарии также, как и в любом Си-подобном языке это

// однострочный комментарий

/* многострочный

комментарий*/

Типизация динамическая, т. е. переменные могут иметь различные типы

во время исполнения.

Базовые типы:

Integer - целые числа разрядностью 4 байта;

Double - вещественные числа с плавающей запятой разрядностью 8 байт;

Boolean - булевой тип может принимать только значения true или false;

String - строки (в строках можно писать escape-последовательности,

        но их пока не много: \n, \t, \r, \* - любой символ);

Table - таблицы, или ассоциативные массивы;

Function - функции (все функции хранятся в переменных).

Также переменные могут хранить любые Java-объекты, которые

могут быть адаптированы для использования их в скрипте

(смотрите об этом в файле "встаивание.txt").

Теперь о переменных. Как уже говорилось переменные имеют динамическую типизацию,

т. е. переменная становится того типа, значение которого ей присваивается.

Переменная может быть объявлена несколькими способами:

var a = 1, b = 1.2, c = false; // переменная a типа Integer, b типа Double, c - Boolean

d : "Hello"; // переменная типа String (сокращённая форма для var)

Глобальные переменные можно объявлять без var или ':'. Например

e = 0x10; // глобальная переменная e типа Integer (0x10 - число в шестнадцатеричной форме записи)

(если в текущей области видимости уже объявлена переменная с таким именем,

то присваивание будет произведено ей, а не глобальной переменной с таким же именем)

Чтобы определить тип переменной можно воспользоваться стандартными функциями определения типа:

isNumber(a) - проверит если a является числом, т.е. принадлежит к типу Integer или Double,

            то вернёт true, иначе false;

ну и так по анологии:

isInteger(x), isDouble(x), isBoolean(x), isString(x), isTable(x), isFunction(x);

В отличие от JavaScript переменная существуют до тех пор, пока ей не будет присвоено null.

*** Примечание ***

В отличие от JavaScript-а одинарные кавычки не означают строки, они

как и в Си или Java предназначены для обозначения символа, но так

как типа Char нет то они конвертируются в Integer, и представляют

код символа. Например:

    '0' - 48

Операции.

Существуют все базовые арифметические операции, такие как:

'+' - сложение чисел либо контеканация строк;

'-' - вычитание;

'*' - умножение;

'/' - деление;

'%' - остаток от деления.

Логические операции, применимы к типам Boolean и Integer:

'&' - логическое И;

'|' - логическое ИЛИ;

'^' - исключающее ИЛИ;

'~' - унарная операция НЕ;

Логические операции, применимые только к типу Boolean:

'&&' - логическое И;

'||' - логическое ИЛИ;

'^^' - исключающее ИЛИ;

'!' - унарная операция НЕ;

Сдвиги применимы только к числам типа Integer:

'>>' - сдвиг вправо;

'<<' - сдвиг влево;

Операции присваивания (простое перечисление):

'=', '+=', '-=', '*=', '/=', '%=', '&=', '|=', '^=', '>>=', '<<=';

Операции сравнения:

'==', '===', '!=', '>=', '<=', '>', '<';

Стоит выделить операции '==' и '===', операция '==' сравнивает операнды по значениям,

в то время как '===' по ссылкам.

Тернарная операция cond ? expr1 : expr2;

Так же как и везде если cond - true значит вернёт значение expr1, иначе expr2.

Унарные операции ++ и --, в отличие от своих аналогов в других языках

реализованы очень плохо, они могут быть как префиксными, так и постфиксными

но сути это не меняет, в выражениях они моментально меняют значение своих переменных,

так что например:

a++ аналогично (a+=1).

Для группирования приоритетов в выражениях используются скобки ():

(2 + 2) * 2 = 8.

*** В конце каждого выражения нужно ставить точку с запятой ';' ***

Массивы.

Массивы существуют только ассоциативные, в индексированных массивах

индексом являются ключи.

Создание массива. Существует два способа создания массива:

a = []; или a = {};

Обращение к элементам массива может осуществляться двумя способами:

a[0] = 1; a["abc"] = "abc"; // через оператор индексации;

a.field = 2; // через оператор доступа точка '.';

определения размера массива так же как и длины строки осуществляется функцией sizeof:

sizeof(a) = 3; sizeof("hello") = 5;

Также можно создать уже проинициализированный массив:

b = {a:1, b:2, c:3}; // либо:

b = {var a = 1, b = 2, c = 3}; // что выглядит как минимум странно

                // и я не советую применение таких конструкций

Также можно инициализировать массив следующим образом:

с = [1, 2, 3];

в этом случае создастся массив, содержащий по индексам

0, 1, 2 значения 1, 2, 3, будет распечатан так:

{0=1, 1=2, 2=3}

*** к глобальным переменным можно обращаться через массив global ***

Функции пишутся по аналогии с JavaScript:

function sum(a, b) {

    return a + b;

}

В этом случае переменная sum будет хранить ссылку на функцию.

Вызов функции осуществляется обычным образом:

a = sum(2, 2);

Функции которые не имеют оператора return всегда возвращают null.

Также эту же фукцию можно написать следующим образом:

sum = function (a, b) {

    return a + b;

};

В данном случае переменной sum будет присвоена безымянная (лямбда) функция;

Можно написать и так:

sum = function sum2(a, b) {

    return a + b;

};

Это аналогично:

sum = sum2 = function (a, b) {

    return a + b;

};

что означает что переменные sum и sum2 будут ссылаться на одну и ту же функцию.

К сожалению в именах функций можно производить некоторые вычисления,

это бывает удобно чтобы например создать функцию не в глобальном пространстве имён:

с = {};

function c.sum(a, b) { return a+ b; }

Однако можно писать и такое:

list_names = ["myFunc", "sum", "abracadabra"];

function list_names[system.random(sizeof(list_names))](a + b) {

    return a + b;

}

Что приведёт к созданию функции с рандомным именем из списка list_names.

Конструкции языка.

Их я не буду полностью расписывать так как они аналогичны

таким же конструкциям из Си-подобных языков:

if (cond) expr1 [else expr2]

while (cond) expr;

do expr while (cond);

for (init; cond; op) expr;

Однако отсутствует конструкция switch..case.

А также есть цикл по массиву который имеет вид

for (variable : array) expr;

Пример:

с = {a:1, b:"hello", c:false};

for (var a : c) {

    system.println(a.key + ' ' + a.value);

}

В переменной a будет массив с полями key (ключ) и value (значение).

Модульность.

Поддерживается много файловая структура скрипта.

Для этого предназначен оператор import который

импортирует и выполняет файл после чего продолжается

выполнение текущего файла. Пример:

import "package/module.ass";

Обьекты.

По сути объектов нет, так как обычные массивы являются объектами

никаких прототипов и мета-таблиц не реализовано, но есть

некоторые конструкции призванные сделать мнимое ООП.

Первое о чём стоит сказать это то что функции всегда

предаётся переменная this, её значение зависит от того

в каком контексте была вызвана функция. Если функция была вызвана

из глобального пространства имён то this будет указывать на массив

global, если функция была вызвана как поле массива то this будет

ссылатся на этот массив. Например:

С = {};

С.createFields = function () {

    for (var item : this) {

        this[item.key] = null;

    }

    this.A = 1;

    this.B = "hello";

    this.C = false;

};

то при вызове:

С.createFields();

функция очистит содержимое массива С и заполнит его полями

A, B, C со значениями 1, "hello" и false.

Явно что функцию createFields можно назвать конструктором,

но этот конструктор является одноразовым так как он очищает

полностью весь массив включая и себя, можно создавать

функцию-конструктор без привязки к массиву. Пример:

function Object() {

    this.num = 1;

    this.add = function (num) {

        this.num += num;

    };

}

При вызове данная функция будет просто засорять глобальное пространство имён,

поэтому её необходимо вызывать при помощи оператора new:

obj = new Object();

obj.sum(1);

теперь функцию Object можно условно назвать конструктором.

Операция new создаёт пустой массив вызывает функцию следующую после new

и ассоциирует this с только что созданным массивом, и после выполнения функции

возвращает этот массив, при этом если функция что-то возвращала,

то её возврат теряется.

Функцию add в данном примере можно назвать методом.

Псевдо-наследование.

Если мы захотим создать конструктор чтобы он создавал объект со всеми

свойствами Object из предыдущего примера, но например с методом вычитания,

то можно написать так:

function Object2() {

    extends Object();

    

    this.sub = function (num) {

        this.num -= num;

    };

}

Теперь мы можем создовать обьекты с методами add и sub:

obj2 = new Object2();

obj2.add(2);

obj2.sub(3);

Здесь операция extends вызывает конструктор Object и в ассоциирует

его this с указателем this из текущего контекста.

Таким образом можно реализовать полиморфизм.

К сожалению инкапсуляция никак не получится из-за плохой реализации

лямбда-замыканий, а точнее почти никакой.

Таким образом можно симулировать ООП, но по сути операции

new и extends бессмысленные.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages