...

воскресенье, 24 февраля 2019 г.

Loli — новый высокопроизводительный язык

В этой статье мы поговорим о новом интерпретируемом языке 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"\0\1\2"
var c = b"\255\254\t"
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

Для отлова исключений существует блок try\except

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. Из этого следует что язык можно применять в разных сферах. Я надеюсь вам понравился этот язык и вы уделите ему немного времени.


Ссылки


Let's block ads! (Why?)

Комментариев нет:

Отправить комментарий