Программный код должен быть в кодировке UTF-8. Символы вне диапазона [32.. 127] допускаются только внутри строковых литералов. Табуляция запрещена.
Виды лексем:
-
Пунктуация
Один символ из набора
{}[]();.. -
Строковый литерал
Множество блоков
"[^"]*", при склеивании блоков между ними вставляется символ", если между блоками нет пробела. Или, один блок'[^']*'."hello ""damn"" world"'hello "damn" world' -
Число
Обычное число с плавающей точкой и возможной экспонентой.
[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?10.234e-12 -
Комментарий
-- Обычный комментарий (считается пробельным символом) --- Документирующий комментарий (может использоваться только в начале файла и перед определениями). -
Имя
Состоит из непустой последовательности непробельных символов в диапазоне [32.. 127], исключая символы
{}[]();.и'".TheModule,TheConstructor,TheType,THE-CONSTANT,ordinary-name
Литерал числа представляет из себя лексему "Число", описанную выше.
Литерал строки представляет из себя лексему "Строка", описанную выше.
Функция может быть задана определением или лямбдой:
(f, g)(x, y) -> g(f(x), f(y))
Есть вариант лямбды с безымянным аргументом, к которому можно обращаться, как к it.
-> it + 4
Содержит определения
{
; foo = 1
; bar = "2"
; { -- 'sin', 'cos' и 'tangent' не просочатся в окружающий объект,
-- в отличие от 'Math.abs' и 'abs-tangent'
; import native.math { sin, cos }
; reexport native.math as Math { abs }
; abs-tangent(x) = Math.abs(tangent(x))
; local tangent(x) = cos(x) / sin(x)
}
; inverse-tangent(x) = Math.abs(1 / tangent(x))
-- 'a' видно в внутреннем объекте, но не видно во внешнем
-- 'b' видно в обоих объектах
-- оба имени не экспортируются
; private
{ ~ a = 1 -- слова private, local и ~ означают одно и то же
; b = 2 + a
}
}
Любое имя можно использовать как оператор.
list map(toString) filter(-> it length () < 4) join(",")
будет воспринято парсером, как
join(",", filter(-> less(4, length(it)), map(toString, list)))
Т.е.,
foo bar(a, b, c) === bar(a, b, c, foo)
Возможно передать более одного списка аргументов, если функция является каррированной.
foo bar(a, b)(c, d) roll(e, f) === roll(e, f, bar(a, b)(c, d, foo))
Выражение может быть
- Литералом
- Вызовом функции
- Условным выражением
if-выражениемif { | a > 2 ? a | not(big(a)) ? 0 | else ? -1 }caseбез захватаcase list { | Cons(x, xs) -> x | Nil -> 0 }caseс захватомcase list as l { | Cons(x, xs) -> l Cons x | Nil -> l }- Лямбда-
caseбез захватаis-empty = case { | Cons? -> True | Nil? -> False } - Лямбда-
caseс захватомdup-head = case as l { | Cons(y, _) -> l Cons y | Nil -> l } - Специальным оператором
foo = some-object with { ; a = 1 ; b -> it + 4 ; c deleted }
Является:
- определением [функции];
- импортом;
- объектом (локальные определения не попадут во внешний объект).
По умолчанию экспортируется. Для подавления экспорта можно использовать слово
private.
Подавляет экпорт следующего за ним объявления или блока объявлений. Может быть заменено на local или ~
private foo = 1
private {
; bar(x) = x * x
; y = 1
; open module('data.list') only 'sort'
}
Имеет вид:
main =
input('Your name?').then (name) ->
log('Hello, ' + name + '!')
или, например
--- Не экспортируется (private).
--- `on(.name, equal)` возвращает функцию
--- `(x, y) -> equal(.name(x), .name(y))`
private on(f, g2)(x, y) =
g2(f(x), f(y))
или, в форме оператора
; a <=> b = if {
| a < b ? Less
| b < a ? Greater
| else ? Equal
}
--- не реэкспортируются
import data.list as List
import data.string as String only {sort; split; join}
--- ИЛИ
import {
; data.list as List
; data.string as String only {
; sort
; split
; join
}
}
--- импортируются и реэкспортируются
; reexport control.monad { when; guard }
Функция возвращает просто своё тело, поэтому никакого смысла писать return нет, что позволит позже объявлять return как метод.
Если необходимо сделать какие-то действия с побочным эффектом, то можно сделать так:
do-stuff (a, b, c) ({log, pure, modify}) =
log(a) then _ ->
modify (state -> a / b + state) then x ->
return (x * c) -- return - это функция!
Да, это выглядит как говно.
Здесь последний аргумент функции - это контекст (ну или монада).
В последствии я планирую ввести синтаксис, чтобы писать так:
do-stuff (a, b, c) = do {log, modify} (
; log (a)
; x <- modify (-> it + a/b)
; return (x * c)
)
Монада, в случае этого языка - это модуль-абстракция над каким-то типом-контейнером, который объявляет:
- Операцию
return(илиpure), которая делает из любого объекта контейнер с одним-единственным этим объектом. - Операцию
join, которая из двух вложенных контейнеров с какими-то объектами делает 1 контейнер. - Контейнер, возвращаемый
return, должен иметь операциюthen, которая цепляет к нему коллбэк, какPromise#then. - Так же у контейнера должна быть функция
map.
Монада позволяет строить последовательные вычисления, которые автоматически будут асинхронными: ввод-вывод, например.
Есть несколько форм:
- Конкретный модуль-монада
Здесь захватывается конкретный toplevel-модуль, и все его методы доступны внутри блока.do module1 ( ; e <- action(args) ; e2 <- action(2, ...args2, e) ... ; return eN ) - Конкретный модуль-монада, отдельные методы
Здесь из конкретного toplevel-модуля захватываются только упомянутые действия.do module1 {action1, action2} ( ; e <- action(args) ; e2 <- action(2, ...args2, e) ... ; return eN ) - Лямбда с захватом конкретных методов
Этот блок аналогичен следующему:do {action1, action2} ( ; e <- action(args) ; e2 <- action(2, ...args2, e) ... ; return eN )
Так же можно использовать(some-module) -> do some-module {action1, action2} ( ; e <- action(args) ; e2 <- action(2, ...args2, e) ... ; return eN )do {...}для захвата всех методов - Вложенный
do-блок
Используется внутри другихdo ( ; e <- action(args) ; e2 <- action(2, ...args2, e) ... ; return eN )do-блоков, повторяет их захват.
То, что в жаваскрипте можно записать как
let qux = (foo, bar) -> {
let x = foo
let y = bar.list
var z = []
return asPromise(fs.readFile, "ctx.ini").then(res => {
for (var i = 0; i < y.length; i++) {
console.log(y[i])
z.push(y[i] + res.length)
}
return z
})
}
здесь будет выглядеть, как
qux (foo, bar) = do (IO)
x = foo
y = bar.list
res <- readFile "ctx.ini"
z <- y for (elem) -> do
log(elem)
return (elem + length(res))
return z
Действие функции for(mapper) (containter) покажу на примере:
res <- [1, 2, 3] for (elem) -> do
print(elem)
return (elem + 1)
эквивалентно
printAndInc (x) = do
print(x)
return (elem + 1)
res <- (
printAndInc(1) then (a) ->
printAndInc(2) then (b) ->
printAndInc(3) map (c) -> [a, b, c]
)
Обе версии печатают 1\n2\n3\n и возвращают [2,3,4].