Язык программирования Кедр. Продолжение

от автора

Рассмотрим синтаксис, обработку ошибок, взаимодействие с языком С и возможности реактивного программирования.

Предыдущая статья здесь.

Особенности синтаксиса

val identity = Matrix4 1 0 0 0                        0 1 0 0                        0 0 1 0                        0 0 0 1

Позиционный способ инициализации полей хорошо подходит для матриц. Целочисленный литерал применяется также для вещественных типов чисел.

control.set_width width         set_height control.measure_height         arrange  import num::vector_f32::Vector3                         Vector4             vector_i32::Vector2i

Методы set_width, set_height и arrange вызываются на одной и той же привязке control.

let enabled_extension_names = ["VK_EXT_debug_report"                                "VK_EXT_debug_utils"                                "VK_KHR_surface"                                "VK_KHR_xcb_surface"                                "VK_KHR_xlib_surface"]

Запятые между элементами не нужны, если каждый находится на отдельной строке или строках. Указатель на массив будет передан в С функцию, поэтому все строковые литералы получат тип CString.

text |> lines |> filter { line -> line.starts_with "layout" } |> map { remove_comment _ }

Операторы |> удобно располагать в вертикальный ряд. Замыкания создаются с помощью фигурных скобок. Каждое использование подчёркивания возвращает следующий параметр.

let up_right = Vector3 left.as_f32 0 border_z let adjusted_size = size as f32 * 1.5 |> as_u32

Преобразование типа можно записать как вызов метода.

let thickness = (left = border.left                  right = border.right                  up = border.up                  down = border.down)  let sizes = List<(u32, u32) width height>.new

Поля кортежа не имеют имён по умолчанию, обращаться к ним можно только по явно заданным.

Внутристрочное объявление типов

type Entry =     path : String     created : DateTime          | File size : u32     | Directory entries : Slice<Entry>                 | Local                 | Remote url : String

Entry и Directory это типы-суммы, Local и Remote — структуры. Поля path и created доступны как для Entry, так и для каждого из подтипов.

type Entry @abstract = struct     path : String     created : DateTime     is_directory : bool      | File size : u32     | Directory entries : Slice<mut ref Entry>                 is_remote : bool                 | Remote url : String

Структуры File и Directory наследуют поля структуры Entry. Ссылка типа mut ref Entry указывает на File, Directory или Remote.

Классы типов

type Comparable = class     def (>) (rhs : Self) : bool     def (<) (rhs : Self) : bool     def (>=) (rhs : Self) : bool     def (<=) (rhs : Self) : bool  type Compare @virtual =     def compare (other : Self) : Ordering      def (>) (rhs : Self) =         let result = compare rhs         result == Ordering::Greater      ...      is Comparable

Тип может быть объявлен принадлежащим к классу, если соответствует всем заданным для класса ограничениям.

Обработка ошибок

def try_read_file (path : String) : Result<String, String> =     ...  object i32     def try_parse (s : String) : Option<i32> =         ...

Result<String, String> содержит либо прочитанный из файла текст в Ok<String>, либо Error<String> с сообщением об ошибке.

def try_add (lhs_path : String) (rhs : i32) =     let lhs =         let! text = try_read_file lhs_path         let! number = i32.try_parse text         number      lhs + rhs |> Some

Привязки text и number имеют типы String и i32, учитывающие только случаи успешного выполнения функций try_read_file и try_parse.

Ниже показан аналогичный код без let!.

def try_add (lhs_path : String) (rhs : i32) =     let lhs =         let text = case try_read_file lhs_path of             Result::Ok text -> text             Result::Error _ -> return None          let number = case i32.try_parse text of             Some number -> number             None -> return None          number      lhs + rhs |> Some

Полученное от try_read_file сообщение об ошибке можно передать выше по стеку вызовов, вернув Result<i32, String> из try_add вместо Option<i32>.

def try_add (lhs_path : String) (rhs : i32) =     let lhs =         let! text = try_read_file lhs_path         let! number = i32.try_parse text |> to_result "cannot parse $text"         number      lhs + rhs |> Result::Ok

