− Скрыть
Сколько же существует языков программирования, еще один? Ну можно и так сказать, а можно сказать и по другому: я программист и пишу программы на разных языках программирования для разных задач. В одних языках есть одни плюсы, в других - другие. Вот я и решил предложить свой универсальный язык программирования для множества задач: от мобильных платформ до веб-технологий.
ObjectScript - новый объектно-ориентированный язык программирования с открытым исходным кодом. Сами исходники занимают 700 Кб (парсер, компилятор и виртуальная машина) и находятся в двух файлах source\objectscript.h и source\objectscript.cpp. Скачать их можно тут. ObjectScript - очень легкий, предназначен для вставки в приложение на C++.
ObjectScript сочетает в себе возможности таких языков, как JavaScript, Lua, Ruby, Python и PHP. Например, синтаксис взят из JavaScript, множественное присваивание - из Lua, синтаксический сахар - из Ruby, ООП - из PHP, магические методы - из Python.
Кроме унификации нескольких существующих языков программирования, ObjectScript добавляет также и свои уникальные и полезные возможности.
Синтаксис
x = 12;
y = "Hello World!";
А что если убрать точки с запятыми?
x = 12
y = "Hello World!"
ObjectScript автоматически распознает отдельные выражения (новая строка тут не причем, все можно писать и в одну строчку), поэтому точку с запятой (;) можно не использовать по желанию.
Вызовы функций
Привычный синтаксис, который используется в большинстве языках программирования:
Иногда в функцию передается только один параметр, например:
print({firstname:"Ivan", lastname:"Petrov"})
В фигурных скобках задан объект в привычном для JavaScript синтаксисе. Такой синтаксис полностью поддерживается в ObjectScript, но подобный вызов выглядит НЕ очень красиво. А что если убрать круглые скобки?
print {firstname:"Ivan", lastname:"Petrov"}Уже симпатичнее?! Эта возможность взята из Lua. Так можно вызывать любые функции и не только с объектом в качестве параметра, например:
Довольно таки просто и читабельно!
Объекты
Но вернемся к предыдущему примеру.
print {
firstname: "Ivan",
lastname: "Petrov"
}На ровне с ':' (js синтаксис) можно использовать '=' (lua синтаксис)
print {
firstname = "Ivan",
lastname = "Petrov"
}Т.е. при формировании пар в объекте (индекс и значение) можно использовать как двоеточие, так и знак равно. Кроме этого, допускается отделение пар запятыми (,) или точкой с запяток (;). Следует также отметить, что использование разделяющих символов после конечного значения допускается.
А что если необходимо в качестве индекса значения использовать выражение, а не константу, легко:
Т.е. выражение в квадратных скобках будет вычислено на этапе выполнения программы и результат будет использован в качестве индекса соответствующего значения в объекте. Иначе говоря:
Выведет five
Порядок значений в объекте сохраняется таким, в каком порядке значения были добавлены в объект (это бывает важно в итерационных процессах, о которых мы поговорим позже).
Еще одной важной особенностью является то, что в качестве индекса значения может выступать значение любого типа, например:
a = {x=1, y=2}
b = {[a]="powerful", 7="greate"}
print b[a]Выведет powerful, причем это никак не уменьшает скорость доступа к данным объекта и не увеличивает потребление памяти. Иначе говоря, если есть потребность, можно использовать смело.
Массивы
Массивы - это индексные списки начиная с 0 (нуля). Как и в JavaScript массив можно записать следующим образом:
Множественное присваивание
ObjectScript полностью поддерживает множественное присваивание и выглядит это следующим образом:
Переменной i присвоится значение 0, j присвоится 1, k - 3. Интересным следствием множественного присваивания является возможность смены значений в переменных одной строкой:
Довольно просто и красиво. С помощью множественного присваивания можно одной строкой инициализировать сразу несколько переменных, менять в переменных значения, а также получать множественные результаты вызываемых функций, например:
var test = function(){ return 1, 2 }
var a, b = test()
Функция test возвращает два значения, в переменную a сохранится 1, а в b - 2. Если затребовать из функции больше значений, чем она возвращает, то количество результатов дополнится пустыми значениями - null
var a, b, c = test()
print(a, b, c)
Выведет: 1 2 null
Итераторы
Итераторы позволяют обработать элементы некоторого списка друг за другом по очереди и выполнить какую-то работу с этими элементами. Например, пусть нам нужно обработать элементы объекта и показать их индексы и значения:
obj = { null, awesome=true, 12, "excellent" }
for(k, v in obj){
print( k " --> " v )
}Данная программа выведет:
0 --> null
awesome --> true
1 --> 12
2 --> excellent
Когда компилятор ObjectScript видит for in, он генерит на выходе следующий код:
obj = { null, awesome=true, 12, "excellent" };
{
var iter_func = obj.__iter()
for(var iter_valid;;){
iter_valid, k, v = iter_func()
if(!iter_valid) break
print( k " --> " v )
}
}Объектно-ориентированное программирование (ООП) в ObjectScript
Как можно было бы понять из названия языка, он просто обязан быть объектно ориентированным и поддерживает ООП во всей своей красе.
Опишем класс следующим образом:
Person = {
__construct = function(firstname, lastname){
this.firstname = firstname
this.lastname = lastname
},
__get@fullname = function(){
return this.firstname .. " " .. this.lastname
},
walk = function(){
print this.fullname .. " is walking!"
},
}Теперь создадим экземпляр данного класса:
var p = Person("James", "Bond")
Фактически Person - это обычный объект, когда объект вызывается, как функция, ObjectScript автоматически создает новый экземпляр данного объекта и инициализирует его методом __construct. Выше приведенный код будет реально выполнен следующим образом:
var p = {}
p.prototype = Person
p.__construct("James", "Bond")Если затем выполнить:
то выведется:
James Bond is walking!
{"firstname":"James","lastname":"Bond"}Следует выделить метод __get@fullname, который неявно вызывается из метода walk:
__get@fullname = function(){
return this.firstname .. " " .. this.lastname
},
walk = function(){
print this.fullname .. " is walking!"
},
Метод __get@fullname возвращает значение свойства fullname. Могут быть также специальные методы для установки свойств, но об этом позже в разделе Свойства, getter-ы и setter-ы.
Наследование
Теперь самое время унаследоваться от Person.
var IvanPerson = extends Person {
__construct = function(){
super("Ivan", "Petrov")
}
}
var p = IvanPerson()
p.walk()
print pВыведется:
Ivan Petrov is walking!
{"firstname":"Ivan","lastname":"Petrov"}Наследование делается оператором extends, который принимает два выражения exp1 и exp2, любых, в том числе рассчитанных на этапе выполнение и эквивалентен следующему коду:
(function(exp1, exp2){
exp2.prototype = exp1
return exp2
})()
Из интересного нужно отметить:
super вызывает метод родительского класса (прототипа) с именем метода, из которого он был вызван, в данном случае - это __construct, который и инициализирует экземпляр объекта.
ООП на закуску
Давайте создадим совершено новый тип данных, который будет работать как трехмерный вектор:
var vec3 = {
__construct = function(x, y, z){
this.x = x
this.y = y
this.z = z
},
__add = function(b){
return vec3(this.x + b.x, this.y + b.y, this.z + b.z)
},
__mul = function(b){
return vec3(this.x * b.x, this.y * b.y, this.z * b.z)
},
}
var v1 = vec3(10, 20, 30)
var v2 = vec3(1, 2, 3)
var v3 = v1 + v2 * v2
print v3Выведется: {"x":11,"y":24,"z":39}
Свойства, getter-ы и setter-ы
Свойство - это некоторая абстрактная сущность (нет, не в виде гномика, хотя...), которая со стороны выглядит как обычное значение в объекте, но при чтении и записи может выполнять некоторую работу в соответствующих методах.
Геттер (getter) - возвращает значение свойства, сеттер (setter) - устанавливает значение. ObjectScript автоматически понимает, считывается свойство или устанавливается, и вызывает соответствующие методы.
a = {
_color = "red",
__get@color = function(){ return this._color },
__set@color = function(v){ this._color = v },
}
print a["color"]
a.color = "blue"
print a.colorВыведется:
Как же это реально работает? При чтении свойства color ObjectScript ищет значение в объекте с именем color. Если таковое найдено, то оно просто возвращается. Если нет, то ищется метод __get@color, нашелся - отлично, значит ObjectScript вызывает и возвращает его результат. Если не нашелся, не беда, ObjectScript ищет метод __get. Если таковой присутствует, то ObjectScript вызывает этот метод с именем запрошенного свойства. Если ничего не нашлось, то возвращается null и точка.
При установке свойства, все происходит аналогично, но вместо __get используется __set. Еще один пример:
a = {
_color = "white",
__get = function(name){
if(name == "color")
return this._color
},
__set = function(name, v){
if(name == "color")
this._color = v
},
__del = function(name){
if(name == "color")
delete this._color
},
}
print a.color
a.color = "green"
print a.color
delete a.color
print a.colorВыведется:
Тут показан новый оператор delete, который удаляет значение в объекте с использованием метода __del.
Многомерные свойства
ObjectScript поддерживает следующий (обычный для некоторых языков) синтаксис, который является полностью эквивалентным:
print a.color
print a["color"]
А что, если в квадратных скобках передать несколько значений? В ObjectScript это вполне реально!
a = {
_matrix = {},
__getdim = function(x, y){
return this._matrix[y*4 + x]
},
__setdim = function(value, x, y){
this._matrix[y*4 + x] = value
},
__deldim = function(x, y){
delete this._matrix[y*4 + x]
},
}
a[1, 2] = 5
print a[1, 2]
delete a[1, 2]
print a[1, 2] Выведется:
Остается только обратить внимание на метод __setdim, который первым параметром принимает новое значение, а в остальных параметрах - атрибуты свойства (их количество может быть любым начиная от двух).
Пустые свойства
А что на счет следующего кода?
b = a[]
a[] = 2
delete a[]
Вполне! ObjectScript в этом случае вызывает следующие методы соответственно: __getempty, __setempty, __delempty. Программист может решить по своему усмотрению, как использовать этот функционал.
Заключение
На закуску несколько не отмеченных выше моментов.
При описании функции в блоке перечисления параметров, запятые ставить также не обязательно:
print function(a b c){ return a + b * c }(1 2 3)
Выведет 7
В функциях можно использовать arguments - возвращает массив всех параметров, с которыми функция была запущена, ... (три точки) - массив дополнительных параметров, которые не описаны в объявлении функции.
Оператор # вызывает для объекта применения метод __len. Для строк он возвращает количество символов в строке, а для объектов и массивов - количество элементов. Также в классе Object заведено свойство:
Object.__get@length = function(){ return #this }
А т.к. все объекты, в том числе строка и массивы, унаследованы от Object, то можно использовать свойство length, например, в массивах, на манер JavaScript.
Из необычных математических операторов можно отметить ** - возведение в степень.
Последнее вычисленное значение в функции возвращается автоматически, поэтому иногда return писать не обязательно, например:
function sqrt(a){ a ** 0.5 }
Для описания функций можно использовать синтаксический сахар, например, выше описанный пример можно записать так:
А сами функции можно никуда не присваивать, а сразу же вызывать, например, вычисление факториала:
print "factorial(20) = " .. {|a| a <= 1 ? 1 : a*_F(a-1)}(20)В данном примере используется _F - ссылка на текущую функцию.
Одинаковые строки ObjectScript хранит в единственном экземпляре, это делается автоматически. Причем не важно, была строка получена на этапе выполнения или компиляции программы.
Локальные переменные имеют область видимости, например.
var i = 1;
{
var i = i
i++
print i
}
print iВыведется
ObjectScript имеет два зарезервированных слова для целей отладки, первое - debugger (как в JavaScript). При срабатывании debugger программа остановится в дебагере, как при точке останова. Второе - debuglocals, которое возвращает ассоциативный объект с названиями видимых из точки использования debuglocals локальных переменных и их значений. Например:
function(a){
var c = a * 2;
{
var c = a - 1
print debuglocals
}
}(10)
Выведется:
Преобразование выражений в тип boolean происходит следующим образом: значения null, false и NaN возвращают false, все др. значения - true, в том числе пустая строка и число 0.
Операторы && и || возвращают то значение, которые было им передано, например:
print 7 && 9
print 7 || 9
Выведется:
При присваивании объектов, их копии не создаются, присваивается ссылка на сам объект. Чтобы создать копию, необходимо воспользоваться методом clone.
Ну вот как-то так. Если вам ObjectScript кажется интересным, давайте разрабатывать и развивать язык вместе. Предлагайте и комментируйте.
Как запустить пример того, что описано в этой статье? Скачать исходники, откомпилированный файл с примером с репозитория на github, перейти в папку OS\examples-os и запустить файл test.cmd.
В сухом остатке
ObjectScript полностью совместим с JSON, т.к. понимает этот формат, как свой родной, но добавляет в описание объектов и массивов свой расширенный и простой синтаксис. ObjectScript реализует плюсы таких языков, как JavaScript, Lua, Ruby, Python и PHP, при этом добавляет свои уникальные возможности программирования. ObjectScript - объектно-ориентированный язык программирования, реализует все его парадигмы. Оператор
new при этом не используется, минимизируя код и делая его более читабельным. Синтаксис ObjectScript позволяет реализовать все необходимые конструкции, но направлен на простоту и читабельность. ObjectScript предназначен для вставки в приложение на C++, позволяет интегрироваться с С++ на уровне функций и пользовательских данных (в том числе объектно-ориентированных).
Веб-технологии базируются на fastcgi демоне os-fcgi.
Некоторые примеры по использованию языка ObjectScript в мобильной разработке (сделаны на одной из первой версии языка):
+ Показать