World Conquest Chronicles

World Conquest Chronicles

Tick-tock, v1.6, описание

Tick-tock — интерпретируемый высокоуровневый язык программирования. Представляет собой конкурентную (по модели акторов) машину Тьюринга.

Общее описание

Структура программы

Программы на данном языке представляют собой список акторов. Каждый актор имеет текущее состояние (по умолчанию — __initialization__) и список всех поддерживаемых состояний. Каждое из состояний характеризуется именем (уникальным в пределах актора) и списком обработчиков сообщений. Обработчик сообщений так же имеет имя (уникальное в пределах состояния) и список команд. Например:

actor Main()
  state __initialization__()
    message __initialize__()
      outln("Hello, world!")
    ;
  ;
;

Кроме прямого описания акторы могут быть созданы во время работы программы по образцу (классу актора).

Работа программы заключается в обмене сообщениями между акторами, переключении их состояний и выполнении соответствующих принятым сообщениям команд.

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

Ключевых команд три:

  • start создаёт актор по указанному классу (как образцу);
  • send отправляет сообщение всем акторам;
  • set переключает состояние у того актора, в котором она указана.

Выполнение скрипта начинается с автоматической рассылки всем акторам сообщения __initialize__.

Гарантии выполнения

Акторы работают независимо друг от друга — конкурентно.

Акторы обрабатывают строго по одному сообщению за раз — обработка принятого сообщения будет обязательно полностью завершена до начала обработки следующего.

Создание актора командой start выполняется синхронно. Таким образом сразу после завершения обработки данной команды созданный актор уже начнёт принимать сообщения.

Отправка сообщения командой send выполняется асинхронно. Таким образом побочные эффекты последующей команды (в том числе другой команды send) могут возникнуть до побочных эффектов предыдующей команды send.

Установка состояния командой set выполняется синхронно. Таким образом следующее сообщение обязательно будет обрабатываться уже одним из обработчиков того состояния, которое будет установлено данной командой. При этом установка параметров состояния будет иметь эффект начиная с обработки только следующего сообщения.

Вычисление аргументов описанных выше команд выполняется синхронно и до обработки самих команд. Таким образом все побочные эффекты этого вычисления возникнут так же до обработки команд.

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

Исходники

Файлы исходного кода должны быть в кодировке UTF-8 и иметь концы строк как в UNIX (LF).

Ключевые слова

10 ключевых слов: actor, class, state, message, let, start, send, set, return, when.

Типы

Типизация: строгая динамическая.

Менеджмент памяти: сборка мусора.

Нулевой тип

Название: nil.

Тип: фиктивный тип; не хранит значение, а служит маркером его отсутствия; существует единственное значение данного типа.

Копирование: по значению.

Хранение: на стеке.

Определение: значение данного типа доступно через вызов функции рантайма nil.

Вещественные числа

Название: num.

Тип: с плавающей запятой, 8 байт.

Копирование: по значению.

Хранение: на стеке.

Определение:

  • целые — /\b((0x[\da-f]+)|(0[0-7]+)|(\d+(e\d+)?)|(\d+e[\+\-]\d+))\b/i;
  • вещественные — /(\.\d+(e[\+\-]\d+)?)\b|\b\d+\.\d*((e[\+\-]\d+)?\b)?/i;
  • символы — /'(\\x[\da-f]{2}|\\.|[^'\n])'/i.

Списки

Название: list.

Тип: LISP-подобный список — или пустой список, или пара из головы и хвоста, где голова может иметь любой тип (и является хранимым элементом), а хвост является аналогичным списком.

Копирование: по ссылке.

Хранение:

  • ссылки — на стеке;
  • значения — в куче.

Определение:

  • пустого списка — через оператор [...] или вызов функции рантайма __empty_list__;
  • списка из головы и хвоста — через оператор : или вызов функции рантайма __cons__;
  • списка из набора элементов — через оператор [...].

Доступ к элементам:

  • к количеству элементов — через вызов функции рантайма size;
  • к голове списка — через вызов функции рантайма head;
  • к хвосту списка — через вызов функции рантайма tail;
  • к элементу по индексу — через оператор ...[...] или вызов функции рантайма __item__.