Option конвертируется в Result с помощью метода to_result.

Взаимодействие с языком С

Типы

type VkInstance = MutPtr type CString = Ptr<c_char>

Именованный кортеж с единственным элементом в С коде представлен как тип элемента, поэтому в объявлениях внешних функций VkInstance и CString можно использовать вместо void* и const char*.

typealias DebugUtilsMessengerCallbackExt = fn     (message_severity : DebugUtilsMessageSeverityFlagsExt)     * (message_types : DebugUtilsMessageTypeFlagsExt)     * (callback_data : ptr DebugUtilsMessengerCallbackDataExt)     * (user_data : MutPtr) -> u32  typealias CharFun = fn GlfwWindow * u32 -> Unit

Псевдонимы для типов указателей на функцию.

type SampleCountFlags = flags     | 1     | 2     | 4     | 8     | 16     | 32     | 64

Символы флагов могут быть числами или начинаться с цифры.

type InstanceCreateInfo = struct     type : StructureType     next : Ptr     flags : InstanceCreateFlags     application_info : ptr ApplicationInfo     enabled_layer_count : u32     enabled_layer_names : ptr CString     enabled_extension_count : u32     enabled_extension_names : ptr CString

Структуры переводятся на С дословно.

Значения

object f64     val min "DBL_MIN" : f64     val max "DBL_MAX" : f64     val epsilon "DBL_EPSILON" : f64

Внешние константы DBL_MIN, DBL_MAX и DBL_EPSILON представлены полями объекта f64.

Функции

module vk  def create_semaphore "vkCreateSemaphore"     (device : VkDevice)     (create_info : ptr SemaphoreCreateInfo)     (allocator : Allocator)     (semaphore : mut ptr VkSemaphore) : Result  endmodule

Библиотечная функция vkCreateSemaphore доступна под именем vk::create_semaphore.

def create_semaphore (device : VkDevice) =     let semaphore_create_info = SemaphoreCreateInfo         type = StructureType::SemaphoreCreateInfo         next = null         flags = SemaphoreCreateFlags@zero      let mut semaphore = null     let result = vk::create_semaphore device semaphore_create_info@ptr null                                       semaphore@mut_ptr     assert result == Result::Success      semaphore

Модификаторы @ptr и @mut_ptr возвращают указатель на значение, @fn — указатель на функцию, @zero — обнулённый блок памяти.

type f32     def Real.sin "sinf" : f32     def Real.cos "cosf" : f32     def Real.floor "floorf" : f32     def Real.sqrt "sqrtf" : f32

Внешние функции могут быть не только свободными, но и методами типа, в том числе служить определением абстрактных.

type f64     def Real.sin : f64     def Real.cos : f64     def Real.floor : f64     def Real.sqrt : f64

Функции для f64 уже имеют правильные имена.

Код

В опубликованном коде есть ящики для работы с vulkan и glfw.

Реактивное программирование

type Border =     inherit Control      var obs left : u32 = 0     var obs right : u32 = 0     var obs up : u32 = 0     var obs down : u32 = 0     var obs content : Option<Control> = None

Модификатор obs создаёт контейнер типа mut Atom<T>, поддерживающий публикацию оповещений об изменении значения.

let content_width = content@atom.map     { case _ of           None -> 0           Some control -> control.width }

content@atom вместо значения поля content возвращает контейнер, на котором определена функция map.

let thickness = (border.left@atom                  border.right@atom                  border.up@atom                  border.down@atom)                 |> to_atom

Можно преобразовать кортеж атомов в атом кортежей, thickness имеет тип Atom<(u32, u32, u32, u32)>.

val memory@atom = (content_width, content_height, thickness)                   |> to_atom                   |> map { width, height, thickness ->                       if width == 0 || height == 0                       then None                       else Some (width, height, thickness) }                   |> option_map { width, height, (left, right, up, down) ->                       ...                       MeshMemory.from_quads quads.as_slice }

Из-за модификатора @atom привязка memory имеет тип Option<MeshMemory>, а не Atom<Option<MeshMemory>>.


ссылка на оригинал статьи https://habr.com/ru/articles/818119/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *