flang — полный язык поверх FTS Синтаксис по факту: модули, функции, выражения, образцы
0%

Синтаксис по факту: модули, функции, выражения, образцы

Синтаксис по факту: модули, функции, выражения, образцы

Правило этой статьи: показывается только то, что либо взято дословно из файла репозитория, либо написано нами и проверено запуском. Там, где мы пробовали угадать форму и ошиблись, приведён отказ парсера — он информативнее описания.

Файл целиком

Начнём с программы, которую мы написали и прогнали. Она короткая, но задевает большинство конструкций:

модуль «Синтаксис»

объект «Позиция»
  цена является числом
  количество является числом

тотальная функция «Стоимость позиции»
  принимает цена: число, количество: число
  возвращает число
  пример «Три по десять»
    дано цена равно 10
    дано количество равно 3
    ожидается 30
  пусть поз равно запись «Позиция» с цена равным цена и количество равным количество
  поз.цена умножить на поз.количество

тотальная функция «Деление и остаток»
  принимает а: число, б: число
  возвращает число
  пример «Семь и два»
    дано а равно 7
    дано б равно 2
    ожидается 4.5
  (а делить на б) плюс (а остаток от б)

тотальная функция «Скидка»
  принимает сумма: число
  возвращает число
  пример «Десять процентов от тысячи»
    дано сумма равно 1000
    ожидается 100
  10 процентов от сумма
$ node flang/bin/flang.mjs test синтаксис.flang --pretty
{ "valid": true, "total": 3, "passed": 3, "failed": 0, … }

Заголовок модуля

Файл начинается строкой модуль «Имя». Импорты идут с отступом под ней:

модуль «Проба импорта»
  использует «Списки» из "../stdlib/lists.flang"

Это файл flang/examples/import-check.flang целиком (без функции). Путь — в обычных двойных кавычках и разрешается относительно текущего файла. Ключевое слово экспортирует в таблице лексера есть, но ни в одном .flang-файле репозитория не используется; без него виден весь модуль. Подробнее про связывание — глава «Стандартная библиотека».

Комментарии — // до конца строки. Файлы stdlib и core открываются многострочными блоками таких комментариев, в которых объясняется, почему модуль устроен именно так; читать их стоит.

Объявление типов

Записи объявляются словом объект — прямо как в FTS:

объект «Группа»
  ключ является строкой
  слова является список строки

Это из flang/examples/leetcode/049-group-anagrams.flang. Суммы типов — словом тип, с перечислением вариантов:

тип «Число или ничто»
  вариант «Есть число» содержит значение: число
  вариант «Нет числа»

Это flang/stdlib/optional.flang. Вариант либо несёт поля (содержит имя: тип, через запятую), либо не несёт ничего. Имена вариантов обязаны быть уникальны в пределах модуля.

Спецификация показывает варианты без кавычек — вариант Слово содержит текст: строка. Обе формы читаются лексером, но в реальных файлах используется форма с ёлочками, и на то есть причина: см. раздел про ключевые слова ниже.

Функции

тотальная функция «Первый элемент или запасное»
  принимает элементы: список числа, запасное: число
  возвращает число
  пример «Список не пуст»
    дано элементы равно [4, 5]
    дано запасное равно 0
    ожидается 4
  пример «Пустой список даёт запасное»
    дано элементы равно пустой список
    дано запасное равно 42
    ожидается 42
  «Значение или запасное» от («Первый элемент» от элементы) и запасное

Разбираем по строкам:

  • тотальная — необязательная пометка класса (глава «Два класса программ»);
  • принимает — параметры через запятую, каждый в форме имя: тип;
  • возвращает — один тип;
  • пример — блок с дано на каждый параметр и одним ожидается;
  • последнее выражение блока — тело функции.

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

Форма ожидается 4 — короткая. В унаследованных от FTS утилитах встречается полная — ожидается результат равен 2000.

Типы в сигнатурах

Скаляры: число, строка, признак, ничто. Формы склонений тоже читаются — лексер знает числа, числом, числу, строки, строкой, строку, признака, признаком. Отсюда естественное список числа вместо список число.

Списки: список числа, список строки, список «Группа», и даже вложенно — список список строки (из решения про анаграммы).

Именованные типы пишутся в ёлочках: возвращает «Число или ничто».

Унаследованные от FTS имена деньги и дата лексер знает; тайпчекер приравнивает Деньги к числу, а Дата — к строке.

Выражения

Связывание

пусть удвоенное равно «Умножить значение» от («Первый элемент» от элементы) и 2
«Значение или запасное» от удвоенное и запасное

пусть не требует слова в: тело идёт следующей строкой того же уровня.

Ветвление

Блочная форма:

если акк начинается с "!"
  то акк
  иначе
    если "([{" содержит буква
      то соединить акк с буква
      иначе …

И однострочная — из того же файла (flang/examples/leetcode/020-valid-parentheses.flang):

если закрывающая равен "]" то "[" иначе "{"

Отдельной конструкции «иначе если» нет — вложенные если в ветке иначе образуют лестницу. Логических связок тоже нет: ни и, ни или, ни не для признаков. Конъюнкция пишется вложенными если, отрицание — сравнением равен нет. Это заметная шероховатость, и она честно записана в flang/examples/leetcode/index.json в списке недостающего.

Вызов

«Длина» от хвоста
«Значение или запасное» от удвоенное и запасное
«Символы с позиции» от текст и (позиция плюс 1)

Имя функции — всегда в кавычках, аргументы соединяются словом и. Вызвать функцию без аргументов нечем: применение записывается только как «Имя» от ….

Арифметика и сравнения

Инфиксные: плюс, минус, умножить на, делить на, остаток от, процентов от. Последние два стоит запомнить именно как инфиксные — мы попробовали написать остаток от а и б по аналогии с вызовом и получили:

{"code":"FLANG_PARSE","message":"не разобрана конструкция: неожиданное 'остаток от'",
 "span":{"line":24,"column":25}}

Правильно — а остаток от б.

Сравнения унаследованы от FTS: равен, не равен, больше, меньше, не больше, не меньше. Формы равна, равно, равным — то же самое.

Порядок сравнения (больше/меньше) работает только для чисел. На строках получите отказ ещё на check:

{"code":"FLANG_TYPE",
 "message":"левый операнд сравнения «gt» имеет тип строка:
            сравнения порядка допустимы только для чисел"}

Записи и варианты

Конструирование записи и вариантa устроено одинаково — словом с и перечислением полей через и:

запись «Группа» с ключ равным ключ и слова равным (добавить слово к группа.слова)
вариант «Есть число» с значение равным голова

Вариант без полей пишется просто: вариант «Нет числа».

Доступ к полю — через точку: группа.ключ, диагностика.код. Единственная точка в языке, где точка вообще используется.

Записи нельзя записать литералом. Мы попробовали очевидное:

$ … дано поз равно {цена: 10, количество: 3}
{"code":"FLANG_PARSE","message":"не разобрана конструкция: неожиданное '{'"}

В корпусе записи всегда строятся конструктором запись «…» с …, а в примерах не встречаются вовсе.

Сопоставление с образцом

разбор элементов
  случай пусто
    то 0
  случай голова и хвост
    то 1 плюс «Длина» от хвоста

Образцы, которые встречаются в репозитории:

Образец Что связывает
случай пусто пустой список
случай голова и хвост голову и хвост непустого списка
случай вариант «Есть число» с значение как зн вариант и его поле под именем зн
случай «Нет числа» вариант без полей

Обратите внимание на голова и хвост: имена не выбираются, это фиксированная форма из таблицы ключевых слов (headTail: ["голова и хвост", "head and tail"]).

Форма случай вариант «Имя» с поле как имя — явная, с ключевым словом вариант. Именно ей пользуется стандартная библиотека, и не из вкуса. В flang/SPEC.md, раздел 10, описано известное ограничение: лексер решает, что перед ним, до того как становится известен тип, поэтому вариант, названный ключевым словом, в образце не разбирается.

Имя варианта Во что превращается
Да, Нет литерал признака
Плюс, Минус арифметический оператор
Больше, Меньше сравнение

Спецификация тут же признаёт долг: диагностика при этом не называет настоящую причину, и «сообщение обязано говорить „имя варианта совпадает с ключевым словом“, а не „ожидался образец“».

Встроенные формы над коллекциями

Функции не являются значениями, поэтому map/filter/fold принимают не функцию, а тело:

свёртка элементы начиная с [первый] как акк и эл → добавить эл к акк
отобразить группы как группа → группа.слова
отфильтровать элементы где эл → эл не равен значение

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

свёртка группы начиная с нет как акк и группа
  если акк равен да то да иначе группа.ключ равен ключ

Также есть добавить … к … (дописывает в конец) и длина. Приписать в начало встроенной формы нет — flang/stdlib/lists.flang заводит для этого первую функцию модуля.

Строки

длина текст
символ 2 в текст
подстрока акк с 1 по ((длина акк) минус 1)
соединить акк с буква
разделить текст по буква
"([{" содержит буква
акк начинается с "!"
к строке (…)
к числу (…)

Две семантические детали зафиксированы в спецификации и повторяются всеми слоями: индексация с 1, включительно с обоих концов, и длина в кодовых точках, а не в единицах UTF-16. Мы проверили второе:

тотальная функция «Длина текста»
  принимает текст: строка
  возвращает число
  пример «Эмодзи считается одним»
    дано текст равно "мир 🌍"
    ожидается 5
  длина текст

Пример проходит. Обоснование в спецификации прямое: «иначе кириллица и эмодзи считаются неверно».

Ещё одна деталь для тех, кто сверяется с index.json: там записано, что форма символ не проходит check из-за расхождения порядка аргументов между парсером и тайпчекером. На момент нашей проверки это уже неверно — символ 2 в текст проходит check и возвращает "и" от "мир".

Литералы

42, -3, 0.5, "строка", да, нет, ничто, [1, 2, 3], пустой список.

Экспоненциальная запись (1e3) лексером flang читается — регулярное выражение в lexer.mjs её допускает. А вот лексер FTS, переписанный на flang в flang/core/lexer.flang, её не поддерживает: это записано отдельным долгом в flang/core/SPEC.md. Два разных лексера, не перепутайте.

Чего в синтаксисе нет

Коротко, чтобы не искать: нет циклов, нет присваивания, нет объявления переменной вне пусть, нет исключений и try, нет логических операторов, нет лямбд и вообще функций как значений, нет литерала записи, нет вызова без аргументов, нет обобщённых типов. Полный проверенный перечень — глава «Чего в языке пока нет».

Дальше — глава «Система типов», про то, как всё это проверяется.

Нашли неточность? Выделите фрагмент текста — рядом появится жучок.

Нужен разбор именно вашей ситуации?

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

Доска запросов
Дальше