Четыре поверхности записи
Одну программу flang можно записать четырьмя наборами слов: русским, английским, эсперанто и китайским. Это не четыре языка и не перевод. если, if, se и 如果 — одно и то же слово языка, записанное четырьмя способами, и разбор даёт одно дерево.
Отсюда следует практический вопрос, ради которого стоит читать дальше: на какой из четырёх вам можно писать? Ответ короткий и неудобный, поэтому он стоит здесь, а не в конце.
Писать можно на четырёх. Доказывать — на двух
| русская | английская | эсперанто | китайская | |
|---|---|---|---|---|
| обычная программа | да | да | да | да |
| печать в чужой язык | да | да | да | да\* |
| доказательство и завершаемость | да | да | нет | нет |
\* Китайская программа печатается в python, java, csharp и elixir. Цели c, cpp, go, js, ts и rust её пока отказываются печатать: иероглифические имена у них схлопываются в одно слово, и печать отказывает столкновением имён. Разбор ниже — «Печать и имена».
Второй строкой всё и сказано. Язык продаёт доказуемость; словаря доказательств на эсперанто и китайском нет, и написать требует, обеспечивает, убывает или индукция по на них не на чем. Программа на китайском проверится и запустится; доказать о ней что-нибудь вы не сможете.
Поэтому дерево и написано на двух поверхностях из четырёх, а не на четырёх: эсперанто и китайский существуют в таблице целиком, но пользоваться ими всерьёз пока нельзя — на них в дереве написаны только две демонстрационные программы, и лежат они в docs/examples/surfaces/.
Полная таблица слов — на странице Словарь языка; она печатается из той же единственной таблицы, по которой разбирается любой файл, и пустая клетка в ней значит, что слова нет.
Что именно молчит
Четырнадцать понятий на двух поверхностях — это всё доказательства
Самое важное здесь не число, а то, ЧТО именно молчит. Все четырнадцать понятий, открытых только на русской и английской поверхностях, — словарь доказательств и завершаемости:
| Понятие | русская | английская | эсперанто | китайская |
|---|---|---|---|---|
requires | требует | requires | — | — |
ensures | обеспечивает | ensures | — | — |
claim | утверждаем | claim | — | — |
forall | для всех | for all | — | — |
induction | индукция по | induction on | — | — |
byProperty | по свойству | by property | — | — |
byExample | по примеру | by example | — | — |
byHypothesis | по предположению | by hypothesis | — | — |
qed | следовательно доказано | therefore proved | — | — |
decreases | убывает | decreases | — | — |
follows | следует | follows | — | — |
suchThat | таких что | such that | — | — |
exists | есть такой | there is | — | — |
namely | а именно | namely | — | — |
Причина названа в самой таблице: у этих понятий нет устоявшегося слова ни в эсперанто, ни в китайском, а requires и ensures стоят парой в Eiffel, JML и Dafny. Придуманное слово хуже отсутствующего: сказать, что китайское доказательство пишется наполовину по-английски, — значит соврать дважды.
Печать и имена
Пустая клетка словаря видна в таблице. Печать в чужой язык упирается в другое — в то, как имя функции превращается в идентификатор цели.
Печать режет имя по «словарным символам» и всё прочее считает разделителем. Для знаков препинания это верно: длинное тире в имени в идентификатор попасть не должно. Цели python, java, csharp и elixir словарными считают и буквы всех четырёх поверхностей, поэтому имена печатаются как есть:
| Имя в исходнике | Идентификатор |
|---|---|
«乘积» | 乘积 |
«阶乘» | 阶乘 |
«De unu ĝis kvin» | de_unu_ĝis_kvin |
«Antaŭmeti» | antaŭmeti |
Цели c, cpp, go, js, ts и rust режут имя по латинице и цифрам. У иероглифического имени словарных символов не остаётся вовсе, все имена схлопываются в одно слово, и печать честно отказывает по столкновению, а не печатает разные функции под одним именем:
$ flang emit docs/examples/surfaces/factorial.zh.flang --target js --out …
flang emit: печать отказала — имена «乘积» и «前置» дают один идентификатор «value» — переименуйте одно из них в модели
Дело не в самом языке C: fn_乘积 собирается и работает и там — cc -std=c99 -Wall -Wextra -Werror -pedantic не говорит ни слова.
Отказ остаётся там, где имя действительно не выводится. Знаки препинания — разделители, имя из одного препинания даёт value, а столкновение имён — отказ, а не молчаливое переименование.
Семь понятий на трёх поверхностях
| Понятие | русская | Молчит | Почему |
|---|---|---|---|
module | модуль | эсперанто | modulo в эсперанто значит и модуль, и остаток от деления, а остаток таблица уже отдала resto de. Одно слово не вправе означать две конструкции |
not | не | эсперанто | логическое «не» в эсперанто ne, но ne таблица уже отдала литералу лжи. Файл на эсперанто пишет отрицание функцией «Не так» из stdlib/logic.flang |
opPercent | процентов | китайская | китайский процент — приставка (百分之十), а не слово после числа |
nested | вложен объект | эсперанто | |
embedding | вложение | эсперанто | |
supervision | надзор | эсперанто | |
hash256 | хеш256 | эсперанто |
Одно понятие на одной поверхности
alias — русское это («список чисел» это …). Английского, эсперантского и китайского слова у него нет вовсе: это единственное понятие таблицы, доступное только на русской поверхности.
Счёт целиком
| Открыто на | Понятий | Доля |
|---|---|---|
| всех четырёх поверхностях | 136 | 86,1 % |
| трёх | 7 | 4,4 % |
| двух | 14 | 8,9 % |
| одной | 1 | 0,6 % |
| всего в таблице | 158 |
По поверхностям: русская — 158 понятий из 158, английская — 157, китайская — 142, эсперанто — 137. Всего фраз в таблице 661: у понятия слов бывает несколько (равен, равна, равно).
Одна строка, четыре записи
если н не больше 1 то 1 иначе н умножить на («Факториал» от (н минус 1))
if n is at most 1 then 1 else n times («Factorial» of (n minus 1))
se n ne pli granda ol 1 tiam 1 alie n fojoj («Faktorialo» de (n minus 1))
如果 n 不大于 1 那么 1 否则 n 乘以 («阶乘» 的 (n 减 1))
Это четыре строки из четырёх файлов дерева, а не выдумка страницы: docs/examples/rosetta/factorial.flang, docs/examples/rosetta/factorial-english.flang, docs/examples/surfaces/factorial.eo.flang, docs/examples/surfaces/factorial.zh.flang. Каждый проходит проверку сам по себе — одной и той же командой, меняется только имя файла:
flang check docs/examples/rosetta/factorial.flang
flang check docs/examples/surfaces/factorial.zh.flang
Все четыре отвечают одинаково: «функций 5, из них с доказанным завершением 3; типов 0» и «замечаний нет».
Имена МОДУЛЕЙ у всех четырёх латинские — «Factorial», «Factorial», «Faktorialo», «Factorial zh», — и это правило Р7 (имена в коде): имя модуля становится именем напечатанного файла, а от кириллического имени выходил бы транслит, от иероглифического — пустое слово value; ни то, ни другое не читается. Поверхность выбирается ключевыми словами и именами функций, а не именем модуля: в русском файле стоит «Факториал» от (н минус 1).
Насколько «одно дерево» — правда
Утверждение «разбор даёт одно дерево» стоит проверить, а не принять на слово, — и в сыром виде оно неверно.
Сырые деревья не совпадают. С русским образцом расходятся 74 слота у английской и 76 у эсперанто и китайской (bootstrap/flang run-script surfaces:run):
| Что разошлось | Слотов |
|---|---|
name — имена функций, параметров, примеров, типов | 45 (английская) / 46 |
ключи args в примерах — имена параметров | 11 |
локальные имена (н/n, начало/start, конец/finish, первое/element, элементы/items) | 14 |
acc, item — связки свёртки | 4 |
module — имя модуля | 0 (английская) / 1 |
Все они — имена, которые выбрал автор: «Факториал», «Factorial», «Faktorialo», «阶乘». Имя — не язык. Ни один из этих слотов не разбор: ни kind, ни op, ни данные задачи. 2432902008176640000 во всех четырёх файлах написано одинаково — двадцать факториал не переводится.
Дальше два хода. Сверка: деревья сличаются с точностью до взаимно однозначного переименования — не «похоже», а биекция, отдельным словарём на каждую область видимости; всякий слот вне списка переименовываемых сверяется побуквенно. Прогон даёт: «совпали», связано имён 26 (английская) и 27 (эсперанто, китайская). Канонизация: имена заменяются порядковыми номерами по обходу дерева — и только они (вычеркнуто 24). После канонизации деревья четырёх файлов побайтово не совпадают: прогон отвечает «совпали побайтово: НЕТ». Сверка с биекцией при этом проходит; что именно расходится после канонизации, прогон не называет, и утверждение «совпадают байт в байт» страница не держит, пока этот прогон не позеленеет.
Стоит понимать, чего это НЕ доказывает. Поверхности — одна таблица слов в лексере, и разборщик не знает, каким словом набрано ключевое понятие; совпадение деревьев тут следует из устройства, а не открывается прогоном. Прогон нужен ради второй половины утверждения — что расходятся только имена автора и больше ничего. Вот её и стоит перепроверять.
Как это устроено
Таблица одна: понятие → слова на четырёх поверхностях. Она лежит в разборе языка одной константой SURFACE_TABLE, и из неё печатается всё остальное — плоский список слов, словарь диагностик, страница Словарь языка и числа этой страницы. Второго списка нет, поэтому разъехаться нечему.
Поверхность файла определяется большинством голосов среди ключевых слов, а не первым словом: файл на эсперанто начинается с английского module, потому что эсперантского слова у этого понятия нет, и по первому слову вся диагностика файла говорила бы по-английски.
Смешение поверхностей законно ровно там, где на поверхности слова нет, — и это не поблажка, а прямое следствие честного молчания таблицы.
Перепроверить
bootstrap/flang run-script surfaces:run перемерить
bootstrap/flang run-script surfaces:check сверить числа этой страницы с прогоном
Эта проверка не стоит в CI, и знать об этом важнее, чем читать числа выше. Сборка сайта (node docs/site/build.mjs --check) о поверхностях не знает и остаётся зелёной, когда страница врёт; звать surfaces:check приходится руками.