Строки

Строками являются списки, хранящие коды символов. Используется кодировка UTF-32.

Определение:

  • интерпретируемые:
    • в одинарных кавычках — /'(\\x[\da-f]{2}|\\.|[^'\n])*?'/i;
    • в двойных кавычках — /"(\\x[\da-f]{2}|\\.|[^"\n])*?"/i;
  • сырые — /`[^`]*?`/.

В интерпретируемых преобразуются управляющие последовательности.

Строки в одинарных кавычках допускаются, только если они длиннее 1 символа.

Классы акторов

Название: class.

Тип: фиктивный тип; не хранит значение, а служит образцом для создания акторов; каждое значение данного типа является отдельным подтипом и существует в единственном экземпляре.

Копирование: по ссылке.

Хранение:

  • ссылки — на стеке;
  • значения — в куче.

Определение:

"class", identifier, "(", [identifier, {",", identifier}, [","]], ")",
  state, {state},
";"

Здесь первый identifier — имя класса актора, а также имя переменной, в которой будет хранится значение, соответствующее определяемому подтипу. Остальные identifier — имена параметров класса актора.

Если число установленных аргументов больше числа объявленных параметров, лишние аргументы отбрасываются. Если число установленных аргументов меньше числа объявленных параметров, параметры, которым не хватает аргументов, получают значение nil.

Поддерживается висящая запятая на конце списка параметров.

Логический тип

В качестве значений логического типа используются значения других типов:

  • ложным логическим значением являются:
    • значение nil;
    • число 0;
    • пустой список;
  • истинным логическим значением являются:
    • числа, отличные от 0;
    • непустые списки;
    • любые классы акторов.

Сущности

Обработчики сообщений

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

Объявление:

"message", identifier, "(", [identifier, {",", identifier}, [","]], ")",
  {command},
";"

Здесь первый identifier — имя обрабатываемого сообщения. Остальные identifier — имена параметров обрабатываемого сообщения.

Если число отправленных аргументов больше числа объявленных параметров, лишние аргументы отбрасываются. Если число отправленных аргументов меньше числа объявленных параметров, параметры, которым не хватает аргументов, получают значение nil.

Поддерживается висящая запятая на конце списка параметров.

Состояния акторов

Содержат имя состояния актора, список параметров состояния актора и список обработчиков сообщений, относящихся к этому состоянию.

Объявление:

"state", identifier, "(", [identifier, {",", identifier}, [","]], ")",
  {message},
";"

Здесь первый identifier — имя состояния актора. Остальные identifier — имена параметров состояния актора.

Если число установленных аргументов больше числа объявленных параметров, лишние аргументы отбрасываются. Если число установленных аргументов меньше числа объявленных параметров, параметры, которым не хватает аргументов, получают значение nil.

Поддерживается висящая запятая на конце списка параметров.

Акторы

Представляют собой зелёные (легковесные) потоки. Содержат список поддерживаемых состояний. По умолчанию находятся в состоянии __initialization__.

Объявление:

"actor", identifier, "(", [identifier, {",", identifier}, [","]], ")",
  state, {state},
";"

При объявлении актора неявно определяется соответсвующий ему класс.

Здесь первый identifier — имя актора, имя определяемого класса, а также имя переменной, в которой будет хранится значение, соответствующее определяемому классу. Остальные identifier — имена параметров актора и определяемого класса.

Если число установленных аргументов больше числа объявленных параметров, лишние аргументы отбрасываются. Если число установленных аргументов меньше числа объявленных параметров, параметры, которым не хватает аргументов, получают значение nil.

Поддерживается висящая запятая на конце списка параметров.

Команды

Команда let

Вычисляет выражение, результат вычисления устанавливает указанной переменной. Если такой переменной ещё нет, то она будет объявлена. Изменение значения переменной, равно как и её объявление, имеет эффект только ниже текущей команды. Операция синхронная.

Синтаксис:

"let", identifier, "=", expression

Здесь identifier — имя переменной.

Результатом команды является значение выражения.

Команда start

Создаёт актор по указанному классу (как образцу). Операция синхронная.

Синтаксис:

"start",
  (identifier | "[", expression, "]"),
  "(", [expression, {",", expression}, [","]], ")"

Здесь identifier — имя класса актора. expression — выражение, результат вычисления которого должен представлять собой значение, соответствующее классу акторов. Список expression — список выражений, результаты вычисления которых используются в качестве аргументов класса актора.

Поддерживается висящая запятая на конце списка выражений.

Результатом команды является значение nil.

Команда send

Отправляет всем акторам указанное сообщение с указанными аргументами. Операция асинхронная.

Синтаксис:

"send", identifier, "(", [expression, {",", expression}, [","]], ")"

Здесь identifier — имя сообщения. Список expression — список выражений, результаты вычисления которых используются в качестве аргументов сообщения.

Поддерживается висящая запятая на конце списка выражений.

Результатом команды является значение nil.

Команда set

Устанавливает текущему актору указанное состояние с указанными аргументами. Операция синхронная.

Синтаксис:

"set", identifier, "(", [expression, {",", expression}, [","]], ")"

Здесь identifier — имя состояния. Список expression — список выражений, результаты вычисления которых используются в качестве аргументов состояния.

Поддерживается висящая запятая на конце списка выражений.

Результатом команды является значение nil.

Команда return

Прерывает выполнение команд текущего обработчика сообщений.

Синтаксис:

"return"

Результатом команды является значение nil.

Выражение

Выражение может выступать в роли команды. В этом случае оно просто вычисляется, а результат вычисления отбрасывается. Может использоваться ради побочных эффектов вычисления. Операция синхронная.

Результатом команды является значение выражения.

Операции

Перечислены в порядке убывания приоритета.

Приоритет Операция Описание Ассоциативность Типы Функция
0 (...) вложенное выражение
0 [...] определение списка из набора элементов
0 ...(...) вызов функции
0 when...; условное выражение
1 ...[...] доступ к элементам списка левая список + число __item__
2 - унарный минус правая число __neg__
2 ! логическое отрицание правая любой __not__
3 * умножение левая числа __mul__
3 / деление левая числа __div__
3 % остаток от деления левая числа __mod__
4 + сложение/конкатенация левая числа/списки __add__
4 - вычитание левая числа __sub__
5 < меньше левая nil/числа/списки __lt__
5 <= меньше или равно левая nil/числа/списки __le__
5 > больше левая nil/числа/списки __gt__
5 >= больше или равно левая nil/числа/списки __ge__
6 == равно левая любые __eq__
6 != не равно левая любые __ne__
7 && конъюнкция левая любые
8 || дизъюнкция левая любые
9 : определение списка из головы и хвоста правая любой + список __cons__

Вложенное выражение

Синтаксис:

"(", expression, ")"

Определение списка из набора элементов

Синтаксис:

"[", [expression, {",", expression}, [","]], "]"

Здесь список expression — список выражений, результаты вычисления которых используются в качестве элементов списка.

Поддерживается висящая запятая на конце списка выражений.

Вызов функции

Синтаксис:

identifier, "(", [expression, {",", expression}, [","]], ")"

Здесь identifier — имя функции. Список expression — список выражений, результаты вычисления которых используются в качестве аргументов функции.

Поддерживается висящая запятая на конце списка выражений.

Условное выражение

Синтаксис:

"when",
  {conditional case},
";"

Здесь conditional case — ветка условия.

Синтаксис ветки условия:

"=>", expression,
  {command}

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

Конъюнкция и дизъюнкция

Конъюнкция и дизъюнкция являются ленивыми операциями — они вычисляют правый операнд только при необходимости. При этом результатом данных операций является последний вычисленный операнд.

Точка входа

Интерпретатор асинхронно запускает все предоставленные акторы. После чего отправляет всем сообщение __initialize__.

Комментарии

Определение:

  • однострочные — /\/\/.*/;
  • многострочные — /\/\*.*?\*\//s.

Полная грамматика

program = {definition};

definition =
  actor
  | actor class;
actor =
  "actor", identifier, "(", [identifier, {",", identifier}, [","]], ")",
    state, {state},
  ";";
actor class =
  "class", identifier, "(", [identifier, {",", identifier}, [","]], ")",
    state, {state},
  ";";
state =
  "state", identifier, "(", [identifier, {",", identifier}, [","]], ")",
    {message},
  ";";
message =
  "message", identifier, "(", [identifier, {",", identifier}, [","]], ")",
    {command},
  ";";

command =
  let command
  | start command
  | send command
  | set command
  | return command
  | expression;
let command = "let", identifier, "=", expression;
start command =
  "start", (identifier | "[", expression, "]"),
  "(", [expression, {",", expression}, [","]], ")";
send command = "send", identifier, "(", [expression, {",", expression}, [","]], ")";
set command = "set", identifier, "(", [expression, {",", expression}, [","]], ")";
return command = "return";

expression = list construction;
list construction = disjunction, [":", list construction];
disjunction = conjunction, ["||", disjunction];
conjunction = equality, ["&&", conjunction];
equality = comparison, [("==" | "!="), equality];
comparison = addition, [("<=" | "<" | ">=" | ">"), comparison];
addition = multiplication, [("+" | "-"), addition];
multiplication = unary, [("*" | "/" | "%"), multiplication];
unary = (("-" | "!"), unary) | accessor;

accessor = atom, {list item access};
list item access = "[", expression, "]";

atom =
  number
  | string
  | list definition
  | function call
  | conditional expression
  | identifier
  | ("(", expression, ")");
number = INTEGER NUMBER | FLOATING-POINT NUMBER | SYMBOL;
string =
  SINGLE-QUOTED INTERPRETED STRING
  | DOUBLE-QUOTED INTERPRETED STRING
  | RAW STRING;
list definition = "[", [expression, {",", expression}, [","]], "]";
function call = identifier, "(", [expression, {",", expression}, [","]], ")";
conditional expression = "when", {conditional case}, ";";
conditional case = "=>", expression, {command};
identifier = IDENTIFIER - key words;
key words =
  "actor"
  | "class"
  | "state"
  | "message"
  | "let"
  | "start"
  | "send"
  | "set"
  | "return"
  | "when";

LINE COMMENT = ? /\/\/.*/ ?;
BLOCK COMMENT = ? /\/\*.*?\*\//s ?;
INTEGER NUMBER = ? /\b((0x[\da-f]+)|(0[0-7]+)|(\d+(e\d+)?)|(\d+e[\+\-]\d+))\b/i ?;
FLOATING-POINT NUMBER = ? /(\.\d+(e[\+\-]\d+)?)\b|\b\d+\.\d*((e[\+\-]\d+)?\b)?/i ?;
SYMBOL = ? /'(\\x[\da-f]{2}|\\.|[^'\n])'/i ?;
SINGLE-QUOTED INTERPRETED STRING = ? /'(\\x[\da-f]{2}|\\.|[^'\n])*?'/i ?;
DOUBLE-QUOTED INTERPRETED STRING = ? /"(\\x[\da-f]{2}|\\.|[^"\n])*?"/i ?;
RAW STRING = ? /`[^`]*?`/ ?
IDENTIFIER = ? /[a-z_]\w*/i ?;

Рантайм

  • константы:
    • общие константы:
      • nil: nil — значение нулевого типа;
    • логические константы:
      • false — число 0;
      • true — число 1;
    • математические константы:
      • nan: num;
      • inf: num — положительная бесконечность;
      • pi: num;
      • e: num;
    • константы для работы со списками:
      • __empty_list__: list<any> — пустой список;
  • функции:
    • общие функции:
      • функции для сравнения:
        • __eq__(x: any, y: any): bool — равно;
        • __ne__(x: any, y: any): bool — не равно;
        • __lt__(x: any, y: any): bool — меньше;
        • __le__(x: any, y: any): bool — меньше или равно;
        • __gt__(x: any, y: any): bool — больше;
        • __ge__(x: any, y: any): bool — больше или равно;
      • __add__(x: num|list<any>, y: num|list<any>): num|list<any> — возвращает результат комбинирования переданных аргументов:
        • если оба аргумента имеют тип num, то функция возвращает результат их сложения;
        • если оба аргумента имеют тип list<any>, то функция возвращает результат их конкатенации;
      • type(value: any): str — возвращает имя типа значения value;
    • функции для работы с логическими значениями:
      • __not__(value: any): bool — логическое отрицание;
      • bool(value: any): bool — преобразует значение в логический тип: возвращает строго 0 или 1;
    • функции для работы с числами:
      • __neg__(x: num): num — унарный минус;
      • __sub__(x: num, y: num): num — вычитание;
      • __mul__(x: num, y: num): num — умножение;
      • __div__(x: num, y: num): num — деление;
      • __mod__(x: num, y: num): num — остаток от деления;
      • математические функции:
        • floor(x: num): num;
        • ceil(x: num): num;
        • trunc(x: num): num;
        • round(x: num): num;
        • sin(x: num): num;
        • cos(x: num): num;
        • tn(x: num): num;
        • arcsin(x: num): num;
        • arccos(x: num): num;
        • arctn(x: num): num;
        • angle(x: num, y: num): num — atan2;
        • pow(base: num, exponent: num): num;
        • sqrt(x: num): num;
        • exp(x: num): num;
        • ln(x: num): num;
        • lg(x: num): num;
        • abs(x: num): num;
        • is_nan(x: num): bool;
        • генерация псевдослучайных чисел:
          • seed(seed: num): nil — устанавливает начальное состояние генератора псевдослучайных чисел;
          • random(): num — возвращает псевдослучайное число в диапазоне [0; 1);
    • функции для работы со списками:
      • __cons__(head: any, tail: list<any>): list<any> — конструирует новый список из головы head и хвоста tail;
      • __item__(list: list<any>, index: num): any — возвращает элемент с индексом index из списка list; если индекс выходит за границы списка, будет возвращён nil;
      • size(list: list<any>): num — возвращает размер (длину) списка list;
      • head(list: list<any>): any — возвращает голову списка list; список не должен быть пустым;
      • tail(list: list<any>): list<any> — возвращает хвост списка list; список не должен быть пустым;
    • функции для работы со строками:
      • num(text: str): nil|num — парсит число из строки text; при ошибке парсинга будет возвращён nil;
      • str(value: any): str — преобразует значение value в строку;
      • strb(value: any): str — преобразует значение value в строку, как логическое: если value истинно, возвращает "true", иначе — "false";
      • strs(text: str): str — преобразует строку text в другую строку, экранируя её символы и окружая всю строку кавычками;
      • strl(list: list<str>): str — преобразует список строк list в строку, отображая при этом строки как строки;
    • функции для работы с классами акторов:
      • name(actorClass: class): str — возвращает имя класса акторов actorClass;
    • системные функции:
      • env(name: str): nil|str — возвращает значение переменной окружения name, если она установлена; в противном случае возвращается nil;
      • time(): num — возвращает текущее UNIX-время по UTC в секундах;
      • sleep(duration: num): nil — останавливает выполнение скрипта на duration секунд; duration может быть вещественным числом;
      • exit(exitCode: num): nil — завершает выполнение скрипта; код возврата будет равен exitCode;
      • функции для ввода/вывода:
        • in(count: num): nil|str — считывает count символов из stdin и возвращает их в виде строки; если количество будет отрицательным, то будут считаны все доступные символы; при ошибке чтения будет возвращён nil;
        • inln(count: num): nil|str — считывает count символов из stdin и возвращает их в виде строки; если количество будет отрицательным, то будут считаны все символы до символа '\n'; при ошибке чтения или отсутствии символа '\n' будет возвращён nil;
        • out(text: str): nil — выводит строку text в stdout;
        • outln(text: str): nil — выводит строку text в stdout и переводит строку (добавляет символ '\n');
        • err(text: str): nil — выводит строку text в stderr;
        • errln(text: str): nil — выводит строку text в stderr и переводит строку (добавляет символ '\n').