В этой статье мы поговорим о новом интерпретируемом языке Loli, рассмотрим синтаксис и сравним с языками C и Python.
Введение
Язык loli по синтаксису больше всего похож на Python. Но по производительности в разы быстрее.
Loli строго типизированный язык. В нём так же присутствуют объекты. То есть Loli одновременно и функциональный и объектный язык. И это несомненно плюс. Расширение файлов у языка это .li
.
Loli имеет возможность написания расширений на C.
Тесты производительности
И так, я не наврал про его производительность, ведь она действительно высокая.
Я написал одинаковые тесты производительности на C, Python и Loli. Тесты на github
Результат вы видите сами:
Небольшой пример
Пример вычисления факториала:
#[
Loli строго типизирован, по этому нужно описать каждую переменную своим типом или классом
]#
fn fact(num: Integer): Integer {
if (num <= 0): {
raise IndexError("Number <= 0") # Если num меньше или равен нулю, то выбрасываем исключение
}
if (num == 1): {`
return 1 # Если 1 то возвращаем 1
}
return num * fact(num - 1) # Ну а тут умножаем num на рекурсивный вызов fact с num - 1
}
sayln(fact(5)) # Вызываем нашу функцию и даём ей на вход число 5, ответ 120
Синтаксис
Ну а теперь давайте рассматривать синтаксис языка с самого начала.
Переменные:
var x = 5 # Авто-определение типа переменной
var y: Double = 5.32 # Определение типа в ручную
var z = [1, 2, 3], hi = "Hello, world!" # Создание множества переменных через запятую
Массивы:
# List
var a_list = [1, 2, 3]
var a_range = range(1, 3)
var empty_list: List[Double] = []
# Hash
var a_hash = ["a" => 1, "b" => 2, "c" => 3]
var empty_hash: Hash[String, Integer] = []
# Hash от List отличается только тем что можно создавать ключи определённого типа
#[
Tuple это как List только Tuple. Tuple имеет фиксированный размер и может содержать типы, которые не имеют никакого сходства друг с другом
]#
var a_tuple: Tuple[String, Double] = <["hi", 34.93]>
Как видим массивы очень интересны, но так же интересны и строки:
# Всё это один тип String
var hi = "Hello, world!"
var multi_line = """Hello,
world!"""
#[
А это уже ByteString, единственное их отличие в том что они хранят в себе массив байтов.
Так же обычные строки можно переводить в ByteString методом to_bs
Конструкция типа var a = "Hello".to_bs() тоже будет иметь свой смысл.
]#
var a = b"123456"
var b = b"12"
var c = b"255254t"
var d = b"""A
multi-line
ByteString"""
Со строками разобрались, теперь числа.
Под капотам в C у типа Integer
стоит uint64_t
, то есть 64-битные числа.
У Byte
это uint8_t
, то есть 8-битные числа.
Ну а у Double
соответственно double
.
Теперь поговорим о функциях:
# Функции
fn empty_fn {
sayln("Hello, World!") # Функция без аргументов и возвращаемого значения
}
fn fn_with_arg(num: Integer) { # Функция с обязательным аргументом
sayln("Num: " ++ num) # ++ в Loli это не инкремент, а соединение типов
}
fn fn_with_arg_and_return(num: Integer): Integer {
return num # Возвращаем num
}
fn fn_without_arg_but_with_return: Integer {
return 2 + 2
}
# Функция с Keyarg
fn sample(:first x: Integer, :second y: Integer, :third z: Integer): Integer {
return x + y + z
}
sample(1, 2, 3) # 6
sample(1, :second 2, :third 3) # 6
sample(:third 30, :first 10, :second 5) # 45
# Не обязательные значения функций
# В данном случаи можно просто вызвать sample() и a будет равна 10
fn sample(a: *Integer = 10): Integer {
return a + 10
}
# Varargs
fn sum(n: Integer...): Integer { # В данном случаи n будет типом List[Integer]
var result = 0
n.each(|x| result += x) # Лямбда функция
return result
}
# Лямбда функции
fn apply(a: Integer, fn: Function(Integer => Integer)): Integer {
return fn(a)
}
sayln(apply(10, (|a| a * a))) # 100
# Future функции
future fn add(Integer, Integer): Integer { ... }
# *a lot of code*
fn add(x: Integer, y: Integer): Integer {
return x + y
}
Такс, с функциями мы разобрались, ну а теперь классы.
# Класс Point с двумя аргументами в конструкторе
class Point(x: Integer, y: Integer) {
pub var @x = x # pub означает что это поле публичное, так же есть pri (private), pro (protected)
pub var @y = y
}
# Можно также сократить предыдущий пример
class Point(pub var @x: Integer, pub var @y: Integer) { }
# Наследование
class Point2D(pub var @x: Integer, pub var @y: Integer) {}
class Point3D(x: Integer, y: Integer, pub var @z: Integer) < Point2D(x, y) {}
#[
По умолчанию методы класса получают неявный параметр self в качестве первого аргумента.
Спецификатор sta при применении к методу класса отключает это поведение
]#
# Джинерики
class Stack[A](element: A) {
pub var @contents = [element]
pub fn push(value: A): self {
@contents.push(value)
}
}
Stack(1).push(2).push(3) # [1, 2, 3]
Классы прошли, теперь рассмотрим исключения. Исключение это обыкновенный класс который унаследован от класса Exception
. В loli по умолчанию уже есть 7 исключений которые можно использовать.
- Exception — базовый класс всех исключений
- DivisionByZero — ошибка деления на 0
- IndexError — Доступ за пределы контейнера (например, List), String или ByteString
- IOError — Некорректная работа с файлами
- KeyError — Попытка прочитать значение из Hash, которое не существует
- RuntimeError — Неправильное действие во время выполнения, например изменение хеша во время итерации или превышение предела рекурсии
- ValueError — Указано неверное или необоснованное значение
Все исключения можно вызвать через ключевое слово raise
raise IndexError("Index out of range") # Тут мы создаём экземпляр класса унаследованного от Exception
Для отлова исключений существует блок tryexcept
try: {
raise IndexError("Index out of range")
except Exception as e:
sayln("Exception: {0}".format(e.message))
}
Импорты
Система импорта похожа на ту что в Python:
import sys
sys.exit()
import package
import package as pkg
import (somefn, somevar) package
import "folder/package"
Пути импортов в loli выглядят таким образом:
./<name>.li
./<name>.(dll/so)
./pkg/<name>/<name>.li
./pkg/<name>/<name>.(dll/so)
<original root>/pkg/<name>/<name>.li
<original root>/pkg/<name>/<name>.(dll/so)
Enums
Небольшой пример enum:
enum Rgb {
Red,
Green,
Blue
fn is_blue: Boolean {
match self: {
case Blue: return true
else: return false
}
}
}
Заключение
Как мы видим Loli это очень хороший и быстрый скриптовой язык который можно бесконечно расширять расширениями на C. На Loli уже есть биндинги OpenGL и FreeGLUT и обёртка над cURL. А так же в разработке порт GTK+. Так же планируется написать HTTP сервер для loli. Из этого следует что язык можно применять в разных сферах. Я надеюсь вам понравился этот язык и вы уделите ему немного времени.
Ссылки
Автор: MWGuy