Micro, v1.0, описание
Posted on

Micro — интерпретируемый высокоуровневый язык программирования. Поддерживает структурную и функциональную парадигмы, обладает строгой динамической типизацией и сборкой мусора.
Общее описание
Вызовы функций
Программы на данном языке представляют собой последовательность вызовов функций. Вызов функции записывается следующим образом:
function, {argument}
При этом и function, и argument могут быть либо константами (числовыми или
строковыми), либо точно такими же вызовами функций.
При исполнении сначала вычисляется выражение для самой функции. По результату определяется арность функции. Затем вычисляется нужное число аргументов. И наконец они передаются функции.
Например, имеем следующее выражение:
+ 2 * 5 12
Первой интерпретатор встречает функцию +. Она имеет арность 2. Интерпретатор
начинает вычислять 2 аргумента для неё. Первый равен 2. При вычислении второго
же обнаруживается новая функция — *, которая также имеет арность 2.
Поэтому начинается расчёт 2 аргументов уже для функции *. Это 5 и 12. После
этого интерпретатор может вычислить результат выражения * 5 12 (это 60),
который и будет вторым аргументом для функции +. Затем интерпретатор может
вычислить результат всего выражения (+ 2 60) — 62.
Разбиение на токены
Константы:
- числа —
\d+(?:\.\d+)?, например:23,4.2; - символы —
`(?:\.|[^`])`, например:`t`; - строки —
"(?:\\.|[^"])*", например:"","test","Say \"Hello!\".".
Идентификаторы:
- буквенные —
/[A-Za-z_]+/ - "fn"; - пунктуационные —
/[[:punct:]]+/ - /[();.`"_']/.
Таким образом идентификаторы разных типов и любые константы можно чередовать без разделения их пробелами.
Например, код #sin2.3 будет преобразован в последовательность токенов
['#', 'sin', '2.3'].
Пользовательские функции
Пользователь может объявлять собственные функции:
"fn", [name], "(", {parameter}, ")",
{function call},
";";
name и parameter — любые идентификаторы. Имя функции указывать
необязательно.
Определение функции является одновременно и её вызовом. Таким образом после обработки определения интерпретатор начнёт вычислять аргументы для вызова определённой функции.
Если имя функции указано, то данная функция также доступна для вызова и посредством этого имени. В том числе и внутри себя самой.
Функции можно переопределять.
Результатом выполнения функции является последнее вычисленное внутри неё значение.
Замыкания
Внутри функции доступны все идентификаторы из внешних для функции областей видимости. Если параметры функции имеют имя, совпадающее с одним из этих идентификаторов, то параметр перекрывает этот идентификатор.
Переменные
В качестве переменных используются функции вида fn name() value;. Они не
требуют аргументов и при вызове просто возвращают сохранённое в них значение.
Для упрощения объявления подобных функций имеется встроенная функция
=(name value). Она полностью эквивалентна вышеприведённой записи.
Также имеется встроенная функция :=(name value). Она аналогична функции =,
но объявляет функцию в родительской для текущей функции области видимости.
Для изменения значения переменных используется возможность переопределять функции.
Карирование
Для карирования используется оператор '. Если интерпретатор встречает данный
оператор на месте очередного аргумента функции, то он прекращает вычисление
аргументов, создаёт новую функцию (замыкание), принимающую оставшиеся аргументы
и возвращает её как результат данного вызова функции.
Так выражение + 2' эквивалентно определению функции fn(x) + 2 x;.
Помимо оператора ' подобным эффектом обладает и недостаток токенов в коде
программы. То есть если интерпретатор при вычислении аргументов функции
встречает конец списка токенов, то он делает тоже самое, что и при встрече
оператора '.
Например, функция вида fn add_x(x) + x'; может быть записана короче (без
оператора '): fn add_x(x) + x;.
Исходники
Файлы исходного кода должны быть в кодировке ASCII.
Ключевые слова
1 ключевое слово: fn.
Типы
Типизация: строгая динамическая.
Менеджмент памяти: сборка мусора.
Копирование: по ссылке.
Хранение:
- ссылки — на стеке;
- значения — в куче.
Нулевой тип
Название: nil.
Тип: фиктивный тип; не хранит значение, а служит маркером его отсутствия; существует единственное значение данного типа.
Определение: значение данного типа доступно через вызов функции рантайма
nil.
Логический тип
Название: bool.
Тип: беззнаковый целый, 1 байт; существует два значения данного типа.
Определение:
- ложное логическое значение доступно через вызов функции рантайма
false; - истинное логическое значение доступно через вызов функции рантайма
true.
Соответствие:
- ложному логическому значению соответствует
nil, числа0и0.0и пустой массив; - истинному логическому значению соответствуют любые другие значения.
Целые числа
Название: int.
Тип: знаковые целые, 8 байт.
Определение:
/\d+/
/`(?:\\.|[^`])`/
Вещественные числа
Название: num.
Тип: с плавающей запятой, 8 байт.
Определение: /\d+\.\d+/.
Массивы
Название: list.
Тип: фиксированный массив.
Определение:
- пустого массива — через вызов функции рантайма
$; - массива заданной длины — через вызов функции рантайма
list;
Доступ к элементам:
- чтение элементов — через вызов функции рантайма
get; - изменение элементов — через вызов функции рантайма
set.
Строки
Строками являются массивы, хранящие коды символов. Используется кодировка ASCII.
Определение: /"(?:\\.|[^"])*"/.
Функции
Название: function.
Объявление:
"fn", [identify], "(", {parameter}, ")",
{function call},
";"
"=", function name, function call
":=", function name, function call
function call, "'"
Здесь function name — это function call имеющий результат типа строка.
Перегрузка функций не поддерживается, но поддерживается их переопределение.
Вызов функции: function name, {argument}.
Здесь function name — это полноценный function call.
Операции
| Операция | Описание | Ассоциативность | Тип(ы) операнда(ов) | Тип результата |
|---|---|---|---|---|
' |
создание замыкания | левая | функции | функции |
Переменные
В качестве переменных используются функции вида fn name() value;.
Объявление, определение и присваивание:
- в текущей области видимости —
"=", name, value; - в родительской области видимости —
":=", name, value.
Здесь:
name— этоfunction callимеющий результат типа строка;value— это полноценныйfunction call.
Присваивание осуществляется через переопределение функций.
Управляющие конструкции
Условие
В качестве условного оператора используется функция if:
"if", condition, true value, false value
Здесь:
condition— этоfunction callимеющий результат логического типа;true value— это полноценныйfunction call, его значение возвращается, еслиconditionравенtrue;false value— это полноценныйfunction call, его значение возвращается, еслиconditionравенfalse.
Стоит учесть, что и true value, и false value вычисляются до выбора
значения результата. Поэтому, если их вычисление производит побочные эффекты,
нужно обернуть их в замыкание:
if condition
fn(_)
true value
;'
fn(_)
false value
;'
nil
Цикл
В качестве оператора цикла используется функция while:
"while", condition, body
Здесь:
condition— этоfunction callвозвращающий результат типа функция; эта функция должна быть замыканием, принимающим 1 фиктивный параметр (всегда передаётсяnil) и возвращающим результат логического типа;body— этоfunction callвозвращающий результат типа функция; эта функция должна быть замыканием, принимающим 1 фиктивный параметр (всегда передаётсяnil); данная функция вызывается до тех пор, покаconditionвозвращаетtrue.
Точка входа
Отсутствует. Интерпретатор просто исполняет весь предоставленный код в порядке его следования.
Комментарии
Однострочные: ? /\bnb\b.*\n/ ?.
Многострочные: ? /\bnb:.*\bnb;/ ?.
Модульность
Тип: включением кода как текста и его последующим исполнением.
Для включения кода используются функции require и require_once:
"require", path
"require_once", path
Здесь path — это function call имеющий результат типа строка. Строка
должна содержать относительный путь к файлу с кодом. Этот путь ищется по пути
расположения файла, переданного интерпретатору.
Функции читают указанный файл и исполняют его, после чего возвращают полученный результат.
Обе функции кешируют свой результат. При этом функция require всё равно каждый
раз читает и исполняет файл заново. А функция require_once делает это, только
если для данного файла ещё нет значения в кеше. В противном случае она
возвращает закешированное значение.
Полная грамматика
program = function call list;
function call list = {function call};
function call = function name, argument list;
function name = value;
argument list = {argument};
argument = value;
value =
| constant
| identify
| CLOSURE MARK
| function definition
| function call;
constant =
INTEGER CONSTANT
| FLOAT CONSTANT
| CHARACTER CONSTANT
| STRING CONSTANT;
identify = ALPHABETIC IDENTIFY | PUNCTUATION IDENTIFY;
function definition =
"fn", [identify], "(", parameter list, ")",
function call list,
";";
parameter list = {parameter};
parameter = identify;
CLOSURE MARK = "'";
INTEGER CONSTANT = ? /\d+/ ?;
FLOAT CONSTANT = ? /\d+\.\d+/ ?;
CHARACTER CONSTANT = ? /`(?:\\.|[^`])`/ ?;
STRING CONSTANT = ? /"(?:\\.|[^"])*"/ ?;
ALPHABETIC IDENTIFY = ? /[A-Za-z_]+/ ? - "fn";
PUNCTUATION IDENTIFY = ? /[[:punct:]]+/ ? - ? /[\(\);\.`"_']/ ?;
Рантайм
- модуль
lang:bool ==(any a, any b);bool !=(any a, any b);- модуль
lang.types:nil nil();str type(any value)— возвращает название типа значенияvalue;int arity(function closure)— возвращает арность функции;str to_str(any value);num to_num(str text);
- модуль
lang.ops:any if(bool condition, any true_value, any false_value);any while(function condition_closure, function body_closure);any eval(str code);any require(str path);any require_once(str path);
- модуль
nums:int|num #(int|num)— унарный минус;int|num +(int|num x, int|num y);int|num -(int|num x, int|num y);int|num *(int|num x, int|num y);int|num /(int|num x, int|num y);int %(int x, int y);bool <(int|num int|num);bool <=(int|num int|num);bool >(int|num int|num);bool >=(int|num int|num);- модуль
nums.maths:num floor(int|num number);num ceil(int|num number);int trunc(int|num number);num sin(int|num number);num cos(int|num number);num tn(int|num number);num arcsin(int|num number);num arccos(int|num number);num arctn(int|num number);num arctn2(int|num y, int|num x);num sh(int|num number);num ch(int|num number);num th(int|num number);num sqrt(int|num number);num pow(int|num base, int|num exponent);num exp(int|num number);num ln(int|num number);num lg(int|num number);num abs(int|num number);num rand()— возвращает случайное число в диапазоне [0; 1);
- модуль
bools:bool true();bool false();any &&(any a, any b)— еслиaсоответствуетfalse, возвращаетa, иначеb;any ||(any a, any b)— еслиaсоответствуетtrue, возвращаетa, иначеb;bool !(any a);
- модуль
lists:list $()— создаёт пустой массив;list :(any value, list array)— создаёт массив из головыvalueи хвостаarray;list list(int number, any value)— создаёт массив длинойnumberи заполняет его значениемvalue;list append(list array, any value);list concat(list array_1, list array_2);any get(list array, int index);list set(list array, int index, any value);int len(list array);
- модуль
sys:list args()— содержит список аргументов командной строки, переданый скрипту; первый элемент списка содержит путь к файлу скрипта;nil exit(int code);- модуль
sys.io:int read()— читает 1 символ изstdin;str print(str text).