From 751290d9381be6d0e0f2d17d0f2963f6a6822a90 Mon Sep 17 00:00:00 2001 From: the-homeless-god Date: Sat, 5 Sep 2026 12:01:28 +0000 Subject: [PATCH 1/2] =?UTF-8?q?=D0=9F=D0=BE=D0=B4=D0=BC=D0=BE=D0=B4=D1=83?= =?UTF-8?q?=D0=BB=D1=8C=20=D1=80=D0=B0=D1=81=D0=BA=D0=BB=D0=B0=D0=B4=D0=BA?= =?UTF-8?q?=D0=B8=20=D0=BF=D0=B5=D1=80=D0=B5=D0=B5=D1=85=D0=B0=D0=BB=20?= =?UTF-8?q?=D0=BD=D0=B0=20=D1=81=D1=82=D0=B2=D0=BE=D0=BB:=20=D0=BF=D0=B5?= =?UTF-8?q?=D1=87=D0=B0=D1=82=D1=8C=20=D1=82=D0=B0=20=D0=B6=D0=B5,=20?= =?UTF-8?q?=D0=BA=D1=80=D0=BE=D0=BC=D0=B5=20=D1=84=D0=BE=D1=80=D0=BC=D0=B0?= =?UTF-8?q?=D1=82=D0=B0=20=D0=B2=D0=B5=D0=BB=D0=B8=D1=87=D0=B8=D0=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `ui-flang/flang-tui` стоял на `6bb627cc` — последнем отпечатке, где библиотека была двуязычной. Ствол ушёл на `4117aea`, и уход не рядовой: Go в ней стало 0 строк, `tools/sverka/` и `Makefile` убраны, точка входа теперь `./ярлык`, а ответы эталона переехали в примеры внутри модулей. Что это дало здесь, числами: примеров в модулях 187 → 590 (`make -C ui-flang проверка`, все прошли) ui-flang/out-go 16 842 → 18 053 строки Go в 18 файлах изменился ОДИН файл out-go/format/flang/format.go (+2244 −999) Пять модулей из шести напечатались байт в байт прежними: примеры в код не печатаются, а тела функций у screen, colour, history, tabs и scroll не менялись. Изменился только `format`: на стволе он догнал эталон, переехавший между digitdisk 0.5.0 и 0.8.0 из шести мест `host/internal/{report,ui,sysinfo}` в один двуязычный `host/internal/lang/format.go`, и обзавёлся русской и английской редакциями каждой величины. Печать снята выпущенным `flang 0.7.10` — тем же тегом `FLANG_TAG`, что стоит в `.github/workflows/check.yml`, поэтому `make -C ui-flang сверка` совпадает с деревом, а не краснеет от нумерации временных. Обе реализации раскладки по-прежнему рисуют один и тот же экран: `tools/sverka-ui.sh` — расхождений 0, снимок 21 835 849 байт, кадры 40×24 3073 Б, 80×24 4045 Б, 120×50 5525 Б, 200×50 6416 Б — все до байта те же, что и до переезда. `ui-flang/README.md` обещал читателю прогон `flang-tui/tools/sverka/run.sh` на 494 869 входах. Файла на стволе нет; сказано, чем сверка стала: две сверки кончились (494 467 входов против 0.5.0 и 1 944 268 против 0.8.0, расхождений 0), а стерегут теперь 590 примеров в самих модулях. Там же и в `Makefile` исправлено `make печать` у библиотеки на `./ярлык печать`. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_012A35w4QRRoanYvErJfwTQA --- ui-flang/Makefile | 5 +- ui-flang/README.md | 17 +- ui-flang/flang-tui | 2 +- ui-flang/out-go/format/flang/format.go | 3243 ++++++++++++++++-------- 4 files changed, 2261 insertions(+), 1006 deletions(-) diff --git a/ui-flang/Makefile b/ui-flang/Makefile index 1052130..d9d61a6 100644 --- a/ui-flang/Makefile +++ b/ui-flang/Makefile @@ -17,8 +17,9 @@ # У всех целей печати имя модуля Go одно — «flangprogram», — а библиотек здесь # шесть, и в одной сборке они столкнулись бы именами. Поэтому после печати имя # переписывается на «flang<имя>». Это ЕДИНСТВЕННАЯ правка напечатанного, и -# делает её `sed`, а не рука; ровно то же делает `make печать` у самой -# библиотеки. +# делает её `sed`, а не рука; ровно то же делает `./ярлык печать` у самой +# библиотеки — `Makefile` у неё убран вместе с последним Go, точка входа теперь +# `./ярлык`. # # ── КАК ОБНОВИТЬ БИБЛИОТЕКУ ───────────────────────────────────────────────── # git -C ui-flang/flang-tui fetch && git -C ui-flang/flang-tui checkout <отпечаток> diff --git a/ui-flang/README.md b/ui-flang/README.md index 15cee8c..3040edc 100644 --- a/ui-flang/README.md +++ b/ui-flang/README.md @@ -31,9 +31,17 @@ git submodule status ui-flang/flang-tui # адрес и ТОЧНЫЙ отпе git -C ui-flang/flang-tui log # чей коммит принёс каждую строку ``` -Библиотека при этом живёт своей жизнью: её примеры, её доказательства -завершения и её сверка с эталоном (`flang-tui/tools/sverka/run.sh`, 494 869 -входов) прогоняются у неё, а не переезжают сюда вместе с кодом. +Библиотека при этом живёт своей жизнью: её примеры и её доказательства +завершения прогоняются у неё, а не переезжают сюда вместе с кодом. **Сверщика +с эталоном у неё больше нет**, и это не потеря, а итог: `flang-tui/tools/sverka/` +был последним рукописным Go в её дереве, работу свою сделал (две сверки — +494 467 входов против digitdisk 0.5.0 и 1 944 268 против 0.8.0, расхождений 0) +и убран вместе с `Makefile`. То, что он стерёг, теперь лежит **примерами внутри +самих модулей** — их 590, и они гоняются на каждый толчок: + +```bash +FLANG=<путь> make -C ui-flang проверка # те же 590 примеров, здешним прогоном +``` ### `out-go/` — это НЕ исходник @@ -57,7 +65,8 @@ ui-flang печать` начинается с `rm -rf out-go` и сотрёт **Единственная правка напечатанного, и её делает `sed`.** У всех целей печати имя модуля Go одно — `flangprogram`, — а библиотек шесть, и в одной сборке они столкнулись бы именами. Поэтому после печати имя переписывается на -`flang<имя>`. То же самое делает `make печать` у самой библиотеки. +`flang<имя>`. То же самое делает `./ярлык печать` у самой библиотеки — `make` +у неё больше нет, точка входа теперь `./ярлык`. ## Признак сборки `flangui` diff --git a/ui-flang/flang-tui b/ui-flang/flang-tui index 6bb627c..4117aea 160000 --- a/ui-flang/flang-tui +++ b/ui-flang/flang-tui @@ -1 +1 @@ -Subproject commit 6bb627cc45966bfc24cc2680e1eec2196fb2a43d +Subproject commit 4117aea5494604fe45682ac8e7181fc6e6398c8e diff --git a/ui-flang/out-go/format/flang/format.go b/ui-flang/out-go/format/flang/format.go index 967d034..ca23a1d 100644 --- a/ui-flang/out-go/format/flang/format.go +++ b/ui-flang/out-go/format/flang/format.go @@ -594,15 +594,13 @@ func OkruglitKChyotnomu(ctx *rt.Ctx, masshtab rt.Value, pogreshnost rt.Value) (r return t164, nil } -// Drobyu — функция flang «Дробью». +// ZnakMinus — функция flang «Знак минус». // // Тотальная: завершение доказано анализом завершаемости (totality.mjs). // // Параметр znachenie — «значение»: число. -// Параметр znakov — «знаков»: «нат». -// Параметр razdelitel — «разделитель»: строка. -// Результат — значение: строка. -func Drobyu(ctx *rt.Ctx, znachenie rt.Value, znakov rt.Value, razdelitel rt.Value) (rt.Value, error) { +// Результат — значение. +func ZnakMinus(ctx *rt.Ctx, znachenie rt.Value) (rt.Value, error) { t178, e179 := rt.Lt(ctx, znachenie, rt.Number(0.0)) if e179 != nil { return rt.Value{}, e179 @@ -612,19 +610,51 @@ func Drobyu(ctx *rt.Ctx, znachenie rt.Value, znakov rt.Value, razdelitel rt.Valu return rt.Value{}, e181 } if t180 { - t182, e183 := rt.Sub(ctx, rt.Number(0.0), znachenie) + return rt.Flag(true), nil + } else { + t182, e183 := rt.Div(ctx, rt.Number(1.0), znachenie) if e183 != nil { return rt.Value{}, e183 } - t184, e185 := DrobyuNeotricatelnogo(ctx, t182, znakov, razdelitel) + t184, e185 := rt.Lt(ctx, t182, rt.Number(0.0)) if e185 != nil { return rt.Value{}, e185 } - t186, e187 := rt.Concat(ctx, rt.Text("-"), t184) - if e187 != nil { - return rt.Value{}, e187 + return t184, nil + } +} + +// Drobyu — функция flang «Дробью». +// +// Тотальная: завершение доказано анализом завершаемости (totality.mjs). +// +// Параметр znachenie — «значение»: число. +// Параметр znakov — «знаков»: «нат». +// Параметр razdelitel — «разделитель»: строка. +// Результат — значение: строка. +func Drobyu(ctx *rt.Ctx, znachenie rt.Value, znakov rt.Value, razdelitel rt.Value) (rt.Value, error) { + t186, e187 := ZnakMinus(ctx, znachenie) + if e187 != nil { + return rt.Value{}, e187 + } + t188, e189 := rt.Cond(ctx, t186) + if e189 != nil { + return rt.Value{}, e189 + } + if t188 { + t190, e191 := rt.Sub(ctx, rt.Number(0.0), znachenie) + if e191 != nil { + return rt.Value{}, e191 + } + t192, e193 := DrobyuNeotricatelnogo(ctx, t190, znakov, razdelitel) + if e193 != nil { + return rt.Value{}, e193 } - return t186, nil + t194, e195 := rt.Concat(ctx, rt.Text("-"), t192) + if e195 != nil { + return rt.Value{}, e195 + } + return t194, nil } else { return DrobyuNeotricatelnogo(ctx, znachenie, znakov, razdelitel) } @@ -639,81 +669,81 @@ func Drobyu(ctx *rt.Ctx, znachenie rt.Value, znakov rt.Value, razdelitel rt.Valu // Параметр razdelitel — «разделитель»: строка. // Результат — значение: строка. func DrobyuNeotricatelnogo(ctx *rt.Ctx, znachenie rt.Value, znakov rt.Value, razdelitel rt.Value) (rt.Value, error) { - t188, e189 := DesyatVStepeni(ctx, znakov) - if e189 != nil { - return rt.Value{}, e189 - } - // пусть «степень» - stepen := t188 - t190, e191 := rt.Mul(ctx, znachenie, stepen) - if e191 != nil { - return rt.Value{}, e191 - } - t192, e193 := PogreshnostProizvedeniya(ctx, znachenie, stepen) - if e193 != nil { - return rt.Value{}, e193 - } - t194, e195 := OkruglitKChyotnomu(ctx, t190, t192) - if e195 != nil { - return rt.Value{}, e195 - } - // пусть «целое» - celoe := t194 - t196, e197 := DelenieNacelo(ctx, celoe, stepen) + t196, e197 := DesyatVStepeni(ctx, znakov) if e197 != nil { return rt.Value{}, e197 } - // пусть «целых» - celyh := t196 - t198, e199 := rt.Mul(ctx, celyh, stepen) + // пусть «степень» + stepen := t196 + t198, e199 := rt.Mul(ctx, znachenie, stepen) if e199 != nil { return rt.Value{}, e199 } - t200, e201 := rt.Sub(ctx, celoe, t198) + t200, e201 := PogreshnostProizvedeniya(ctx, znachenie, stepen) if e201 != nil { return rt.Value{}, e201 } - // пусть «остаток» - ostatok := t200 - t202, e203 := rt.Lte(ctx, znakov, rt.Number(0.0)) + t202, e203 := OkruglitKChyotnomu(ctx, t198, t200) if e203 != nil { return rt.Value{}, e203 } - t204, e205 := rt.Cond(ctx, t202) + // пусть «целое» + celoe := t202 + t204, e205 := DelenieNacelo(ctx, celoe, stepen) if e205 != nil { return rt.Value{}, e205 } - if t204 { + // пусть «целых» + celyh := t204 + t206, e207 := rt.Mul(ctx, celyh, stepen) + if e207 != nil { + return rt.Value{}, e207 + } + t208, e209 := rt.Sub(ctx, celoe, t206) + if e209 != nil { + return rt.Value{}, e209 + } + // пусть «остаток» + ostatok := t208 + t210, e211 := rt.Lte(ctx, znakov, rt.Number(0.0)) + if e211 != nil { + return rt.Value{}, e211 + } + t212, e213 := rt.Cond(ctx, t210) + if e213 != nil { + return rt.Value{}, e213 + } + if t212 { // «к строке» - t206, e207 := rt.BToString(ctx, celyh) - if e207 != nil { - return rt.Value{}, e207 + t214, e215 := rt.BToString(ctx, celyh) + if e215 != nil { + return rt.Value{}, e215 } - return t206, nil + return t214, nil } else { // «к строке» - t208, e209 := rt.BToString(ctx, celyh) - if e209 != nil { - return rt.Value{}, e209 + t216, e217 := rt.BToString(ctx, celyh) + if e217 != nil { + return rt.Value{}, e217 } - t210, e211 := rt.Concat(ctx, t208, razdelitel) - if e211 != nil { - return rt.Value{}, e211 + t218, e219 := rt.Concat(ctx, t216, razdelitel) + if e219 != nil { + return rt.Value{}, e219 } // «к строке» - t212, e213 := rt.BToString(ctx, ostatok) - if e213 != nil { - return rt.Value{}, e213 + t220, e221 := rt.BToString(ctx, ostatok) + if e221 != nil { + return rt.Value{}, e221 } - t214, e215 := SlevaNulyami(ctx, t212, znakov) - if e215 != nil { - return rt.Value{}, e215 + t222, e223 := SlevaNulyami(ctx, t220, znakov) + if e223 != nil { + return rt.Value{}, e223 } - t216, e217 := rt.Concat(ctx, t210, t214) - if e217 != nil { - return rt.Value{}, e217 + t224, e225 := rt.Concat(ctx, t218, t222) + if e225 != nil { + return rt.Value{}, e225 } - return t216, nil + return t224, nil } } @@ -731,43 +761,43 @@ func StupenBayt(ctx *rt.Ctx, vitki rt.Value, zapas rt.Value) (rt.Value, error) { return rt.Value{}, err } defer ctx.Leave() - t218, e219 := rt.Lte(ctx, zapas, rt.Number(0.0)) - if e219 != nil { - return rt.Value{}, e219 + t226, e227 := rt.Lte(ctx, zapas, rt.Number(0.0)) + if e227 != nil { + return rt.Value{}, e227 } - t220, e221 := rt.Cond(ctx, t218) - if e221 != nil { - return rt.Value{}, e221 + t228, e229 := rt.Cond(ctx, t226) + if e229 != nil { + return rt.Value{}, e229 } - if t220 { + if t228 { return rt.Number(0.0), nil } else { - t222, e223 := rt.Gte(ctx, vitki, rt.Number(1024.0)) - if e223 != nil { - return rt.Value{}, e223 - } - t224, e225 := rt.Cond(ctx, t222) - if e225 != nil { - return rt.Value{}, e225 - } - if t224 { - t226, e227 := DelenieNacelo(ctx, vitki, rt.Number(1024.0)) - if e227 != nil { - return rt.Value{}, e227 + t230, e231 := rt.Gte(ctx, vitki, rt.Number(1024.0)) + if e231 != nil { + return rt.Value{}, e231 + } + t232, e233 := rt.Cond(ctx, t230) + if e233 != nil { + return rt.Value{}, e233 + } + if t232 { + t234, e235 := DelenieNacelo(ctx, vitki, rt.Number(1024.0)) + if e235 != nil { + return rt.Value{}, e235 } - t228, e229 := rt.Sub(ctx, zapas, rt.Number(1.0)) - if e229 != nil { - return rt.Value{}, e229 + t236, e237 := rt.Sub(ctx, zapas, rt.Number(1.0)) + if e237 != nil { + return rt.Value{}, e237 } - t230, e231 := StupenBayt(ctx, t226, t228) - if e231 != nil { - return rt.Value{}, e231 + t238, e239 := StupenBayt(ctx, t234, t236) + if e239 != nil { + return rt.Value{}, e239 } - t232, e233 := rt.Add(ctx, rt.Number(1.0), t230) - if e233 != nil { - return rt.Value{}, e233 + t240, e241 := rt.Add(ctx, rt.Number(1.0), t238) + if e241 != nil { + return rt.Value{}, e241 } - return t232, nil + return t240, nil } else { return rt.Number(0.0), nil } @@ -788,43 +818,43 @@ func DelitelBayt(ctx *rt.Ctx, vitki rt.Value, zapas rt.Value) (rt.Value, error) return rt.Value{}, err } defer ctx.Leave() - t234, e235 := rt.Lte(ctx, zapas, rt.Number(0.0)) - if e235 != nil { - return rt.Value{}, e235 + t242, e243 := rt.Lte(ctx, zapas, rt.Number(0.0)) + if e243 != nil { + return rt.Value{}, e243 } - t236, e237 := rt.Cond(ctx, t234) - if e237 != nil { - return rt.Value{}, e237 + t244, e245 := rt.Cond(ctx, t242) + if e245 != nil { + return rt.Value{}, e245 } - if t236 { + if t244 { return rt.Number(1024.0), nil } else { - t238, e239 := rt.Gte(ctx, vitki, rt.Number(1024.0)) - if e239 != nil { - return rt.Value{}, e239 - } - t240, e241 := rt.Cond(ctx, t238) - if e241 != nil { - return rt.Value{}, e241 - } - if t240 { - t242, e243 := DelenieNacelo(ctx, vitki, rt.Number(1024.0)) - if e243 != nil { - return rt.Value{}, e243 + t246, e247 := rt.Gte(ctx, vitki, rt.Number(1024.0)) + if e247 != nil { + return rt.Value{}, e247 + } + t248, e249 := rt.Cond(ctx, t246) + if e249 != nil { + return rt.Value{}, e249 + } + if t248 { + t250, e251 := DelenieNacelo(ctx, vitki, rt.Number(1024.0)) + if e251 != nil { + return rt.Value{}, e251 } - t244, e245 := rt.Sub(ctx, zapas, rt.Number(1.0)) - if e245 != nil { - return rt.Value{}, e245 + t252, e253 := rt.Sub(ctx, zapas, rt.Number(1.0)) + if e253 != nil { + return rt.Value{}, e253 } - t246, e247 := DelitelBayt(ctx, t242, t244) - if e247 != nil { - return rt.Value{}, e247 + t254, e255 := DelitelBayt(ctx, t250, t252) + if e255 != nil { + return rt.Value{}, e255 } - t248, e249 := rt.Mul(ctx, rt.Number(1024.0), t246) - if e249 != nil { - return rt.Value{}, e249 + t256, e257 := rt.Mul(ctx, rt.Number(1024.0), t254) + if e257 != nil { + return rt.Value{}, e257 } - return t248, nil + return t256, nil } else { return rt.Number(1024.0), nil } @@ -838,44 +868,44 @@ func DelitelBayt(ctx *rt.Ctx, vitki rt.Value, zapas rt.Value) (rt.Value, error) // Параметр stupen — «ступень»: число. // Результат — значение: строка. func EdinicaBayt(ctx *rt.Ctx, stupen rt.Value) (rt.Value, error) { - t250, e251 := rt.Lte(ctx, stupen, rt.Number(0.0)) - if e251 != nil { - return rt.Value{}, e251 + t258, e259 := rt.Lte(ctx, stupen, rt.Number(0.0)) + if e259 != nil { + return rt.Value{}, e259 } - t252, e253 := rt.Cond(ctx, t250) - if e253 != nil { - return rt.Value{}, e253 + t260, e261 := rt.Cond(ctx, t258) + if e261 != nil { + return rt.Value{}, e261 } - if t252 { + if t260 { return rt.Text("КиБ"), nil } else { - t254, e255 := rt.Gte(ctx, stupen, rt.Number(4.0)) - if e255 != nil { - return rt.Value{}, e255 + t262, e263 := rt.Gte(ctx, stupen, rt.Number(4.0)) + if e263 != nil { + return rt.Value{}, e263 } - t256, e257 := rt.Cond(ctx, t254) - if e257 != nil { - return rt.Value{}, e257 + t264, e265 := rt.Cond(ctx, t262) + if e265 != nil { + return rt.Value{}, e265 } - if t256 { + if t264 { return rt.Text("ПиБ"), nil } else { - t258, e259 := rt.Add(ctx, stupen, rt.Number(1.0)) - if e259 != nil { - return rt.Value{}, e259 - } - t260 := make([]rt.Value, 5) - t260[0] = rt.Text("КиБ") - t260[1] = rt.Text("МиБ") - t260[2] = rt.Text("ГиБ") - t260[3] = rt.Text("ТиБ") - t260[4] = rt.Text("ПиБ") + t266, e267 := rt.Add(ctx, stupen, rt.Number(1.0)) + if e267 != nil { + return rt.Value{}, e267 + } + t268 := make([]rt.Value, 5) + t268[0] = rt.Text("КиБ") + t268[1] = rt.Text("МиБ") + t268[2] = rt.Text("ГиБ") + t268[3] = rt.Text("ТиБ") + t268[4] = rt.Text("ПиБ") // «элемент» - t261, e262 := rt.BElement(ctx, t258, rt.List(t260)) - if e262 != nil { - return rt.Value{}, e262 + t269, e270 := rt.BElement(ctx, t266, rt.List(t268)) + if e270 != nil { + return rt.Value{}, e270 } - return t261, nil + return t269, nil } } } @@ -888,110 +918,7 @@ func EdinicaBayt(ctx *rt.Ctx, stupen rt.Value) (rt.Value, error) { // Параметр razdelitel — «разделитель»: строка. // Результат — значение: строка. func BaytyZnakom(ctx *rt.Ctx, skolko rt.Value, razdelitel rt.Value) (rt.Value, error) { - t263, e264 := rt.Lt(ctx, skolko, rt.Number(0.0)) - if e264 != nil { - return rt.Value{}, e264 - } - t265, e266 := rt.Cond(ctx, t263) - if e266 != nil { - return rt.Value{}, e266 - } - var t267 rt.Value - if t265 { - t267 = rt.Text("-") - } else { - t267 = rt.Text("") - } - // пусть «знак» - znak := t267 - t268, e269 := rt.Lt(ctx, skolko, rt.Number(0.0)) - if e269 != nil { - return rt.Value{}, e269 - } - t270, e271 := rt.Cond(ctx, t268) - if e271 != nil { - return rt.Value{}, e271 - } - var t272 rt.Value - if t270 { - t273, e274 := rt.Sub(ctx, rt.Number(0.0), skolko) - if e274 != nil { - return rt.Value{}, e274 - } - t272 = t273 - } else { - t272 = skolko - } - t275, e276 := CeloeKNulyu(ctx, t272) - if e276 != nil { - return rt.Value{}, e276 - } - // пусть «величина» - velichina := t275 - t277, e278 := rt.Lt(ctx, velichina, rt.Number(1024.0)) - if e278 != nil { - return rt.Value{}, e278 - } - t279, e280 := rt.Cond(ctx, t277) - if e280 != nil { - return rt.Value{}, e280 - } - if t279 { - // «к строке» - t281, e282 := rt.BToString(ctx, velichina) - if e282 != nil { - return rt.Value{}, e282 - } - t283, e284 := rt.Concat(ctx, znak, t281) - if e284 != nil { - return rt.Value{}, e284 - } - t285, e286 := rt.Concat(ctx, t283, rt.Text(" Б")) - if e286 != nil { - return rt.Value{}, e286 - } - return t285, nil - } else { - t287, e288 := DelenieNacelo(ctx, velichina, rt.Number(1024.0)) - if e288 != nil { - return rt.Value{}, e288 - } - // пусть «витки» - vitki := t287 - t289, e290 := DelitelBayt(ctx, vitki, rt.Number(4.0)) - if e290 != nil { - return rt.Value{}, e290 - } - t291, e292 := rt.Div(ctx, velichina, t289) - if e292 != nil { - return rt.Value{}, e292 - } - t293, e294 := Drobyu(ctx, t291, rt.Number(1.0), razdelitel) - if e294 != nil { - return rt.Value{}, e294 - } - t295, e296 := rt.Concat(ctx, znak, t293) - if e296 != nil { - return rt.Value{}, e296 - } - t297, e298 := rt.Concat(ctx, t295, rt.Text(" ")) - if e298 != nil { - return rt.Value{}, e298 - } - t299, e300 := StupenBayt(ctx, vitki, rt.Number(4.0)) - if e300 != nil { - return rt.Value{}, e300 - } - t301, e302 := EdinicaBayt(ctx, t299) - if e302 != nil { - return rt.Value{}, e302 - } - t303, e304 := rt.Concat(ctx, t297, t301) - if e304 != nil { - return rt.Value{}, e304 - } - return t303, nil - } + return BaytyYazykom(ctx, skolko, razdelitel, rt.Text("ru")) } // Bayty — функция flang «Байты». @@ -1018,43 +945,43 @@ func StupenTysyach(ctx *rt.Ctx, vitki rt.Value, zapas rt.Value) (rt.Value, error return rt.Value{}, err } defer ctx.Leave() - t305, e306 := rt.Lte(ctx, zapas, rt.Number(0.0)) - if e306 != nil { - return rt.Value{}, e306 + t271, e272 := rt.Lte(ctx, zapas, rt.Number(0.0)) + if e272 != nil { + return rt.Value{}, e272 } - t307, e308 := rt.Cond(ctx, t305) - if e308 != nil { - return rt.Value{}, e308 + t273, e274 := rt.Cond(ctx, t271) + if e274 != nil { + return rt.Value{}, e274 } - if t307 { + if t273 { return rt.Number(0.0), nil } else { - t309, e310 := rt.Gte(ctx, vitki, rt.Number(1000.0)) - if e310 != nil { - return rt.Value{}, e310 - } - t311, e312 := rt.Cond(ctx, t309) - if e312 != nil { - return rt.Value{}, e312 - } - if t311 { - t313, e314 := DelenieNacelo(ctx, vitki, rt.Number(1000.0)) - if e314 != nil { - return rt.Value{}, e314 + t275, e276 := rt.Gte(ctx, vitki, rt.Number(1000.0)) + if e276 != nil { + return rt.Value{}, e276 + } + t277, e278 := rt.Cond(ctx, t275) + if e278 != nil { + return rt.Value{}, e278 + } + if t277 { + t279, e280 := DelenieNacelo(ctx, vitki, rt.Number(1000.0)) + if e280 != nil { + return rt.Value{}, e280 } - t315, e316 := rt.Sub(ctx, zapas, rt.Number(1.0)) - if e316 != nil { - return rt.Value{}, e316 + t281, e282 := rt.Sub(ctx, zapas, rt.Number(1.0)) + if e282 != nil { + return rt.Value{}, e282 } - t317, e318 := StupenTysyach(ctx, t313, t315) - if e318 != nil { - return rt.Value{}, e318 + t283, e284 := StupenTysyach(ctx, t279, t281) + if e284 != nil { + return rt.Value{}, e284 } - t319, e320 := rt.Add(ctx, rt.Number(1.0), t317) - if e320 != nil { - return rt.Value{}, e320 + t285, e286 := rt.Add(ctx, rt.Number(1.0), t283) + if e286 != nil { + return rt.Value{}, e286 } - return t319, nil + return t285, nil } else { return rt.Number(0.0), nil } @@ -1075,43 +1002,43 @@ func DelitelTysyach(ctx *rt.Ctx, vitki rt.Value, zapas rt.Value) (rt.Value, erro return rt.Value{}, err } defer ctx.Leave() - t321, e322 := rt.Lte(ctx, zapas, rt.Number(0.0)) - if e322 != nil { - return rt.Value{}, e322 + t287, e288 := rt.Lte(ctx, zapas, rt.Number(0.0)) + if e288 != nil { + return rt.Value{}, e288 } - t323, e324 := rt.Cond(ctx, t321) - if e324 != nil { - return rt.Value{}, e324 + t289, e290 := rt.Cond(ctx, t287) + if e290 != nil { + return rt.Value{}, e290 } - if t323 { + if t289 { return rt.Number(1000.0), nil } else { - t325, e326 := rt.Gte(ctx, vitki, rt.Number(1000.0)) - if e326 != nil { - return rt.Value{}, e326 + t291, e292 := rt.Gte(ctx, vitki, rt.Number(1000.0)) + if e292 != nil { + return rt.Value{}, e292 } - t327, e328 := rt.Cond(ctx, t325) - if e328 != nil { - return rt.Value{}, e328 + t293, e294 := rt.Cond(ctx, t291) + if e294 != nil { + return rt.Value{}, e294 } - if t327 { - t329, e330 := DelenieNacelo(ctx, vitki, rt.Number(1000.0)) - if e330 != nil { - return rt.Value{}, e330 + if t293 { + t295, e296 := DelenieNacelo(ctx, vitki, rt.Number(1000.0)) + if e296 != nil { + return rt.Value{}, e296 } - t331, e332 := rt.Sub(ctx, zapas, rt.Number(1.0)) - if e332 != nil { - return rt.Value{}, e332 + t297, e298 := rt.Sub(ctx, zapas, rt.Number(1.0)) + if e298 != nil { + return rt.Value{}, e298 } - t333, e334 := DelitelTysyach(ctx, t329, t331) - if e334 != nil { - return rt.Value{}, e334 + t299, e300 := DelitelTysyach(ctx, t295, t297) + if e300 != nil { + return rt.Value{}, e300 } - t335, e336 := rt.Mul(ctx, rt.Number(1000.0), t333) - if e336 != nil { - return rt.Value{}, e336 + t301, e302 := rt.Mul(ctx, rt.Number(1000.0), t299) + if e302 != nil { + return rt.Value{}, e302 } - return t335, nil + return t301, nil } else { return rt.Number(1000.0), nil } @@ -1125,44 +1052,44 @@ func DelitelTysyach(ctx *rt.Ctx, vitki rt.Value, zapas rt.Value) (rt.Value, erro // Параметр stupen — «ступень»: число. // Результат — значение: строка. func EdinicaTysyach(ctx *rt.Ctx, stupen rt.Value) (rt.Value, error) { - t337, e338 := rt.Lte(ctx, stupen, rt.Number(0.0)) - if e338 != nil { - return rt.Value{}, e338 + t303, e304 := rt.Lte(ctx, stupen, rt.Number(0.0)) + if e304 != nil { + return rt.Value{}, e304 } - t339, e340 := rt.Cond(ctx, t337) - if e340 != nil { - return rt.Value{}, e340 + t305, e306 := rt.Cond(ctx, t303) + if e306 != nil { + return rt.Value{}, e306 } - if t339 { + if t305 { return rt.Text("КБ"), nil } else { - t341, e342 := rt.Gte(ctx, stupen, rt.Number(4.0)) - if e342 != nil { - return rt.Value{}, e342 + t307, e308 := rt.Gte(ctx, stupen, rt.Number(4.0)) + if e308 != nil { + return rt.Value{}, e308 } - t343, e344 := rt.Cond(ctx, t341) - if e344 != nil { - return rt.Value{}, e344 + t309, e310 := rt.Cond(ctx, t307) + if e310 != nil { + return rt.Value{}, e310 } - if t343 { + if t309 { return rt.Text("ПБ"), nil } else { - t345, e346 := rt.Add(ctx, stupen, rt.Number(1.0)) - if e346 != nil { - return rt.Value{}, e346 - } - t347 := make([]rt.Value, 5) - t347[0] = rt.Text("КБ") - t347[1] = rt.Text("МБ") - t347[2] = rt.Text("ГБ") - t347[3] = rt.Text("ТБ") - t347[4] = rt.Text("ПБ") + t311, e312 := rt.Add(ctx, stupen, rt.Number(1.0)) + if e312 != nil { + return rt.Value{}, e312 + } + t313 := make([]rt.Value, 5) + t313[0] = rt.Text("КБ") + t313[1] = rt.Text("МБ") + t313[2] = rt.Text("ГБ") + t313[3] = rt.Text("ТБ") + t313[4] = rt.Text("ПБ") // «элемент» - t348, e349 := rt.BElement(ctx, t345, rt.List(t347)) - if e349 != nil { - return rt.Value{}, e349 + t314, e315 := rt.BElement(ctx, t311, rt.List(t313)) + if e315 != nil { + return rt.Value{}, e315 } - return t348, nil + return t314, nil } } } @@ -1175,109 +1102,109 @@ func EdinicaTysyach(ctx *rt.Ctx, stupen rt.Value) (rt.Value, error) { // Параметр razdelitel — «разделитель»: строка. // Результат — значение: строка. func BaytyDesyatichno(ctx *rt.Ctx, skolko rt.Value, razdelitel rt.Value) (rt.Value, error) { - t350, e351 := rt.Lt(ctx, skolko, rt.Number(0.0)) - if e351 != nil { - return rt.Value{}, e351 + t316, e317 := rt.Lt(ctx, skolko, rt.Number(0.0)) + if e317 != nil { + return rt.Value{}, e317 } - t352, e353 := rt.Cond(ctx, t350) - if e353 != nil { - return rt.Value{}, e353 + t318, e319 := rt.Cond(ctx, t316) + if e319 != nil { + return rt.Value{}, e319 } - var t354 rt.Value - if t352 { - t354 = rt.Text("-") + var t320 rt.Value + if t318 { + t320 = rt.Text("-") } else { - t354 = rt.Text("") + t320 = rt.Text("") } // пусть «знак» - znak := t354 - t355, e356 := rt.Lt(ctx, skolko, rt.Number(0.0)) - if e356 != nil { - return rt.Value{}, e356 - } - t357, e358 := rt.Cond(ctx, t355) - if e358 != nil { - return rt.Value{}, e358 - } - var t359 rt.Value - if t357 { - t360, e361 := rt.Sub(ctx, rt.Number(0.0), skolko) - if e361 != nil { - return rt.Value{}, e361 + znak := t320 + t321, e322 := rt.Lt(ctx, skolko, rt.Number(0.0)) + if e322 != nil { + return rt.Value{}, e322 + } + t323, e324 := rt.Cond(ctx, t321) + if e324 != nil { + return rt.Value{}, e324 + } + var t325 rt.Value + if t323 { + t326, e327 := rt.Sub(ctx, rt.Number(0.0), skolko) + if e327 != nil { + return rt.Value{}, e327 } - t359 = t360 + t325 = t326 } else { - t359 = skolko + t325 = skolko } - t362, e363 := CeloeKNulyu(ctx, t359) - if e363 != nil { - return rt.Value{}, e363 + t328, e329 := CeloeKNulyu(ctx, t325) + if e329 != nil { + return rt.Value{}, e329 } // пусть «величина» - velichina := t362 - t364, e365 := rt.Lt(ctx, velichina, rt.Number(1000.0)) - if e365 != nil { - return rt.Value{}, e365 + velichina := t328 + t330, e331 := rt.Lt(ctx, velichina, rt.Number(1000.0)) + if e331 != nil { + return rt.Value{}, e331 } - t366, e367 := rt.Cond(ctx, t364) - if e367 != nil { - return rt.Value{}, e367 + t332, e333 := rt.Cond(ctx, t330) + if e333 != nil { + return rt.Value{}, e333 } - if t366 { + if t332 { // «к строке» - t368, e369 := rt.BToString(ctx, velichina) - if e369 != nil { - return rt.Value{}, e369 + t334, e335 := rt.BToString(ctx, velichina) + if e335 != nil { + return rt.Value{}, e335 } - t370, e371 := rt.Concat(ctx, znak, t368) - if e371 != nil { - return rt.Value{}, e371 + t336, e337 := rt.Concat(ctx, znak, t334) + if e337 != nil { + return rt.Value{}, e337 } - t372, e373 := rt.Concat(ctx, t370, rt.Text(" Б")) - if e373 != nil { - return rt.Value{}, e373 + t338, e339 := rt.Concat(ctx, t336, rt.Text(" Б")) + if e339 != nil { + return rt.Value{}, e339 } - return t372, nil + return t338, nil } else { - t374, e375 := DelenieNacelo(ctx, velichina, rt.Number(1000.0)) - if e375 != nil { - return rt.Value{}, e375 + t340, e341 := DelenieNacelo(ctx, velichina, rt.Number(1000.0)) + if e341 != nil { + return rt.Value{}, e341 } // пусть «витки» - vitki := t374 - t376, e377 := DelitelTysyach(ctx, vitki, rt.Number(4.0)) - if e377 != nil { - return rt.Value{}, e377 + vitki := t340 + t342, e343 := DelitelTysyach(ctx, vitki, rt.Number(4.0)) + if e343 != nil { + return rt.Value{}, e343 } - t378, e379 := rt.Div(ctx, velichina, t376) - if e379 != nil { - return rt.Value{}, e379 + t344, e345 := rt.Div(ctx, velichina, t342) + if e345 != nil { + return rt.Value{}, e345 } - t380, e381 := Drobyu(ctx, t378, rt.Number(1.0), razdelitel) - if e381 != nil { - return rt.Value{}, e381 + t346, e347 := Drobyu(ctx, t344, rt.Number(1.0), razdelitel) + if e347 != nil { + return rt.Value{}, e347 } - t382, e383 := rt.Concat(ctx, znak, t380) - if e383 != nil { - return rt.Value{}, e383 + t348, e349 := rt.Concat(ctx, znak, t346) + if e349 != nil { + return rt.Value{}, e349 } - t384, e385 := rt.Concat(ctx, t382, rt.Text(" ")) - if e385 != nil { - return rt.Value{}, e385 + t350, e351 := rt.Concat(ctx, t348, rt.Text(" ")) + if e351 != nil { + return rt.Value{}, e351 } - t386, e387 := StupenTysyach(ctx, vitki, rt.Number(4.0)) - if e387 != nil { - return rt.Value{}, e387 + t352, e353 := StupenTysyach(ctx, vitki, rt.Number(4.0)) + if e353 != nil { + return rt.Value{}, e353 } - t388, e389 := EdinicaTysyach(ctx, t386) - if e389 != nil { - return rt.Value{}, e389 + t354, e355 := EdinicaTysyach(ctx, t352) + if e355 != nil { + return rt.Value{}, e355 } - t390, e391 := rt.Concat(ctx, t384, t388) - if e391 != nil { - return rt.Value{}, e391 + t356, e357 := rt.Concat(ctx, t350, t354) + if e357 != nil { + return rt.Value{}, e357 } - return t390, nil + return t356, nil } } @@ -1289,18 +1216,18 @@ func BaytyDesyatichno(ctx *rt.Ctx, skolko rt.Value, razdelitel rt.Value) (rt.Val // Параметр celoe — «целое»: число. // Результат — значение: число. func DolyaOt(ctx *rt.Ctx, chast rt.Value, celoe rt.Value) (rt.Value, error) { - t392, e393 := rt.Cond(ctx, rt.Flag(rt.Equal(celoe, rt.Number(0.0)))) - if e393 != nil { - return rt.Value{}, e393 + t358, e359 := rt.Cond(ctx, rt.Flag(rt.Equal(celoe, rt.Number(0.0)))) + if e359 != nil { + return rt.Value{}, e359 } - if t392 { + if t358 { return rt.Number(0.0), nil } else { - t394, e395 := rt.Div(ctx, chast, celoe) - if e395 != nil { - return rt.Value{}, e395 + t360, e361 := rt.Div(ctx, chast, celoe) + if e361 != nil { + return rt.Value{}, e361 } - return t394, nil + return t360, nil } } @@ -1312,22 +1239,22 @@ func DolyaOt(ctx *rt.Ctx, chast rt.Value, celoe rt.Value) (rt.Value, error) { // Параметр celoe — «целое»: число. // Результат — значение: число. func ProcentCelogo(ctx *rt.Ctx, chast rt.Value, celoe rt.Value) (rt.Value, error) { - t396, e397 := rt.Cond(ctx, rt.Flag(rt.Equal(celoe, rt.Number(0.0)))) - if e397 != nil { - return rt.Value{}, e397 + t362, e363 := rt.Cond(ctx, rt.Flag(rt.Equal(celoe, rt.Number(0.0)))) + if e363 != nil { + return rt.Value{}, e363 } - if t396 { + if t362 { return rt.Number(0.0), nil } else { - t398, e399 := rt.Mul(ctx, rt.Number(100.0), chast) - if e399 != nil { - return rt.Value{}, e399 + t364, e365 := rt.Mul(ctx, rt.Number(100.0), chast) + if e365 != nil { + return rt.Value{}, e365 } - t400, e401 := rt.Div(ctx, t398, celoe) - if e401 != nil { - return rt.Value{}, e401 + t366, e367 := rt.Div(ctx, t364, celoe) + if e367 != nil { + return rt.Value{}, e367 } - return t400, nil + return t366, nil } } @@ -1339,19 +1266,19 @@ func ProcentCelogo(ctx *rt.Ctx, chast rt.Value, celoe rt.Value) (rt.Value, error // Параметр razdelitel — «разделитель»: строка. // Результат — значение: строка. func ProcentyZnakom(ctx *rt.Ctx, dolya rt.Value, razdelitel rt.Value) (rt.Value, error) { - t402, e403 := rt.Mul(ctx, dolya, rt.Number(100.0)) - if e403 != nil { - return rt.Value{}, e403 + t368, e369 := rt.Mul(ctx, dolya, rt.Number(100.0)) + if e369 != nil { + return rt.Value{}, e369 } - t404, e405 := Drobyu(ctx, t402, rt.Number(1.0), razdelitel) - if e405 != nil { - return rt.Value{}, e405 + t370, e371 := Drobyu(ctx, t368, rt.Number(1.0), razdelitel) + if e371 != nil { + return rt.Value{}, e371 } - t406, e407 := rt.Concat(ctx, t404, rt.Text("%")) - if e407 != nil { - return rt.Value{}, e407 + t372, e373 := rt.Concat(ctx, t370, rt.Text("%")) + if e373 != nil { + return rt.Value{}, e373 } - return t406, nil + return t372, nil } // Procenty — функция flang «Проценты». @@ -1371,110 +1298,7 @@ func Procenty(ctx *rt.Ctx, dolya rt.Value) (rt.Value, error) { // Параметр sekundy — «секунды»: число. // Результат — значение: строка. func Dlitelnost(ctx *rt.Ctx, sekundy rt.Value) (rt.Value, error) { - t408, e409 := rt.Lte(ctx, sekundy, rt.Number(0.0)) - if e409 != nil { - return rt.Value{}, e409 - } - t410, e411 := rt.Cond(ctx, t408) - if e411 != nil { - return rt.Value{}, e411 - } - if t410 { - return rt.Text(""), nil - } else { - t412, e413 := CeloeKNulyu(ctx, sekundy) - if e413 != nil { - return rt.Value{}, e413 - } - // пусть «всего» - vsego := t412 - t414, e415 := DelenieNacelo(ctx, vsego, rt.Number(3600.0)) - if e415 != nil { - return rt.Value{}, e415 - } - // пусть «часов всего» - chasovVsego := t414 - t416, e417 := DelenieNacelo(ctx, chasovVsego, rt.Number(24.0)) - if e417 != nil { - return rt.Value{}, e417 - } - // пусть «дней» - dney := t416 - t418, e419 := rt.Mod(ctx, chasovVsego, rt.Number(24.0)) - if e419 != nil { - return rt.Value{}, e419 - } - // пусть «часов» - chasov := t418 - t420, e421 := DelenieNacelo(ctx, vsego, rt.Number(60.0)) - if e421 != nil { - return rt.Value{}, e421 - } - t422, e423 := rt.Mod(ctx, t420, rt.Number(60.0)) - if e423 != nil { - return rt.Value{}, e423 - } - // пусть «минут» - minut := t422 - // «к строке» - t424, e425 := rt.BToString(ctx, chasov) - if e425 != nil { - return rt.Value{}, e425 - } - t426, e427 := SlevaNulyami(ctx, t424, rt.Number(2.0)) - if e427 != nil { - return rt.Value{}, e427 - } - t428, e429 := rt.Concat(ctx, t426, rt.Text(":")) - if e429 != nil { - return rt.Value{}, e429 - } - // «к строке» - t430, e431 := rt.BToString(ctx, minut) - if e431 != nil { - return rt.Value{}, e431 - } - t432, e433 := SlevaNulyami(ctx, t430, rt.Number(2.0)) - if e433 != nil { - return rt.Value{}, e433 - } - t434, e435 := rt.Concat(ctx, t428, t432) - if e435 != nil { - return rt.Value{}, e435 - } - // пусть «время» - vremya := t434 - t436, e437 := rt.Gt(ctx, dney, rt.Number(0.0)) - if e437 != nil { - return rt.Value{}, e437 - } - t438, e439 := rt.Cond(ctx, t436) - if e439 != nil { - return rt.Value{}, e439 - } - if t438 { - // «к строке» - t440, e441 := rt.BToString(ctx, dney) - if e441 != nil { - return rt.Value{}, e441 - } - t442, e443 := rt.Concat(ctx, t440, rt.Text("д")) - if e443 != nil { - return rt.Value{}, e443 - } - t444, e445 := rt.Concat(ctx, t442, rt.Text(" ")) - if e445 != nil { - return rt.Value{}, e445 - } - t446, e447 := rt.Concat(ctx, t444, vremya) - if e447 != nil { - return rt.Value{}, e447 - } - return t446, nil - } else { - return vremya, nil - } - } + return DlitelnostYazykom(ctx, sekundy, rt.Text("ru")) } // Proshlo — функция flang «Прошло». @@ -1484,88 +1308,88 @@ func Dlitelnost(ctx *rt.Ctx, sekundy rt.Value) (rt.Value, error) { // Параметр sekundy — «секунды»: число. // Результат — значение: строка. func Proshlo(ctx *rt.Ctx, sekundy rt.Value) (rt.Value, error) { - t448, e449 := rt.Lt(ctx, sekundy, rt.Number(3600.0)) - if e449 != nil { - return rt.Value{}, e449 + t374, e375 := rt.Lt(ctx, sekundy, rt.Number(3600.0)) + if e375 != nil { + return rt.Value{}, e375 } - t450, e451 := rt.Cond(ctx, t448) - if e451 != nil { - return rt.Value{}, e451 + t376, e377 := rt.Cond(ctx, t374) + if e377 != nil { + return rt.Value{}, e377 } - if t450 { - t452, e453 := DelenieNacelo(ctx, sekundy, rt.Number(60.0)) - if e453 != nil { - return rt.Value{}, e453 + if t376 { + t378, e379 := DelenieNacelo(ctx, sekundy, rt.Number(60.0)) + if e379 != nil { + return rt.Value{}, e379 } // «к строке» - t454, e455 := rt.BToString(ctx, t452) - if e455 != nil { - return rt.Value{}, e455 + t380, e381 := rt.BToString(ctx, t378) + if e381 != nil { + return rt.Value{}, e381 } - t456, e457 := rt.Concat(ctx, t454, rt.Text(" ")) - if e457 != nil { - return rt.Value{}, e457 + t382, e383 := rt.Concat(ctx, t380, rt.Text(" ")) + if e383 != nil { + return rt.Value{}, e383 } - t458, e459 := rt.Concat(ctx, t456, rt.Text("мин")) - if e459 != nil { - return rt.Value{}, e459 + t384, e385 := rt.Concat(ctx, t382, rt.Text("мин")) + if e385 != nil { + return rt.Value{}, e385 } - return t458, nil + return t384, nil } else { - t460, e461 := rt.Lt(ctx, sekundy, rt.Number(172800.0)) - if e461 != nil { - return rt.Value{}, e461 + t386, e387 := rt.Lt(ctx, sekundy, rt.Number(172800.0)) + if e387 != nil { + return rt.Value{}, e387 } - t462, e463 := rt.Cond(ctx, t460) - if e463 != nil { - return rt.Value{}, e463 + t388, e389 := rt.Cond(ctx, t386) + if e389 != nil { + return rt.Value{}, e389 } - if t462 { - t464, e465 := DelenieNacelo(ctx, sekundy, rt.Number(3600.0)) - if e465 != nil { - return rt.Value{}, e465 + if t388 { + t390, e391 := DelenieNacelo(ctx, sekundy, rt.Number(3600.0)) + if e391 != nil { + return rt.Value{}, e391 } // «к строке» - t466, e467 := rt.BToString(ctx, t464) - if e467 != nil { - return rt.Value{}, e467 + t392, e393 := rt.BToString(ctx, t390) + if e393 != nil { + return rt.Value{}, e393 } - t468, e469 := rt.Concat(ctx, t466, rt.Text(" ")) - if e469 != nil { - return rt.Value{}, e469 + t394, e395 := rt.Concat(ctx, t392, rt.Text(" ")) + if e395 != nil { + return rt.Value{}, e395 } - t470, e471 := rt.Concat(ctx, t468, rt.Text("ч")) - if e471 != nil { - return rt.Value{}, e471 + t396, e397 := rt.Concat(ctx, t394, rt.Text("ч")) + if e397 != nil { + return rt.Value{}, e397 } - return t470, nil + return t396, nil } else { - t472, e473 := rt.Div(ctx, sekundy, rt.Number(3600.0)) - if e473 != nil { - return rt.Value{}, e473 + t398, e399 := rt.Div(ctx, sekundy, rt.Number(3600.0)) + if e399 != nil { + return rt.Value{}, e399 } - t474, e475 := rt.Div(ctx, t472, rt.Number(24.0)) - if e475 != nil { - return rt.Value{}, e475 + t400, e401 := rt.Div(ctx, t398, rt.Number(24.0)) + if e401 != nil { + return rt.Value{}, e401 } - t476, e477 := CeloeKNulyu(ctx, t474) - if e477 != nil { - return rt.Value{}, e477 + t402, e403 := CeloeKNulyu(ctx, t400) + if e403 != nil { + return rt.Value{}, e403 } // «к строке» - t478, e479 := rt.BToString(ctx, t476) - if e479 != nil { - return rt.Value{}, e479 + t404, e405 := rt.BToString(ctx, t402) + if e405 != nil { + return rt.Value{}, e405 } - t480, e481 := rt.Concat(ctx, t478, rt.Text(" ")) - if e481 != nil { - return rt.Value{}, e481 + t406, e407 := rt.Concat(ctx, t404, rt.Text(" ")) + if e407 != nil { + return rt.Value{}, e407 } - t482, e483 := rt.Concat(ctx, t480, rt.Text("дн")) - if e483 != nil { - return rt.Value{}, e483 + t408, e409 := rt.Concat(ctx, t406, rt.Text("дн")) + if e409 != nil { + return rt.Value{}, e409 } - return t482, nil + return t408, nil } } } @@ -1578,44 +1402,7 @@ func Proshlo(ctx *rt.Ctx, sekundy rt.Value) (rt.Value, error) { // Параметр razdelitel — «разделитель»: строка. // Результат — значение: строка. func ZanyaloZnakom(ctx *rt.Ctx, millisekundy rt.Value, razdelitel rt.Value) (rt.Value, error) { - t484, e485 := rt.Lt(ctx, millisekundy, rt.Number(1000.0)) - if e485 != nil { - return rt.Value{}, e485 - } - t486, e487 := rt.Cond(ctx, t484) - if e487 != nil { - return rt.Value{}, e487 - } - if t486 { - t488, e489 := CeloeKNulyu(ctx, millisekundy) - if e489 != nil { - return rt.Value{}, e489 - } - // «к строке» - t490, e491 := rt.BToString(ctx, t488) - if e491 != nil { - return rt.Value{}, e491 - } - t492, e493 := rt.Concat(ctx, t490, rt.Text(" мс")) - if e493 != nil { - return rt.Value{}, e493 - } - return t492, nil - } else { - t494, e495 := rt.Div(ctx, millisekundy, rt.Number(1000.0)) - if e495 != nil { - return rt.Value{}, e495 - } - t496, e497 := Drobyu(ctx, t494, rt.Number(1.0), razdelitel) - if e497 != nil { - return rt.Value{}, e497 - } - t498, e499 := rt.Concat(ctx, t496, rt.Text(" с")) - if e499 != nil { - return rt.Value{}, e499 - } - return t498, nil - } + return ZanyaloYazykom(ctx, millisekundy, razdelitel, rt.Text("ru")) } // Zanyalo — функция flang «Заняло». @@ -1636,141 +1423,141 @@ func Zanyalo(ctx *rt.Ctx, millisekundy rt.Value) (rt.Value, error) { // Параметр cifra — «цифра»: строка. // Результат — значение: «Ход разрядов». func ShagRazryada(ctx *rt.Ctx, hod rt.Value, cifra rt.Value) (rt.Value, error) { - t500, e501 := rt.FieldGet(ctx, hod, "осталось") - if e501 != nil { - return rt.Value{}, e501 + t410, e411 := rt.FieldGet(ctx, hod, "осталось") + if e411 != nil { + return rt.Value{}, e411 } - t502, e503 := rt.Mod(ctx, t500, rt.Number(3.0)) - if e503 != nil { - return rt.Value{}, e503 + t412, e413 := rt.Mod(ctx, t410, rt.Number(3.0)) + if e413 != nil { + return rt.Value{}, e413 } - t504, e505 := rt.Cond(ctx, rt.Flag(rt.Equal(t502, rt.Number(0.0)))) - if e505 != nil { - return rt.Value{}, e505 + t414, e415 := rt.Cond(ctx, rt.Flag(rt.Equal(t412, rt.Number(0.0)))) + if e415 != nil { + return rt.Value{}, e415 } - var t506 rt.Value - if t504 { - t507, e508 := rt.FieldGet(ctx, hod, "собрано") - if e508 != nil { - return rt.Value{}, e508 + var t416 rt.Value + if t414 { + t417, e418 := rt.FieldGet(ctx, hod, "собрано") + if e418 != nil { + return rt.Value{}, e418 } // «длина» - t509, e510 := rt.BLength(ctx, t507) - if e510 != nil { - return rt.Value{}, e510 + t419, e420 := rt.BLength(ctx, t417) + if e420 != nil { + return rt.Value{}, e420 } - t511, e512 := rt.Gt(ctx, t509, rt.Number(0.0)) - if e512 != nil { - return rt.Value{}, e512 + t421, e422 := rt.Gt(ctx, t419, rt.Number(0.0)) + if e422 != nil { + return rt.Value{}, e422 } - t506 = t511 + t416 = t421 } else { - t506 = rt.Flag(false) + t416 = rt.Flag(false) } - t513, e514 := rt.Cond(ctx, t506) - if e514 != nil { - return rt.Value{}, e514 + t423, e424 := rt.Cond(ctx, t416) + if e424 != nil { + return rt.Value{}, e424 } - var t515 rt.Value - if t513 { - t516, e517 := rt.FieldGet(ctx, hod, "собрано") - if e517 != nil { - return rt.Value{}, e517 + var t425 rt.Value + if t423 { + t426, e427 := rt.FieldGet(ctx, hod, "собрано") + if e427 != nil { + return rt.Value{}, e427 } - t518, e519 := rt.FieldGet(ctx, hod, "разделитель") - if e519 != nil { - return rt.Value{}, e519 + t428, e429 := rt.FieldGet(ctx, hod, "разделитель") + if e429 != nil { + return rt.Value{}, e429 } - t520, e521 := rt.Concat(ctx, t516, t518) - if e521 != nil { - return rt.Value{}, e521 + t430, e431 := rt.Concat(ctx, t426, t428) + if e431 != nil { + return rt.Value{}, e431 } - t522, e523 := rt.Concat(ctx, t520, cifra) - if e523 != nil { - return rt.Value{}, e523 + t432, e433 := rt.Concat(ctx, t430, cifra) + if e433 != nil { + return rt.Value{}, e433 } - t524, e525 := rt.FieldGet(ctx, hod, "осталось") - if e525 != nil { - return rt.Value{}, e525 + t434, e435 := rt.FieldGet(ctx, hod, "осталось") + if e435 != nil { + return rt.Value{}, e435 } - t526, e527 := rt.Sub(ctx, t524, rt.Number(1.0)) - if e527 != nil { - return rt.Value{}, e527 + t436, e437 := rt.Sub(ctx, t434, rt.Number(1.0)) + if e437 != nil { + return rt.Value{}, e437 } - t528, e529 := rt.FieldGet(ctx, hod, "разделитель") - if e529 != nil { - return rt.Value{}, e529 + t438, e439 := rt.FieldGet(ctx, hod, "разделитель") + if e439 != nil { + return rt.Value{}, e439 } - t530 := make([]rt.Field, 3) - t530[0] = rt.Field{Name: "собрано", Value: t522} - t530[1] = rt.Field{Name: "осталось", Value: t526} - t530[2] = rt.Field{Name: "разделитель", Value: t528} - t515 = rt.Record(t530) + t440 := make([]rt.Field, 3) + t440[0] = rt.Field{Name: "собрано", Value: t432} + t440[1] = rt.Field{Name: "осталось", Value: t436} + t440[2] = rt.Field{Name: "разделитель", Value: t438} + t425 = rt.Record(t440) } else { - t531, e532 := rt.FieldGet(ctx, hod, "собрано") - if e532 != nil { - return rt.Value{}, e532 + t441, e442 := rt.FieldGet(ctx, hod, "собрано") + if e442 != nil { + return rt.Value{}, e442 } - t533, e534 := rt.Concat(ctx, t531, cifra) - if e534 != nil { - return rt.Value{}, e534 + t443, e444 := rt.Concat(ctx, t441, cifra) + if e444 != nil { + return rt.Value{}, e444 } - t535, e536 := rt.FieldGet(ctx, hod, "осталось") - if e536 != nil { - return rt.Value{}, e536 + t445, e446 := rt.FieldGet(ctx, hod, "осталось") + if e446 != nil { + return rt.Value{}, e446 } - t537, e538 := rt.Sub(ctx, t535, rt.Number(1.0)) - if e538 != nil { - return rt.Value{}, e538 + t447, e448 := rt.Sub(ctx, t445, rt.Number(1.0)) + if e448 != nil { + return rt.Value{}, e448 } - t539, e540 := rt.FieldGet(ctx, hod, "разделитель") - if e540 != nil { - return rt.Value{}, e540 + t449, e450 := rt.FieldGet(ctx, hod, "разделитель") + if e450 != nil { + return rt.Value{}, e450 } - t541 := make([]rt.Field, 3) - t541[0] = rt.Field{Name: "собрано", Value: t533} - t541[1] = rt.Field{Name: "осталось", Value: t537} - t541[2] = rt.Field{Name: "разделитель", Value: t539} - t515 = rt.Record(t541) + t451 := make([]rt.Field, 3) + t451[0] = rt.Field{Name: "собрано", Value: t443} + t451[1] = rt.Field{Name: "осталось", Value: t447} + t451[2] = rt.Field{Name: "разделитель", Value: t449} + t425 = rt.Record(t451) } - t542 := t515 - t543, e544 := rt.FieldGet(ctx, t542, "осталось") - if e544 != nil { - return rt.Value{}, e544 + t452 := t425 + t453, e454 := rt.FieldGet(ctx, t452, "осталось") + if e454 != nil { + return rt.Value{}, e454 } - t545, e546 := rt.FieldGet(ctx, hod, "осталось") - if e546 != nil { - return rt.Value{}, e546 + t455, e456 := rt.FieldGet(ctx, hod, "осталось") + if e456 != nil { + return rt.Value{}, e456 } - t547, e548 := rt.Lt(ctx, t543, t545) - if e548 != nil { - return rt.Value{}, e548 + t457, e458 := rt.Lt(ctx, t453, t455) + if e458 != nil { + return rt.Value{}, e458 } // постусловие «осталось убывает» - t549, e550 := rt.Post(ctx, t547, "осталось убывает", "Шаг разряда") - if e550 != nil { - return rt.Value{}, e550 + t459, e460 := rt.Post(ctx, t457, "осталось убывает", "Шаг разряда") + if e460 != nil { + return rt.Value{}, e460 } - if !t549 { + if !t459 { return rt.Value{}, rt.Fail("FLANG_PROPERTY", "%s", "нарушено свойство «осталось убывает» функции «Шаг разряда»") } - t551, e552 := rt.FieldGet(ctx, t542, "разделитель") - if e552 != nil { - return rt.Value{}, e552 + t461, e462 := rt.FieldGet(ctx, t452, "разделитель") + if e462 != nil { + return rt.Value{}, e462 } - t553, e554 := rt.FieldGet(ctx, hod, "разделитель") - if e554 != nil { - return rt.Value{}, e554 + t463, e464 := rt.FieldGet(ctx, hod, "разделитель") + if e464 != nil { + return rt.Value{}, e464 } // постусловие «разделитель не меняется» - t555, e556 := rt.Post(ctx, rt.Flag(rt.Equal(t551, t553)), "разделитель не меняется", "Шаг разряда") - if e556 != nil { - return rt.Value{}, e556 + t465, e466 := rt.Post(ctx, rt.Flag(rt.Equal(t461, t463)), "разделитель не меняется", "Шаг разряда") + if e466 != nil { + return rt.Value{}, e466 } - if !t555 { + if !t465 { return rt.Value{}, rt.Fail("FLANG_PROPERTY", "%s", "нарушено свойство «разделитель не меняется» функции «Шаг разряда»") } - return t542, nil + return t452, nil } // Razryadami — функция flang «Разрядами». @@ -1781,89 +1568,89 @@ func ShagRazryada(ctx *rt.Ctx, hod rt.Value, cifra rt.Value) (rt.Value, error) { // Параметр razdelitel — «разделитель»: строка. // Результат — значение: строка. func Razryadami(ctx *rt.Ctx, znachenie rt.Value, razdelitel rt.Value) (rt.Value, error) { - t557, e558 := rt.Lt(ctx, znachenie, rt.Number(0.0)) - if e558 != nil { - return rt.Value{}, e558 + t467, e468 := rt.Lt(ctx, znachenie, rt.Number(0.0)) + if e468 != nil { + return rt.Value{}, e468 } - t559, e560 := rt.Cond(ctx, t557) - if e560 != nil { - return rt.Value{}, e560 + t469, e470 := rt.Cond(ctx, t467) + if e470 != nil { + return rt.Value{}, e470 } - var t561 rt.Value - if t559 { - t561 = rt.Text("-") + var t471 rt.Value + if t469 { + t471 = rt.Text("-") } else { - t561 = rt.Text("") + t471 = rt.Text("") } // пусть «знак» - znak := t561 - t562, e563 := rt.Lt(ctx, znachenie, rt.Number(0.0)) - if e563 != nil { - return rt.Value{}, e563 - } - t564, e565 := rt.Cond(ctx, t562) - if e565 != nil { - return rt.Value{}, e565 - } - var t566 rt.Value - if t564 { - t567, e568 := rt.Sub(ctx, rt.Number(0.0), znachenie) - if e568 != nil { - return rt.Value{}, e568 - } - t566 = t567 + znak := t471 + t472, e473 := rt.Lt(ctx, znachenie, rt.Number(0.0)) + if e473 != nil { + return rt.Value{}, e473 + } + t474, e475 := rt.Cond(ctx, t472) + if e475 != nil { + return rt.Value{}, e475 + } + var t476 rt.Value + if t474 { + t477, e478 := rt.Sub(ctx, rt.Number(0.0), znachenie) + if e478 != nil { + return rt.Value{}, e478 + } + t476 = t477 } else { - t566 = znachenie + t476 = znachenie } - t569, e570 := CeloeKNulyu(ctx, t566) - if e570 != nil { - return rt.Value{}, e570 + t479, e480 := CeloeKNulyu(ctx, t476) + if e480 != nil { + return rt.Value{}, e480 } // «к строке» - t571, e572 := rt.BToString(ctx, t569) - if e572 != nil { - return rt.Value{}, e572 + t481, e482 := rt.BToString(ctx, t479) + if e482 != nil { + return rt.Value{}, e482 } // пусть «цифры» - cifry := t571 + cifry := t481 // «символы» - t573, e574 := rt.BCharacters(ctx, cifry) - if e574 != nil { - return rt.Value{}, e574 + t483, e484 := rt.BCharacters(ctx, cifry) + if e484 != nil { + return rt.Value{}, e484 } - t575, e576 := rt.RequireList(ctx, t573, "свёртка") - if e576 != nil { - return rt.Value{}, e576 + t485, e486 := rt.RequireList(ctx, t483, "свёртка") + if e486 != nil { + return rt.Value{}, e486 } // «длина» - t577, e578 := rt.BLength(ctx, cifry) - if e578 != nil { - return rt.Value{}, e578 - } - t579 := make([]rt.Field, 3) - t579[0] = rt.Field{Name: "собрано", Value: rt.Text("")} - t579[1] = rt.Field{Name: "осталось", Value: t577} - t579[2] = rt.Field{Name: "разделитель", Value: razdelitel} + t487, e488 := rt.BLength(ctx, cifry) + if e488 != nil { + return rt.Value{}, e488 + } + t489 := make([]rt.Field, 3) + t489[0] = rt.Field{Name: "собрано", Value: rt.Text("")} + t489[1] = rt.Field{Name: "осталось", Value: t487} + t489[2] = rt.Field{Name: "разделитель", Value: razdelitel} // «ход» - hod := rt.Record(t579) - for t580 := range t575 { + hod := rt.Record(t489) + for t490 := range t485 { // «цифра» - cifra := t575[t580] - t581, e582 := ShagRazryada(ctx, hod, cifra) - if e582 != nil { - return rt.Value{}, e582 + cifra := t485[t490] + t491, e492 := ShagRazryada(ctx, hod, cifra) + if e492 != nil { + return rt.Value{}, e492 } - hod = t581 + hod = t491 } - t583, e584 := rt.FieldGet(ctx, hod, "собрано") - if e584 != nil { - return rt.Value{}, e584 + t493, e494 := rt.FieldGet(ctx, hod, "собрано") + if e494 != nil { + return rt.Value{}, e494 } - t585, e586 := rt.Concat(ctx, znak, t583) - if e586 != nil { - return rt.Value{}, e586 + t495, e496 := rt.Concat(ctx, znak, t493) + if e496 != nil { + return rt.Value{}, e496 } - return t585, nil + return t495, nil } // Probelnyy — функция flang «Пробельный». @@ -1882,113 +1669,113 @@ func Probelnyy(ctx *rt.Ctx, znak rt.Value) (rt.Value, error) { hvost := rt.ChainTail(znak) _ = hvost // «код символа» - t587, e588 := rt.BCharCodeProven(ctx, golova) - if e588 != nil { - return rt.Value{}, e588 + t497, e498 := rt.BCharCodeProven(ctx, golova) + if e498 != nil { + return rt.Value{}, e498 } - t589, e590 := rt.Cond(ctx, rt.Flag(rt.Equal(t587, rt.Number(32.0)))) - if e590 != nil { - return rt.Value{}, e590 + t499, e500 := rt.Cond(ctx, rt.Flag(rt.Equal(t497, rt.Number(32.0)))) + if e500 != nil { + return rt.Value{}, e500 } - var t591 rt.Value - if t589 { - t591 = rt.Flag(true) + var t501 rt.Value + if t499 { + t501 = rt.Flag(true) } else { // «код символа» - t592, e593 := rt.BCharCodeProven(ctx, golova) - if e593 != nil { - return rt.Value{}, e593 + t502, e503 := rt.BCharCodeProven(ctx, golova) + if e503 != nil { + return rt.Value{}, e503 } - t591 = rt.Flag(rt.Equal(t592, rt.Number(9.0))) + t501 = rt.Flag(rt.Equal(t502, rt.Number(9.0))) } - t594, e595 := rt.Cond(ctx, t591) - if e595 != nil { - return rt.Value{}, e595 + t504, e505 := rt.Cond(ctx, t501) + if e505 != nil { + return rt.Value{}, e505 } - var t596 rt.Value - if t594 { - t596 = rt.Flag(true) + var t506 rt.Value + if t504 { + t506 = rt.Flag(true) } else { // «код символа» - t597, e598 := rt.BCharCodeProven(ctx, golova) - if e598 != nil { - return rt.Value{}, e598 + t507, e508 := rt.BCharCodeProven(ctx, golova) + if e508 != nil { + return rt.Value{}, e508 } - t596 = rt.Flag(rt.Equal(t597, rt.Number(10.0))) + t506 = rt.Flag(rt.Equal(t507, rt.Number(10.0))) } - t599, e600 := rt.Cond(ctx, t596) - if e600 != nil { - return rt.Value{}, e600 + t509, e510 := rt.Cond(ctx, t506) + if e510 != nil { + return rt.Value{}, e510 } - var t601 rt.Value - if t599 { - t601 = rt.Flag(true) + var t511 rt.Value + if t509 { + t511 = rt.Flag(true) } else { // «код символа» - t602, e603 := rt.BCharCodeProven(ctx, golova) - if e603 != nil { - return rt.Value{}, e603 + t512, e513 := rt.BCharCodeProven(ctx, golova) + if e513 != nil { + return rt.Value{}, e513 } - t601 = rt.Flag(rt.Equal(t602, rt.Number(11.0))) + t511 = rt.Flag(rt.Equal(t512, rt.Number(11.0))) } - t604, e605 := rt.Cond(ctx, t601) - if e605 != nil { - return rt.Value{}, e605 + t514, e515 := rt.Cond(ctx, t511) + if e515 != nil { + return rt.Value{}, e515 } - var t606 rt.Value - if t604 { - t606 = rt.Flag(true) + var t516 rt.Value + if t514 { + t516 = rt.Flag(true) } else { // «код символа» - t607, e608 := rt.BCharCodeProven(ctx, golova) - if e608 != nil { - return rt.Value{}, e608 + t517, e518 := rt.BCharCodeProven(ctx, golova) + if e518 != nil { + return rt.Value{}, e518 } - t606 = rt.Flag(rt.Equal(t607, rt.Number(12.0))) + t516 = rt.Flag(rt.Equal(t517, rt.Number(12.0))) } - t609, e610 := rt.Cond(ctx, t606) - if e610 != nil { - return rt.Value{}, e610 + t519, e520 := rt.Cond(ctx, t516) + if e520 != nil { + return rt.Value{}, e520 } - var t611 rt.Value - if t609 { - t611 = rt.Flag(true) + var t521 rt.Value + if t519 { + t521 = rt.Flag(true) } else { // «код символа» - t612, e613 := rt.BCharCodeProven(ctx, golova) - if e613 != nil { - return rt.Value{}, e613 + t522, e523 := rt.BCharCodeProven(ctx, golova) + if e523 != nil { + return rt.Value{}, e523 } - t611 = rt.Flag(rt.Equal(t612, rt.Number(13.0))) + t521 = rt.Flag(rt.Equal(t522, rt.Number(13.0))) } - t614, e615 := rt.Cond(ctx, t611) - if e615 != nil { - return rt.Value{}, e615 + t524, e525 := rt.Cond(ctx, t521) + if e525 != nil { + return rt.Value{}, e525 } - var t616 rt.Value - if t614 { - t616 = rt.Flag(true) + var t526 rt.Value + if t524 { + t526 = rt.Flag(true) } else { // «код символа» - t617, e618 := rt.BCharCodeProven(ctx, golova) - if e618 != nil { - return rt.Value{}, e618 + t527, e528 := rt.BCharCodeProven(ctx, golova) + if e528 != nil { + return rt.Value{}, e528 } - t616 = rt.Flag(rt.Equal(t617, rt.Number(133.0))) + t526 = rt.Flag(rt.Equal(t527, rt.Number(133.0))) } - t619, e620 := rt.Cond(ctx, t616) - if e620 != nil { - return rt.Value{}, e620 + t529, e530 := rt.Cond(ctx, t526) + if e530 != nil { + return rt.Value{}, e530 } - if t619 { + if t529 { return rt.Flag(true), nil } else { // «код символа» - t621, e622 := rt.BCharCodeProven(ctx, golova) - if e622 != nil { - return rt.Value{}, e622 + t531, e532 := rt.BCharCodeProven(ctx, golova) + if e532 != nil { + return rt.Value{}, e532 } - return rt.Flag(rt.Equal(t621, rt.Number(160.0))), nil + return rt.Flag(rt.Equal(t531, rt.Number(160.0))), nil } } else { return rt.Value{}, rt.MatchFail(ctx, znak) @@ -2003,50 +1790,50 @@ func Probelnyy(ctx *rt.Ctx, znak rt.Value) (rt.Value, error) { // Результат — значение: строка. func Procherk(ctx *rt.Ctx, tekst rt.Value) (rt.Value, error) { // «символы» - t623, e624 := rt.BCharacters(ctx, tekst) - if e624 != nil { - return rt.Value{}, e624 + t533, e534 := rt.BCharacters(ctx, tekst) + if e534 != nil { + return rt.Value{}, e534 } - t625, e626 := rt.RequireList(ctx, t623, "отфильтровать") - if e626 != nil { - return rt.Value{}, e626 + t535, e536 := rt.RequireList(ctx, t533, "отфильтровать") + if e536 != nil { + return rt.Value{}, e536 } - t627 := make([]rt.Value, 0, len(t625)) - for t628 := range t625 { + t537 := make([]rt.Value, 0, len(t535)) + for t538 := range t535 { // «знак» - znak := t625[t628] - t629, e630 := Probelnyy(ctx, znak) - if e630 != nil { - return rt.Value{}, e630 - } - t631, e632 := rt.Cond(ctx, t629) - if e632 != nil { - return rt.Value{}, e632 - } - var t633 rt.Value - if t631 { - t633 = rt.Flag(false) + znak := t535[t538] + t539, e540 := Probelnyy(ctx, znak) + if e540 != nil { + return rt.Value{}, e540 + } + t541, e542 := rt.Cond(ctx, t539) + if e542 != nil { + return rt.Value{}, e542 + } + var t543 rt.Value + if t541 { + t543 = rt.Flag(false) } else { - t633 = rt.Flag(true) + t543 = rt.Flag(true) } - t634, e635 := rt.Keep(ctx, t633) - if e635 != nil { - return rt.Value{}, e635 + t544, e545 := rt.Keep(ctx, t543) + if e545 != nil { + return rt.Value{}, e545 } - if t634 { - t627 = append(t627, znak) + if t544 { + t537 = append(t537, znak) } } // «длина» - t636, e637 := rt.BLength(ctx, rt.List(t627)) - if e637 != nil { - return rt.Value{}, e637 + t546, e547 := rt.BLength(ctx, rt.List(t537)) + if e547 != nil { + return rt.Value{}, e547 } - t638, e639 := rt.Cond(ctx, rt.Flag(rt.Equal(t636, rt.Number(0.0)))) - if e639 != nil { - return rt.Value{}, e639 + t548, e549 := rt.Cond(ctx, rt.Flag(rt.Equal(t546, rt.Number(0.0)))) + if e549 != nil { + return rt.Value{}, e549 } - if t638 { + if t548 { return rt.Text("—"), nil } else { return tekst, nil @@ -2062,127 +1849,1452 @@ func Procherk(ctx *rt.Ctx, tekst rt.Value) (rt.Value, error) { // Результат — значение: строка. func Obrezat(ctx *rt.Ctx, tekst rt.Value, skolko rt.Value) (rt.Value, error) { // «длина» - t640, e641 := rt.BLength(ctx, tekst) - if e641 != nil { - return rt.Value{}, e641 + t550, e551 := rt.BLength(ctx, tekst) + if e551 != nil { + return rt.Value{}, e551 } - t642, e643 := rt.Lte(ctx, t640, skolko) - if e643 != nil { - return rt.Value{}, e643 + t552, e553 := rt.Lte(ctx, t550, skolko) + if e553 != nil { + return rt.Value{}, e553 } - t644, e645 := rt.Cond(ctx, t642) + t554, e555 := rt.Cond(ctx, t552) + if e555 != nil { + return rt.Value{}, e555 + } + var t556 rt.Value + if t554 { + t556 = tekst + } else { + t557, e558 := rt.Lte(ctx, skolko, rt.Number(0.0)) + if e558 != nil { + return rt.Value{}, e558 + } + t559, e560 := rt.Cond(ctx, t557) + if e560 != nil { + return rt.Value{}, e560 + } + var t561 rt.Value + if t559 { + t561 = rt.Text("") + } else { + t562, e563 := rt.Lte(ctx, skolko, rt.Number(1.0)) + if e563 != nil { + return rt.Value{}, e563 + } + t564, e565 := rt.Cond(ctx, t562) + if e565 != nil { + return rt.Value{}, e565 + } + var t566 rt.Value + if t564 { + // «подстрока» + t567, e568 := rt.BSubstring(ctx, tekst, rt.Number(1.0), rt.Number(1.0)) + if e568 != nil { + return rt.Value{}, e568 + } + t566 = t567 + } else { + t569, e570 := rt.Sub(ctx, skolko, rt.Number(1.0)) + if e570 != nil { + return rt.Value{}, e570 + } + // «подстрока» + t571, e572 := rt.BSubstring(ctx, tekst, rt.Number(1.0), t569) + if e572 != nil { + return rt.Value{}, e572 + } + t573, e574 := rt.Concat(ctx, t571, rt.Text("…")) + if e574 != nil { + return rt.Value{}, e574 + } + t566 = t573 + } + t561 = t566 + } + t556 = t561 + } + t575 := t556 + t576, e577 := rt.Gte(ctx, skolko, rt.Number(0.0)) + if e577 != nil { + return rt.Value{}, e577 + } + t578, e579 := rt.Cond(ctx, t576) + if e579 != nil { + return rt.Value{}, e579 + } + var t580 rt.Value + if t578 { + // «длина» + t581, e582 := rt.BLength(ctx, t575) + if e582 != nil { + return rt.Value{}, e582 + } + // «длина» + t583, e584 := rt.BLength(ctx, tekst) + if e584 != nil { + return rt.Value{}, e584 + } + t585, e586 := rt.Lte(ctx, t583, skolko) + if e586 != nil { + return rt.Value{}, e586 + } + t587, e588 := rt.Cond(ctx, t585) + if e588 != nil { + return rt.Value{}, e588 + } + var t589 rt.Value + if t587 { + // «длина» + t590, e591 := rt.BLength(ctx, tekst) + if e591 != nil { + return rt.Value{}, e591 + } + t589 = t590 + } else { + t589 = skolko + } + t592, e593 := rt.Lte(ctx, t581, t589) + if e593 != nil { + return rt.Value{}, e593 + } + t580 = t592 + } else { + t580 = rt.Flag(true) + } + // постусловие «обрезанное не длиннее заказа» + t594, e595 := rt.Post(ctx, t580, "обрезанное не длиннее заказа", "Обрезать") + if e595 != nil { + return rt.Value{}, e595 + } + if !t594 { + return rt.Value{}, rt.Fail("FLANG_PROPERTY", "%s", "нарушено свойство «обрезанное не длиннее заказа» функции «Обрезать»") + } + return t575, nil +} + +// Umestit — функция flang «Уместить». +// +// Тотальная: завершение доказано анализом завершаемости (totality.mjs). +// +// Параметр tekst — «текст»: строка. +// Параметр shirina — «ширина»: число. +// Результат — значение: строка. +func Umestit(ctx *rt.Ctx, tekst rt.Value, shirina rt.Value) (rt.Value, error) { + t596, e597 := rt.Lte(ctx, shirina, rt.Number(0.0)) + if e597 != nil { + return rt.Value{}, e597 + } + t598, e599 := rt.Cond(ctx, t596) + if e599 != nil { + return rt.Value{}, e599 + } + var t600 rt.Value + if t598 { + t600 = rt.Text("") + } else { + // «длина» + t601, e602 := rt.BLength(ctx, tekst) + if e602 != nil { + return rt.Value{}, e602 + } + t603, e604 := rt.Cond(ctx, rt.Flag(rt.Equal(t601, shirina))) + if e604 != nil { + return rt.Value{}, e604 + } + var t605 rt.Value + if t603 { + t605 = tekst + } else { + // «длина» + t606, e607 := rt.BLength(ctx, tekst) + if e607 != nil { + return rt.Value{}, e607 + } + t608, e609 := rt.Lt(ctx, t606, shirina) + if e609 != nil { + return rt.Value{}, e609 + } + t610, e611 := rt.Cond(ctx, t608) + if e611 != nil { + return rt.Value{}, e611 + } + var t612 rt.Value + if t610 { + // «длина» + t613, e614 := rt.BLength(ctx, tekst) + if e614 != nil { + return rt.Value{}, e614 + } + t615, e616 := rt.Sub(ctx, shirina, t613) + if e616 != nil { + return rt.Value{}, e616 + } + t617, e618 := Povtorit(ctx, rt.Text(" "), t615) + if e618 != nil { + return rt.Value{}, e618 + } + t619, e620 := rt.Concat(ctx, tekst, t617) + if e620 != nil { + return rt.Value{}, e620 + } + t612 = t619 + } else { + t621, e622 := rt.Cond(ctx, rt.Flag(rt.Equal(shirina, rt.Number(1.0)))) + if e622 != nil { + return rt.Value{}, e622 + } + var t623 rt.Value + if t621 { + t623 = rt.Text("…") + } else { + t624, e625 := rt.Sub(ctx, shirina, rt.Number(1.0)) + if e625 != nil { + return rt.Value{}, e625 + } + // «подстрока» + t626, e627 := rt.BSubstring(ctx, tekst, rt.Number(1.0), t624) + if e627 != nil { + return rt.Value{}, e627 + } + t628, e629 := rt.Concat(ctx, t626, rt.Text("…")) + if e629 != nil { + return rt.Value{}, e629 + } + t623 = t628 + } + t612 = t623 + } + t605 = t612 + } + t600 = t605 + } + t630 := t600 + t631, e632 := rt.Gt(ctx, shirina, rt.Number(0.0)) + if e632 != nil { + return rt.Value{}, e632 + } + t633, e634 := rt.Cond(ctx, t631) + if e634 != nil { + return rt.Value{}, e634 + } + var t635 rt.Value + if t633 { + // «длина» + t636, e637 := rt.BLength(ctx, t630) + if e637 != nil { + return rt.Value{}, e637 + } + t635 = rt.Flag(rt.Equal(t636, shirina)) + } else { + // «длина» + t638, e639 := rt.BLength(ctx, t630) + if e639 != nil { + return rt.Value{}, e639 + } + t635 = rt.Flag(rt.Equal(t638, rt.Number(0.0))) + } + // постусловие «ширина ровно заказанная» + t640, e641 := rt.Post(ctx, t635, "ширина ровно заказанная", "Уместить") + if e641 != nil { + return rt.Value{}, e641 + } + if !t640 { + return rt.Value{}, rt.Fail("FLANG_PROPERTY", "%s", "нарушено свойство «ширина ровно заказанная» функции «Уместить»") + } + return t630, nil +} + +// Sprava — функция flang «Справа». +// +// Тотальная: завершение доказано анализом завершаемости (totality.mjs). +// +// Параметр tekst — «текст»: строка. +// Параметр shirina — «ширина»: число. +// Результат — значение: строка. +func Sprava(ctx *rt.Ctx, tekst rt.Value, shirina rt.Value) (rt.Value, error) { + // «длина» + t642, e643 := rt.BLength(ctx, tekst) + if e643 != nil { + return rt.Value{}, e643 + } + t644, e645 := rt.Sub(ctx, shirina, t642) if e645 != nil { return rt.Value{}, e645 } - var t646 rt.Value - if t644 { - t646 = tekst + t646, e647 := rt.Gt(ctx, t644, rt.Number(0.0)) + if e647 != nil { + return rt.Value{}, e647 + } + t648, e649 := rt.Cond(ctx, t646) + if e649 != nil { + return rt.Value{}, e649 + } + var t650 rt.Value + if t648 { + // «длина» + t651, e652 := rt.BLength(ctx, tekst) + if e652 != nil { + return rt.Value{}, e652 + } + t653, e654 := rt.Sub(ctx, shirina, t651) + if e654 != nil { + return rt.Value{}, e654 + } + t655, e656 := Povtorit(ctx, rt.Text(" "), t653) + if e656 != nil { + return rt.Value{}, e656 + } + t657, e658 := rt.Concat(ctx, t655, tekst) + if e658 != nil { + return rt.Value{}, e658 + } + t650 = t657 + } else { + t659, e660 := Umestit(ctx, tekst, shirina) + if e660 != nil { + return rt.Value{}, e660 + } + t650 = t659 + } + t661 := t650 + t662, e663 := rt.Gt(ctx, shirina, rt.Number(0.0)) + if e663 != nil { + return rt.Value{}, e663 + } + t664, e665 := rt.Cond(ctx, t662) + if e665 != nil { + return rt.Value{}, e665 + } + var t666 rt.Value + if t664 { + // «длина» + t667, e668 := rt.BLength(ctx, t661) + if e668 != nil { + return rt.Value{}, e668 + } + t666 = rt.Flag(rt.Equal(t667, shirina)) + } else { + // «длина» + t669, e670 := rt.BLength(ctx, t661) + if e670 != nil { + return rt.Value{}, e670 + } + t666 = rt.Flag(rt.Equal(t669, rt.Number(0.0))) + } + // постусловие «ширина ровно заказанная» + t671, e672 := rt.Post(ctx, t666, "ширина ровно заказанная", "Справа") + if e672 != nil { + return rt.Value{}, e672 + } + if !t671 { + return rt.Value{}, rt.Fail("FLANG_PROPERTY", "%s", "нарушено свойство «ширина ровно заказанная» функции «Справа»") + } + return t661, nil +} + +// Angliyskiy — функция flang «Английский». +// +// Тотальная: завершение доказано анализом завершаемости (totality.mjs). +// +// Параметр yazyk — «язык»: строка. +// Результат — значение. +func Angliyskiy(ctx *rt.Ctx, yazyk rt.Value) (rt.Value, error) { + return rt.Flag(rt.Equal(yazyk, rt.Text("en"))), nil +} + +// DrobyuYazykom — функция flang «Дробью языком». +// +// Тотальная: завершение доказано анализом завершаемости (totality.mjs). +// +// Параметр znachenie — «значение»: число. +// Параметр znakov — «знаков»: «нат». +// Параметр yazyk — «язык»: строка. +// Результат — значение: строка. +func DrobyuYazykom(ctx *rt.Ctx, znachenie rt.Value, znakov rt.Value, yazyk rt.Value) (rt.Value, error) { + t673, e674 := Angliyskiy(ctx, yazyk) + if e674 != nil { + return rt.Value{}, e674 + } + t675, e676 := rt.Cond(ctx, t673) + if e676 != nil { + return rt.Value{}, e676 + } + var t677 rt.Value + if t675 { + t677 = rt.Text(".") + } else { + t677 = rt.Text(",") + } + return Drobyu(ctx, znachenie, znakov, t677) +} + +// Procentom — функция flang «Процентом». +// +// Тотальная: завершение доказано анализом завершаемости (totality.mjs). +// +// Параметр znachenie — «значение»: число. +// Параметр znakov — «знаков»: «нат». +// Параметр razdelitel — «разделитель»: строка. +// Результат — значение: строка. +func Procentom(ctx *rt.Ctx, znachenie rt.Value, znakov rt.Value, razdelitel rt.Value) (rt.Value, error) { + t678, e679 := Drobyu(ctx, znachenie, znakov, razdelitel) + if e679 != nil { + return rt.Value{}, e679 + } + t680, e681 := rt.Concat(ctx, t678, rt.Text("%")) + if e681 != nil { + return rt.Value{}, e681 + } + return t680, nil +} + +// ProcentomYazykom — функция flang «Процентом языком». +// +// Тотальная: завершение доказано анализом завершаемости (totality.mjs). +// +// Параметр znachenie — «значение»: число. +// Параметр znakov — «знаков»: «нат». +// Параметр yazyk — «язык»: строка. +// Результат — значение: строка. +func ProcentomYazykom(ctx *rt.Ctx, znachenie rt.Value, znakov rt.Value, yazyk rt.Value) (rt.Value, error) { + t682, e683 := Angliyskiy(ctx, yazyk) + if e683 != nil { + return rt.Value{}, e683 + } + t684, e685 := rt.Cond(ctx, t682) + if e685 != nil { + return rt.Value{}, e685 + } + var t686 rt.Value + if t684 { + t686 = rt.Text(".") + } else { + t686 = rt.Text(",") + } + return Procentom(ctx, znachenie, znakov, t686) +} + +// RazryadyYazykom — функция flang «Разряды языком». +// +// Тотальная: завершение доказано анализом завершаемости (totality.mjs). +// +// Параметр znachenie — «значение»: число. +// Параметр yazyk — «язык»: строка. +// Результат — значение: строка. +func RazryadyYazykom(ctx *rt.Ctx, znachenie rt.Value, yazyk rt.Value) (rt.Value, error) { + t687, e688 := Angliyskiy(ctx, yazyk) + if e688 != nil { + return rt.Value{}, e688 + } + t689, e690 := rt.Cond(ctx, t687) + if e690 != nil { + return rt.Value{}, e690 + } + var t691 rt.Value + if t689 { + t691 = rt.Text(",") + } else { + t691 = rt.Text(" ") + } + return Razryadami(ctx, znachenie, t691) +} + +// EdinicaBaytYazykom — функция flang «Единица байт языком». +// +// Тотальная: завершение доказано анализом завершаемости (totality.mjs). +// +// Параметр stupen — «ступень»: число. +// Параметр yazyk — «язык»: строка. +// Результат — значение: строка. +func EdinicaBaytYazykom(ctx *rt.Ctx, stupen rt.Value, yazyk rt.Value) (rt.Value, error) { + t692, e693 := rt.Lte(ctx, stupen, rt.Number(-1.0)) + if e693 != nil { + return rt.Value{}, e693 + } + t694, e695 := rt.Cond(ctx, t692) + if e695 != nil { + return rt.Value{}, e695 + } + if t694 { + t696, e697 := Angliyskiy(ctx, yazyk) + if e697 != nil { + return rt.Value{}, e697 + } + t698, e699 := rt.Cond(ctx, t696) + if e699 != nil { + return rt.Value{}, e699 + } + if t698 { + return rt.Text("B"), nil + } else { + return rt.Text("Б"), nil + } + } else { + t700, e701 := rt.Gte(ctx, stupen, rt.Number(4.0)) + if e701 != nil { + return rt.Value{}, e701 + } + t702, e703 := rt.Cond(ctx, t700) + if e703 != nil { + return rt.Value{}, e703 + } + if t702 { + t704, e705 := Angliyskiy(ctx, yazyk) + if e705 != nil { + return rt.Value{}, e705 + } + t706, e707 := rt.Cond(ctx, t704) + if e707 != nil { + return rt.Value{}, e707 + } + if t706 { + return rt.Text("PiB"), nil + } else { + return rt.Text("ПиБ"), nil + } + } else { + t708, e709 := Angliyskiy(ctx, yazyk) + if e709 != nil { + return rt.Value{}, e709 + } + t710, e711 := rt.Cond(ctx, t708) + if e711 != nil { + return rt.Value{}, e711 + } + if t710 { + t712, e713 := rt.Add(ctx, stupen, rt.Number(1.0)) + if e713 != nil { + return rt.Value{}, e713 + } + t714 := make([]rt.Value, 5) + t714[0] = rt.Text("KiB") + t714[1] = rt.Text("MiB") + t714[2] = rt.Text("GiB") + t714[3] = rt.Text("TiB") + t714[4] = rt.Text("PiB") + // «элемент» + t715, e716 := rt.BElement(ctx, t712, rt.List(t714)) + if e716 != nil { + return rt.Value{}, e716 + } + return t715, nil + } else { + t717, e718 := rt.Add(ctx, stupen, rt.Number(1.0)) + if e718 != nil { + return rt.Value{}, e718 + } + t719 := make([]rt.Value, 5) + t719[0] = rt.Text("КиБ") + t719[1] = rt.Text("МиБ") + t719[2] = rt.Text("ГиБ") + t719[3] = rt.Text("ТиБ") + t719[4] = rt.Text("ПиБ") + // «элемент» + t720, e721 := rt.BElement(ctx, t717, rt.List(t719)) + if e721 != nil { + return rt.Value{}, e721 + } + return t720, nil + } + } + } +} + +// BaytyYazykom — функция flang «Байты языком». +// +// Тотальная: завершение доказано анализом завершаемости (totality.mjs). +// +// Параметр skolko — «сколько»: число. +// Параметр razdelitel — «разделитель»: строка. +// Параметр yazyk — «язык»: строка. +// Результат — значение: строка. +func BaytyYazykom(ctx *rt.Ctx, skolko rt.Value, razdelitel rt.Value, yazyk rt.Value) (rt.Value, error) { + t722, e723 := rt.Lt(ctx, skolko, rt.Number(0.0)) + if e723 != nil { + return rt.Value{}, e723 + } + t724, e725 := rt.Cond(ctx, t722) + if e725 != nil { + return rt.Value{}, e725 + } + var t726 rt.Value + if t724 { + t726 = rt.Text("-") + } else { + t726 = rt.Text("") + } + // пусть «знак» + znak := t726 + t727, e728 := rt.Lt(ctx, skolko, rt.Number(0.0)) + if e728 != nil { + return rt.Value{}, e728 + } + t729, e730 := rt.Cond(ctx, t727) + if e730 != nil { + return rt.Value{}, e730 + } + var t731 rt.Value + if t729 { + t732, e733 := rt.Sub(ctx, rt.Number(0.0), skolko) + if e733 != nil { + return rt.Value{}, e733 + } + t731 = t732 + } else { + t731 = skolko + } + t734, e735 := CeloeKNulyu(ctx, t731) + if e735 != nil { + return rt.Value{}, e735 + } + // пусть «величина» + velichina := t734 + t736, e737 := rt.Lt(ctx, velichina, rt.Number(1024.0)) + if e737 != nil { + return rt.Value{}, e737 + } + t738, e739 := rt.Cond(ctx, t736) + if e739 != nil { + return rt.Value{}, e739 + } + if t738 { + // «к строке» + t740, e741 := rt.BToString(ctx, velichina) + if e741 != nil { + return rt.Value{}, e741 + } + t742, e743 := rt.Concat(ctx, znak, t740) + if e743 != nil { + return rt.Value{}, e743 + } + t744, e745 := rt.Concat(ctx, t742, rt.Text(" ")) + if e745 != nil { + return rt.Value{}, e745 + } + t746, e747 := EdinicaBaytYazykom(ctx, rt.Number(-1.0), yazyk) + if e747 != nil { + return rt.Value{}, e747 + } + t748, e749 := rt.Concat(ctx, t744, t746) + if e749 != nil { + return rt.Value{}, e749 + } + return t748, nil + } else { + t750, e751 := DelenieNacelo(ctx, velichina, rt.Number(1024.0)) + if e751 != nil { + return rt.Value{}, e751 + } + // пусть «витки» + vitki := t750 + t752, e753 := DelitelBayt(ctx, vitki, rt.Number(4.0)) + if e753 != nil { + return rt.Value{}, e753 + } + t754, e755 := rt.Div(ctx, velichina, t752) + if e755 != nil { + return rt.Value{}, e755 + } + t756, e757 := Drobyu(ctx, t754, rt.Number(1.0), razdelitel) + if e757 != nil { + return rt.Value{}, e757 + } + t758, e759 := rt.Concat(ctx, znak, t756) + if e759 != nil { + return rt.Value{}, e759 + } + t760, e761 := rt.Concat(ctx, t758, rt.Text(" ")) + if e761 != nil { + return rt.Value{}, e761 + } + t762, e763 := StupenBayt(ctx, vitki, rt.Number(4.0)) + if e763 != nil { + return rt.Value{}, e763 + } + t764, e765 := EdinicaBaytYazykom(ctx, t762, yazyk) + if e765 != nil { + return rt.Value{}, e765 + } + t766, e767 := rt.Concat(ctx, t760, t764) + if e767 != nil { + return rt.Value{}, e767 + } + return t766, nil + } +} + +// BaytyKakEst — функция flang «Байты как есть». +// +// Тотальная: завершение доказано анализом завершаемости (totality.mjs). +// +// Параметр skolko — «сколько»: число. +// Параметр yazyk — «язык»: строка. +// Результат — значение: строка. +func BaytyKakEst(ctx *rt.Ctx, skolko rt.Value, yazyk rt.Value) (rt.Value, error) { + t768, e769 := RazryadyYazykom(ctx, skolko, yazyk) + if e769 != nil { + return rt.Value{}, e769 + } + t770, e771 := rt.Concat(ctx, t768, rt.Text(" ")) + if e771 != nil { + return rt.Value{}, e771 + } + t772, e773 := EdinicaBaytYazykom(ctx, rt.Number(-1.0), yazyk) + if e773 != nil { + return rt.Value{}, e773 + } + t774, e775 := rt.Concat(ctx, t770, t772) + if e775 != nil { + return rt.Value{}, e775 + } + return t774, nil +} + +// SlovoDney — функция flang «Слово дней». +// +// Тотальная: завершение доказано анализом завершаемости (totality.mjs). +// +// Параметр yazyk — «язык»: строка. +// Результат — значение: строка. +func SlovoDney(ctx *rt.Ctx, yazyk rt.Value) (rt.Value, error) { + t776, e777 := Angliyskiy(ctx, yazyk) + if e777 != nil { + return rt.Value{}, e777 + } + t778, e779 := rt.Cond(ctx, t776) + if e779 != nil { + return rt.Value{}, e779 + } + if t778 { + return rt.Text("d"), nil + } else { + return rt.Text("дн"), nil + } +} + +// Dney — функция flang «Дней». +// +// Тотальная: завершение доказано анализом завершаемости (totality.mjs). +// +// Параметр skolko — «сколько»: число. +// Параметр yazyk — «язык»: строка. +// Результат — значение: строка. +func Dney(ctx *rt.Ctx, skolko rt.Value, yazyk rt.Value) (rt.Value, error) { + t780, e781 := DrobyuYazykom(ctx, skolko, rt.Number(0.0), yazyk) + if e781 != nil { + return rt.Value{}, e781 + } + t782, e783 := rt.Concat(ctx, t780, rt.Text(" ")) + if e783 != nil { + return rt.Value{}, e783 + } + t784, e785 := SlovoDney(ctx, yazyk) + if e785 != nil { + return rt.Value{}, e785 + } + t786, e787 := rt.Concat(ctx, t782, t784) + if e787 != nil { + return rt.Value{}, e787 + } + return t786, nil +} + +// Nazad — функция flang «Назад». +// +// Тотальная: завершение доказано анализом завершаемости (totality.mjs). +// +// Параметр sekundy — «секунды»: число. +// Параметр yazyk — «язык»: строка. +// Результат — значение: строка. +func Nazad(ctx *rt.Ctx, sekundy rt.Value, yazyk rt.Value) (rt.Value, error) { + t788, e789 := rt.Lt(ctx, sekundy, rt.Number(60.0)) + if e789 != nil { + return rt.Value{}, e789 + } + t790, e791 := rt.Cond(ctx, t788) + if e791 != nil { + return rt.Value{}, e791 + } + if t790 { + t792, e793 := CeloeKNulyu(ctx, sekundy) + if e793 != nil { + return rt.Value{}, e793 + } + // «к строке» + t794, e795 := rt.BToString(ctx, t792) + if e795 != nil { + return rt.Value{}, e795 + } + t796, e797 := rt.Concat(ctx, t794, rt.Text(" ")) + if e797 != nil { + return rt.Value{}, e797 + } + t798, e799 := Angliyskiy(ctx, yazyk) + if e799 != nil { + return rt.Value{}, e799 + } + t800, e801 := rt.Cond(ctx, t798) + if e801 != nil { + return rt.Value{}, e801 + } + var t802 rt.Value + if t800 { + t802 = rt.Text("s") + } else { + t802 = rt.Text("с") + } + t803, e804 := rt.Concat(ctx, t796, t802) + if e804 != nil { + return rt.Value{}, e804 + } + return t803, nil + } else { + t805, e806 := rt.Lt(ctx, sekundy, rt.Number(3600.0)) + if e806 != nil { + return rt.Value{}, e806 + } + t807, e808 := rt.Cond(ctx, t805) + if e808 != nil { + return rt.Value{}, e808 + } + if t807 { + t809, e810 := rt.Div(ctx, sekundy, rt.Number(60.0)) + if e810 != nil { + return rt.Value{}, e810 + } + t811, e812 := CeloeKNulyu(ctx, t809) + if e812 != nil { + return rt.Value{}, e812 + } + // «к строке» + t813, e814 := rt.BToString(ctx, t811) + if e814 != nil { + return rt.Value{}, e814 + } + t815, e816 := rt.Concat(ctx, t813, rt.Text(" ")) + if e816 != nil { + return rt.Value{}, e816 + } + t817, e818 := Angliyskiy(ctx, yazyk) + if e818 != nil { + return rt.Value{}, e818 + } + t819, e820 := rt.Cond(ctx, t817) + if e820 != nil { + return rt.Value{}, e820 + } + var t821 rt.Value + if t819 { + t821 = rt.Text("min") + } else { + t821 = rt.Text("мин") + } + t822, e823 := rt.Concat(ctx, t815, t821) + if e823 != nil { + return rt.Value{}, e823 + } + return t822, nil + } else { + t824, e825 := rt.Lt(ctx, sekundy, rt.Number(172800.0)) + if e825 != nil { + return rt.Value{}, e825 + } + t826, e827 := rt.Cond(ctx, t824) + if e827 != nil { + return rt.Value{}, e827 + } + if t826 { + t828, e829 := rt.Div(ctx, sekundy, rt.Number(3600.0)) + if e829 != nil { + return rt.Value{}, e829 + } + t830, e831 := CeloeKNulyu(ctx, t828) + if e831 != nil { + return rt.Value{}, e831 + } + // «к строке» + t832, e833 := rt.BToString(ctx, t830) + if e833 != nil { + return rt.Value{}, e833 + } + t834, e835 := rt.Concat(ctx, t832, rt.Text(" ")) + if e835 != nil { + return rt.Value{}, e835 + } + t836, e837 := Angliyskiy(ctx, yazyk) + if e837 != nil { + return rt.Value{}, e837 + } + t838, e839 := rt.Cond(ctx, t836) + if e839 != nil { + return rt.Value{}, e839 + } + var t840 rt.Value + if t838 { + t840 = rt.Text("h") + } else { + t840 = rt.Text("ч") + } + t841, e842 := rt.Concat(ctx, t834, t840) + if e842 != nil { + return rt.Value{}, e842 + } + return t841, nil + } else { + t843, e844 := rt.Div(ctx, sekundy, rt.Number(86400.0)) + if e844 != nil { + return rt.Value{}, e844 + } + t845, e846 := CeloeKNulyu(ctx, t843) + if e846 != nil { + return rt.Value{}, e846 + } + // «к строке» + t847, e848 := rt.BToString(ctx, t845) + if e848 != nil { + return rt.Value{}, e848 + } + t849, e850 := rt.Concat(ctx, t847, rt.Text(" ")) + if e850 != nil { + return rt.Value{}, e850 + } + t851, e852 := Angliyskiy(ctx, yazyk) + if e852 != nil { + return rt.Value{}, e852 + } + t853, e854 := rt.Cond(ctx, t851) + if e854 != nil { + return rt.Value{}, e854 + } + var t855 rt.Value + if t853 { + t855 = rt.Text("d") + } else { + t855 = rt.Text("дн") + } + t856, e857 := rt.Concat(ctx, t849, t855) + if e857 != nil { + return rt.Value{}, e857 + } + return t856, nil + } + } + } +} + +// ZanyaloYazykom — функция flang «Заняло языком». +// +// Тотальная: завершение доказано анализом завершаемости (totality.mjs). +// +// Параметр millisekundy — «миллисекунды»: число. +// Параметр razdelitel — «разделитель»: строка. +// Параметр yazyk — «язык»: строка. +// Результат — значение: строка. +func ZanyaloYazykom(ctx *rt.Ctx, millisekundy rt.Value, razdelitel rt.Value, yazyk rt.Value) (rt.Value, error) { + t858, e859 := rt.Lt(ctx, millisekundy, rt.Number(1000.0)) + if e859 != nil { + return rt.Value{}, e859 + } + t860, e861 := rt.Cond(ctx, t858) + if e861 != nil { + return rt.Value{}, e861 + } + if t860 { + t862, e863 := CeloeKNulyu(ctx, millisekundy) + if e863 != nil { + return rt.Value{}, e863 + } + // «к строке» + t864, e865 := rt.BToString(ctx, t862) + if e865 != nil { + return rt.Value{}, e865 + } + t866, e867 := rt.Concat(ctx, t864, rt.Text(" ")) + if e867 != nil { + return rt.Value{}, e867 + } + t868, e869 := Angliyskiy(ctx, yazyk) + if e869 != nil { + return rt.Value{}, e869 + } + t870, e871 := rt.Cond(ctx, t868) + if e871 != nil { + return rt.Value{}, e871 + } + var t872 rt.Value + if t870 { + t872 = rt.Text("ms") + } else { + t872 = rt.Text("мс") + } + t873, e874 := rt.Concat(ctx, t866, t872) + if e874 != nil { + return rt.Value{}, e874 + } + return t873, nil } else { - t647, e648 := rt.Lte(ctx, skolko, rt.Number(0.0)) - if e648 != nil { - return rt.Value{}, e648 + t875, e876 := rt.Div(ctx, millisekundy, rt.Number(1000.0)) + if e876 != nil { + return rt.Value{}, e876 + } + t877, e878 := Drobyu(ctx, t875, rt.Number(1.0), razdelitel) + if e878 != nil { + return rt.Value{}, e878 + } + t879, e880 := rt.Concat(ctx, t877, rt.Text(" ")) + if e880 != nil { + return rt.Value{}, e880 + } + t881, e882 := Angliyskiy(ctx, yazyk) + if e882 != nil { + return rt.Value{}, e882 + } + t883, e884 := rt.Cond(ctx, t881) + if e884 != nil { + return rt.Value{}, e884 + } + var t885 rt.Value + if t883 { + t885 = rt.Text("s") + } else { + t885 = rt.Text("с") + } + t886, e887 := rt.Concat(ctx, t879, t885) + if e887 != nil { + return rt.Value{}, e887 + } + return t886, nil + } +} + +// BezHvostovyhNuley — функция flang «Без хвостовых нулей». +// +// Тотальная: завершение доказано анализом завершаемости (totality.mjs). +// +// Хвостовой самовызов развёрнут в цикл: стек не растёт. +// +// Рекурсивная: считает глубину, на превышении — FLANG_RECURSION_LIMIT. +// +// Параметр tekst — «текст»: строка. +// Параметр zapas — «запас»: «нат». +// Результат — значение: строка. +func BezHvostovyhNuley(ctx *rt.Ctx, tekst rt.Value, zapas rt.Value) (rt.Value, error) { + if err := ctx.Enter("Без хвостовых нулей"); err != nil { + return rt.Value{}, err + } + defer ctx.Leave() + for { + t888, e889 := rt.Lte(ctx, zapas, rt.Number(0.0)) + if e889 != nil { + return rt.Value{}, e889 } - t649, e650 := rt.Cond(ctx, t647) - if e650 != nil { - return rt.Value{}, e650 + t890, e891 := rt.Cond(ctx, t888) + if e891 != nil { + return rt.Value{}, e891 } - var t651 rt.Value - if t649 { - t651 = rt.Text("") + if t890 { + return tekst, nil } else { - t652, e653 := rt.Lte(ctx, skolko, rt.Number(1.0)) - if e653 != nil { - return rt.Value{}, e653 + // «длина» + t892, e893 := rt.BLength(ctx, tekst) + if e893 != nil { + return rt.Value{}, e893 } - t654, e655 := rt.Cond(ctx, t652) - if e655 != nil { - return rt.Value{}, e655 + t894, e895 := rt.Lte(ctx, t892, rt.Number(0.0)) + if e895 != nil { + return rt.Value{}, e895 } - var t656 rt.Value - if t654 { - // «подстрока» - t657, e658 := rt.BSubstring(ctx, tekst, rt.Number(1.0), rt.Number(1.0)) - if e658 != nil { - return rt.Value{}, e658 - } - t656 = t657 + t896, e897 := rt.Cond(ctx, t894) + if e897 != nil { + return rt.Value{}, e897 + } + if t896 { + return tekst, nil } else { - t659, e660 := rt.Sub(ctx, skolko, rt.Number(1.0)) - if e660 != nil { - return rt.Value{}, e660 + // «длина» + t898, e899 := rt.BLength(ctx, tekst) + if e899 != nil { + return rt.Value{}, e899 + } + // «длина» + t900, e901 := rt.BLength(ctx, tekst) + if e901 != nil { + return rt.Value{}, e901 } // «подстрока» - t661, e662 := rt.BSubstring(ctx, tekst, rt.Number(1.0), t659) - if e662 != nil { - return rt.Value{}, e662 + t902, e903 := rt.BSubstring(ctx, tekst, t898, t900) + if e903 != nil { + return rt.Value{}, e903 + } + t904, e905 := rt.Cond(ctx, rt.Flag(rt.Equal(t902, rt.Text("0")))) + if e905 != nil { + return rt.Value{}, e905 } - t663, e664 := rt.Concat(ctx, t661, rt.Text("…")) - if e664 != nil { - return rt.Value{}, e664 + if t904 { + // «длина» + t906, e907 := rt.BLength(ctx, tekst) + if e907 != nil { + return rt.Value{}, e907 + } + t908, e909 := rt.Sub(ctx, t906, rt.Number(1.0)) + if e909 != nil { + return rt.Value{}, e909 + } + // «подстрока» + t910, e911 := rt.BSubstring(ctx, tekst, rt.Number(1.0), t908) + if e911 != nil { + return rt.Value{}, e911 + } + t912, e913 := rt.Sub(ctx, zapas, rt.Number(1.0)) + if e913 != nil { + return rt.Value{}, e913 + } + tekst = t910 + zapas = t912 + // виток цикла — тоже шаг вычисления: незавершающийся хвостовой + // самовызов обязан упереться в лимит, а не крутиться вечно + if e914 := ctx.Step("Без хвостовых нулей"); e914 != nil { + return rt.Value{}, e914 + } + continue + } else { + return tekst, nil } - t656 = t663 } - t651 = t656 } - t646 = t651 } - t665 := t646 - t666, e667 := rt.Gte(ctx, skolko, rt.Number(0.0)) - if e667 != nil { - return rt.Value{}, e667 +} + +// BezHvostovogoRazdelitelya — функция flang «Без хвостового разделителя». +// +// Тотальная: завершение доказано анализом завершаемости (totality.mjs). +// +// Параметр tekst — «текст»: строка. +// Параметр razdelitel — «разделитель»: строка. +// Результат — значение: строка. +func BezHvostovogoRazdelitelya(ctx *rt.Ctx, tekst rt.Value, razdelitel rt.Value) (rt.Value, error) { + // «длина» + t915, e916 := rt.BLength(ctx, tekst) + if e916 != nil { + return rt.Value{}, e916 + } + t917, e918 := rt.Lte(ctx, t915, rt.Number(0.0)) + if e918 != nil { + return rt.Value{}, e918 } - t668, e669 := rt.Cond(ctx, t666) - if e669 != nil { - return rt.Value{}, e669 + t919, e920 := rt.Cond(ctx, t917) + if e920 != nil { + return rt.Value{}, e920 } - var t670 rt.Value - if t668 { + var t921 rt.Value + if t919 { + t921 = tekst + } else { // «длина» - t671, e672 := rt.BLength(ctx, t665) - if e672 != nil { - return rt.Value{}, e672 + t922, e923 := rt.BLength(ctx, tekst) + if e923 != nil { + return rt.Value{}, e923 } // «длина» - t673, e674 := rt.BLength(ctx, tekst) - if e674 != nil { - return rt.Value{}, e674 + t924, e925 := rt.BLength(ctx, tekst) + if e925 != nil { + return rt.Value{}, e925 } - t675, e676 := rt.Lte(ctx, t673, skolko) - if e676 != nil { - return rt.Value{}, e676 + // «подстрока» + t926, e927 := rt.BSubstring(ctx, tekst, t922, t924) + if e927 != nil { + return rt.Value{}, e927 } - t677, e678 := rt.Cond(ctx, t675) - if e678 != nil { - return rt.Value{}, e678 + t928, e929 := rt.Cond(ctx, rt.Flag(rt.Equal(t926, razdelitel))) + if e929 != nil { + return rt.Value{}, e929 } - var t679 rt.Value - if t677 { + var t930 rt.Value + if t928 { // «длина» - t680, e681 := rt.BLength(ctx, tekst) - if e681 != nil { - return rt.Value{}, e681 + t931, e932 := rt.BLength(ctx, tekst) + if e932 != nil { + return rt.Value{}, e932 + } + t933, e934 := rt.Sub(ctx, t931, rt.Number(1.0)) + if e934 != nil { + return rt.Value{}, e934 } - t679 = t680 + // «подстрока» + t935, e936 := rt.BSubstring(ctx, tekst, rt.Number(1.0), t933) + if e936 != nil { + return rt.Value{}, e936 + } + t930 = t935 + } else { + t930 = tekst + } + t921 = t930 + } + t937 := t921 + // «длина» + t938, e939 := rt.BLength(ctx, t937) + if e939 != nil { + return rt.Value{}, e939 + } + // «длина» + t940, e941 := rt.BLength(ctx, tekst) + if e941 != nil { + return rt.Value{}, e941 + } + t942, e943 := rt.Lte(ctx, t938, t940) + if e943 != nil { + return rt.Value{}, e943 + } + // постусловие «длиннее не становится» + t944, e945 := rt.Post(ctx, t942, "длиннее не становится", "Без хвостового разделителя") + if e945 != nil { + return rt.Value{}, e945 + } + if !t944 { + return rt.Value{}, rt.Fail("FLANG_PROPERTY", "%s", "нарушено свойство «длиннее не становится» функции «Без хвостового разделителя»") + } + return t937, nil +} + +// Kazhdye — функция flang «Каждые». +// +// Тотальная: завершение доказано анализом завершаемости (totality.mjs). +// +// Параметр millisekundy — «миллисекунды»: число. +// Параметр yazyk — «язык»: строка. +// Результат — значение: строка. +func Kazhdye(ctx *rt.Ctx, millisekundy rt.Value, yazyk rt.Value) (rt.Value, error) { + t946, e947 := rt.Lt(ctx, millisekundy, rt.Number(1000.0)) + if e947 != nil { + return rt.Value{}, e947 + } + t948, e949 := rt.Cond(ctx, t946) + if e949 != nil { + return rt.Value{}, e949 + } + if t948 { + t950, e951 := CeloeKNulyu(ctx, millisekundy) + if e951 != nil { + return rt.Value{}, e951 + } + // «к строке» + t952, e953 := rt.BToString(ctx, t950) + if e953 != nil { + return rt.Value{}, e953 + } + t954, e955 := rt.Concat(ctx, t952, rt.Text(" ")) + if e955 != nil { + return rt.Value{}, e955 + } + t956, e957 := Angliyskiy(ctx, yazyk) + if e957 != nil { + return rt.Value{}, e957 + } + t958, e959 := rt.Cond(ctx, t956) + if e959 != nil { + return rt.Value{}, e959 + } + var t960 rt.Value + if t958 { + t960 = rt.Text("ms") } else { - t679 = skolko + t960 = rt.Text("мс") } - t682, e683 := rt.Lte(ctx, t671, t679) - if e683 != nil { - return rt.Value{}, e683 + t961, e962 := rt.Concat(ctx, t954, t960) + if e962 != nil { + return rt.Value{}, e962 } - t670 = t682 + return t961, nil } else { - t670 = rt.Flag(true) + t963, e964 := Angliyskiy(ctx, yazyk) + if e964 != nil { + return rt.Value{}, e964 + } + t965, e966 := rt.Cond(ctx, t963) + if e966 != nil { + return rt.Value{}, e966 + } + var t967 rt.Value + if t965 { + t967 = rt.Text(".") + } else { + t967 = rt.Text(",") + } + // пусть «разделитель» + razdelitel := t967 + t968, e969 := rt.Div(ctx, millisekundy, rt.Number(1000.0)) + if e969 != nil { + return rt.Value{}, e969 + } + t970, e971 := Drobyu(ctx, t968, rt.Number(3.0), razdelitel) + if e971 != nil { + return rt.Value{}, e971 + } + // пусть «с тремя знаками» + sTremyaZnakami := t970 + t972, e973 := BezHvostovyhNuley(ctx, sTremyaZnakami, rt.Number(3.0)) + if e973 != nil { + return rt.Value{}, e973 + } + // пусть «без нулей» + bezNuley := t972 + t974, e975 := BezHvostovogoRazdelitelya(ctx, bezNuley, razdelitel) + if e975 != nil { + return rt.Value{}, e975 + } + t976, e977 := rt.Concat(ctx, t974, rt.Text(" ")) + if e977 != nil { + return rt.Value{}, e977 + } + t978, e979 := Angliyskiy(ctx, yazyk) + if e979 != nil { + return rt.Value{}, e979 + } + t980, e981 := rt.Cond(ctx, t978) + if e981 != nil { + return rt.Value{}, e981 + } + var t982 rt.Value + if t980 { + t982 = rt.Text("s") + } else { + t982 = rt.Text("с") + } + t983, e984 := rt.Concat(ctx, t976, t982) + if e984 != nil { + return rt.Value{}, e984 + } + return t983, nil } - // постусловие «обрезанное не длиннее заказа» - t684, e685 := rt.Post(ctx, t670, "обрезанное не длиннее заказа", "Обрезать") - if e685 != nil { - return rt.Value{}, e685 +} + +// DlitelnostYazykom — функция flang «Длительность языком». +// +// Тотальная: завершение доказано анализом завершаемости (totality.mjs). +// +// Параметр sekundy — «секунды»: число. +// Параметр yazyk — «язык»: строка. +// Результат — значение: строка. +func DlitelnostYazykom(ctx *rt.Ctx, sekundy rt.Value, yazyk rt.Value) (rt.Value, error) { + t985, e986 := rt.Lte(ctx, sekundy, rt.Number(0.0)) + if e986 != nil { + return rt.Value{}, e986 } - if !t684 { - return rt.Value{}, rt.Fail("FLANG_PROPERTY", "%s", "нарушено свойство «обрезанное не длиннее заказа» функции «Обрезать»") + t987, e988 := rt.Cond(ctx, t985) + if e988 != nil { + return rt.Value{}, e988 + } + if t987 { + return rt.Text(""), nil + } else { + t989, e990 := CeloeKNulyu(ctx, sekundy) + if e990 != nil { + return rt.Value{}, e990 + } + // пусть «всего» + vsego := t989 + t991, e992 := DelenieNacelo(ctx, vsego, rt.Number(3600.0)) + if e992 != nil { + return rt.Value{}, e992 + } + // пусть «часов всего» + chasovVsego := t991 + t993, e994 := DelenieNacelo(ctx, chasovVsego, rt.Number(24.0)) + if e994 != nil { + return rt.Value{}, e994 + } + // пусть «дней» + dney := t993 + t995, e996 := rt.Mod(ctx, chasovVsego, rt.Number(24.0)) + if e996 != nil { + return rt.Value{}, e996 + } + // пусть «часов» + chasov := t995 + t997, e998 := DelenieNacelo(ctx, vsego, rt.Number(60.0)) + if e998 != nil { + return rt.Value{}, e998 + } + t999, e1000 := rt.Mod(ctx, t997, rt.Number(60.0)) + if e1000 != nil { + return rt.Value{}, e1000 + } + // пусть «минут» + minut := t999 + // «к строке» + t1001, e1002 := rt.BToString(ctx, chasov) + if e1002 != nil { + return rt.Value{}, e1002 + } + t1003, e1004 := SlevaNulyami(ctx, t1001, rt.Number(2.0)) + if e1004 != nil { + return rt.Value{}, e1004 + } + t1005, e1006 := rt.Concat(ctx, t1003, rt.Text(":")) + if e1006 != nil { + return rt.Value{}, e1006 + } + // «к строке» + t1007, e1008 := rt.BToString(ctx, minut) + if e1008 != nil { + return rt.Value{}, e1008 + } + t1009, e1010 := SlevaNulyami(ctx, t1007, rt.Number(2.0)) + if e1010 != nil { + return rt.Value{}, e1010 + } + t1011, e1012 := rt.Concat(ctx, t1005, t1009) + if e1012 != nil { + return rt.Value{}, e1012 + } + // пусть «время» + vremya := t1011 + t1013, e1014 := rt.Gt(ctx, dney, rt.Number(0.0)) + if e1014 != nil { + return rt.Value{}, e1014 + } + t1015, e1016 := rt.Cond(ctx, t1013) + if e1016 != nil { + return rt.Value{}, e1016 + } + if t1015 { + // «к строке» + t1017, e1018 := rt.BToString(ctx, dney) + if e1018 != nil { + return rt.Value{}, e1018 + } + t1019, e1020 := Angliyskiy(ctx, yazyk) + if e1020 != nil { + return rt.Value{}, e1020 + } + t1021, e1022 := rt.Cond(ctx, t1019) + if e1022 != nil { + return rt.Value{}, e1022 + } + var t1023 rt.Value + if t1021 { + t1023 = rt.Text("d") + } else { + t1023 = rt.Text("д") + } + t1024, e1025 := rt.Concat(ctx, t1017, t1023) + if e1025 != nil { + return rt.Value{}, e1025 + } + t1026, e1027 := rt.Concat(ctx, t1024, rt.Text(" ")) + if e1027 != nil { + return rt.Value{}, e1027 + } + t1028, e1029 := rt.Concat(ctx, t1026, vremya) + if e1029 != nil { + return rt.Value{}, e1029 + } + return t1028, nil + } else { + return vremya, nil + } } - return t665, nil } // MeraUbyvaet — функция flang «мера убывает». @@ -2193,20 +3305,20 @@ func Obrezat(ctx *rt.Ctx, tekst rt.Value, skolko rt.Value) (rt.Value, error) { // Параметр mera — «мера»: число. // Результат — значение: число. func MeraUbyvaet(ctx *rt.Ctx, shag rt.Value, mera rt.Value) (rt.Value, error) { - t686 := shag - t687, e688 := rt.Lt(ctx, t686, mera) - if e688 != nil { - return rt.Value{}, e688 + t1030 := shag + t1031, e1032 := rt.Lt(ctx, t1030, mera) + if e1032 != nil { + return rt.Value{}, e1032 } // постусловие «мера убывает» - t689, e690 := rt.Post(ctx, t687, "мера убывает", "мера убывает") - if e690 != nil { - return rt.Value{}, e690 + t1033, e1034 := rt.Post(ctx, t1031, "мера убывает", "мера убывает") + if e1034 != nil { + return rt.Value{}, e1034 } - if !t689 { + if !t1033 { return rt.Value{}, rt.Fail("FLANG_MEASURE", "%s", "тотальная функция «Повторить»: мера не убыла — аргумент 2 вызова «Повторить» не стал меньше параметра «сколько». Завершение доказано убыванием этой меры, а числа flang — IEEE-754 double: при большом |«сколько»| постоянный шаг не меняет значение, и спуск не идёт. Отказ здесь честнее зацикливания") } - return t686, nil + return t1030, nil } // Call — вызов функции по её исходному имени flang. @@ -2279,6 +3391,13 @@ func Call(ctx *rt.Ctx, name string, args []rt.Value) (rt.Value, error) { "Округлить к чётному", 2, len(args)) } return OkruglitKChyotnomu(ctx, args[0], args[1]) + case "Знак минус": + if len(args) != 1 { + return rt.Value{}, rt.Fail(rt.CodeType, + "функция «%s» принимает %d аргум., получено %d", + "Знак минус", 1, len(args)) + } + return ZnakMinus(ctx, args[0]) case "Дробью": if len(args) != 3 { return rt.Value{}, rt.Fail(rt.CodeType, @@ -2447,6 +3566,132 @@ func Call(ctx *rt.Ctx, name string, args []rt.Value) (rt.Value, error) { "Обрезать", 2, len(args)) } return Obrezat(ctx, args[0], args[1]) + case "Уместить": + if len(args) != 2 { + return rt.Value{}, rt.Fail(rt.CodeType, + "функция «%s» принимает %d аргум., получено %d", + "Уместить", 2, len(args)) + } + return Umestit(ctx, args[0], args[1]) + case "Справа": + if len(args) != 2 { + return rt.Value{}, rt.Fail(rt.CodeType, + "функция «%s» принимает %d аргум., получено %d", + "Справа", 2, len(args)) + } + return Sprava(ctx, args[0], args[1]) + case "Английский": + if len(args) != 1 { + return rt.Value{}, rt.Fail(rt.CodeType, + "функция «%s» принимает %d аргум., получено %d", + "Английский", 1, len(args)) + } + return Angliyskiy(ctx, args[0]) + case "Дробью языком": + if len(args) != 3 { + return rt.Value{}, rt.Fail(rt.CodeType, + "функция «%s» принимает %d аргум., получено %d", + "Дробью языком", 3, len(args)) + } + return DrobyuYazykom(ctx, args[0], args[1], args[2]) + case "Процентом": + if len(args) != 3 { + return rt.Value{}, rt.Fail(rt.CodeType, + "функция «%s» принимает %d аргум., получено %d", + "Процентом", 3, len(args)) + } + return Procentom(ctx, args[0], args[1], args[2]) + case "Процентом языком": + if len(args) != 3 { + return rt.Value{}, rt.Fail(rt.CodeType, + "функция «%s» принимает %d аргум., получено %d", + "Процентом языком", 3, len(args)) + } + return ProcentomYazykom(ctx, args[0], args[1], args[2]) + case "Разряды языком": + if len(args) != 2 { + return rt.Value{}, rt.Fail(rt.CodeType, + "функция «%s» принимает %d аргум., получено %d", + "Разряды языком", 2, len(args)) + } + return RazryadyYazykom(ctx, args[0], args[1]) + case "Единица байт языком": + if len(args) != 2 { + return rt.Value{}, rt.Fail(rt.CodeType, + "функция «%s» принимает %d аргум., получено %d", + "Единица байт языком", 2, len(args)) + } + return EdinicaBaytYazykom(ctx, args[0], args[1]) + case "Байты языком": + if len(args) != 3 { + return rt.Value{}, rt.Fail(rt.CodeType, + "функция «%s» принимает %d аргум., получено %d", + "Байты языком", 3, len(args)) + } + return BaytyYazykom(ctx, args[0], args[1], args[2]) + case "Байты как есть": + if len(args) != 2 { + return rt.Value{}, rt.Fail(rt.CodeType, + "функция «%s» принимает %d аргум., получено %d", + "Байты как есть", 2, len(args)) + } + return BaytyKakEst(ctx, args[0], args[1]) + case "Слово дней": + if len(args) != 1 { + return rt.Value{}, rt.Fail(rt.CodeType, + "функция «%s» принимает %d аргум., получено %d", + "Слово дней", 1, len(args)) + } + return SlovoDney(ctx, args[0]) + case "Дней": + if len(args) != 2 { + return rt.Value{}, rt.Fail(rt.CodeType, + "функция «%s» принимает %d аргум., получено %d", + "Дней", 2, len(args)) + } + return Dney(ctx, args[0], args[1]) + case "Назад": + if len(args) != 2 { + return rt.Value{}, rt.Fail(rt.CodeType, + "функция «%s» принимает %d аргум., получено %d", + "Назад", 2, len(args)) + } + return Nazad(ctx, args[0], args[1]) + case "Заняло языком": + if len(args) != 3 { + return rt.Value{}, rt.Fail(rt.CodeType, + "функция «%s» принимает %d аргум., получено %d", + "Заняло языком", 3, len(args)) + } + return ZanyaloYazykom(ctx, args[0], args[1], args[2]) + case "Без хвостовых нулей": + if len(args) != 2 { + return rt.Value{}, rt.Fail(rt.CodeType, + "функция «%s» принимает %d аргум., получено %d", + "Без хвостовых нулей", 2, len(args)) + } + return BezHvostovyhNuley(ctx, args[0], args[1]) + case "Без хвостового разделителя": + if len(args) != 2 { + return rt.Value{}, rt.Fail(rt.CodeType, + "функция «%s» принимает %d аргум., получено %d", + "Без хвостового разделителя", 2, len(args)) + } + return BezHvostovogoRazdelitelya(ctx, args[0], args[1]) + case "Каждые": + if len(args) != 2 { + return rt.Value{}, rt.Fail(rt.CodeType, + "функция «%s» принимает %d аргум., получено %d", + "Каждые", 2, len(args)) + } + return Kazhdye(ctx, args[0], args[1]) + case "Длительность языком": + if len(args) != 2 { + return rt.Value{}, rt.Fail(rt.CodeType, + "функция «%s» принимает %d аргум., получено %d", + "Длительность языком", 2, len(args)) + } + return DlitelnostYazykom(ctx, args[0], args[1]) case "мера убывает": if len(args) != 2 { return rt.Value{}, rt.Fail(rt.CodeType, From 054915aafb900fbf7ba8db8a7da724af3797ab50 Mon Sep 17 00:00:00 2001 From: the-homeless-god Date: Sat, 5 Sep 2026 12:19:03 +0000 Subject: [PATCH 2/2] =?UTF-8?q?=D0=9D=D0=B0=D0=BF=D0=B5=D1=87=D0=B0=D1=82?= =?UTF-8?q?=D0=B0=D0=BD=D0=BD=D0=BE=D0=B5=20=D0=B2=20git=20=D0=BD=D0=B0?= =?UTF-8?q?=D0=B7=D0=B2=D0=B0=D0=BD=D0=BE=20=D0=BF=D0=BE=20=D0=BF=D1=80?= =?UTF-8?q?=D0=B8=D0=B7=D0=BD=D0=B0=D0=BA=D1=83=20=D1=81=D0=B1=D0=BE=D1=80?= =?UTF-8?q?=D0=BA=D0=B8,=20=D0=B0=20=D0=BF=D0=B5=D1=87=D0=B0=D1=82=D1=8C?= =?UTF-8?q?=20=D0=B2=20C=20=D1=83=D0=B1=D1=80=D0=B0=D0=BD=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Довод «чтобы сборка из чистого клона требовала только Go» стоял в четырёх местах (`core/README.md`, `AGENTS.md`, `.gitignore`, `ui-flang/README.md`) и был шире правды: он не называл ни признака, за которым напечатанное вообще нужно, ни каталога, которому не подходит вовсе. Замерено в чистом клоне на `AMD EPYC 7742` — той же машине, на которой сняты замеры раскладки. ЧТО ОКАЗАЛОСЬ НЕПРАВДОЙ. «Только Go» — это не про `go build`. Снесите `core/out-go`, `core/out-c` и `ui-flang/out-go` целиком: `go build ./...`, `go vet ./...` и `go test ./...` в `host/` останутся зелёными — решающий слой стоит за `flangcore`, без признака собирается заглушка. ЧТО ОКАЗАЛОСЬ ПРАВДОЙ, И ПОТОМУ ДОВОД НЕ ОТМЕНЁН. Довод про выпуск. `scripts/build-release.sh` собирает все четыре цели с `-tags flangcore` (стр. 119), падает, если ядра в двоичном не оказалось (стр. 234), и компилятор flang не зовёт ни разу. Без `core/out-go` тот же путь отвечает «replacement directory ../core/out-go does not exist». То же у `tools/sverka-ui.sh`, единственного прогона, доказывающего, что две реализации раскладки рисуют один экран: он собирает дерево дважды и в своей шапке обещает, что компилятор не нужен. ЧЕГО ЭТО СТОИЛО БЫ. «Один двоичный из одного `cc`» — правда, но не «даром»: `make -C bootstrap` на v0.7.10 — 38 МБ C в четырёх единицах трансляции, 5 мин 12 с одним вызовом, 1 мин 54 с при `-j4`, 1 мин 48 с при `-j8`, пик 1,7 ГиБ. Язык обещает «около минуты» с `-j8` — вышло почти вдвое больше. Число растёт, а не падает, и потому 6 304 строки в git остаются дешевле двух минут и полутора гигабайт на каждой машине, которая собирает выпуск. ЧТО УБРАНО. `core/out-c` — 12 130 строк, 6 файлов — доводом не покрыт ничем. Его не читает ни `host/`, ни `scripts/`; зовёт его один `differential.flang` через `out-c/flang_cli`, а цель `сверка` зависит от `печать`, которая начинается с `rm -rf out-go out-c`. То есть закоммиченный C не читал никто и никогда: кому он нужен, тот печатает его заново, и компилятор у него уже есть. Теперь печатается на месте и игнорируется (`core/.gitignore`). напечатанного в git было 29 файлов, 36 487 строк — 46,6 % дерева стало 23 файла, 24 357 строк — 36,7 % для сравнения, весь хозяин на Go 33 467 строк Проверено после правки: `make -C core` — сверка печати 240 входов, расхождений 0, дерево после прогона чистое; `flang io tools/licensing.flang` — бед 0; `go build`/`vet`/`test` во всех трёх сочетаниях признаков; `tools/sverka-ui.sh` — расхождений 0. `tools/licensing.flang` не тронут намеренно: его «Напечатанное» — предикат о ПУТИ, и для `core/out-c/` он остаётся верным; сторож ходит по `git ls-files`, так что нечитаемых веток он не завёл. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_012A35w4QRRoanYvErJfwTQA --- .gitignore | 14 +- AGENTS.md | 25 +- README.md | 13 +- README.ru.md | 12 +- core/.gitignore | 16 +- core/Makefile | 10 +- core/README.md | 69 +- core/out-c/Makefile | 27 - core/out-c/flang_cli.c | 1257 --------- core/out-c/flang_runtime.c | 5319 ------------------------------------ core/out-c/flang_runtime.h | 1002 ------- core/out-c/opis_diska.c | 3816 -------------------------- core/out-c/opis_diska.h | 736 ----- ui-flang/README.md | 12 +- 14 files changed, 126 insertions(+), 12202 deletions(-) delete mode 100644 core/out-c/Makefile delete mode 100644 core/out-c/flang_cli.c delete mode 100644 core/out-c/flang_runtime.c delete mode 100644 core/out-c/flang_runtime.h delete mode 100644 core/out-c/opis_diska.c delete mode 100644 core/out-c/opis_diska.h diff --git a/.gitignore b/.gitignore index 7b2dcae..f17e035 100644 --- a/.gitignore +++ b/.gitignore @@ -15,10 +15,16 @@ *.out vendor/ -# core/out-go НЕ игнорируется, и это выбор: напечатанный Go коммитится, чтобы -# сборка из чистого клона требовала только Go (AGENTS.md, «core/out-go -# печатается, а не пишется»). Игнорируется лишь временный каталог сверки -# печати — тот, с которым сравнивают дерево. +# core/out-go и ui-flang/out-go НЕ игнорируются, и это выбор: напечатанный Go +# коммитится, потому что его читает выпуск. `scripts/build-release.sh` собирает +# с `-tags flangcore` и компилятор flang не зовёт ни разу; без печати в дереве +# каждый выпуск начинался бы со сборки семени — 1 мин 54 с при `-j4` и 1,7 ГиБ +# памяти. Числа и разбор по каталогам — в core/README.md. +# +# core/out-c ИГНОРИРУЕТСЯ (core/.gitignore): его не читает ни один прогон, +# которому хватает Go, а тот, кто его зовёт, печатает его сам. +# +# Здесь же — временный каталог сверки печати, тот, с которым сравнивают дерево. /core/out-go.check/ /core/out-c.check/ diff --git a/AGENTS.md b/AGENTS.md index a7cce34..9b8ada0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -117,19 +117,26 @@ SPDX-License-Identifier: BSD-2-Clause flang emit core/<модуль>.flang --target go --out core/out-go ``` -Он коммитится напечатанным (сборка из чистого клона тогда требует только Go), и -правка руками в нём — дефект, а не правка: следующая печать её сотрет молча. -Меняется поведение ядра — меняется спецификация в `core/`, затем печать, затем -коммит обоих изменений вместе. Сверка при сомнении: перепечатать во временный -каталог и сравнить с деревом; расхождение значит, что кто-то правил вывод. +Он коммитится напечатанным — потому что его читает выпуск: `build-release.sh` +собирает с `-tags flangcore` и компилятор не зовёт ни разу, а собрать семя +стоит 1 мин 54 с при `-j4` и 1,7 ГиБ. Печать в C (`core/out-c`) этим доводом не +покрыта ничем и в git НЕ лежит: её не читает ни один прогон, которому хватает +Go. Разбор по каталогам числами — [`core/README.md`](core/README.md). + +Правка руками в напечатанном — дефект, а не правка: следующая печать её сотрет +молча. Меняется поведение ядра — меняется спецификация в `core/`, затем печать, +затем коммит обоих изменений вместе. Сверка при сомнении: перепечатать во +временный каталог и сравнить с деревом; расхождение значит, что кто-то правил +вывод. Поэтому же напечатанное освобождено от требования заголовка в `tools/licensing.flang` (функция «Напечатанное») — но не от проверки на GPL. -Что здесь исходник, а что вывод компилятора — таблицей и без жаргона: -[`core/README.md`](core/README.md). Внутри самих `out-go/` и `out-c/` -пояснения нет намеренно: `make -C core печать` начинается с `rm -rf`, и любой -файл, положенный туда руками, исчезнет при первой же перепечатке. +Что здесь исходник, что вывод компилятора и что из вывода лежит в git — +таблицей и без жаргона: [`core/README.md`](core/README.md). Внутри самих +`out-go/` и `out-c/` пояснения нет намеренно: `make -C core печать` начинается +с `rm -rf`, и любой файл, положенный туда руками, исчезнет при первой же +перепечатке. То же правило слово в слово держит `ui-flang/out-go` — печать библиотеки раскладки экрана [flang-tui](https://github.com/digitable-lol/flang-tui). Разница diff --git a/README.md b/README.md index 3e09b3c..4995db5 100644 --- a/README.md +++ b/README.md @@ -89,9 +89,11 @@ formula, so the two ways install the same bytes. ### From source -A Go toolchain is enough from a clean checkout, because the printed Go is -committed. The build runs inside `host/`: that is where the module lives, and -there is no module at the root. +A Go toolchain is enough from a clean checkout — not for `go build` in general, +but for the `flangcore` tag below: the print that tag links in is committed +(`core/out-go`, 6,304 lines; why that one and not every print, see +[`core/README.md`](core/README.md)). The build runs inside `host/`: that is +where the module lives, and there is no module at the root. ```bash cd host && go build -tags flangcore -o ../digitdisk . @@ -1008,8 +1010,9 @@ dictionary, and 29 names of the договор translated as words. The licensing guard and the emission cross-check are written in flang, not in Python or JavaScript: neither is present in this tree. The flang compiler is a -single binary that needs only a C compiler (`brew install flang`, `asdf`, or -`make -C bootstrap` in a clone of the language); it does not require Node. +single binary that needs only a C compiler (`brew install +digitable-lol/tap/flang`, `asdf`, or `make -C bootstrap` in a clone of the +language — 1 min 54 s at `-j4`); it does not require Node. ## State diff --git a/README.ru.md b/README.ru.md index 495d87c..ba64797 100644 --- a/README.ru.md +++ b/README.ru.md @@ -86,8 +86,11 @@ sudo install -m 0755 digitdisk-$V-linux-$A/digitdisk /usr/local/bin/digitdisk ### Сборка из исходников -Для сборки из чистого клона хватает Go: напечатанный Go лежит в дереве. -Собирается изнутри `host/` — модуль там, в корне дерева модуля нет. +Из чистого клона хватает Go — и это не про `go build` вообще, а про признак +`flangcore` ниже: печать, которую он кладёт внутрь, лежит в дереве +(`core/out-go`, 6 304 строки; почему именно она, а не всё напечатанное, — +[`core/README.md`](core/README.md)). Собирается изнутри `host/` — модуль там, в +корне дерева модуля нет. ```bash cd host && go build -tags flangcore -o ../digitdisk . @@ -974,8 +977,9 @@ cd host && go test ./internal/lang/ -v -run 'Пары|Заполнители|Д Сторож лицензий и сверка печати написаны на flang, а не на Python и JavaScript: в дереве нет ни того, ни другого. Компилятор flang — один двоичный файл, -которому нужен только `cc` (`brew install flang`, `asdf`, либо -`make -C bootstrap` в клоне языка); Node он не требует. +которому нужен только `cc` (`brew install digitable-lol/tap/flang`, `asdf`, +либо `make -C bootstrap` в клоне языка — 1 мин 54 с при `-j4`); Node он не +требует. ## Состояние diff --git a/core/.gitignore b/core/.gitignore index 08de33d..21d5bfa 100644 --- a/core/.gitignore +++ b/core/.gitignore @@ -1,8 +1,12 @@ -# Собранное из напечатанного. Сам напечатанный Go и C коммитятся (AGENTS.md, -# «core/out-go печатается, а не пишется»), их сборка — нет: бинарь молча -# разойдётся с исходником, а восстанавливается он одной командой +# Собранное из напечатанного. Напечатанный Go коммитится (AGENTS.md, +# «core/out-go печатается, а не пишется»), его сборка — нет: бинарь молча +# разошёлся бы с исходником, а восстанавливается он одной командой # `make -C core печать`. out-go/flang_cli -out-c/flang_cli -out-c/*.o -out-c/*.a + +# Напечатанный C НЕ коммитится, и это отдельное решение с отдельным доводом: +# его не читает ни один прогон, которому хватает Go. `host/` и `scripts/` на +# него не ссылаются, а `make -C core сверка` начинается с `rm -rf out-c` и +# печатает его заново — то есть закоммиченные 12 130 строк ни разу не были +# прочитаны. Числа и весь довод — в core/README.md. +out-c/ diff --git a/core/Makefile b/core/Makefile index bd1dddc..e254ef6 100644 --- a/core/Makefile +++ b/core/Makefile @@ -19,10 +19,16 @@ # ни JavaScript тут больше нет. Взять его можно тремя дорогами, и все три дают # один и тот же файл под одним именем: # -# brew install flang asdf plugin add flang make -C bootstrap +# brew install digitable-lol/tap/flang asdf plugin add flang +# make -C bootstrap +# +# «Один двоичный из одного cc» не значит «даром»: последняя дорога — 38 МБ C, +# 5 мин 12 с одним вызовом и 1 мин 54 с при `-j4`, пик 1,7 ГиБ (AMD EPYC 7742, +# v0.7.10). Ради этих двух минут out-go/ и лежит в git; см. core/README.md. # # Каталоги out-go/ и out-c/ ПЕЧАТАЮТСЯ, а не правятся руками: `make -C core` -# восстанавливает их целиком. +# восстанавливает их целиком. В git при этом лежит ТОЛЬКО out-go/ — out-c/ не +# читает ни один прогон, которому хватает Go, и он игнорируется. # Команда flang. По умолчанию — та, что стоит в системе; FLANG=путь/к/flang # переопределяет её, а FLANG_HOME указывает на клон flang с собранным семенем. diff --git a/core/README.md b/core/README.md index 067da2a..59400e3 100644 --- a/core/README.md +++ b/core/README.md @@ -6,12 +6,12 @@ ## Что здесь исходник, а что вывод компилятора -| Путь | Что это | Правится руками? | -|---|---|---| -| `disk-inventory.flang` | **исходник.** Правила, пороги, примеры, постусловия | **да, только он** | -| `differential.flang` | **исходник.** Сверка двух печатей между собой | да | -| `out-go/` | вывод компилятора: тот же модуль, напечатанный в Go | **НЕТ** | -| `out-c/` | вывод компилятора: тот же модуль, напечатанный в C | **НЕТ** | +| Путь | Что это | Лежит в git? | Правится руками? | +|---|---|---|---| +| `disk-inventory.flang` | **исходник.** Правила, пороги, примеры, постусловия | да | **да, только он** | +| `differential.flang` | **исходник.** Сверка двух печатей между собой | да | да | +| `out-go/` | вывод компилятора: тот же модуль, напечатанный в Go | **да**, 6 304 строки | **НЕТ** | +| `out-c/` | вывод компилятора: тот же модуль, напечатанный в C | **нет**, печатается на месте | **НЕТ** | ### `out-go/` и `out-c/` — это НЕ исходники @@ -22,9 +22,55 @@ flang emit --target go disk-inventory.flang --out out-go flang emit --target c disk-inventory.flang --out out-c ``` -**Зачем они закоммичены, если это вывод.** Чтобы сборка из чистого клона -требовала только Go: кто клонировал digitdisk и хочет собрать двоичный файл, не -обязан ставить компилятор flang — решающий слой уже напечатан и лежит в дереве. +### Почему `out-go/` закоммичен, а `out-c/` — нет + +Довод «чтобы сборка из чистого клона требовала только Go» стоял здесь с первого +дня и был **шире правды**: он не называл ни признака сборки, за которым +напечатанное вообще нужно, ни каталога, которому он не подходит вовсе. Ниже — +тот же довод, но по каталогам и числами. Все числа сняты в чистом клоне на +`AMD EPYC 7742` — на той же машине, что и замеры раскладки в +[`../ui-flang/README.md`](../ui-flang/README.md). + +**Обычная сборка не читает напечатанное вообще.** Удалите `core/out-go`, +`core/out-c` и `ui-flang/out-go` целиком — и `go build ./...`, `go vet ./...`, +`go test ./...` в `host/` останутся зелёными: решающий слой стоит за признаком +`flangcore` (`host/decider_flang.go`, `host/internal/coreflang/bridge.go`), а +без признака собирается заглушка. Значит «только Go» — это НЕ про `go build`. + +**Про что оно на самом деле — про выпуск.** `scripts/build-release.sh` собирает +все четыре цели с `-tags flangcore` (строка 119) и падает на самопроверке, если +ядра на flang в двоичном файле не оказалось (строка 234). Компилятор flang он +не зовёт ни разу. То же и у того, кто собирает себе: `cd host && go build -tags +flangcore`. Уберите `out-go/` из дерева — и этот путь отвечает + +``` +flangprogram@v0.0.0: replacement directory ../core/out-go does not exist +``` + +то есть выпуск начинает требовать компилятор. + +**Чего это стоило бы, замерено.** Компилятор — действительно один двоичный из +одного `cc`, и это не значит «дёшево»: `make -C bootstrap` на v0.7.10 — это +38 МБ C в четырёх единицах трансляции, **5 мин 12 с** одним вызовом, **1 мин +54 с** при `-j4` и **1 мин 48 с** при `-j8` (единиц четыре, больше ядер занять +нечем), пик памяти **1,7 ГиБ**. Готовый двоичный — 21 МБ, зависимости `libm` и +`libc`. Число это не падает, а растёт: язык обещает `make -C bootstrap -j8` +«около минуты», здесь вышло почти вдвое больше. Именно поэтому довод не +отменён — 6 304 строки в git против двух минут и полутора гигабайт на каждой +машине, которая собирает выпуск. + +**А `out-c/` этим доводом не покрыт ничем, и потому из git убран.** Его не +читает ни `host/`, ни `scripts/`; единственный, кто его зовёт, — +`differential.flang` через `out-c/flang_cli`, а цель `сверка` зависит от +`печать`, которая начинается с `rm -rf out-go out-c`. То есть закоммиченные +12 130 строк C не читал никто и никогда: любой прогон, которому они нужны, +печатает их заново, и у него уже есть компилятор. Теперь они печатаются на +месте и игнорируются (`core/.gitignore`). + +Напечатанного в git было 36 487 строк из 78 376 — **46,6 %** дерева, больше, +чем весь рукописный хозяин (33 467 строк `host/*.go`). Стало 24 357 из 66 296, +**36,7 %**: `core/out-go` (6 304) и `ui-flang/out-go` (18 053), и у каждого — +свой признак сборки и свой прогон, который без него краснеет. **Цена этого решения.** Вывод обязан оставаться в согласии с исходником. Поэтому правка руками внутри `out-go/` или `out-c/` — **дефект, а не правка:** @@ -47,8 +93,9 @@ make -C core сверка # 240 входов через оба напеча ``` Компилятор flang — один двоичный файл, которому нужен только `cc`: -`brew install flang`, `asdf`, либо `make -C bootstrap` в клоне языка. Node он не -требует, и в этом дереве нет ни JavaScript, ни Python. +`brew install digitable-lol/tap/flang`, `asdf`, либо `make -C bootstrap` в клоне +языка (что это стоит — числами выше). Node он не требует, и в этом дереве нет ни +JavaScript, ни Python. ## Почему ядро не ходит в файловую систему само diff --git a/core/out-c/Makefile b/core/out-c/Makefile deleted file mode 100644 index b3157bd..0000000 --- a/core/out-c/Makefile +++ /dev/null @@ -1,27 +0,0 @@ -# Сгенерировано flang (бэкенд C). Флаги здесь — часть контракта бэкенда: -# сгенерированный код обязан собираться без единого предупреждения. -# -flto: рантайм и модуль программы — разные единицы трансляции, и без -# межмодульной оптимизации ни один вызов рантайма не подставляется на место. -# Замерено: работа быстрее в 1,1—1,6 раза, сборка дольше примерно вдвое. -# Нужна быстрая сборка вместо быстрой работы: make CFLAGS='-std=c99 -O2'. -# -lpthread: расчёт идёт на потоке с заданным стеком, иначе объявленный -# предел глубины не несётся стеком (flang_cli.c). С glibc 2.34 и новее он -# уже в libc, но на старых системах без него не слинкуется. -CC ?= cc -CFLAGS ?= -std=c99 -Wall -Wextra -Werror -pedantic -O2 -flto -LDLIBS ?= -lm -lpthread - -OBJECTS = flang_runtime.o opis_diska.o - -all: libopis_diska.a flang_cli - -libopis_diska.a: $(OBJECTS) - ar rcs $@ $(OBJECTS) - -flang_cli: flang_cli.o $(OBJECTS) - $(CC) $(CFLAGS) -o $@ flang_cli.o $(OBJECTS) $(LDLIBS) - -clean: - rm -f $(OBJECTS) flang_cli.o flang_cli libopis_diska.a - -.PHONY: all clean diff --git a/core/out-c/flang_cli.c b/core/out-c/flang_cli.c deleted file mode 100644 index 3521b11..0000000 --- a/core/out-c/flang_cli.c +++ /dev/null @@ -1,1257 +0,0 @@ -/* - * Сгенерировано flang (бэкенд C, flang/self/emit-c.flang). Не редактировать руками. - * Модуль flang: «Опись диска». - * Файл: прогонщик: JSON на входе, JSON на выходе. - * Правьте исходник на flang и печатайте заново: любая правка здесь потеряется. - */ -#define FL_PROGRAM_CALL opis_diska_call -#define FL_PROGRAM_ENTRY opis_diska_entry - -/* SPDX-FileCopyrightText: 2026 Digitable (Marat Zimnurov) */ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Прогонщик программы flang: JSON на входе, JSON на выходе. - * - * Зачем он вообще есть. Сгенерированный модуль на C — это библиотека, и вызвать - * её можно только из C. Но проверить кодогенератор нужно ровно одним способом — - * сверкой с интерпретатором на сетке из тысяч входов, а перекомпилировать - * программу ради каждой точки сетки невозможно. Поэтому бэкенд печатает ещё и - * этот прогонщик: одна сборка, дальше поток запросов через трубу. - * - * Побочная польза больше основной: точно так же программу на flang вызывает - * любой язык, у которого есть трубы, — Python, Node, shell. Ни FFI, ни ABI. - * - * ── Протокол ─────────────────────────────────────────────────────────────── - * Запрос — одна строка: {"fn":"Имя функции","args":[…],"depth":"10000","steps":"1000000"} - * Ответ — одна строка: {"ok":true,"value":…} - * или {"ok":false,"code":"FLANG_TYPE","message":"…"} - * - * У программы с процессами есть второй запрос — прогон: - * {"run":"имя прогона","seed":"4172","turns":"10000","journal":"1"} - * {"run":"имя прогона","workers":"8"} рабочий режим: восемь потоков, и семя - * чередования больше НЕ определяет (см. flang_conc.h) - * {"ok":true,"run":"…","исход":"покой","время":"4","пробегов":"4", - * "состояния":[["Счётчик",…]],"живые":[…],"отказы":[…],"решения":[…], - * "журнал":[{"время":"0","процесс":"…","исход":"обработано","код":"","сообщение":…}]} - * Семя обязательно в том смысле, что от него зависит ВСЁ чередование: при одном - * и том же семени журнал доставок совпадает со свидетелем побайтово, и это - * проверяется, а не обещается. - * - * `journal` — наблюдение, и оно НЕОБЯЗАТЕЛЬНО. По умолчанию единица: прогон - * ведёт журнал целиком, иначе сверять со свидетелем было бы нечего. `"journal":"0"` - * — рабочий режим: журнала нет ни в памяти, ни в ответе, и поля «журнал» в - * ответе тоже нет (пустой список означал бы «пробегов не было»). Разница не в - * удобстве вывода: запись журнала стоит памяти на КАЖДОМ пробеге, а арена - * рантайма не возвращает ничего, поэтому долгоживущий процесс с журналом течёт - * тем быстрее, чем дольше живёт. Наблюдаемое поведение от признака не зависит - * ничем — те же исходы, состояния и решения надзора при том же семени. - * - * Значения размечены тегами, потому что JSON беднее flang: - * null «ничто» - * true / false признак - * {"n":"1.5"} число — строкой, иначе потерялись бы NaN, Infinity и −0 - * (по той же причине строкой едут необязательные «depth» - * и «steps») - * {"s":"текст"} строка - * {"l":[…]} список - * {"r":[["поле",…]]} запись (порядок полей сохраняется) - * {"v":"Имя","f":[["поле",…]]} вариант - * - * ── Память ───────────────────────────────────────────────────────────────── - * На каждый запрос — один fl_arena_reset. Всё, что построил разбор запроса, - * само вычисление и текст диагностики, освобождается одним движением, и объём - * памяти не зависит от числа запросов. В конце — fl_arena_release: под - * valgrind это ноль недостижимых байт. - * - * ── Второй вход: человеческие команды ────────────────────────────────────── - * Когда этой программой оказывается сам компилятор flang, у бинарника есть и - * человеческий вход: `--help`, `--version`, `check <файл>`, `repl`. Это тот же - * прогонщик — те же вызовы функций по имени, — только запросы придумывает не - * инструмент, а человек, и ответы печатаются словами, а не JSON. Живёт он в - * СОСЕДНЕМ файле (flang_repl.c), и не для порядка: человеческому входу нужно - * спрашивать у мира, где `cc`, где каталоги установки и что лежит в названном - * файле, а этот файл обязан остаться переносимым C99, который ни от чего не - * зависит и ничего не спрашивает. Здесь остаётся одна строка — передача - * управления. - * - * Обещание «переносимый C99» это правило и держит: единственное, чего этому - * файлу не хватает в C99, — стек под объявленный предел глубины, и просит он - * его не сам, а `fl_call_deep` из рантайма, где платформенная часть и обнесена - * проверкой платформы. Здесь по-прежнему ни одного `#include`, которого нет в - * стандарте (см. хвост файла). - * - * Без аргументов программа читает JSON со стандартного ввода — если на том - * конце не человек. Стоит на входе терминал, и у бинарника есть человеческий - * вход, — голая команда открывает оболочку, как `iex` и как `python`; решает - * это `fl_human_bare` из соседнего файла, потому что `isatty` здесь не наш. - * Конвейеру ничего не меняется, и это контракт, а не мелочь: им живут тест - * формулы Homebrew, проба плагина asdf и `scripts/build-release-c.mjs`. Явное - * `--json` нужно тому, кто зовёт прогонщик из скрипта и хочет сказать это - * вслух, а не полагаться на то, что аргументов случайно не оказалось. - */ -#include "flang_runtime.h" - -#include -#include -#include - -/* - * Единственная связь прогонщика с конкретной программой — вызов функции по - * имени. Бэкенд печатает `#define FL_PROGRAM_CALL <префикс>_call` перед этим - * файлом; запасное имя ниже нужно, чтобы файл компилировался и сам по себе. - */ -#ifndef FL_PROGRAM_CALL -#define FL_PROGRAM_CALL fl_program_call -#endif - -extern fl_status FL_PROGRAM_CALL(fl_ctx *ctx, const char *name, const fl_value *args, size_t count, - fl_value *result, fl_error *error); - -/* - * Вторая связь с программой — её ГРАНИЦА ВХОДА: объявленные типы параметров, - * напечатанные данными (`<префикс>_entry`). Прогонщик сверяет по ним значения - * из JSON ДО вызова. - * - * Почему это здесь, а не внутри функций. Значения приходят СНАРУЖИ, а типов в - * напечатанном коде нет вовсе — и `«Факториал» принимает н: неотрицательное` спокойно - * считался при `н` равном −3 и 2.5. Хуже того, при 1e300 он упирался в - * FLANG_RECURSION_LIMIT: доказательство завершения `тотальной` стоит НА ТИПЕ (у - * `неотрицательное` есть потолок 2^53−1, ниже которого `н минус 1` точно меньше `н`), и - * сторож убывания в такую функцию не печатается вовсе. Значение вне типа - * выносит вместе с типом и доказательство — цепочка вечна, ловить её нечем. - * Поэтому дверь стоит ДО вычисления и ровно одна. - */ -#ifndef FL_PROGRAM_ENTRY -#define FL_PROGRAM_ENTRY fl_program_entry -#endif - -extern const fl_entry_table *FL_PROGRAM_ENTRY(void); - -/* - * Человеческий вход — соседний файл печати (flang_repl.c), и печатается он по - * просьбе, а не всегда: осмыслен он ровно у одной программы — у самого - * компилятора flang, потому что только у него есть точки входа, которые он - * зовёт. Просьба приезжает тем же способом, что и имя функции вызова, — - * `#define` перед этим файлом. - */ -#ifdef FL_WITH_REPL -extern int fl_human_main(int argc, char **argv, const char *self); -/* - * Голая команда: человеку — оболочка, конвейеру — прогонщик. Спрашивать «есть - * ли человек на том конце» этот файл не имеет права сам (обещание переносимого - * C99 без единого нестандартного #include), поэтому спрашивает соседний, где - * платформенная часть и живёт. - */ -extern int fl_human_bare(void); -#endif - -/* - * Второй запрос протокола — прогон конкурентной программы, и он есть только у - * той программы, в которой объявлен хоть один `процесс`. Без него ключ «run» - * остаётся неизвестным полем запроса, как и было: обещать прогон программе, - * которой нечего прогонять, значило бы врать в протоколе. - */ -#ifdef FL_WITH_CONC -#include "flang_conc.h" - -#ifndef FL_PROGRAM_CONC_PLAN -#define FL_PROGRAM_CONC_PLAN fl_program_conc_plan -#endif - -extern const fl_conc_plan *FL_PROGRAM_CONC_PLAN(void); -#endif - -/* ───────────────────────────── чтение входа ───────────────────────────── */ - -/* - * ── СТРОКА, КОТОРАЯ НЕ ТЕКСТ ─────────────────────────────────────────────── - * - * Запрос протокола — строка, а строка в этом языке UTF-8 (SPEC, раздел 5). - * До 22 августа 2026 негодный октет проходил сквозь восемь прогонщиков ПЯТЬЮ - * разными способами, и отказом не был ни один (замер снят прогоном, таблица — - * в scripts/bad-octet-guard.sh): - * - * C возил октеты как есть и отвечал FLANG_UNKNOWN_NAME на мусор; - * Go, Java, - * C#, JS подменяли октет знаком замены U+FFFD и отвечали тем же - * FLANG_UNKNOWN_NAME — то есть врали о содержимом запроса; - * Elixir звал это «неразборчивым запросом», сваливая не-текст на JSON; - * Rust МОЛЧА обрывал цикл и выходил кодом 0 — худший из восьми: - * зелёный код при несделанной работе; - * Python падал трассировкой UnicodeDecodeError, а при локали C и - * C.UTF-8 протаскивал октет суррогатом и отвечал кодом 0. - * - * Образец поведения у языка уже был — `FLANG_IO_NOT_TEXT` у текстовой пары - * ввода-вывода: номер октета, его значение и чем возить октеты. Прогонщик - * отвечает ТАК ЖЕ, и у семи целей из восьми — байт в байт одинаково: - * диагностика в поток ошибок, код возврата 1, разбора нет. Строки ДО негодной - * уже отвечены и остаются отвеченными — отменять сделанное отказ не обязан. - * Восьмая, JS, названа долгом вслух: её прогонщик — рукописный JavaScript, - * править который в этом дереве запрещено. - * - * Почему в поток ошибок, а не ответом протокола. Ответ протокола обещан один - * на запрос, а строка, которая не текст, запросом не является: ответить на неё - * `{"ok":false}` значило бы разобрать неразобранное. Ровно этим и был прежний - * FLANG_UNKNOWN_NAME на мусоре. - */ - -/* Отказ «строка не текст»: номер строки, номер октета в ней (с единицы), длина - строки в октетах и значение негодного октета. Текст один на семь целей — - сторож сверяет его байт в байт. */ -static void cli_not_text(size_t line, const char *bytes, size_t length, size_t at) { - fprintf(stderr, - "FLANG_IO_NOT_TEXT: строка %lu не текст: октет %lu из %lu (0x%02X) не складывается в UTF-8; запрос обязан ехать в UTF-8\n", - (unsigned long)line, (unsigned long)at, (unsigned long)length, - (unsigned)(unsigned char)bytes[at - 1]); -} - -static char *read_all(FILE *stream, size_t *length) { - size_t capacity = 65536; - size_t used = 0; - char *data = (char *)malloc(capacity); - if (data == NULL) { - return NULL; - } - for (;;) { - const size_t got = fread(data + used, 1, capacity - used - 1, stream); - used += got; - if (used + 1 < capacity) { - break; - } - { - char *bigger = (char *)realloc(data, capacity * 2); - if (bigger == NULL) { - free(data); - return NULL; - } - data = bigger; - capacity *= 2; - } - } - data[used] = '\0'; - *length = used; - return data; -} - -/* ───────────────────────────── разбор JSON ───────────────────────────── */ - -typedef struct fl_reader { - const char *text; - size_t bytes; - size_t offset; - fl_ctx *ctx; -} fl_reader; - -static bool read_value(fl_reader *reader, fl_value *out); - -static void skip_spaces(fl_reader *reader) { - while (reader->offset < reader->bytes) { - const char symbol = reader->text[reader->offset]; - if (symbol == ' ' || symbol == '\t' || symbol == '\r' || symbol == '\n') { - reader->offset += 1; - } else { - break; - } - } -} - -static bool expect(fl_reader *reader, char symbol) { - skip_spaces(reader); - if (reader->offset < reader->bytes && reader->text[reader->offset] == symbol) { - reader->offset += 1; - return true; - } - return false; -} - -static bool peek(fl_reader *reader, char symbol) { - skip_spaces(reader); - return reader->offset < reader->bytes && reader->text[reader->offset] == symbol; -} - -static bool read_literal(fl_reader *reader, const char *word) { - const size_t length = strlen(word); - skip_spaces(reader); - if (reader->offset + length > reader->bytes || memcmp(reader->text + reader->offset, word, length) != 0) { - return false; - } - reader->offset += length; - return true; -} - -static size_t encode_utf8(unsigned long code, char *target) { - if (code < 0x80u) { - target[0] = (char)code; - return 1; - } - if (code < 0x800u) { - target[0] = (char)(0xC0u | (code >> 6)); - target[1] = (char)(0x80u | (code & 0x3Fu)); - return 2; - } - if (code < 0x10000u) { - target[0] = (char)(0xE0u | (code >> 12)); - target[1] = (char)(0x80u | ((code >> 6) & 0x3Fu)); - target[2] = (char)(0x80u | (code & 0x3Fu)); - return 3; - } - target[0] = (char)(0xF0u | (code >> 18)); - target[1] = (char)(0x80u | ((code >> 12) & 0x3Fu)); - target[2] = (char)(0x80u | ((code >> 6) & 0x3Fu)); - target[3] = (char)(0x80u | (code & 0x3Fu)); - return 4; -} - -static unsigned long read_hex4(const char *text) { - unsigned long code = 0; - size_t index = 0; - for (index = 0; index < 4; index += 1) { - const char symbol = text[index]; - code <<= 4; - if (symbol >= '0' && symbol <= '9') { - code |= (unsigned long)(symbol - '0'); - } else if (symbol >= 'a' && symbol <= 'f') { - code |= (unsigned long)(symbol - 'a' + 10); - } else if (symbol >= 'A' && symbol <= 'F') { - code |= (unsigned long)(symbol - 'A' + 10); - } else { - return 0xFFFFFFFFu; - } - } - return code; -} - -/* Строка JSON → UTF-8 в арене. Выход никогда не длиннее входа, поэтому размер - известен заранее и растить нечего. */ -static bool read_text(fl_reader *reader, char **out, size_t *bytes) { - size_t start = 0; - size_t used = 0; - char *data = NULL; - if (!expect(reader, '"')) { - return false; - } - start = reader->offset; - while (reader->offset < reader->bytes && reader->text[reader->offset] != '"') { - reader->offset += reader->text[reader->offset] == '\\' ? 2 : 1; - } - if (reader->offset > reader->bytes) { - return false; - } - data = (char *)fl_arena_alloc(reader->ctx->arena, (reader->offset - start) + 1); - if (data == NULL) { - return false; - } - { - size_t index = start; - while (index < reader->offset) { - const char symbol = reader->text[index]; - if (symbol != '\\') { - data[used] = symbol; - used += 1; - index += 1; - continue; - } - index += 1; - if (index >= reader->offset) { - return false; - } - switch (reader->text[index]) { - case 'n': data[used] = '\n'; used += 1; index += 1; break; - case 't': data[used] = '\t'; used += 1; index += 1; break; - case 'r': data[used] = '\r'; used += 1; index += 1; break; - case 'b': data[used] = '\b'; used += 1; index += 1; break; - case 'f': data[used] = '\f'; used += 1; index += 1; break; - case '"': data[used] = '"'; used += 1; index += 1; break; - case '\\': data[used] = '\\'; used += 1; index += 1; break; - case '/': data[used] = '/'; used += 1; index += 1; break; - case 'u': { - unsigned long code = 0; - if (index + 5 > reader->offset) { - return false; - } - code = read_hex4(reader->text + index + 1); - index += 5; - if (code >= 0xD800u && code <= 0xDBFFu && index + 6 <= reader->offset && - reader->text[index] == '\\' && reader->text[index + 1] == 'u') { - const unsigned long low = read_hex4(reader->text + index + 2); - if (low >= 0xDC00u && low <= 0xDFFFu) { - code = 0x10000u + ((code - 0xD800u) << 10) + (low - 0xDC00u); - index += 6; - } - } - used += encode_utf8(code, data + used); - break; - } - default: - return false; - } - } - } - data[used] = '\0'; - reader->offset += 1; /* закрывающая кавычка */ - *out = data; - *bytes = used; - return true; -} - -static bool read_number_text(fl_reader *reader, double *out) { - char *text = NULL; - size_t bytes = 0; - if (!read_text(reader, &text, &bytes)) { - return false; - } - if (strcmp(text, "NaN") == 0) { - *out = 0.0 / 0.0; - } else if (strcmp(text, "Infinity") == 0) { - *out = 1.0 / 0.0; - } else if (strcmp(text, "-Infinity") == 0) { - *out = -1.0 / 0.0; - } else { - *out = strtod(text, NULL); - } - return true; -} - -static bool read_pairs(fl_reader *reader, const char ***names, fl_value **values, size_t *count) { - size_t capacity = 8; - size_t used = 0; - const char **name_list = (const char **)fl_arena_alloc(reader->ctx->arena, capacity * sizeof(const char *)); - fl_value *value_list = (fl_value *)fl_arena_alloc(reader->ctx->arena, capacity * sizeof(fl_value)); - if (name_list == NULL || value_list == NULL) { - return false; - } - if (!expect(reader, '[')) { - return false; - } - if (peek(reader, ']')) { - reader->offset += 1; - *names = name_list; - *values = value_list; - *count = 0; - return true; - } - for (;;) { - char *name = NULL; - size_t bytes = 0; - if (used == capacity) { - const char **bigger_names = (const char **)fl_arena_alloc(reader->ctx->arena, capacity * 2 * sizeof(const char *)); - fl_value *bigger_values = (fl_value *)fl_arena_alloc(reader->ctx->arena, capacity * 2 * sizeof(fl_value)); - if (bigger_names == NULL || bigger_values == NULL) { - return false; - } - memcpy(bigger_names, name_list, used * sizeof(const char *)); - memcpy(bigger_values, value_list, used * sizeof(fl_value)); - name_list = bigger_names; - value_list = bigger_values; - capacity *= 2; - } - if (!expect(reader, '[') || !read_text(reader, &name, &bytes) || !expect(reader, ',')) { - return false; - } - if (!read_value(reader, &value_list[used]) || !expect(reader, ']')) { - return false; - } - name_list[used] = name; - used += 1; - if (expect(reader, ',')) { - continue; - } - break; - } - if (!expect(reader, ']')) { - return false; - } - *names = name_list; - *values = value_list; - *count = used; - return true; -} - -static bool read_items(fl_reader *reader, fl_value *out) { - size_t capacity = 8; - size_t used = 0; - fl_value *items = (fl_value *)fl_arena_alloc(reader->ctx->arena, capacity * sizeof(fl_value)); - if (items == NULL) { - return false; - } - if (!expect(reader, '[')) { - return false; - } - if (peek(reader, ']')) { - reader->offset += 1; - *out = fl_list(items, 0); - return true; - } - for (;;) { - if (used == capacity) { - fl_value *bigger = (fl_value *)fl_arena_alloc(reader->ctx->arena, capacity * 2 * sizeof(fl_value)); - if (bigger == NULL) { - return false; - } - memcpy(bigger, items, used * sizeof(fl_value)); - items = bigger; - capacity *= 2; - } - if (!read_value(reader, &items[used])) { - return false; - } - used += 1; - if (expect(reader, ',')) { - continue; - } - break; - } - if (!expect(reader, ']')) { - return false; - } - *out = fl_list(items, used); - return true; -} - -static bool read_value(fl_reader *reader, fl_value *out) { - skip_spaces(reader); - if (read_literal(reader, "null")) { - *out = fl_nothing(); - return true; - } - if (read_literal(reader, "true")) { - *out = fl_flag(true); - return true; - } - if (read_literal(reader, "false")) { - *out = fl_flag(false); - return true; - } - if (!expect(reader, '{')) { - return false; - } - { - char *tag = NULL; - size_t bytes = 0; - if (!read_text(reader, &tag, &bytes) || !expect(reader, ':')) { - return false; - } - if (strcmp(tag, "n") == 0) { - double number = 0.0; - if (!read_number_text(reader, &number)) { - return false; - } - *out = fl_number(number); - return expect(reader, '}'); - } - if (strcmp(tag, "s") == 0) { - char *text = NULL; - size_t length = 0; - if (!read_text(reader, &text, &length)) { - return false; - } - /* Разбор уже положил текст в арену — копировать второй раз незачем. */ - if (fl_text(reader->ctx, text, length, out, NULL) != FL_OK) { - return false; - } - return expect(reader, '}'); - } - if (strcmp(tag, "l") == 0) { - if (!read_items(reader, out)) { - return false; - } - return expect(reader, '}'); - } - if (strcmp(tag, "r") == 0) { - const char **names = NULL; - fl_value *values = NULL; - size_t count = 0; - if (!read_pairs(reader, &names, &values, &count)) { - return false; - } - if (fl_record_new(reader->ctx, names, values, count, out, NULL) != FL_OK) { - return false; - } - return expect(reader, '}'); - } - if (strcmp(tag, "v") == 0) { - char *name = NULL; - size_t length = 0; - const char **names = NULL; - fl_value *values = NULL; - size_t count = 0; - if (!read_text(reader, &name, &length)) { - return false; - } - if (expect(reader, ',')) { - char *key = NULL; - size_t key_bytes = 0; - if (!read_text(reader, &key, &key_bytes) || strcmp(key, "f") != 0 || !expect(reader, ':')) { - return false; - } - if (!read_pairs(reader, &names, &values, &count)) { - return false; - } - } - if (fl_variant_new(reader->ctx, name, names, values, count, out, NULL) != FL_OK) { - return false; - } - return expect(reader, '}'); - } - return false; - } -} - -/* ───────────────────────────── печать JSON ───────────────────────────── */ - -static void write_text(const char *utf8, size_t bytes) { - size_t index = 0; - putchar('"'); - for (index = 0; index < bytes; index += 1) { - const unsigned char symbol = (unsigned char)utf8[index]; - switch (symbol) { - case '"': fputs("\\\"", stdout); break; - case '\\': fputs("\\\\", stdout); break; - case '\n': fputs("\\n", stdout); break; - case '\r': fputs("\\r", stdout); break; - case '\t': fputs("\\t", stdout); break; - case '\b': fputs("\\b", stdout); break; - case '\f': fputs("\\f", stdout); break; - default: - if (symbol < 0x20u) { - printf("\\u%04x", (unsigned)symbol); - } else { - putchar((char)symbol); - } - } - } - putchar('"'); -} - -static void write_value(fl_value value) { - size_t index = 0; - char number[FL_NUMBER_TEXT_MAX]; - switch (value.tag) { - case FL_NOTHING: - fputs("null", stdout); - return; - case FL_FLAG: - fputs(value.as.flag ? "true" : "false", stdout); - return; - case FL_NUMBER: - /* −0 обязан доехать до сверки со знаком: Object.is(0, −0) ложно. */ - fl_number_text(value.as.number, number); - fputs("{\"n\":", stdout); - if (value.as.number == 0.0 && !(1.0 / value.as.number > 0.0)) { - write_text("-0", 2); - } else { - write_text(number, strlen(number)); - } - putchar('}'); - return; - case FL_STRING: - fputs("{\"s\":", stdout); - write_text(value.as.string.utf8, value.as.string.bytes); - putchar('}'); - return; - case FL_LIST: - fputs("{\"l\":[", stdout); - for (index = 0; index < value.as.list.count; index += 1) { - if (index > 0) { - putchar(','); - } - write_value(value.as.list.items[index]); - } - fputs("]}", stdout); - return; - case FL_RECORD: - fputs("{\"r\":[", stdout); - for (index = 0; index < value.as.record->count; index += 1) { - if (index > 0) { - putchar(','); - } - putchar('['); - write_text(value.as.record->fields[index].name, strlen(value.as.record->fields[index].name)); - putchar(','); - write_value(value.as.record->fields[index].value); - putchar(']'); - } - fputs("]}", stdout); - return; - case FL_VARIANT: - fputs("{\"v\":", stdout); - write_text(value.as.variant->name, strlen(value.as.variant->name)); - fputs(",\"f\":[", stdout); - for (index = 0; index < value.as.variant->count; index += 1) { - if (index > 0) { - putchar(','); - } - putchar('['); - write_text(value.as.variant->fields[index].name, strlen(value.as.variant->fields[index].name)); - putchar(','); - write_value(value.as.variant->fields[index].value); - putchar(']'); - } - fputs("]}", stdout); - return; - } - fputs("null", stdout); -} - -/* ───────────────────────────── прогон конкурентной программы ───────────────────────────── - Ответ отличается от ответа на вызов функции не украшением, а существом: у - конкурентной программы нет одного значения, есть исход, состояния всех - процессов и ЖУРНАЛ ДОСТАВОК — то самое чередование, которое обязано совпасть - со свидетелем побайтово при том же семени (контракт, «Что проверяется и чем», - пункт 2). Ради этого журнал и печатается целиком, а не сводкой. */ - -#ifdef FL_WITH_CONC -/* Число едет строкой по той же причине, что и в «n»: иначе потерялись бы NaN, - бесконечности и −0, а виртуальное время — обычное число flang. */ -static void write_number_text(double value) { - char number[FL_NUMBER_TEXT_MAX]; - fl_number_text(value, number); - write_text(number, strlen(number)); -} - -static void write_name(const char *text) { - write_text(text == NULL ? "" : text, strlen(text == NULL ? "" : text)); -} - -/** - * Предел из запроса в `size_t`, и почему это отдельная функция. - * - * `(size_t)x` для отрицательного, бесконечного или нечислового `x` — поведение - * НЕОПРЕДЕЛЁННОЕ, и на обычной машине даёт огромное число. Для `turns` это - * значило бы «крутись почти вечно», для `processes` — хуже: предел числа - * процессов и есть тотальность слоя, и снятый мусором из запроса он перестаёт - * быть пределом. Поэтому такой запрос ОТВЕРГАЕТСЯ, а не округляется молча. - * - * Ноль и всё, что больше разрядной сетки, законны: ноль означает умолчание - * планировщика, а слишком большое упирается в память задолго до предела. - */ -static bool limit_from_number(double value, size_t *out) { - if (value != value || value < 0.0) { - return false; - } - if (value >= 18446744073709551616.0) { - *out = (size_t)-1; - return true; - } - *out = (size_t)value; - return true; -} - -static void write_run(const char *name, const fl_conc_result *result) { - size_t index = 0; - bool first = true; - fputs("{\"ok\":true,\"run\":", stdout); - write_name(name); - fputs(",\"исход\":", stdout); - write_name(result->outcome); - fputs(",\"время\":", stdout); - write_number_text(result->time); - fputs(",\"пробегов\":", stdout); - write_number_text((double)result->turns); - /* Поле появляется РОВНО ТОГДА, когда прогон шёл больше чем одним потоком. - Печатать его всегда значило бы засорить вывод проверочного режима, по - которому идёт побайтовая сверка; не печатать никогда значило бы разменять - воспроизводимость молча. */ - if (result->workers > 1) { - fputs(",\"рабочих\":", stdout); - write_number_text((double)result->workers); - } - - /* Состояния — в порядке объявления процессов, том же, что у свидетеля; за - объявленными идут порождённые, в порядке рождения. Имена берутся из ИТОГА, - а не из плана: у прогона с `породить` процессов больше, чем в плане. */ - fputs(",\"состояния\":[", stdout); - for (index = 0; index < result->process_count; index += 1) { - if (index > 0) { - putchar(','); - } - putchar('['); - write_name(result->names[index]); - putchar(','); - write_value(result->states[index]); - putchar(']'); - } - - fputs("],\"живые\":[", stdout); - for (index = 0; index < result->process_count; index += 1) { - if (!result->alive[index]) { - continue; - } - if (!first) { - putchar(','); - } - first = false; - write_name(result->names[index]); - } - - fputs("],\"отказы\":[", stdout); - for (index = 0; index < result->failure_count; index += 1) { - if (index > 0) { - putchar(','); - } - putchar('['); - write_name(result->names[result->failures[index].process]); - putchar(','); - write_name(result->failures[index].code); - putchar(']'); - } - - fputs("],\"решения\":[", stdout); - for (index = 0; index < result->decision_count; index += 1) { - if (index > 0) { - putchar(','); - } - putchar('['); - write_name(result->names[result->decisions[index].process]); - putchar(','); - write_name(result->decisions[index].supervisor); - putchar(','); - write_name(result->decisions[index].strategy); - putchar(']'); - } - - /* Журнала может не быть вовсе — тогда поля нет, а не пустой список. Пустой - список означал бы «пробегов не было», и читатель, сверяющий журнал с - свидетелем, сравнил бы пустоту с пустотой и промолчал. Отсутствие поля он - заметит сразу. */ - putchar(']'); - if (result->journal_kept) { - fputs(",\"журнал\":[", stdout); - for (index = 0; index < result->journal_count; index += 1) { - const fl_conc_entry *entry = &result->journal[index]; - if (index > 0) { - putchar(','); - } - fputs("{\"время\":", stdout); - write_number_text(entry->time); - fputs(",\"процесс\":", stdout); - write_name(result->names[entry->process]); - fputs(",\"исход\":", stdout); - write_name(entry->outcome); - fputs(",\"код\":", stdout); - write_name(entry->code); - fputs(",\"сообщение\":", stdout); - write_value(entry->message); - putchar('}'); - } - putchar(']'); - } - fputs("}\n", stdout); -} -#endif - -/* ───────────────────────────── запрос ───────────────────────────── */ - -static void run_request(fl_arena *arena, const char *line, size_t bytes) { - fl_ctx ctx; - fl_reader reader; - fl_error error; - fl_value result = fl_nothing(); - fl_value args[FL_MAX_ARGS]; - char *name = NULL; - size_t name_bytes = 0; - size_t count = 0; - fl_status status = FL_OK; -#ifdef FL_WITH_CONC - char *run = NULL; - double seed = 0.0; - double turns = 0.0; - /* Сколько процессов прогону позволено завести всего, объявленных плюс - порождённых (`породить`, шаг Б1). Ноль — умолчание планировщика, ровно как - у `turns`: это настройка запроса, а не потолок модели. */ - double processes = 0.0; - /* Сколько потоков ведут прогон. Ноль и единица — проверочный режим: один - поток, чередование по семени, побайтовая сверка со свидетелем. Больше единицы — - рабочий: столько потоков ОС, и семя больше НЕ ОПРЕДЕЛЯЕТ чередования. - Умолчание — проверочный, и это не осторожность: воспроизводимость здесь - часть договора языка, и терять её молча, «потому что машина многоядерная», - нельзя. Кто хочет ядер, говорит об этом вслух — и получает в ответе поле - «рабочих», чтобы не спутать один режим с другим. - - Слово «все» просит столько потоков, сколько у машины ядер: «параллельно, - сколько даёт среда» из контракта — это число, а не оборот речи. */ - double workers = 0.0; - /* Журнал по умолчанию ВЕДЁТСЯ: прогон — основной способ звать эту программу, и - по журналу он сверяется со свидетелем. Выключает его тот, кто знает, что зовёт - не прогон, а работу, — и знает, что платит за наблюдение памятью на каждом - пробеге. Умолчание наоборот сломало бы сверку молча. */ - double journal = 1.0; - /* Хозяин ввода-вывода (седьмое действие, `поручить`). По умолчанию ВЫКЛЮЧЕН, и - это не осторожность: прогон — свидетель, с которым побайтово сверяется журнал - `flang/src/conc.mjs`, а хозяин, читающий настоящие файлы, сделал бы журнал - зависящим от содержимого диска. Просит его тот, кто зовёт программу - РАБОТАТЬ, а не проверяться, — и просит вслух, полем `"host": 1`. */ - double host = 0.0; -#endif - - fl_arena_reset(arena); - fl_ctx_init(&ctx, arena); - error.code = NULL; - error.message = NULL; - - reader.text = line; - reader.bytes = bytes; - reader.offset = 0; - reader.ctx = &ctx; - - if (!expect(&reader, '{')) { - fputs("{\"ok\":false,\"code\":\"CLI\",\"message\":\"ожидался объект запроса\"}\n", stdout); - return; - } - for (;;) { - char *key = NULL; - size_t key_bytes = 0; - if (!read_text(&reader, &key, &key_bytes) || !expect(&reader, ':')) { - fputs("{\"ok\":false,\"code\":\"CLI\",\"message\":\"неразборчивый запрос\"}\n", stdout); - return; - } - if (strcmp(key, "fn") == 0) { - if (!read_text(&reader, &name, &name_bytes)) { - fputs("{\"ok\":false,\"code\":\"CLI\",\"message\":\"неразборчивое имя функции\"}\n", stdout); - return; - } - } else if (strcmp(key, "depth") == 0) { - double depth = 0.0; - if (!read_number_text(&reader, &depth)) { - fputs("{\"ok\":false,\"code\":\"CLI\",\"message\":\"неразборчивый предел глубины\"}\n", stdout); - return; - } - ctx.max_depth = (size_t)depth; - } else if (strcmp(key, "steps") == 0) { - double steps = 0.0; - if (!read_number_text(&reader, &steps)) { - fputs("{\"ok\":false,\"code\":\"CLI\",\"message\":\"неразборчивый предел шагов\"}\n", stdout); - return; - } - ctx.max_steps = (size_t)steps; -#ifdef FL_WITH_CONC - } else if (strcmp(key, "run") == 0) { - size_t run_bytes = 0; - if (!read_text(&reader, &run, &run_bytes)) { - fputs("{\"ok\":false,\"code\":\"CLI\",\"message\":\"неразборчивое имя прогона\"}\n", stdout); - return; - } - } else if (strcmp(key, "seed") == 0) { - if (!read_number_text(&reader, &seed)) { - fputs("{\"ok\":false,\"code\":\"CLI\",\"message\":\"неразборчивое семя\"}\n", stdout); - return; - } - } else if (strcmp(key, "turns") == 0) { - if (!read_number_text(&reader, &turns)) { - fputs("{\"ok\":false,\"code\":\"CLI\",\"message\":\"неразборчивый предел пробегов\"}\n", stdout); - return; - } - } else if (strcmp(key, "processes") == 0) { - if (!read_number_text(&reader, &processes)) { - fputs("{\"ok\":false,\"code\":\"CLI\",\"message\":\"неразборчивый предел числа процессов\"}\n", stdout); - return; - } - } else if (strcmp(key, "workers") == 0) { - if (!read_number_text(&reader, &workers)) { - fputs("{\"ok\":false,\"code\":\"CLI\",\"message\":\"неразборчивое число потоков\"}\n", stdout); - return; - } - } else if (strcmp(key, "journal") == 0) { - if (!read_number_text(&reader, &journal)) { - fputs("{\"ok\":false,\"code\":\"CLI\",\"message\":\"неразборчивый признак журнала\"}\n", stdout); - return; - } - } else if (strcmp(key, "host") == 0) { - if (!read_number_text(&reader, &host)) { - fputs("{\"ok\":false,\"code\":\"CLI\",\"message\":\"неразборчивый признак хозяина\"}\n", stdout); - return; - } -#endif - } else if (strcmp(key, "args") == 0) { - fl_value list = fl_nothing(); - if (!read_items(&reader, &list) || list.as.list.count > FL_MAX_ARGS) { - fputs("{\"ok\":false,\"code\":\"CLI\",\"message\":\"неразборчивые аргументы\"}\n", stdout); - return; - } - count = list.as.list.count; - for (name_bytes = 0; name_bytes < count; name_bytes += 1) { - args[name_bytes] = list.as.list.items[name_bytes]; - } - } else { - fputs("{\"ok\":false,\"code\":\"CLI\",\"message\":\"неизвестное поле запроса\"}\n", stdout); - return; - } - if (expect(&reader, ',')) { - continue; - } - break; - } -#ifdef FL_WITH_CONC - if (run != NULL) { - const fl_conc_plan *plan = FL_PROGRAM_CONC_PLAN(); - /* Обнуление здесь не перестраховка, а следствие двух правок, встретившихся - в одном дереве: рабочий режим планировщика дал `fl_conc_run` пути, на - которых итог заполняется не целиком, а межмодульная оптимизация (`-flto`) - позволила компилятору увидеть это через границу файла. По отдельности ни - одна ветка не краснела; вместе — `-Werror=maybe-uninitialized` на - `outcome.outcome` и `outcome.time`. Стоит обнуление ноль тактов и снимает - вопрос целиком, а не глушит предупреждение. */ - fl_conc_result outcome = {0}; - size_t turn_limit = 0; - size_t process_limit = 0; - size_t worker_count = 0; - if (!limit_from_number(turns, &turn_limit) || !limit_from_number(processes, &process_limit) || - !limit_from_number(workers, &worker_count)) { - fputs("{\"ok\":false,\"code\":\"CLI\",\"message\":\"предел в запросе отрицателен или не число\"}\n", - stdout); - return; - } - if (fl_conc_run_host(&ctx, plan, run, seed, turn_limit, process_limit, worker_count, journal != 0.0, - host != 0.0, &outcome, &error) == FL_OK) { - write_run(run, &outcome); - return; - } - fputs("{\"ok\":false,\"code\":", stdout); - write_name(error.code == NULL ? "FLANG_UNKNOWN" : error.code); - fputs(",\"message\":", stdout); - write_name(error.message); - fputs("}\n", stdout); - return; - } -#endif - - if (name == NULL) { - fputs("{\"ok\":false,\"code\":\"CLI\",\"message\":\"в запросе нет имени функции\"}\n", stdout); - return; - } - - /* Граница входа — ДО вызова: значение вне объявленного типа выносит вместе с - типом и доказательство завершения, и поймать вечную цепочку потом нечем. */ - status = fl_check_entry(&ctx, FL_PROGRAM_ENTRY(), name, args, count, &error); - if (status == FL_OK) { - status = FL_PROGRAM_CALL(&ctx, name, args, count, &result, &error); - } - if (status == FL_OK) { - fputs("{\"ok\":true,\"value\":", stdout); - write_value(result); - fputs("}\n", stdout); - return; - } - fputs("{\"ok\":false,\"code\":", stdout); - write_text(error.code == NULL ? "FLANG_UNKNOWN" : error.code, strlen(error.code == NULL ? "FLANG_UNKNOWN" : error.code)); - fputs(",\"message\":", stdout); - write_text(error.message == NULL ? "" : error.message, strlen(error.message == NULL ? "" : error.message)); - fputs("}\n", stdout); -} - -static int run_main(int argc, char **argv) { - fl_arena arena; - size_t length = 0; - size_t start = 0; - size_t index = 0; - size_t line = 0; - char *input = NULL; -#ifdef FL_WITH_REPL - /* - * Любой аргумент — человеческий вход; без аргументов всё как было, JSON со - * стандартного ввода. Разбор здесь нарочно в одну строку: у прогонщика ключей - * нет и не должно быть, иначе его протокол перестал бы быть протоколом, — а - * что означают слова, решает соседний файл, где эти слова и живут. - * - * `--json` пропускается сюда, а не в человеческий вход: это единственный - * способ сказать «мне нужен прогонщик» вслух. Он же оставляет прежним - * контракт для всех, кто уже зовёт бинарник трубой, — тест формулы Homebrew, - * пробу плагина asdf, `scripts/build-release-c.mjs`. - */ - if (argc > 1 ? strcmp(argv[1], "--json") != 0 : fl_human_bare() == 1) { - return fl_human_main(argc, argv, argv[0]); - } -#else - (void)argc; - (void)argv; -#endif - input = read_all(stdin, &length); - if (input == NULL) { - fputs("{\"ok\":false,\"code\":\"CLI\",\"message\":\"не прочитан вход\"}\n", stdout); - return 1; - } - fl_arena_init(&arena); - for (index = 0; index <= length; index += 1) { - if (index == length || input[index] == '\n') { - /* Хвостовой «\r» снимается ТОЛЬКО для счёта: он ASCII и текстом быть не - мешает, а вот число «из скольких» обязано совпасть с теми целями, чей - построчный читатель снимает его сам (Go, Java, C#). */ - size_t stop = index; - size_t bad = 0; - if (index == length && start == length) { - break; - } - line += 1; - if (stop > start && input[stop - 1] == '\r') { - stop -= 1; - } - /* Вопрос задаётся рантайму (`fl_utf8_not_text_at`), а не решается здесь: - тот же вопрос задают хозяин `flang io` и планировщик конкурентности, - и третий ответ на один вопрос был бы третьим способом разойтись. */ - bad = fl_utf8_not_text_at(input + start, stop - start); - if (bad > 0) { - fflush(stdout); - cli_not_text(line, input + start, stop - start, bad); - fl_arena_release(&arena); - free(input); - return 1; - } - if (index > start) { - run_request(&arena, input + start, index - start); - fflush(stdout); - } - start = index + 1; - } - } - fl_arena_release(&arena); - free(input); - return 0; -} - -/* - * ── Почему прогонщик считает не на своём стеке ───────────────────────────── - * - * Счётчик глубины считает КАДРЫ, а несёт их стек, и толщина кадра — свойство - * программы, а не языка. Замер холодными процессами: у функции с одним - * параметром напечатанный C проходит 23 807 кадров при стеке 8 МиБ, а у - * функции с сорока связываниями — 1 518, и на пределах ПО УМОЛЧАНИЮ (10 000) - * она умирала по SIGSEGV: без кода, без текста, без возможности перехвата. - * Объявленный предел глубины в этой цели пределом НЕ БЫЛ. - * - * Лечение то же, что в бэкенде Python (`rt.call_with_deep_stack`): расчёт идёт - * на потоке с ЯВНО ЗАДАННЫМ стеком, а размер стека соотнесён с объявленным - * пределом — FL_MAX_DEPTH кадров по FL_STACK_PER_FRAME байт. Размер кадра взят - * не с потолка: `cc -fstack-usage` по всему корпусу репозитория (7 896 функций) - * дал худший кадр 6 496 байт — у самого компилятора flang, — и 16 КиБ на кадр - * несут его с запасом в 2,5 раза. - * - * Предел берётся впечатанный (`--max-depth` при печати) либо названный ключом - * `--предел-глубины` — но НЕ тот, что приедет в запросе: стек заводится один раз - * на процесс, а запросов в трубе сколько угодно. Запрос, попросивший больше - * заведённого, упрётся в сторож `fl_enter` и получит объявленный отказ с честным - * текстом про хозяина — не молчаливую смерть. - * - * Если потока с таким стеком система не даёт (не POSIX, `ulimit -v`, контейнер - * с пределом адресного пространства), расчёт идёт здесь же. Обещание держится и - * тогда — его держит сторож, а не стек: предел окажется ниже объявленного, и - * программа СКАЖЕТ об этом, а не упадёт. - * - * ── Почему ключ глубины разбирается ЗДЕСЬ, а не у команды ────────────────── - * - * Стек заводится ДО того, как разобран хоть один ключ, — строкой ниже. Значит - * команда, разобравшая `--предел-глубины` у себя, опоздала бы ровно на стек: - * число в `ctx->max_depth` легло бы, а нести его было бы нечем, и `fl_enter` - * упёрся бы в `fl_stack_spent` — «исчерпала стек хозяина», не дойдя до - * объявленного предела. Поднять предел и поднять стек — одно движение, и делать - * его надо здесь. - * - * Отсюда же второе следствие: ключ общий для всех команд бинарника, а не свой у - * каждой. Он снимается из `argv` до передачи управления, поэтому разборы команд - * его не видят вовсе и своего «непонятный ключ» на него не печатают. - * - * Задача 7444: до этой правки предела глубины у идущего прогона не менял НИЧТО, - * кроме пересборки, — `--max-depth` кладёт число в напечатанную программу, а не - * в свою. - */ - -/* - * Строгий разбор числа: только цифры, только больше нуля, без переполнения. - * Тот же разбор и по тому же доводу, что у `--предел-шагов` в человеческом - * входе: эталон на flang «1e3» не принимает вовсе, и `strtoul` здесь дал бы - * 1000 там, где эталон отказывает. - * - * Ноль не принимается: он значит «не сказано», а снятие предела — это - * `ctx->max_depth = 0` на самом контексте, и одним числом эти два смысла - * называть нельзя. - */ -static int cli_whole(const char *text, size_t *out) { - size_t value = 0; - size_t at = 0; - if (text == NULL || text[0] == 0) { - return 0; - } - for (at = 0; text[at] != 0; at += 1) { - if (text[at] < '0' || text[at] > '9') { - return 0; - } - if (value > ((size_t)-1 - (size_t)(text[at] - '0')) / 10) { - return 0; - } - value = value * 10 + (size_t)(text[at] - '0'); - } - if (value == 0) { - return 0; - } - *out = value; - return 1; -} - -/* - * Снимает `--предел-глубины N` (латиницей `--depth-limit N`) из `argv` и - * говорит найденное число. Ключ может стоять где угодно: человек пишет его и - * перед командой, и после файла, и оба написания обязаны работать одинаково. - * - * Даёт 0 — разобрано; 2 — ключ назван, а число при нём негодное. Код 2 тот же, - * что у прочих ошибок ВЫЗОВА во всём бинарнике. - */ -static int cli_depth_key(int *argc, char **argv, size_t *depth) { - int read = 1; - int write = 1; - int count = *argc; - while (read < count) { - const char *word = argv[read]; - if (strcmp(word, "--предел-глубины") == 0 || strcmp(word, "--depth-limit") == 0) { - if (read + 1 >= count) { - fputs("flang --предел-глубины: не названо число кадров\n", stderr); - return 2; - } - if (!cli_whole(argv[read + 1], depth)) { - fprintf(stderr, - "flang --предел-глубины: «%s» — не целое число кадров больше нуля\n", - argv[read + 1]); - return 2; - } - read += 2; - continue; - } - argv[write] = argv[read]; - write += 1; - read += 1; - } - argv[write] = NULL; - *argc = write; - return 0; -} - -typedef struct cli_run { - int argc; - char **argv; - int status; -} cli_run; - -static void cli_body(void *raw) { - cli_run *run = (cli_run *)raw; - run->status = run_main(run->argc, run->argv); -} - -int main(int argc, char **argv) { - cli_run run; - size_t depth = 0; - int bad = cli_depth_key(&argc, argv, &depth); - if (bad != 0) { - return bad; - } - if (depth != 0) { - fl_max_depth_default_set(depth); - } - run.argc = argc; - run.argv = argv; - run.status = 0; - /* Стек — под ДЕЙСТВУЮЩИЙ предел, а не под впечатанный. Ключа не назвали — - `fl_max_depth_default` отдаёт ровно `FL_MAX_DEPTH`, и прогон идёт байт в - байт как прежде. */ - if (!fl_call_deep(fl_stack_wanted(fl_max_depth_default()), cli_body, &run)) { - cli_body(&run); - } - return run.status; -} diff --git a/core/out-c/flang_runtime.c b/core/out-c/flang_runtime.c deleted file mode 100644 index 8c121a5..0000000 --- a/core/out-c/flang_runtime.c +++ /dev/null @@ -1,5319 +0,0 @@ -/* - * Сгенерировано flang (бэкенд C, flang/self/emit-c.flang). Не редактировать руками. - * Модуль flang: «Опись диска». - * Файл: рантайм: реализация. - * Правьте исходник на flang и печатайте заново: любая правка здесь потеряется. - */ -/* SPDX-FileCopyrightText: 2026 Digitable (Marat Zimnurov) */ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Рантайм flang для бэкенда C — реализация. - * - * Всё, что здесь есть, существует ради одного требования: сгенерированная - * программа обязана давать те же значения и те же диагностики (код И текст), - * что flang/src/interpret.mjs. Поэтому сообщения скопированы дословно, вплоть - * до кавычек-ёлочек, порядок операций в процентах — как в ядре FTS, а число - * печатается по правилам ECMAScript Number::toString, а не «как выйдет у %g». - * - * Зависимости — только стандартная библиотека C99. Единственное исключение — - * стек: `fl_call_deep` и `fl_stack_room` спрашивают о нём POSIX (pthread, - * getrlimit), потому что в C99 стека нет вовсе, а обещание «завершится ИЛИ - * ОТКАЖЕТ ЧЕСТНО» без него не держится. Исключение обнесено проверкой платформы - * и выключается одним `-DFL_NO_POSIX_STACK`: тогда файл снова чистый C99, а - * сторож в `fl_enter` считает по FL_STACK_ROOM_FALLBACK. - * - * Второе исключение — WebAssembly, и оно ровно про то же самое, только отвечает - * не система, а сам двоичный файл: у wasm стека тоже нет в языке, зато есть в - * РАЗМЕТКЕ ПАМЯТИ модуля, которую делает компоновщик, и оттуда его настоящий - * размер виден без единого системного вызова (см. `fl_wasm_room`). Выключается - * одним `-DFL_NO_WASM_STACK`. - */ -#if !defined(FL_NO_POSIX_STACK) && (defined(__unix__) || defined(__unix) || \ - (defined(__APPLE__) && defined(__MACH__))) -/* Просить POSIX-объявления обязательно: под `-std=c99` их не видно. */ -#if !defined(_POSIX_C_SOURCE) -#define _POSIX_C_SOURCE 200809L -#endif -#define FL_POSIX_STACK 1 -#endif - -/* - * ТОТ ЖЕ РАЗРЫВ, ЧТО У `flang_repl.c` (см. его комментарий выше по дереву и - * `docs/zettel/a-feature-macro-that-opens-on-glibc-closes-on-darwin.md`): - * `_POSIX_C_SOURCE` на Darwin ЗАКРЫВАЕТ то, что на glibc открывает. Здесь под - * ним `sigaction`/`sigemptyset` (окно наблюдения) и `getrlimit` (предел - * стека) — не проверено на настоящем Darwin (машины нет), но фикс безвреден - * там, где не нужен: `_DARWIN_C_SOURCE` только поднимает видимость, ничего - * не забирая. Без него этот файл рискует тем же самым `error: call to - * undeclared function`, только с другим именем функции. - */ -#if defined(__APPLE__) && !defined(_DARWIN_C_SOURCE) -#define _DARWIN_C_SOURCE -#endif - -#if defined(__wasm__) && !defined(FL_NO_WASM_STACK) -#define FL_WASM_STACK 1 -#endif - -#include "flang_runtime.h" - -#include -#include -#include -#include -#include -#include - -#ifdef FL_POSIX_STACK -#include -#include -/* Окно наблюдения: сигнал, `write` в дескриптор 2 и чтение /proc/self/statm. - Всё, что здесь нужно, объявлено тем же _POSIX_C_SOURCE, что и стек. */ -#include -#include -#include -#include -#define FL_WATCH 1 -#endif - -#ifdef FL_WASM_STACK -/* - * Границы теневого стека, объявленные компоновщиком wasm. Символы слабые: модуль, - * собранный так, что их нет, обязан собираться и работать — просто без этого - * знания, по запасному числу. Значений у них нет, значение имеет их АДРЕС: это - * отметки в разметке линейной памяти, а не переменные. - */ -extern char __heap_base __attribute__((weak)); -extern char __data_end __attribute__((weak)); -#endif - -/* ═════════════════════════════ арена ═════════════════════════════ */ - -/* - * Выравнивание: объединение самых «требовательных» типов. В C99 нет - * max_align_t, а угадывать «наверное, 8» значило бы поймать SIGBUS на первой - * же платформе со строгим выравниванием — а целятся сюда как раз такие - * (RISC-V, старые ARM). - */ -typedef union fl_align { - long double as_long_double; - double as_double; - void *as_pointer; - size_t as_size; -} fl_align; - -#define FL_ALIGNMENT (sizeof(fl_align)) -#define FL_CHUNK_MIN (size_t)(64u * 1024u) - -/* - * Сколько кусков откат ОСТАВЛЯЕТ арене, отдавая остальные системе. Ноль значит - * «отдать весь хвост»; разбор и цена каждого варианта — в замере над - * `fl_arena_rollback`. - */ -#ifndef FL_ARENA_KEEP -#define FL_ARENA_KEEP (size_t)4 -#endif - -struct fl_chunk { - fl_chunk *next; - size_t used; - size_t capacity; -}; - -static size_t fl_round_up(size_t size) { - const size_t remainder = size % FL_ALIGNMENT; - return remainder == 0 ? size : size + (FL_ALIGNMENT - remainder); -} - -static char *fl_chunk_data(fl_chunk *chunk) { - return (char *)chunk + fl_round_up(sizeof(fl_chunk)); -} - -void fl_arena_init(fl_arena *arena) { - fl_arena_init_small(arena, FL_CHUNK_MIN); -} - -void fl_arena_init_small(fl_arena *arena, size_t least) { - if (arena == NULL) { - return; - } - arena->chunks = NULL; - arena->current = NULL; - arena->reserved = 0; - arena->handed = 0; - arena->guard_chunk = NULL; - arena->guard_used = 0; - arena->staging = NULL; - arena->staging_size = 0; - arena->deny = NULL; - arena->recycle_mark = 0; - arena->recycle_next = 0; - /* Нулевой кусок не бывает, а кусок больше общего минимума и есть общий - минимум: мельчить умеем, крупнить незачем. */ - arena->least = least == 0 || least > FL_CHUNK_MIN ? FL_CHUNK_MIN : fl_round_up(least); -} - -void *fl_arena_alloc(fl_arena *arena, size_t size) { - if (arena == NULL) { - return NULL; - } - const size_t wanted = fl_round_up(size == 0 ? 1 : size); - - /* Сперва текущий кусок, затем следующие уже купленные (после reset они - пусты и переиспользуются), и только потом покупка нового. */ - while (arena->current != NULL) { - if (arena->current->capacity - arena->current->used >= wanted) { - char *block = fl_chunk_data(arena->current) + arena->current->used; - arena->current->used += wanted; - arena->handed += wanted; - return block; - } - if (arena->current->next == NULL || arena->current->next->used != 0) { - break; - } - arena->current = arena->current->next; - } - - { - const size_t header = fl_round_up(sizeof(fl_chunk)); - size_t capacity = arena->least == 0 ? FL_CHUNK_MIN : arena->least; - fl_chunk *chunk = NULL; - if (capacity < wanted) { - /* - * Запас сверх запрошенного. Кусок, купленный впритык, полон в момент - * покупки, и ни fl_arena_extend, ни удвоение запаса в fl_b_dobavit не - * найдут в нём ни байта: линейное «добавить» выродилось бы обратно в - * копию на каждом шаге — ровно то, от чего оно и заведено. Полтора раза - * дают геометрический рост: новый кусок покупается всё реже, суммарный - * перерасход ограничен, а накопление списка остаётся линейным. - */ - capacity = wanted; - if (capacity <= ((size_t)-1) / 3 * 2) { - capacity = wanted + wanted / 2; - } - } - if (capacity > (size_t)-1 - header) { - return NULL; /* переполнение размера — только при абсурдном запросе */ - } - chunk = (fl_chunk *)malloc(header + capacity); - if (chunk == NULL) { - return NULL; - } - chunk->capacity = capacity; - chunk->used = wanted; - chunk->next = NULL; - if (arena->current == NULL) { - chunk->next = arena->chunks; - arena->chunks = chunk; - } else { - chunk->next = arena->current->next; - arena->current->next = chunk; - } - arena->current = chunk; - arena->reserved += header + capacity; - arena->handed += wanted; - /* Следующий кусок вдвое больше — до общего минимума и не дальше. Арена с - мелким первым куском обязана оставаться дешёвой для того, кто ничего не - считает, и не становиться дорогой для того, кто считает: без удвоения - процесс, накопивший мегабайт, купил бы его двумя тысячами походов к - malloc. С удвоением их одиннадцать. */ - if (arena->least < FL_CHUNK_MIN) { - arena->least *= 2u; - } - return fl_chunk_data(chunk); - } -} - -/* - * Продление последней выдачи. Арена — бамп указателя, и единственное, что она - * умеет переиграть, это самый последний блок: он лежит в конце занятой части - * текущего куска, и за ним заведомо нет ничего чужого. - * - * Отсюда обе проверки: блок обязан кончаться ровно на границе занятого - * (`used`), и добавка обязана поместиться в тот же кусок. Ни то, ни другое не - * «осторожность на всякий случай» — не выполнись любая, и продление затёрло бы - * чужие данные. - * - * ── Третья проверка: продление не переходит границу области ──────────────── - * Она появилась вместе с областью на вызов, и без неё область была бы - * НЕИСПРАВНОЙ — молча, на редком входе. Разбор целиком, потому что случай - * тонкий и найден рассуждением, а не тестом: - * - * вызывающий накопил список, его массив — последняя выдача арены; - * вызываемый открыл область (отметка встала ровно за массивом); - * внутри области «добавить» к тому же списку берёт быстрый путь, видит, что - * запас исчерпан, зовёт fl_arena_extend — продление ложится ВЫШЕ отметки, — - * и тут же правит `grow->capacity` на месте, удваивая его; - * область закрывается, откат забирает продлённую половину, а `grow` лежит - * НИЖЕ отметки, и удвоенный `capacity` остаётся в нём навсегда. - * - * Дальше первое же «добавить» к этому списку проходит проверку - * `count < grow->capacity` и пишет в память, которую арена уже отдала кому-то - * другому. Это порча чужих данных, а не потеря своих. - * - * Лечится там, где рождается: продлевать блок, начало которого лежит ниже - * границы ближайшей открытой области, нельзя. Вызывающий тогда идёт медленным - * путём — копией с запасом, — и копия эта ложится выше отметки, то есть - * откатывается штатно. - */ -bool fl_arena_extend(fl_arena *arena, const void *block, size_t size, size_t extra) { - fl_chunk *chunk = NULL; - size_t taken = 0; - size_t added = 0; - if (arena == NULL || arena->current == NULL || block == NULL || extra == 0) { - return false; - } - chunk = arena->current; - taken = fl_round_up(size == 0 ? 1 : size); - if (chunk->used < taken || (const char *)block != fl_chunk_data(chunk) + (chunk->used - taken)) { - return false; /* выдавали не это или выдавали не последним */ - } - /* - * Граница открытой области. Куски выдаются вперёд по цепочке, а откат - * возвращает и границу, и текущий кусок разом, — поэтому `guard_chunk` - * всегда стоит на текущем куске или раньше него. Значит другой кусок - * означает «блок выдан позже границы», и продлевать его можно. - */ - if (arena->guard_chunk == chunk && (chunk->used - taken) < arena->guard_used) { - return false; - } - if (size > ((size_t)-1) - extra) { - return false; - } - /* Выравнивание оплачено ещё при первой выдаче, поэтому добавка считается по - разнице округлённых размеров, а не по самому `extra`. */ - added = fl_round_up(size + extra) - taken; - if (chunk->capacity - chunk->used < added) { - return false; /* кусок кончился: пусть вызывающий выделит новый и скопирует */ - } - chunk->used += added; - arena->handed += added; - return true; -} - -static void fl_region_forget(fl_arena *arena); - -void fl_arena_reset(fl_arena *arena) { - fl_chunk *chunk = NULL; - if (arena == NULL) { - return; - } - for (chunk = arena->chunks; chunk != NULL; chunk = chunk->next) { - chunk->used = 0; - } - arena->current = arena->chunks; - arena->handed = 0; - /* Открытых областей после сброса не бывает: сбрасывают между запросами, а не - посреди вызова. Граница снимается, иначе она сторожила бы пустоту. */ - arena->guard_chunk = NULL; - arena->guard_used = 0; - fl_region_forget(arena); -} - -void fl_arena_release(fl_arena *arena) { - fl_chunk *chunk = NULL; - if (arena == NULL) { - return; - } - chunk = arena->chunks; - while (chunk != NULL) { - fl_chunk *next = chunk->next; - free(chunk); - chunk = next; - } - free(arena->staging); - arena->chunks = NULL; - arena->current = NULL; - arena->reserved = 0; - arena->handed = 0; - arena->guard_chunk = NULL; - arena->guard_used = 0; - arena->staging = NULL; - arena->staging_size = 0; - free(arena->deny); - arena->deny = NULL; - arena->recycle_mark = 0; - arena->recycle_next = 0; -} - -/* ═════════════════════════════ стек ═════════════════════════════ */ - -/* - * Сколько байт стека несёт расчёт. Ноль означает «ещё не спрашивали»; после - * первого вопроса здесь лежит ответ, и второй раз никто не спрашивает — на - * POSIX это системный вызов, а `fl_ctx_init` зовут на каждый запрос. Выключение - * сторожа этим нулём не делается и делаться не может: выключает его поле - * `ctx->stack_room` в конкретном контексте. - */ -static size_t fl_stack_known = 0; - -/* - * Сказали ли нам про стек, или мы его угадали. - * - * Разница между двумя этими случаями стоит целого вида отказа, поэтому она - * названа полем, а не выведена из `fl_stack_known != 0`: там оба случая - * выглядят одинаково. Угаданное число (FL_STACK_ROOM_FALLBACK) любой ЗАМЕР - * вправе отменить — на то он и замер; объявленное `fl_stack_room_set` не вправе - * отменить никто, потому что объявил его тот, кто знает, что делает, и берёт - * последствия на себя. - */ -static bool fl_stack_told = false; - -size_t fl_stack_wanted(size_t max_depth) { - size_t wanted = 0; - if (max_depth == 0) { - /* Предел выключен — стеком его не обеспечить; берём столько, сколько берём - по умолчанию, а дальше говорит сторож. */ - return FL_STACK_MIN; - } - /* Умножение с проверкой: `--max-depth` приходит от пользователя, а - переполнение size_t дало бы КРОШЕЧНЫЙ стек там, где просили огромный. */ - if (max_depth > FL_STACK_MAX / (size_t)FL_STACK_PER_FRAME) { - return FL_STACK_MAX; - } - wanted = max_depth * (size_t)FL_STACK_PER_FRAME + FL_STACK_MARGIN; - if (wanted < FL_STACK_MIN) { - return FL_STACK_MIN; - } - if (wanted > FL_STACK_MAX) { - return FL_STACK_MAX; - } - return wanted; -} - -void fl_stack_room_set(size_t bytes) { - fl_stack_known = bytes; - fl_stack_told = bytes != 0; -} - -#ifdef FL_WASM_STACK -/* - * Сколько теневого стека осталось под точкой `point`. Ноль значит «ответа нет», - * а НЕ «стека нет»: разметка незнакомая, и врать про неё нельзя. - * - * ── Откуда вообще берётся ответ ───────────────────────────────────────────── - * - * У WebAssembly нет ни getrlimit, ни сторожевой страницы, и потому казалось, что - * спросить не у кого. Спросить есть у кого: теневой стек — не свойство среды, а - * КУСОК ЛИНЕЙНОЙ ПАМЯТИ, который отвёл компоновщик, и его границы компоновщик же - * и объявляет символами. Замер (clang 21.1.8, wasm32-wasi, `-O2`): - * - * сборка __data_end __heap_base ёмкость - * как есть (умолчание wasm-ld) 4 868 70 416 65 548 - * -Wl,-z,stack-size=1048576 4 868 1 053 456 1 048 588 - * - * То есть формула не «знает» про 64 КиБ и не зашивает их: она читает то, что - * компоновщик отвёл на самом деле. Кто просит стек больше — получает и предел - * глубины больше, тем же двоичным файлом и без второго флага, который надо не - * забыть согласовать с первым. - * - * ── Почему проверка, а не формула ────────────────────────────────────────── - * - * Разметок у wasm-ld две. Обычная: данные, потом стек, потом куча — стек лежит - * МЕЖДУ отметками, растёт вниз, и остаток под точкой это `point - __data_end`. - * И `--stack-first`: стек уезжает в самое начало памяти, а `__heap_base` минус - * `__data_end` даёт 12 байт — число, по которому сторож отказал бы всему на - * свете. Отличить их нельзя рассуждением, зато можно спросить у самого стека: - * где лежит `point`, там и стек. Замерено на обеих разметках, обе ветки ниже - * проверены прогоном. - * - * Всё, что не легло ни в одну разметку (модуль считает на чужом стеке — нити - * wasm, стек в куче), честно отвечает нулём и уходит к запасному числу. - */ -static size_t fl_wasm_room(const char *point) { - /* Приведение указателя к `size_t`, а не к `uintptr_t` из : этот - заголовок в напечатанном C не появляется НИ ОДНИМ включением (сторож - `emit-c.test.mjs` — «кроме стандартной библиотеки C зависимостей быть не - может» — проверяет список поимённо), а у wasm32 и у всех целей бэкенда - `size_t` ровно ширины указателя. */ - const size_t here = (size_t)point; - const size_t low = (size_t)&__data_end; - const size_t high = (size_t)&__heap_base; - if (here == 0u || low == 0u || high == 0u || high <= low) { - return 0; - } - if (here > low && here <= high) { - return here - low; /* данные, стек, куча: дно стека — конец данных */ - } - if (here < low) { - /* `--stack-first`: стек занимает начало линейной памяти, и дном ему служит - нулевой адрес — весь остаток под точкой это её собственный адрес. */ - return here; - } - return 0; -} -#endif - -size_t fl_stack_room(void) { - if (fl_stack_known != 0) { - return fl_stack_known; - } -#ifdef FL_POSIX_STACK - { - struct rlimit limit; - if (getrlimit(RLIMIT_STACK, &limit) == 0) { - if (limit.rlim_cur == RLIM_INFINITY) { - /* Стек без объявленного предела: врать про бесконечность нельзя, потому - что система всё равно во что-нибудь упрётся. Берём тот же потолок, до - которого доводит `fl_stack_wanted`. */ - fl_stack_known = FL_STACK_MAX; - return fl_stack_known; - } - if ((size_t)limit.rlim_cur > FL_STACK_MARGIN) { - /* Отметку сторож снимает не на самом дне стека, а там, где начался - расчёт, и над ней уже лежат кадры main и разбора запроса. Их немного - (самый толстый кадр прогонщика — 416 байт), но списать на них полный - запас честнее, чем не списать ничего. */ - fl_stack_known = (size_t)limit.rlim_cur - FL_STACK_MARGIN; - return fl_stack_known; - } - } - } -#endif - fl_stack_known = FL_STACK_ROOM_FALLBACK; - return fl_stack_known; -} - -#ifdef FL_POSIX_STACK -typedef struct fl_deep_work { - void (*work)(void *); - void *state; -} fl_deep_work; - -static void *fl_deep_entry(void *raw) { - fl_deep_work *carry = (fl_deep_work *)raw; - carry->work(carry->state); - return NULL; -} -#endif - -bool fl_call_deep(size_t stack_bytes, void (*work)(void *), void *state) { -#ifdef FL_POSIX_STACK - fl_deep_work carry; - pthread_attr_t attr; - pthread_t worker; - struct rlimit space; - size_t asked = stack_bytes; - if (work == NULL) { - return false; - } - carry.work = work; - carry.state = state; - /* - * Стек берётся отображением, и под пределом адресного пространства он ОТНИМАЕТ - * его у арены. Замер, который это и нашёл: под `ulimit -v 16384` восьми - * мегабайт стека хватало, чтобы прогон конкурентности перестал доходить до - * исхода — куча кончалась у планировщика, а не у процесса, и надзору было - * нечего разбирать (`emit-c-conc.test.mjs`, шаг Г2). - * - * Отсюда правило: под объявленным пределом адресного пространства стек берёт - * ЧЕТВЕРТЬ и не больше. Если четверти не хватает даже на системный минимум, - * поток не заводится вовсе — и программа считает ровно там же и с той же - * памятью, что до починки, а обещание держит сторож. Глубина, купленная ценой - * чужой памяти, была бы не починкой, а переносом отказа в другое место. - */ - if (getrlimit(RLIMIT_AS, &space) == 0 && space.rlim_cur != RLIM_INFINITY) { - const size_t share = (size_t)(space.rlim_cur / 4); - if (asked > share) { - asked = share; - } - if (asked < FL_STACK_MIN) { - return false; - } - } - /* - * Уступка вниз, а не отказ. Стек берётся у системы отображением, и под - * `ulimit -v` (или в контейнере с пределом адресного пространства) большой - * кусок не дадут. Отказаться совсем значило бы потерять и то, что дают; - * поэтому просьба половинится до самого системного минимума, и лишь потом - * расчёт идёт на своём стеке — со сторожем, который и скажет правду. - */ - for (;;) { - if (pthread_attr_init(&attr) != 0) { - return false; - } - /* Запас объявляется ДО запуска, и это не придирчивость к порядку строк: - поток спросит о нём в первом же `fl_ctx_init`. Объявить после - `pthread_join` значило бы сторожить весь расчёт по стеку ГЛАВНОГО потока, - то есть не воспользоваться заведённым ни на байт — снаружи это выглядит - как «починка не работает», а не как ошибка порядка. */ - fl_stack_room_set(asked > FL_STACK_MARGIN ? asked - FL_STACK_MARGIN : asked); - if (pthread_attr_setstacksize(&attr, asked) == 0 && - pthread_create(&worker, &attr, fl_deep_entry, &carry) == 0) { - pthread_attr_destroy(&attr); - return pthread_join(worker, NULL) == 0; - } - pthread_attr_destroy(&attr); - fl_stack_room_set(0); /* поток не завёлся — объявленного запаса нет */ - if (asked <= FL_STACK_MIN) { - return false; - } - asked /= 2; - if (asked < FL_STACK_MIN) { - asked = FL_STACK_MIN; - } - } -#else - (void)stack_bytes; - (void)work; - (void)state; - return false; -#endif -} - -/* ═════════════════════════════ контекст ═════════════════════════════ */ - -/* - * ПРЕДЕЛ ШАГОВ — ОДНО ЧИСЛО НА ПРОГРАММУ, И ЖИВЁТ ОНО ЗДЕСЬ. - * - * `FL_MAX_STEPS` печатается бэкендом перед этим файлом и потому неизменен: он - * УМОЛЧАНИЕ, а не потолок. Кто знает про свой счёт больше компоновщика — - * человек, сказавший `flang check --предел-шагов N`, — говорит об этом вслух, и - * сказанное ложится сюда, в одно место. Иначе поднимать предел пришлось бы - * пересборкой (scripts/raskrutka.sh), то есть переписывать число в двух местах: - * в семени и в скрипте. - * - * Почему не поле контекста, а общее умолчание: контекст заводится заново на - * КАЖДЫЙ вызов компилятора (`repl_cycle`), и назначенное одному контексту - * следующий уже не помнил бы. Умолчание помнит. - * - * Ноль здесь значит «не сказано», а не «счёт выключен»: выключение счёта — - * `ctx->max_steps = 0` на самом контексте, и путать эти два смысла в одном - * числе нельзя. - */ -static size_t fl_max_steps_told = 0; - -size_t fl_max_steps_default(void) { - return fl_max_steps_told == 0 ? (size_t)FL_MAX_STEPS : fl_max_steps_told; -} - -void fl_max_steps_default_set(size_t steps) { fl_max_steps_told = steps; } - -/* - * ПРЕДЕЛ ГЛУБИНЫ — ТО ЖЕ САМОЕ И ПО ТЕМ ЖЕ ДОВОДАМ, что предел шагов выше. - * - * Здесь его не было, и это стоило двух суток работы (задача 7444). `FL_MAX_DEPTH` - * читался прямо в `fl_ctx_init`, поэтому предел глубины у идущего двоичного не - * менялся НИЧЕМ, кроме пересборки: ключ `--max-depth` у `emit` кладёт число в - * НАПЕЧАТАННУЮ программу, а своему прогону не говорит ничего. Человек, поднявший - * ключом предел, получал тот же отказ с тем же старым числом — и читал его как - * беду в своём файле. - * - * Ноль значит «не сказано», а не «предел снят»: снятие — `ctx->max_depth = 0` на - * самом контексте, и путать эти два смысла в одном числе нельзя. Точно так же - * устроен предел шагов, и расхождение здесь было бы ловушкой. - * - * ОДНОГО ЭТОГО ЧИСЛА МАЛО, и об этом надо знать. Глубину несёт стек, а стек - * заводится один раз на процесс — до того, как разобран хоть один ключ - * (`fl_call_deep` в flang_cli.c). Поэтому тот, кто поднимает предел здесь, - * обязан поднять и стек: иначе `fl_enter` упрётся в `fl_stack_spent` и честно - * скажет «исчерпала стек хозяина», не дойдя до объявленного предела. - */ -static size_t fl_max_depth_told = 0; - -size_t fl_max_depth_default(void) { - return fl_max_depth_told == 0 ? (size_t)FL_MAX_DEPTH : fl_max_depth_told; -} - -void fl_max_depth_default_set(size_t depth) { fl_max_depth_told = depth; } - -void fl_ctx_init(fl_ctx *ctx, fl_arena *arena) { - /* Отметка стека: адрес локальной этой самой функции. Всё, что расчёт займёт - под ней, сторож и меряет. Брать её здесь правильно потому, что `fl_ctx_init` - зовут ровно там, где расчёт начинается, — на запрос прогонщика, на сессию - оболочки, на вызов встраивающего. */ - char here = 0; - if (ctx == NULL) { - return; - } - ctx->arena = arena; - ctx->depth = 0; - ctx->max_depth = fl_max_depth_default(); - ctx->steps = 0; - ctx->max_steps = fl_max_steps_default(); - ctx->stack_base = &here; - ctx->stack_room = fl_stack_room(); -#ifdef FL_WASM_STACK - /* - * На wasm запас не угадывают, а МЕРЯЮТ, и меряют здесь, а не в - * `fl_stack_room`, по одной причине: ответ зависит от того, откуда позвали. - * `fl_stack_room` отвечает один раз на процесс и запоминает ответ, а расчёт - * может начаться и у самой вершины стека (прогонщик), и глубоко внутри чужой - * работы (встраивающий) — запомненное число во втором случае было бы завышено - * ровно на всё, что уже съедено. Отметка `&here` снята там же, где `stack_base`, - * поэтому меряется именно то, что расчёту и остаётся. - * - * Замер отменяет догадку, но не отменяет объявленное: сказавший - * `fl_stack_room_set` знает про свой стек больше, чем компоновщик, — так - * говорит, например, встраивающий, который считает на стеке, отведённом им - * самим. - */ - if (!fl_stack_told) { - const size_t измерено = fl_wasm_room(&here); - if (измерено != 0) { - ctx->stack_room = измерено; - } - } -#endif - ctx->stack_seen = 0; - ctx->stack_step = 0; - /* Окно наблюдения показывает ЭТОТ расчёт: `fl_ctx_init` зовут ровно там, где - расчёт начинается. Обработчик ставится один раз на процесс. */ - fl_watch_open(ctx); -} - -const char *fl_status_text(fl_status status) { - switch (status) { - case FL_OK: - return "ok"; - case FL_ERROR: - return "вычисление прекращено ошибкой"; - case FL_INVALID_ARGUMENT: - return "недопустимый аргумент"; - } - return "неизвестный статус"; -} - -/* ═════════════════════════════ ошибки ═════════════════════════════ */ - -static const char *fl_vformat(fl_ctx *ctx, const char *format, va_list args) { - va_list probe; - int needed = 0; - char *buffer = NULL; - if (ctx == NULL || ctx->arena == NULL) { - return NULL; - } - va_copy(probe, args); - needed = vsnprintf(NULL, 0, format, probe); - va_end(probe); - if (needed < 0) { - return NULL; - } - buffer = (char *)fl_arena_alloc(ctx->arena, (size_t)needed + 1); - if (buffer == NULL) { - return NULL; - } - vsnprintf(buffer, (size_t)needed + 1, format, args); - return buffer; -} - -fl_status fl_fail(fl_ctx *ctx, fl_error *error, const char *code, const char *format, ...) { - va_list args; - const char *message = NULL; - if (error == NULL) { - /* Вызывающему текст не нужен — не строим его вовсе: диагностика стоит - выделения памяти, а платить за неё молча неправильно. */ - return FL_ERROR; - } - va_start(args, format); - message = fl_vformat(ctx, format, args); - va_end(args); - if (message == NULL) { - error->code = FL_CODE_MEMORY; - error->message = "недостаточно памяти для текста диагностики"; - return FL_ERROR; - } - error->code = code; - error->message = message; - return FL_ERROR; -} - -static fl_status fl_no_memory(fl_error *error) { - if (error != NULL) { - error->code = FL_CODE_MEMORY; - error->message = "недостаточно памяти"; - } - return FL_ERROR; -} - -/* ═════════════════════════════ граница входа ═════════════════════════════ - * - * Значения, пришедшие СНАРУЖИ, против объявленных типов параметров. Таблица - * печатается вместе с программой (`<модуль>_entry`), а сверяет её этот код — - * один и тот же для всех программ, потому что второе понимание слов «значение - * подходит типу» разошлось бы с первым молча. - * - * Тексты отказов — дословно те же, что у `checkValue` в flang/src/types.mjs. - * Сверять их надо равенством строк: они и есть контракт, а не украшение. - */ - -/* Текст в арену: метка пути внутрь значения строится на ходу («…[0]»). */ -static const char *fl_label(fl_ctx *ctx, const char *format, ...) { - va_list args; - const char *text = NULL; - va_start(args, format); - text = fl_vformat(ctx, format, args); - va_end(args); - return text; -} - -static fl_status fl_check_typed(fl_ctx *ctx, const fl_entry_table *table, size_t index, fl_value value, - const char *label, fl_error *error); - -static fl_status fl_check_number_type(fl_ctx *ctx, const fl_type *type, fl_value value, const char *label, - fl_error *error) { - char text[FL_NUMBER_TEXT_MAX]; - if (value.tag != FL_NUMBER || !isfinite(value.as.number)) { - return fl_fail(ctx, error, FL_CODE_TYPE, "%s не соответствует типу %s", label, type->name); - } - fl_number_text(value.as.number, text); - /* Целость проверяется ДО отрезка и на ней же кончается: у свидетеля тот же - порядок, и второй отказ на одном значении был бы вторым текстом про одну - беду. */ - if (type->integral && floor(value.as.number) != value.as.number) { - return fl_fail(ctx, error, FL_CODE_TYPE, "%s: %s не целое, а тип %s — целый", label, text, type->name); - } - if (type->bounded && (value.as.number < type->low || value.as.number > type->high)) { - return fl_fail(ctx, error, FL_CODE_TYPE, "%s: %s вне %s", label, text, type->name); - } - return FL_OK; -} - -/* - * ── Сравнение имён и строк без похода в libc ─────────────────────────────── - * - * Имена полей сверяются линейным перебором на КАЖДОМ взятии поля, а взятие поля - * — самое частое, что делает напечатанный код. Профиль `flang check - * flang/self/tags.flang` (perf, 14 тысяч проб): `__strcmp_avx2` 12,3 %, - * `__memcmp_avx2_movbe` 10,9 % — почти четверть всей работы уходит в libc на - * сравнения, которые в подавляющем большинстве кончаются НЕсовпадением. - * - * Имена в flang русские, и это меняет отсечку: первый байт кириллической буквы - * — D0 либо D1, то есть по нему не различается почти ничто. Различает ВТОРОЙ. - * Поэтому отсев идёт по двум байтам сразу, и только сойдись они оба — зовётся - * strcmp на остаток. Ответ тот же до буквы: это та же посимвольная сверка, - * просто первые два символа сверены на месте. - * - * Указатели сверяются первыми не ради скорости на совпадении, а потому что имя - * поля в напечатанном коде — литерал, и одинаковые литералы единицы трансляции - * компилятор сливает: у совпавшего имени указатели чаще всего РАВНЫ. - */ -static bool fl_name_same(const char *left, const char *right) { - if (left == right) { - return true; - } - if (left[0] != right[0]) { - return false; - } - if (left[0] == '\0') { - return true; - } - if (left[1] != right[1]) { - return false; - } - if (left[1] == '\0') { - return true; - } - return strcmp(left + 2, right + 2) == 0; -} - -/* То же для строк flang: длина уже сошлась, сверяются края, потом середина. */ -static bool fl_bytes_same(const char *left, const char *right, size_t bytes) { - if (left == right || bytes == 0) { - return true; - } - if (left[0] != right[0] || left[bytes - 1] != right[bytes - 1]) { - return false; - } - return memcmp(left, right, bytes) == 0; -} - -/* - * ── ДВА ПРОХОДА: сперва одни указатели, потом имена ──────────────────────── - * - * Замер 24 августа, счётчики врезаны прямо в `fl_field_get` семени, прогон - * `check flang/stdlib/strings.flang --proof`: - * - * вызовов 53 614 560 шагов цикла 78 527 481 - * СОВПАЛО ПО УКАЗАТЕЛЮ 53 614 560 совпало по strcmp 0 - * - * и на `check flang/self/builtins.flang --proof` — 243 879 126 вызовов, - * 416 544 557 шагов, и снова ВСЕ до единого совпадения по указателю, ни одного - * по strcmp. Имя поля в напечатанном коде — литерал и на постройке записи, и на - * её чтении; одинаковые литералы единицы трансляции компилятор сливает, и - * указатели у них равны. - * - * ОТСЮДА ПРОВЕРЕННЫЙ ОТКАЗ ОТ ИНТЕРНИРОВАНИЯ. Общая таблица имён, где все копии - * одного имени получают один указатель, чинила бы совпадение по strcmp — а его - * НОЛЬ из 297 миллионов. Интернирование стоило бы таблицы, хеша и лишнего шага - * на каждой постройке записи и не убрало бы ни одного strcmp. Не сделано, и - * дальше пробовать незачем: числа выше это закрывают. - * - * Работа сидит не в совпадении, а В ПРОМАХАХ: 24,9 млн промахов на strings, 172,7 - * млн на builtins — это те поля, мимо которых цикл прошёл до нужного. У промаха - * указатели разные ВСЕГДА, и `fl_name_same` идёт дальше — читать имя. А имя - * лежит в другой строке кэша, чем массив полей, и этот поход и есть цена. - * - * Поэтому цикл разделён надвое: первый проход сверяет ТОЛЬКО указатели и в - * память имён не заглядывает вовсе, второй (полный, посимвольный) заводится, - * лишь если первый ничего не нашёл. Ответ тот же: второй проход — прежний цикл - * целиком. - * - * Цена, снятая прогоном (эталон `check ... --proof`, машина свободна, прогоны - * по одному): - * - * flang/self/builtins.flang 78,31 с → 76,53 с (−2,3 %) - * flang/stdlib/strings.flang 4,70 с → 4,68 с (−0,4 %, на грани шума) - * - * Отсчёт вёлся от семени, в которое уже внесён `fl_name_same` выше, — то есть - * это цена ИМЕННО двух проходов, а не сравнения имён вообще. Сам `fl_name_same` - * против голого strcmp дал на том же эталоне 83,30 с → 78,31 с (−6,0 %). - * - * Вывод `--proof` сверен полностью и совпал знак в знак на обоих файлах - * (md5 006b9622a844a6acb429277c92d1b103 у всех четырёх сборок). - * - * Что осталось и чего здесь НЕТ. В профиле после правки первое место занимает - * не взятие поля (4,7 %), а обмер области — `fl_region_size` с - * `fl_region_fields_size` и `fl_region_close` вместе около 40 %. Пробная правка - * обмера (не звать себя рекурсивно на число, признак и «ничто», подняв проверку - * глубины перед цикл) на семени дала 76,53 с → 75,08 с на builtins и - * 4,68 с → 4,53 с на strings при том же выводе знак в знак, но сюда не - * внесена: обмер в этом файле уже переписан на пропуск подграфа ниже отметки - * (`fl_live`), и старое число к новому коду не относится. Мерить надо заново. - */ -/* Поле по имени среди полей записи или варианта; NULL — поля нет. */ -static const fl_field *fl_find_field(const fl_field *fields, size_t count, const char *name) { - size_t index = 0; - for (index = 0; index < count; index += 1) { - if (fields[index].name == name) { - return &fields[index]; - } - } - for (index = 0; index < count; index += 1) { - if (fl_name_same(fields[index].name, name)) { - return &fields[index]; - } - } - return NULL; -} - -/* - * Объявленные поля против заданных. `owner` — имя записи либо имя варианта: - * сообщения о пропущенном поле у них разной формы, и обе выписаны здесь - * дословно, потому что сверяются они со свидетелем равенством строк. - */ -static fl_status fl_check_fields(fl_ctx *ctx, const fl_entry_table *table, size_t from, size_t count, - const fl_field *given, size_t given_count, const char *label, - const char *owner, bool of_variant, fl_error *error) { - size_t index = 0; - for (index = 0; index < count; index += 1) { - const fl_type_field *declared = &table->fields[from + index]; - const fl_field *found = fl_find_field(given, given_count, declared->name); - const char *inner = NULL; - if (found == NULL) { - /* Необязательное поле можно не задавать: отсутствие — это «ничто». */ - if (table->types[declared->type].optional) { - continue; - } - if (of_variant) { - return fl_fail(ctx, error, FL_CODE_TYPE, "%s: вариант «%s» требует поле «%s»", label, owner, - declared->name); - } - return fl_fail(ctx, error, FL_CODE_TYPE, "%s: не задано поле «%s» записи «%s»", label, declared->name, - owner); - } - inner = fl_label(ctx, "%s.%s", label, declared->name); - if (inner == NULL) { - return fl_no_memory(error); - } - FL_TRY(fl_check_typed(ctx, table, declared->type, found->value, inner, error)); - } - return FL_OK; -} - -static fl_status fl_check_typed(fl_ctx *ctx, const fl_entry_table *table, size_t index, fl_value value, - const char *label, fl_error *error) { - const fl_type *type = NULL; - size_t item = 0; - if (index >= table->type_count) { - return FL_OK; - } - type = &table->types[index]; - /* Необязательный аргумент можно не задавать: отсутствие — это «ничто», а не - пропуск. Так же считает и ядро FTS. */ - if (type->optional && value.tag == FL_NOTHING) { - return FL_OK; - } - switch (type->kind) { - case FL_TYPE_NUMBER: - return fl_check_number_type(ctx, type, value, label, error); - case FL_TYPE_STRING: - if (value.tag != FL_STRING) { - return fl_fail(ctx, error, FL_CODE_TYPE, "%s не соответствует типу %s", label, type->name); - } - return FL_OK; - case FL_TYPE_FLAG: - if (value.tag != FL_FLAG) { - return fl_fail(ctx, error, FL_CODE_TYPE, "%s не соответствует типу %s", label, type->name); - } - return FL_OK; - case FL_TYPE_NULL: - if (value.tag != FL_NOTHING) { - return fl_fail(ctx, error, FL_CODE_TYPE, "%s не соответствует типу %s", label, type->name); - } - return FL_OK; - case FL_TYPE_LIST: - if (value.tag != FL_LIST) { - return fl_fail(ctx, error, FL_CODE_TYPE, "%s не соответствует типу %s", label, type->name); - } - for (item = 0; item < value.as.list.count; item += 1) { - const char *inner = fl_label(ctx, "%s[%lu]", label, (unsigned long)item); - if (inner == NULL) { - return fl_no_memory(error); - } - FL_TRY(fl_check_typed(ctx, table, type->of, value.as.list.items[item], inner, error)); - } - return FL_OK; - case FL_TYPE_RECORD: { - if (value.tag != FL_RECORD) { - return fl_fail(ctx, error, FL_CODE_TYPE, "%s не соответствует типу %s", label, type->name); - } - FL_TRY(fl_check_fields(ctx, table, type->field_from, type->field_count, value.as.record->fields, - value.as.record->count, label, type->owner, false, error)); - /* Лишнее поле — тоже несоответствие типу: запись flang тотальна, и поля - сверх объявленных в ней взяться неоткуда. */ - for (item = 0; item < value.as.record->count; item += 1) { - size_t at = 0; - bool declared = false; - for (at = 0; at < type->field_count; at += 1) { - if (fl_name_same(table->fields[type->field_from + at].name, value.as.record->fields[item].name)) { - declared = true; - break; - } - } - if (!declared) { - return fl_fail(ctx, error, FL_CODE_TYPE, "%s: запись «%s» не имеет поля «%s»", label, type->owner, - value.as.record->fields[item].name); - } - } - return FL_OK; - } - case FL_TYPE_SUM: { - const fl_type_variant *variant = NULL; - if (value.tag != FL_VARIANT && value.tag != FL_RECORD) { - return fl_fail(ctx, error, FL_CODE_TYPE, "%s не соответствует типу %s", label, type->name); - } - if (value.tag == FL_VARIANT) { - for (item = 0; item < type->variant_count; item += 1) { - if (fl_name_same(table->variants[type->variant_from + item].name, value.as.variant->name)) { - variant = &table->variants[type->variant_from + item]; - break; - } - } - } - if (variant == NULL) { - return fl_fail(ctx, error, FL_CODE_TYPE, "%s: ожидался вариант типа «%s»", label, type->owner); - } - return fl_check_fields(ctx, table, variant->field_from, variant->field_count, value.as.variant->fields, - value.as.variant->count, label, variant->name, true, error); - } - case FL_TYPE_UNKNOWN: - default: - return FL_OK; - } -} - -/* ═════════════════════════ окно наблюдения ═════════════════════════ - * - * К ИДУЩЕЙ ПРОГРАММЕ ПОДКЛЮЧАЮТСЯ СИГНАЛОМ, И ОТВЕЧАЕТ ХОЗЯИН. - * - * Зачем — задача 0061. 25–26 августа 2026 перепечатка семени шла девять с - * половиной часов и не сказала о себе ни слова; чтобы понять, жива она или - * зависла, пришлось читать /proc и сравнивать два профиля `perf` с получасовым - * промежутком. Строка хода задачи 0031 говорит раз в полминуты и говорит имя - * ШАГА; здесь тот же прибор отвечает, КОГДА СПРОСИЛИ, и называет функцию, на - * которой счёт стоит прямо сейчас. - * - * Форма выбрана и обоснована в - * docs/adr/0019-the-host-answers-the-signal-not-the-program.md. Три довода - * оттуда, без которых этот код читается неверно: - * - * 1. ПРОГРАММА В ЭТОМ НЕ УЧАСТВУЕТ. Ни нового слова, ни нового поручения — - * и никакого способа заметить, что к ней подключились. Раз прогон под - * наблюдением неотличим от прогона без него, ни одно обещание программы от - * наблюдения не меняется. Отладка не входит в смысл программы. - * - * 2. НАБЛЮДАЕМОЕ ТЕЧЁТ ТОЛЬКО НАРУЖУ. Ни одно поле окна не лежит в `fl_ctx`, - * поэтому ни одна ветка расчёта до него не дотянется. Дай программе прочесть - * свои витки — она сможет по ним ветвиться, и два прогона одной программы - * дадут разные ответы. - * - * 3. ХОЗЯИН И БЕЗ ТОГО ЭТО СЧИТАЕТ. Витки считает `fl_tick`, глубину — - * `fl_enter`, имя функции приходит в `fl_enter` первым доводом. Новый здесь - * не факт, а СРОК: те же числа, но когда спросили, а не когда сломалось. - * - * УМОЛЧАНИЕ SIGUSR1 В POSIX — ЗАВЕРШИТЬ ПРОЦЕСС. То есть до этого окна - * `kill -USR1 ` не «ничего не делал», а УБИВАЛ идущий расчёт. - * - * ПОЧЕМУ ВСЁ РУКАМИ, А НЕ `fprintf`. Обработчик сигнала вправе звать только то, - * что POSIX объявил безопасным; stdio в этот список не входит и, прерванный - * посреди собственного буфера, кладёт процесс. Отсюда `write` в дескриптор 2 и - * сборка строки в свой буфер. Ровно тем же и по той же причине устроена строка - * хода в flang_repl.c — и её помощники переехали сюда, чтобы одно и то же число - * не выглядело в двух строках по-разному. - * - * ПОЧЕМУ В stderr, А НЕ В stdout. Машинный вывод (`flang check --proof --json`, - * `flang emit`, сверка семени байт в байт) идёт в stdout и обязан остаться - * прежним до байта. - */ - -/* ── печать без stdio ── */ - -size_t fl_say_text(char *out, size_t room, size_t at, const char *text) { - size_t index = 0; - if (text == NULL) { - return at; - } - while (text[index] != 0 && at + 1 < room) { - out[at] = text[index]; - at += 1; - index += 1; - } - return at; -} - -/* Число пробелами по три разряда: «4 800 000 000» читается глазом, а - «4800000000» приходится считать пальцем по экрану. */ -size_t fl_say_number(char *out, size_t room, size_t at, unsigned long value) { - char digits[24]; - size_t count = 0; - do { - digits[count] = (char)('0' + (int)(value % 10)); - value /= 10; - count += 1; - } while (value != 0 && count < sizeof(digits)); - while (count > 0) { - count -= 1; - if (at + 1 < room) { - out[at] = digits[count]; - at += 1; - } - if (count > 0 && count % 3 == 0) { - at = fl_say_text(out, room, at, " "); - } - } - return at; -} - -/* Память теми же словами, какими её называет остальное дерево: ГиБ с одним - знаком после запятой, ниже гигабайта — целые МиБ. */ -size_t fl_say_size(char *out, size_t room, size_t at, unsigned long bytes) { - const unsigned long gib = 1024UL * 1024UL * 1024UL; - if (bytes >= gib) { - at = fl_say_number(out, room, at, bytes / gib); - at = fl_say_text(out, room, at, ","); - at = fl_say_number(out, room, at, (bytes % gib) / (gib / 10)); - return fl_say_text(out, room, at, " ГиБ"); - } - at = fl_say_number(out, room, at, bytes / (1024UL * 1024UL)); - return fl_say_text(out, room, at, " МиБ"); -} - -/* - * Размер страницы снимается ЗАРАНЕЕ, вне обработчика: `sysconf` в списке - * безопасных не значится. Там, где /proc нет (macOS, BSD, wasm), ответ 0, и - * снимок просто обходится без памяти, а не пропадает целиком. - */ -static unsigned long fl_say_page = 0; - -void fl_say_page_ready(void) { -#ifdef FL_WATCH - if (fl_say_page == 0) { - const long page = sysconf(_SC_PAGESIZE); - fl_say_page = page > 0 ? (unsigned long)page : 0; - } -#endif -} - -unsigned long fl_say_resident(void) { -#ifdef FL_WATCH - char raw[128]; - ssize_t got = 0; - int file = -1; - size_t at = 0; - unsigned long pages = 0; - if (fl_say_page == 0) { - return 0; - } - file = open("/proc/self/statm", O_RDONLY); - if (file < 0) { - return 0; - } - got = read(file, raw, sizeof(raw) - 1); - close(file); - if (got <= 0) { - return 0; - } - raw[got] = 0; - while (raw[at] != 0 && raw[at] != ' ') { - at += 1; - } - while (raw[at] == ' ') { - at += 1; - } - while (raw[at] >= '0' && raw[at] <= '9') { - pages = pages * 10 + (unsigned long)(raw[at] - '0'); - at += 1; - } - return pages * fl_say_page; -#else - return 0; -#endif -} - -/* ── что окно показывает ── */ - -/* - * ИМЕНА ЖИВЫХ КАДРОВ ЛЕЖАТ ЗДЕСЬ, А НЕ В `fl_ctx`, и на то три причины. - * - * 1. `fl_ctx` заводится на стеке хозяина. Сигнал, пришедший после конца - * расчёта, читал бы мёртвый кадр. Здесь лежат указатели на строковые - * литералы напечатанного кода — они живут всю жизнь процесса, и - * разыменование не падает никогда. - * 2. Ширина `fl_ctx` не меняется, значит `flang_conc.h` и всё, что кладёт - * контекст в свои структуры, не трогается вовсе. - * 3. Программа до статики рантайма не дотянется, а до поля контекста дотянулась - * бы (см. довод 2 в шапке). - * - * ПЛАТА НАЗВАНА: планировщик считает на нескольких потоках ОС (задача 0013), а - * массив у них один — на многопоточном прогоне снимок может назвать имя из - * чужого потока. Это искажение ДИАГНОСТИКИ, а не порча расчёта: любой - * записанный сюда указатель остаётся годным литералом. - */ -static const char *fl_watch_frames[FL_WATCH_FRAMES]; - -/* - * Контекст, чьи числа показывать. Хранится указателем, а не копией: копировать - * пришлось бы на каждом витке, а это ровно та цена, которой здесь быть не - * должно. Числа снимаются НА ХОДУ и потому могут отстать на виток — это - * диагностика, а не учёт. - */ -static fl_ctx *fl_watch_ctx = NULL; - -/* Имя шага от хозяина и готовая строка «повторить». Обе собраны ВНЕ - обработчика: обработчик их только пишет. */ -static char fl_watch_label[192]; -static char fl_watch_repeat[768]; - -#ifdef FL_WATCH -static volatile sig_atomic_t fl_watch_set = 0; -static volatile sig_atomic_t fl_watch_want = 0; -#endif - -void fl_watch_step(const char *name) { - size_t fit = name == NULL ? 0 : strlen(name); - if (fit > sizeof(fl_watch_label) - 1) { - fit = sizeof(fl_watch_label) - 1; - } - memcpy(fl_watch_label, name == NULL ? "" : name, fit); - fl_watch_label[fit] = 0; -} - -void fl_watch_repeat_set(const char *line) { - size_t fit = line == NULL ? 0 : strlen(line); - if (fit > sizeof(fl_watch_repeat) - 1) { - /* Обрезаем по БУКВЕ буфера и говорим об этом многоточием: обрезанную строку - нельзя вставить и повторить, и молчать об этом нельзя. Три точки одним - знаком, а не тремя: три знака могли бы рассечь букву UTF-8 пополам. */ - fit = sizeof(fl_watch_repeat) - 4; - /* Отступить до начала буквы: продолжающие байты UTF-8 несут 10xxxxxx, и - обрыв на них дал бы в снимке битую букву. */ - while (fit > 0 && ((unsigned char)line[fit] & 0xC0u) == 0x80u) { - fit -= 1; - } - memcpy(fl_watch_repeat, line, fit); - memcpy(fl_watch_repeat + fit, "…", 3); - fl_watch_repeat[fit + 3] = 0; - return; - } - memcpy(fl_watch_repeat, line == NULL ? "" : line, fit); - fl_watch_repeat[fit] = 0; -} - -bool fl_watch_asked(void) { -#ifdef FL_WATCH - if (fl_watch_want == 0) { - return false; - } - fl_watch_want = 0; - return true; -#else - return false; -#endif -} - -#ifdef FL_WATCH -/* - * Ответ на подключение. Всё, что здесь есть, законно внутри обработчика: ни - * stdio, ни выделения памяти, ни `sysconf`. - * - * Строка первая — где счёт стоит. Строка вторая — кто позвал, от глубокого к - * мелкому; она и отвечает на вопрос «как мы сюда попали». Строка третья — - * шаг, которым хозяин назвал бы работу целиком. Четвёртая — готовый вызов для - * `flang run`: снимок отладки, который можно вставить и повторить. - */ -static void fl_watch_say(int signal_number) { - char line[2048]; - size_t at = 0; - size_t depth = 0; - size_t seen = 0; - size_t index = 0; - unsigned long steps = 0; - unsigned long limit = 0; - unsigned long bytes = 0; - ssize_t wrote = 0; - (void)signal_number; - fl_watch_want = 1; - if (fl_watch_ctx != NULL) { - depth = fl_watch_ctx->depth; - steps = (unsigned long)fl_watch_ctx->steps; - limit = (unsigned long)fl_watch_ctx->max_steps; - } - seen = depth < FL_WATCH_FRAMES ? depth : FL_WATCH_FRAMES; - - at = fl_say_text(line, sizeof(line), at, "ПОДКЛЮЧЕНИЕ: идёт "); - if (seen > 0 && fl_watch_frames[seen - 1] != NULL) { - at = fl_say_text(line, sizeof(line), at, "«"); - at = fl_say_text(line, sizeof(line), at, fl_watch_frames[seen - 1]); - at = fl_say_text(line, sizeof(line), at, "»"); - } else { - /* Ни одного живого кадра: расчёт либо ещё не начался, либо уже кончился, и - врать про функцию нельзя. */ - at = fl_say_text(line, sizeof(line), at, "не функция — расчёт вне вызова"); - } - at = fl_say_text(line, sizeof(line), at, ", глубина "); - at = fl_say_number(line, sizeof(line), at, (unsigned long)depth); - if (limit == 0) { - at = fl_say_text(line, sizeof(line), at, ", витков "); - at = fl_say_number(line, sizeof(line), at, steps); - at = fl_say_text(line, sizeof(line), at, " (предел снят)"); - } else { - /* Доля считается делением предела, а не умножением витков: витков к концу - перепечатки миллиарды, и «витки × 100» переполнило бы 32-разрядное - `unsigned long` там, где оно 32-разрядное. */ - const unsigned long part = limit >= 100 ? steps / (limit / 100) : steps * 100 / limit; - at = fl_say_text(line, sizeof(line), at, ", витков "); - at = fl_say_number(line, sizeof(line), at, steps); - at = fl_say_text(line, sizeof(line), at, " из "); - at = fl_say_number(line, sizeof(line), at, limit); - at = fl_say_text(line, sizeof(line), at, " ("); - at = fl_say_number(line, sizeof(line), at, part); - at = fl_say_text(line, sizeof(line), at, " %)"); - } - bytes = fl_say_resident(); - if (bytes != 0) { - at = fl_say_text(line, sizeof(line), at, ", "); - at = fl_say_size(line, sizeof(line), at, bytes); - } - at = fl_say_text(line, sizeof(line), at, "\n"); - - if (seen > 1) { - at = fl_say_text(line, sizeof(line), at, " звали:"); - for (index = seen - 1; index > 0; index -= 1) { - const char *name = fl_watch_frames[index - 1]; - if (at + 256 > sizeof(line)) { - at = fl_say_text(line, sizeof(line), at, " …"); - break; - } - at = fl_say_text(line, sizeof(line), at, index == seen - 1 ? " «" : " ← «"); - at = fl_say_text(line, sizeof(line), at, name == NULL ? "?" : name); - at = fl_say_text(line, sizeof(line), at, "»"); - } - if (depth > seen) { - at = fl_say_text(line, sizeof(line), at, " (и ещё "); - at = fl_say_number(line, sizeof(line), at, (unsigned long)(depth - seen)); - at = fl_say_text(line, sizeof(line), at, " глубже)"); - } - at = fl_say_text(line, sizeof(line), at, "\n"); - } - - if (fl_watch_label[0] != 0) { - at = fl_say_text(line, sizeof(line), at, " шаг хозяина: «"); - at = fl_say_text(line, sizeof(line), at, fl_watch_label); - at = fl_say_text(line, sizeof(line), at, "»\n"); - } - if (fl_watch_repeat[0] != 0) { - at = fl_say_text(line, sizeof(line), at, " повторить: "); - at = fl_say_text(line, sizeof(line), at, fl_watch_repeat); - at = fl_say_text(line, sizeof(line), at, "\n"); - } - wrote = write(2, line, at); - (void)wrote; -} -#endif - -void fl_watch_open(fl_ctx *ctx) { - fl_watch_ctx = ctx; - fl_say_page_ready(); -#ifdef FL_WATCH - if (!fl_watch_set) { - struct sigaction action; - memset(&action, 0, sizeof(action)); - action.sa_handler = fl_watch_say; - sigemptyset(&action.sa_mask); - /* SA_RESTART обязателен: без него подключение рвало бы `read` оболочки и - `waitpid` внутри `system` кодом EINTR — то есть наблюдение ломало бы - наблюдаемое. */ - action.sa_flags = SA_RESTART; - if (sigaction(SIGUSR1, &action, NULL) != 0) { - return; - } - fl_watch_set = 1; - } -#endif -} - -/* - * СТРОКА «ПОВТОРИТЬ»: вызов, которым расчёт начался, — готовым доводом для - * `flang run --args`. - * - * Собирается ЗДЕСЬ, в двери программы, а не в обработчике, и не на витке: - * `fl_check_entry` зовётся один раз на внешний вызов, и `snprintf` в нём - * законен. Обработчику остаётся написать готовые байты. - * - * ЧТО СЮДА НЕ ВЛЕЗАЕТ И ПОЧЕМУ ЭТО СКАЗАНО ВСЛУХ. Доводы ТЕКУЩЕГО внутреннего - * вызова до рантайма не доезжают вовсе: напечатанный код зовёт - * `fl_enter(ctx, "Имя", error)` — имя и ничего больше. Чтобы он понёс доводы, - * надо править печать (`flang/self/emit-c.flang`), а печатает её двоичный - * компилятор — значит правка доедет только перепечаткой семени. Разбор — - * ADR-0019, раздел «Чего это решение НЕ делает». - * - * `--args` знает только ПЛОСКИЙ объект скаляров (см. шапку `flang run` в - * flang_repl.c). Значит вызов с составным доводом повторить этой строкой - * нельзя, и снимок говорит об этом прямо, а не печатает то, что не примут. - */ -static void fl_watch_note(const fl_entry_table *table, const char *name, const fl_value *args, - size_t count) { - size_t at = 0; - size_t index = 0; - size_t said = 0; - int wrote = 0; - fl_watch_repeat[0] = 0; - if (table == NULL || name == NULL) { - return; - } - /* Имя в одинарных кавычках, а не в «ёлочках»: строку вставляют в оболочку, и - пробел в имени функции без кавычек разорвал бы её на два довода. */ - wrote = snprintf(fl_watch_repeat, sizeof(fl_watch_repeat), "--function '%s'", name); - if (wrote <= 0 || (size_t)wrote >= sizeof(fl_watch_repeat)) { - fl_watch_repeat[0] = 0; - return; - } - at = (size_t)wrote; - if (count == 0) { - return; /* функция без параметров зовётся без `--args` */ - } - wrote = snprintf(fl_watch_repeat + at, sizeof(fl_watch_repeat) - at, " --args '{"); - if (wrote <= 0 || (size_t)wrote >= sizeof(fl_watch_repeat) - at) { - fl_watch_repeat[0] = 0; - return; - } - at += (size_t)wrote; - for (index = 0; index < table->param_count && said < count; index += 1) { - const fl_entry_param *param = &table->params[index]; - const fl_value value = args[said]; - char shown[FL_NUMBER_TEXT_MAX]; - size_t shown_bytes = 0; - if (!fl_name_same(param->function, name)) { - continue; - } - if (said > 0) { - if (at + 2 >= sizeof(fl_watch_repeat)) { - fl_watch_repeat[0] = 0; - return; - } - fl_watch_repeat[at] = ','; - at += 1; - fl_watch_repeat[at] = 0; - } - wrote = snprintf(fl_watch_repeat + at, sizeof(fl_watch_repeat) - at, "\"%s\":", param->name); - if (wrote <= 0 || (size_t)wrote >= sizeof(fl_watch_repeat) - at) { - fl_watch_repeat[0] = 0; - return; - } - at += (size_t)wrote; - switch (value.tag) { - case FL_NUMBER: - shown_bytes = fl_number_text(value.as.number, shown); - wrote = snprintf(fl_watch_repeat + at, sizeof(fl_watch_repeat) - at, "%.*s", (int)shown_bytes, - shown); - break; - case FL_FLAG: - wrote = snprintf(fl_watch_repeat + at, sizeof(fl_watch_repeat) - at, "%s", - value.as.flag ? "true" : "false"); - break; - case FL_NOTHING: - wrote = snprintf(fl_watch_repeat + at, sizeof(fl_watch_repeat) - at, "null"); - break; - case FL_STRING: { - /* - * Строка едет как есть или не едет вовсе. Экранирования здесь НЕТ - * нарочно: строку вставляют в оболочку внутри одинарных кавычек, и - * кавычка, обратная косая или перевод строки разорвали бы команду — - * а не «выглядели бы некрасиво». Длинную не режем по той же причине: - * обрезанный довод дал бы ДРУГОЙ вызов, а строка обещает тот же. - */ - size_t byte = 0; - for (byte = 0; byte < value.as.string.bytes; byte += 1) { - const unsigned char symbol = (unsigned char)value.as.string.utf8[byte]; - if (symbol == '"' || symbol == '\\' || symbol == '\'' || symbol < 0x20u) { - break; - } - } - if (byte != value.as.string.bytes || value.as.string.bytes > 200) { - fl_watch_repeat[0] = 0; - snprintf(fl_watch_repeat, sizeof(fl_watch_repeat), - "--function '%s' — довод «%s» строкой команды не передаётся дословно", name, - param->name); - return; - } - wrote = snprintf(fl_watch_repeat + at, sizeof(fl_watch_repeat) - at, "\"%.*s\"", - (int)value.as.string.bytes, value.as.string.utf8); - break; - } - default: - /* Список, запись, вариант: `--args` их не знает (см. шапку `flang run`), - и врать нечем. */ - fl_watch_repeat[0] = 0; - snprintf(fl_watch_repeat, sizeof(fl_watch_repeat), - "--function '%s' — доводы не скаляры, строкой для «flang run» не повторяется", name); - return; - } - if (wrote <= 0 || (size_t)wrote >= sizeof(fl_watch_repeat) - at) { - fl_watch_repeat[0] = 0; - return; - } - at += (size_t)wrote; - said += 1; - } - if (said != count || at + 3 >= sizeof(fl_watch_repeat)) { - /* Имени в таблице нет или число доводов не сошлось — об этом скажет - диспетчер своим текстом, а строка «повторить» врать не станет. */ - fl_watch_repeat[0] = 0; - return; - } - snprintf(fl_watch_repeat + at, sizeof(fl_watch_repeat) - at, "}'"); -} - -fl_status fl_check_entry(fl_ctx *ctx, const fl_entry_table *table, const char *name, const fl_value *args, - size_t count, fl_error *error) { - size_t index = 0; - size_t declared = 0; - size_t at = 0; - if (ctx == NULL || table == NULL || name == NULL) { - return FL_OK; - } - /* Дверь программы — единственное место, где рантайм видит доводы вызова. - Отсюда снимок и берёт строку «повторить»; цена — один `snprintf` на - ВНЕШНИЙ вызов, не на виток. */ - fl_watch_note(table, name, args, count); - for (index = 0; index < table->param_count; index += 1) { - if (fl_name_same(table->params[index].function, name)) { - declared += 1; - } - } - /* Сверять нечем: имени в таблице нет (о нём скажет диспетчер), параметров у - функции нет вовсе, либо число значений не сошлось с числом параметров — - это отдельный отказ, и он тоже принадлежит диспетчеру, чтобы текст про - арность был в программе один. */ - if (declared == 0 || declared != count) { - return FL_OK; - } - for (index = 0; index < table->param_count; index += 1) { - const fl_entry_param *param = &table->params[index]; - const char *label = NULL; - if (!fl_name_same(param->function, name)) { - continue; - } - label = fl_label(ctx, "вызов функции «%s»: аргумент «%s»", name, param->name); - if (label == NULL) { - return fl_no_memory(error); - } - FL_TRY(fl_check_typed(ctx, table, param->type, args[at], label, error)); - at += 1; - } - return FL_OK; -} - -/* - * Виток на элемент: цена цикла по списку, снятая до входа в цикл. - * - * До 26 августа 2026 счётчик витков не видел `отфильтровать`, `отобразить` и - * `свёртку` вовсе: `fl_tick` стоит на `fl_enter` и на витке хвостового цикла, а - * три эти формы печатаются обычным `for` со встроенным телом — ни одного тика - * на элемент. Замер задачи 0052 на синтетике из n функций: витков на функцию - * 387,6 → 390,7 при росте входа в 12 раз, показатель роста 1,00, тогда как - * время шло в квадрат (2,34). Счётчик считал ВЫЗОВЫ, а не РАБОТУ. - * - * Почему это опасно, а не просто неточно. Граница «функция стоит не больше N - * витков», выведенная из убывающей меры (задача 0060), ограничивала бы - * СЧЁТЧИК, а не работу: функция с фильтром по списку из миллиона элементов - * честно уложилась бы в свою границу и считалась бы час. Обещание было бы - * верным и бесполезным — и поехало бы в SLO. - * - * Почему заряд снимается ЗДЕСЬ, до цикла, а не внутри него. Внутрь рантайм не - * попадает: на элемент напечатанный цикл зовёт `fl_keep` только у фильтра, а у - * отображения и свёртки не зовёт ничего. Единственная точка, общая всем трём и - * знающая длину, — вот эта: `fl_require_list` получает сам список. Число витков - * от места заряда не меняется: печатаемый цикл идёт `for (i = 0; i < count; …)` - * без единого досрочного выхода, значит витков ровно `count`. - * - * Так же считает и толкователь на flang: у него список обходится кадрами - * «Кадр шага свёртки» и «Кадр шага обхода», по кадру на элемент, а - * «Шаг машины» тратит виток на каждый кадр (`обеспечивает «виток тратит не - * меньше одного шага»`, flang/self/interpret.flang). Расходились не замеры — - * расходились бэкенд C и толкователь, и здесь это сходится. - */ -/* - * ── ГДЕ ЕЩЁ СНИМАЕТСЯ ЗАРЯД, и почему список именно такой ────────────────── - * - * Сперва `fl_charge` звалась из одного места — `fl_require_list`, вход трёх - * форм по списку. Замер 27 августа 2026 показал, что этого мало: встроенные - * формы, которые тоже проходят вход целиком, шли счётчику бесплатно. - * `соединить` списка из 100 000 строк стоил 395,8 мкс и НОЛЬ витков, и время - * росло линейно, пока счётчик стоял. - * - * Заряжается ровно то, что пройдено, и в тех единицах, в каких работа и идёт: - * - * соединить (две строки) октеты склейки - * соединить (список) элементы плюс октеты склейки - * содержит (список) элементы ДО найденного, а не весь список - * содержит (строка) пройденная часть стога, а не весь стог - * начинается с октеты префикса - * разделить октеты строки дважды: счёт кусков и нарезка - * символы октеты строки дважды: счёт точек и нарезка - * символ, подстрока знаки от начала до дальнего края среза - * к числу октеты строки - * к строке (число) пробы точности: snprintf и strtod на пробу - * добавить, приписать длина списка — ТОЛЬКО на медленном пути, с копией - * - * Чего в списке НЕТ и почему. `длина`, `пусто`, `голова`, `хвост`, `элемент`, - * `код символа`, `символ по коду`, `остаток от`, `процентов от` работают за - * постоянное время: список в C — указатель и счётчик, `хвост` и срез строки - * не копируют ничего. Зарядить их длиной значило бы соврать вверх — ошибка - * того же рода, что прежний ноль, только в другую сторону. - * - * Остаётся ОДНА известная дыра, и она названа числом в задаче 0060: `fl_equal` - * сравнивает списки и записи вглубь, а `ctx` у неё нет. Верхний уровень - * `содержит` заряжен, глубина сравнения — нет. - */ -/* - * Имя работающей функции. Кладётся в `fl_tick` и в `fl_enter`, читается тремя - * приборами: выборкой по времени (обработчик SIGPROF), окном наблюдения и - * ведомостью зарядов. Объявлено ЗДЕСЬ, а не у выборки по времени, потому что - * `fl_charge` стоит выше по файлу и читает его первым. - */ -static const char *volatile fl_now = NULL; - -/* Ведомость зарядов: определена ниже, вместе с ведомостью витков. Заряд и виток - тратят ОДИН и тот же предел, и считаются в одном месте и в один файл. */ -static void fl_charge_count(size_t count); - -void fl_charge(fl_ctx *ctx, size_t count) { - fl_charge_count(count); - if (ctx == NULL || ctx->max_steps == 0 || count == 0) { - return; - } - /* Переполнение считается, а не исключается допущением о длине списка: при - нём счётчик встаёт на потолке size_t, и предел заведомо перейден. */ - if (count > (size_t)-1 - ctx->steps) { - ctx->steps = (size_t)-1; - return; - } - ctx->steps += count; -} - -/* - * ВТОРОЙ ПУЛЬС — ПО ВИТКАМ, А НЕ ПО ВХОДАМ. - * - * Пульс `fl_pulse` висит на `fl_enter`, то есть считает ВХОДЫ В ФУНКЦИЮ. Для - * дерева вызовов это почти то же, что работа. Для ХВОСТОВОГО САМОВЫЗОВА — нет: - * печатник разворачивает его в `for (;;)`, и вход там ОДИН на всю петлю, - * сколько бы витков она ни сделала. Перебор списка — ровно такая петля. Значит - * пульс по входам занижает цену перебора ровно во столько раз, какова длина - * перебора, и на выборке линейный поиск виден не будет НИКОГДА, как бы дорог - * он ни был. - * - * Виток же начисляется каждой итерации петли и несёт имя функции — той самой, - * чья петля крутится. Выборка по виткам — это выборка по работе. - * - * Цена, когда FLANG_WATCH_TICK не задан: одно сравнение указателя с NULL на - * виток. Задан — плюс деление с остатком. Пишем в ОТДЕЛЬНЫЙ файл, чтобы две - * выборки — по входам и по виткам — никогда не смешались в одной куче. - */ -static void fl_pulse_tick(fl_ctx *ctx, const char *function) { - static FILE *pulse = NULL; - static time_t started = 0; - static unsigned long long ticks = 0; - static unsigned long long every = 0; - static int tried = 0; - if (tried == 0) { - const char *path = getenv("FLANG_WATCH_TICK"); - const char *step = getenv("FLANG_PULSE_TICK"); - tried = 1; - started = time(NULL); - every = (step != NULL && step[0] != '\0') ? strtoull(step, NULL, 10) : 5000000ULL; - if (every == 0) { - every = 5000000ULL; - } - if (path != NULL && path[0] != '\0') { - pulse = fopen(path, "a"); - } - } - if (pulse == NULL) { - return; - } - ticks += 1; - if (ticks % every == 0) { - long spent = (long)(time(NULL) - started); - fprintf(pulse, "%4ld:%02ld витков %llu млн, глубина %lu, сейчас «%s»\n", spent / 60, - spent % 60, ticks / 1000000ULL, (unsigned long)(ctx != NULL ? ctx->depth : 0), - function != NULL ? function : "?"); - fflush(pulse); - } -} - -/* - * ТОЧНЫЙ СЧЁТ ШАГОВ ПО ИМЕНАМ: ВИТКИ, ЗАРЯД И ВХОДЫ В ОДНОЙ ВЕДОМОСТИ. - * - * Выборка отвечает «примерно» и на редких именах ошибается заметно. Здесь счёт - * ТОЧНЫЙ: каждый виток, каждый заряд и каждый вход кладутся в ячейку по имени - * своей функции, а при выходе таблица печатается. Ответ на вопрос «во что ушла - * работа» — числом, а не долей на глаз. - * - * ТРИ СТРОКИ, А НЕ ОДНА, И ЭТО ГЛАВНОЕ. Предел шагов тратят два счётчика: - * `fl_tick` — по единице за виток, `fl_charge` — числом за проход встроенной - * формы. Шаг — это виток ИЛИ заряд, `ctx->steps` — их сумма, и шапка печатает - * сумму первой строкой. Ведомость, писавшая одни витки, называла шагами от - * 0,3 % до 11 % шагов (замер 29 августа 2026 на семи прогонах) и тем разводила - * себя с часами: `«Закрыть уровни»` шла у неё 0,07 % «шагов» при 16,40 % - * времени, а шагов берёт 22,11 %. - * - * ПОЧЕМУ ЭТО ДЁШЕВО. Имена функций — строковые литералы, напечатанные - * бэкендом; их адреса неизменны за весь прогон. Значит ключ — УКАЗАТЕЛЬ, а не - * строка: умножение, сдвиг и одно сравнение на виток, без единого `strcmp`. - * Три таблицы по 8192 ячейки (384 КиБ вместе) держатся в кэше. Одинаковые - * строки с разных адресов складываются при выводе, а не на горячем пути. - * - * ЧТО ДАЁТ СТОЛБЕЦ «ВИТКОВ НА ВХОД». Хвостовой самовызов печатник разворачивает - * в `for (;;)`: вход туда ОДИН, а витков столько, сколько оборотов сделала - * петля. Значит частное «витки ÷ входы» — это длина петли, то есть ровно та - * величина, которая у перебора растёт с размером входа, а у поиска по хешу не - * растёт. Мерить её больше нечем: счёт по входам её не видит вовсе. - * - * Переполнение таблицы НЕ ЗАМАЛЧИВАЕТСЯ: непоместившиеся витки идут отдельным - * счётчиком и печатаются строкой «не поместилось», непоместившийся заряд — - * своей строкой, потому что складывать штуки с единицами работы нельзя. - * - * Включается FLANG_TICKS_OUT=<файл>. Без него — одно сравнение с NULL на виток - * и одно на заряд. - */ -#define FL_TICKS_SLOTS 8192u - -struct fl_ticks_slot { - const char *name; - unsigned long long count; -}; - -static struct fl_ticks_slot fl_ticks_table[FL_TICKS_SLOTS]; -static struct fl_ticks_slot fl_enters_table[FL_TICKS_SLOTS]; -static struct fl_ticks_slot fl_charge_table[FL_TICKS_SLOTS]; -static unsigned long long fl_ticks_total = 0; -static unsigned long long fl_enters_total = 0; -static unsigned long long fl_charge_total = 0; -static unsigned long long fl_ticks_lost = 0; -static unsigned long long fl_charge_lost = 0; -static const char *fl_ticks_path = NULL; -static int fl_ticks_tried = 0; - -static void fl_ticks_dump(void) { - FILE *out = NULL; - unsigned index = 0; - if (fl_ticks_path == NULL) { - return; - } - out = fopen(fl_ticks_path, "w"); - if (out == NULL) { - return; - } - fprintf(out, "шагов всего\t%llu\n", fl_ticks_total + fl_charge_total); - fprintf(out, "витков всего\t%llu\n", fl_ticks_total); - fprintf(out, "заряда всего\t%llu\n", fl_charge_total); - fprintf(out, "входов всего\t%llu\n", fl_enters_total); - fprintf(out, "не поместилось\t%llu\n", fl_ticks_lost); - fprintf(out, "заряда не поместилось\t%llu\n", fl_charge_lost); - for (index = 0; index < FL_TICKS_SLOTS; index++) { - if (fl_ticks_table[index].name != NULL) { - fprintf(out, "виток\t%llu\t%s\n", fl_ticks_table[index].count, fl_ticks_table[index].name); - } - } - for (index = 0; index < FL_TICKS_SLOTS; index++) { - if (fl_charge_table[index].name != NULL) { - fprintf(out, "заряд\t%llu\t%s\n", fl_charge_table[index].count, fl_charge_table[index].name); - } - } - for (index = 0; index < FL_TICKS_SLOTS; index++) { - if (fl_enters_table[index].name != NULL) { - fprintf(out, "вход\t%llu\t%s\n", fl_enters_table[index].count, fl_enters_table[index].name); - } - } - fclose(out); -} - -/* Возвращает 0, если имя не поместилось в таблицу: непоместившееся считает - вызвавший, каждый в свою строку «не поместилось». */ -static int fl_ticks_add(struct fl_ticks_slot *table, const char *function, - unsigned long long count) { - const unsigned long long key = (unsigned long long)(size_t)function; - const unsigned start = (unsigned)((key * 2654435761ULL) >> 19) & (FL_TICKS_SLOTS - 1u); - unsigned probe = 0; - for (probe = 0; probe < 16u; probe++) { - struct fl_ticks_slot *slot = &table[(start + probe) & (FL_TICKS_SLOTS - 1u)]; - if (slot->name == function) { - slot->count += count; - return 1; - } - if (slot->name == NULL) { - slot->name = function; - slot->count = count; - return 1; - } - } - return 0; -} - -static void fl_ticks_open(void) { - if (fl_ticks_tried != 0) { - return; - } - fl_ticks_tried = 1; - fl_ticks_path = getenv("FLANG_TICKS_OUT"); - if (fl_ticks_path != NULL && fl_ticks_path[0] == '\0') { - fl_ticks_path = NULL; - } - if (fl_ticks_path != NULL) { - atexit(fl_ticks_dump); - } -} - -static void fl_ticks_count(const char *function) { - fl_ticks_open(); - if (fl_ticks_path == NULL) { - return; - } - fl_ticks_total += 1; - if (!fl_ticks_add(fl_ticks_table, function, 1)) { - fl_ticks_lost += 1; - } -} - -static void fl_enters_count(const char *function) { - fl_ticks_open(); - if (fl_ticks_path == NULL) { - return; - } - fl_enters_total += 1; - if (!fl_ticks_add(fl_enters_table, function, 1)) { - fl_ticks_lost += 1; - } -} - -/* - * ЗАРЯД ПО ИМЕНАМ — ТРЕТИЙ СТОЛБЕЦ ТОЙ ЖЕ ВЕДОМОСТИ, А НЕ ОТДЕЛЬНЫЙ ПРИБОР. - * - * Предел шагов тратят ДВА счётчика: `fl_tick` по единице за виток и `fl_charge` - * числом за проход встроенной формы. `ctx->steps` — их сумма. Ведомость же до - * 29 августа 2026 писала только витки, и её шапку «витков всего» читали как - * «шагов всего». На замыкании компилятора это давало ошибку в три с лишним - * раза: витков 27,5 млрд, заряда 72,7 млрд, предел кончился на 100,2 млрд. - * - * Отсюда и расхождение с часами. Ведомость витков отдавала `«Закрыть уровни»` - * 0,07 % при 16,40 % времени — в 234 раза мимо, — потому что вся работа этой - * функции идёт во встроенных формах, а их ведомость не видела вовсе. Столбца - * заряда не хватало не для полноты, а для того, чтобы два прибора отвечали про - * одну величину. - * - * Имя берётся из `fl_now` — то же самое, по которому начисляет точку выборка по - * времени. Значит доли заряда и доли времени считаны ОДНИМ ключом и сравнимы - * прямо; будь ключи разные, сведение было бы подгонкой. - * - * Цена, когда `FLANG_TICKS_OUT` не задан: один вызов и одно сравнение с NULL на - * заряд, ровно как у витка. - */ -static void fl_charge_count(size_t count) { - const char *function = fl_now; - fl_ticks_open(); - if (fl_ticks_path == NULL || count == 0) { - return; - } - fl_charge_total += (unsigned long long)count; - if (function == NULL || !fl_ticks_add(fl_charge_table, function, (unsigned long long)count)) { - fl_charge_lost += (unsigned long long)count; - } -} - -/* - * ВЫБОРКА ПО ВРЕМЕНИ. - * - * Ни счёт входов, ни счёт витков ВРЕМЕНЕМ не являются, и это не мелочь: на - * `check flang/self/lexer.flang --proof` «Найти описание» берёт 9,94 % ВИТКОВ и - * 0,26 % ВРЕМЕНИ. Причина в том, что встроенные формы — «равен» над записью, - * «длина», «добавить», обход списка внутри рантайма — делают работу, за которую - * НЕ НАЧИСЛЯЕТСЯ НИ ОДНОГО ВИТКА. Петля, крутящая такую форму, стоит минуты при - * тысячах витков, и оба счётных прибора покажут, что её почти нет. - * - * Здесь мерится время. Каждый виток и каждый вход кладут своё имя в `fl_now` - * (одна запись в глобальную переменную), часы ITIMER_PROF будят обработчик раз - * в 10 мс ПРОЦЕССОРНОГО времени, и он начисляет точку тому имени, которое - * стоит в `fl_now`. Доля имени — доля времени, а не доля вызовов. - * - * Обработчик не зовёт ни malloc, ни stdio: хеш указателя и инкремент в - * статической таблице, и ничего больше. - * - * ТРИ ПРИБОРА ОТВЕЧАЮТ НА ТРИ РАЗНЫХ ВОПРОСА, и подменять один другим нельзя: - * входы — «сколько раз позвали», витки — «во что ушёл ПРЕДЕЛ ШАГОВ», время — - * «во что ушли ЧАСЫ». Программа умирает от предела шагов по первой причине, а - * идёт сутки по третьей. - * - * Включается FLANG_TIME_OUT=<файл>. Без него — одна запись указателя на виток. - */ -#define FL_TIME_SLOTS 8192u - -static struct { - const char *name; - unsigned long long count; -} fl_time_table[FL_TIME_SLOTS]; - -/* `fl_now` объявлено выше, у `fl_charge`: ведомость зарядов читает его раньше. */ -static volatile sig_atomic_t fl_time_points = 0; -static volatile sig_atomic_t fl_time_nameless = 0; -static const char *fl_time_path = NULL; -static int fl_time_tried = 0; - -static void fl_time_say(int signal_number) { - const char *name = fl_now; - unsigned long long key = 0; - unsigned start = 0; - unsigned probe = 0; - (void)signal_number; - fl_time_points += 1; - if (name == NULL) { - fl_time_nameless += 1; - return; - } - key = (unsigned long long)(size_t)name; - start = (unsigned)((key * 2654435761ULL) >> 19) & (FL_TIME_SLOTS - 1u); - for (probe = 0; probe < 16u; probe++) { - const unsigned at = (start + probe) & (FL_TIME_SLOTS - 1u); - if (fl_time_table[at].name == name) { - fl_time_table[at].count += 1; - return; - } - if (fl_time_table[at].name == NULL) { - fl_time_table[at].name = name; - fl_time_table[at].count = 1; - return; - } - } - fl_time_nameless += 1; -} - -static void fl_time_dump(void) { - FILE *out = NULL; - unsigned index = 0; - if (fl_time_path == NULL) { - return; - } - out = fopen(fl_time_path, "w"); - if (out == NULL) { - return; - } - fprintf(out, "точек всего\t%lu\n", (unsigned long)fl_time_points); - fprintf(out, "без имени\t%lu\n", (unsigned long)fl_time_nameless); - for (index = 0; index < FL_TIME_SLOTS; index++) { - if (fl_time_table[index].name != NULL) { - fprintf(out, "%llu\t%s\n", fl_time_table[index].count, fl_time_table[index].name); - } - } - fclose(out); -} - -static void fl_time_open(void) { - struct sigaction action; - struct itimerval clock_setting; - if (fl_time_tried != 0) { - return; - } - fl_time_tried = 1; - fl_time_path = getenv("FLANG_TIME_OUT"); - if (fl_time_path != NULL && fl_time_path[0] == '\0') { - fl_time_path = NULL; - } - if (fl_time_path == NULL) { - return; - } - memset(&action, 0, sizeof(action)); - action.sa_handler = fl_time_say; - sigemptyset(&action.sa_mask); - action.sa_flags = SA_RESTART; - sigaction(SIGPROF, &action, NULL); - clock_setting.it_interval.tv_sec = 0; - clock_setting.it_interval.tv_usec = 10000; - clock_setting.it_value.tv_sec = 0; - clock_setting.it_value.tv_usec = 10000; - setitimer(ITIMER_PROF, &clock_setting, NULL); - atexit(fl_time_dump); -} - -fl_status fl_tick(fl_ctx *ctx, const char *function, fl_error *error) { - if (ctx == NULL) { - return FL_INVALID_ARGUMENT; - } - fl_pulse_tick(ctx, function); - fl_ticks_count(function); - fl_time_open(); - fl_now = function; - /* Предел 0 — счёт отключён; иначе первый же виток при max_steps == 0 объявил - бы исчерпанной любую программу. */ - if (ctx->max_steps == 0) { - return FL_OK; - } - ctx->steps += 1; - if (ctx->steps > ctx->max_steps) { - /* Текст дословно как у интерпретатора: предел, затем глубина вызовов на - момент исчерпания (не число шагов). */ - return fl_fail(ctx, error, FL_CODE_RECURSION_LIMIT, - "функция «%s» исчерпала лимит шагов (%lu) на глубине вызовов %lu", function, - (unsigned long)ctx->max_steps, (unsigned long)ctx->depth); - } - return FL_OK; -} - -/* - * Сторож стека: сколько его уже съедено и не пора ли отказать. - * - * Мерить нужно именно байты, а не кадры. Счётчик глубины считает кадры, а - * несёт их стек, и толщина кадра — свойство ПРОГРАММЫ: 352 байта у функции с - * одним параметром и 5 526 у функции с сорока связываниями. Один и тот же - * предел 10 000 в первом случае вдвое ниже стека, а во втором выше него в - * шесть с половиной раз — и вторая программа умирала по SIGSEGV. - * - * Запас под последним входом считается по САМОМУ ТОЛСТОМУ кадру, который эта - * программа уже показала, а не по числу из заголовка: между двумя проверками - * успевает лечь ровно такой кадр (а с вложенным телом «отобрать» или свёртки — - * несколько), и запас в четыре его толщины покрывает это с обеих сторон. - * Программе с тонким кадром это не стоит ничего: её запас тоже тонкий. - * - * `stack_seen` — верхняя отметка, и разматывание её не опускает. Значит после - * глубокого спуска толщина следующего кадра может быть посчитана крупнее, чем - * есть. Это ошибка в БЕЗОПАСНУЮ сторону — запас окажется больше нужного, а не - * меньше, — и опускать отметку на возврате незачем: `fl_leave` стоит на каждом - * кадре, и работа в нём стоит дороже, чем эта неточность. - */ -static bool fl_stack_spent(fl_ctx *ctx) { - char here = 0; - const char *point = &here; - size_t used = 0; - size_t reserve = 0; - if (ctx->stack_room == 0 || ctx->stack_base == NULL) { - return false; /* сторож выключен: так решил тот, кто знает, что делает */ - } - /* Стек растёт вниз почти везде, но «почти» здесь не годится: разность берётся - по модулю, и направление роста перестаёт быть допущением. */ - used = point < ctx->stack_base ? (size_t)(ctx->stack_base - point) - : (size_t)(point - ctx->stack_base); - if (used > ctx->stack_seen) { - const size_t step = used - ctx->stack_seen; - if (step > ctx->stack_step) { - ctx->stack_step = step; - } - ctx->stack_seen = used; - } - /* - * Запас — МЕНЬШЕЕ из объявленного потолка и четверти всего стека, и второе - * слагаемое приехало от wasm. FL_STACK_MARGIN (128 КиБ) считался «сотнями - * кадров рантайма», и на стеке в 8 МиБ он ими и остаётся. Но теневой стек wasm - * по умолчанию 64 КиБ — ВДВОЕ МЕНЬШЕ запаса, — и правило «отказать, если - * осталось меньше запаса» сработало бы на первом же входе в первую функцию: - * программа, считающая на глубине два, получала бы FLANG_RECURSION_LIMIT. - * Это была бы вторая ложь вместо первой, а не починка. - * - * Четверть — а не половина и не восьмая — потому, что запас обязан оставаться - * запасом: три четверти стека остаются работе. На 64 КиБ это 16 КиБ, то есть - * 68 кадров рантайма (240 байт худший) или 39 кадров прогонщика (416) — с тем - * же порядком кратности, ради которого 128 КиБ и брались. - * На 8 МиБ четверть равна 2 МиБ, потолок ниже, и ни одно число не меняется. - */ - reserve = FL_STACK_MARGIN; - if (reserve > ctx->stack_room / 4u) { - reserve = ctx->stack_room / 4u; - } - if (ctx->stack_step > (FL_STACK_MAX - reserve) / 4u) { - return true; /* кадр толще всего мыслимого стека — дальше идти некуда */ - } - reserve += ctx->stack_step * 4u; - return used + reserve > ctx->stack_room; -} - -/* - * ПУЛЬС — наблюдение за идущей программой без остановки и без правки программы. - * - * ЗАЧЕМ. Перепечатка компилятора идёт часами и до сих пор не говорила о себе - * ничего: понять, работает она или встала, можно было только двумя снимками - * `perf` с получасовым промежутком. Замер 28 августа 2026: заход шёл 25 часов и - * не напечатал ни строки о том, где он. - * - * ЧЕМ ОТЛИЧАЕТСЯ ОТ ПОДКЛЮЧЕНИЯ СИГНАЛОМ. Сигнал (`kill -USR1`) отвечает на - * вопрос «где ты СЕЙЧАС» и требует, чтобы кто-то спросил. Пульс пишет сам, и - * потому годится там, где спрашивать некому: ночной прогон, прогон под сборщиком, - * прогон, который упал и унёс ответ с собой. Одно не заменяет другого. - * - * ПОЧЕМУ ЗДЕСЬ. Через `fl_enter` проходит каждый вызов языка, способный к - * рекурсии, — другого места, где видно И имя функции, И глубину, И число шагов, - * в рантайме нет. - * - * ПОЧЕМУ НЕ НА КАЖДЫЙ ВЫЗОВ. Их миллиарды: строка на вызов стоила бы времени - * больше, чем сама работа. Пишем раз в FLANG_PULSE вызовов. Умолчание пять - * миллионов — замерено: на перепечатке это строка примерно раз в минуту. - * - * ЦЕНА. Пока FLANG_WATCH не задан, всё стоит одного сравнения указателя с NULL - * на вызов. Задан — плюс одно деление с остатком. - * - * Буфер сбрасывается сразу: при убийстве прогона пропали бы ровно последние - * строки, а они самые нужные. - */ -static void fl_pulse(fl_ctx *ctx, const char *function) { - static FILE *pulse = NULL; - static time_t started = 0; - static unsigned long calls = 0; - static unsigned long every = 0; - static int tried = 0; - if (tried == 0) { - const char *path = getenv("FLANG_WATCH"); - const char *step = getenv("FLANG_PULSE"); - tried = 1; - started = time(NULL); - every = (step != NULL && step[0] != '\0') ? strtoul(step, NULL, 10) : 5000000UL; - if (every == 0) { - every = 5000000UL; - } - if (path != NULL && path[0] != '\0') { - pulse = fopen(path, "a"); - } - } - if (pulse == NULL) { - return; - } - calls += 1; - if (calls % every == 0) { - long spent = (long)(time(NULL) - started); - fprintf(pulse, "%4ld:%02ld вызовов %lu млн, глубина %lu, сейчас «%s»\n", - spent / 60, spent % 60, calls / 1000000UL, - (unsigned long)(ctx != NULL ? ctx->depth : 0), - function != NULL ? function : "?"); - fflush(pulse); - } -} - -fl_status fl_enter(fl_ctx *ctx, const char *function, fl_error *error) { - if (ctx == NULL) { - return FL_INVALID_ARGUMENT; - } - fl_pulse(ctx, function); - fl_enters_count(function); - fl_now = function; - /* Вход в функцию — тоже виток: иначе нерекурсивная по хвосту, но бесконечно - ветвящаяся программа считала бы глубину и не считала шаги. */ - FL_TRY(fl_tick(ctx, function, error)); - if (fl_stack_spent(ctx)) { - /* - * Стек хозяина кончился раньше объявленного предела. Отказ всё равно - * ОБЪЯВЛЕННЫЙ — код из закрытого набора, — а текст называет хозяина, а не - * предел, до которого не добрались: врать про предел нельзя, молчать тоже. - * Тот же текст и по той же причине печатает бэкенд JavaScript ($hostDepth), - * где поднять стек изнутри модуля нечем вовсе. - */ - return fl_fail(ctx, error, FL_CODE_RECURSION_LIMIT, - "функция «%s» исчерпала стек хозяина на глубине %lu, не дойдя до предела " - "глубины вызовов (%lu)", - function, (unsigned long)ctx->depth, (unsigned long)ctx->max_depth); - } - /* - * ОКНО НАБЛЮДЕНИЯ: имя этого кадра — по его глубине. Одно сравнение и одна - * запись; ни `fl_tick`, ни `fl_leave`, ни виток цикла хвостового самовызова - * не тронуты. `fl_leave` ничего не стирает нарочно: глубина убывает сама, и - * снимок читает `frames[depth - 1]`, то есть всегда живой кадр. - */ - if (ctx->depth < FL_WATCH_FRAMES) { - fl_watch_frames[ctx->depth] = function; - } - ctx->depth += 1; - if (ctx->depth > ctx->max_depth) { - /* Текст дословно как у интерпретатора: сперва предел, потом достигнутая - глубина (она на единицу больше предела). */ - return fl_fail(ctx, error, FL_CODE_RECURSION_LIMIT, - "функция «%s» превысила предел глубины вызовов (%lu) на глубине %lu", function, - (unsigned long)ctx->max_depth, (unsigned long)ctx->depth); - } - return FL_OK; -} - -void fl_leave(fl_ctx *ctx) { - if (ctx != NULL && ctx->depth > 0) { - ctx->depth -= 1; - } -} - -/* ═════════════════════════ область на вызов ═════════════════════════ */ - -/* - * Сколько байт область обязана нарастить, чтобы откат стоило делать. - * - * Порог не взят с потолка — он равен куску арены. Ниже куска область не стоила - * арене НИ ОДНОЙ покупки у malloc: её мусор лежит в памяти, которая всё равно - * уже куплена, и откат вернул бы то, что и так вернётся ближайшему `reset`. - * Выше куска область и есть та причина, по которой арена растёт, — и вот тогда - * за перекладку результата есть чем платить. - * - * Замерено, а не выбрано: порог 8 КиБ на самом компиляторе flang, собранном в - * C, даёт 787,2 МиБ вместо 795,8 — то есть 1,1 % памяти за 23 % времени - * (3,8–4,0 с против 3,1–3,3). Кусок арены — та точка, где ещё платят за дело. - * - * Величина настраиваемая (-DFL_REGION_MIN=…), но умолчание привязано к - * устройству арены, а не к вкусу: поменяется кусок — поменяется и порог. - */ -#ifndef FL_REGION_MIN -#define FL_REGION_MIN FL_CHUNK_MIN -#endif - -/* - * Во сколько раз отданное обязано превысить переложенное: результат не больше - * ЧЕТВЕРТИ наросшего, то есть область отдаёт втрое больше, чем копирует. - * - * Четвёрка тоже замерена. Перекладка стоит `live` байт буфера, и буфер этот - * входит в пик кучи наравне с ареной — а куски арены откат системе НЕ - * возвращает. При «не больше половины» буфер сравним с отданным, и на двух - * настоящих программах это чистый убыток: - * - * накопление списка через «добавить», миллион витков: - * пик 96,1 → 126,6 МиБ, время 0,10 → 0,14 с; - * сам компилятор flang, собранный в C, на flang/self/emit-c.flang: - * пик 799,0 → 844,9 МиБ, время 3,1 → 3,5 с. - * - * При четверти оба возвращаются к прежним числам до цифры, а сортировка - * слиянием четырёх тысяч чисел теряет всего 2,2 → 3,5 МиБ при 1 655 МиБ без - * области. Ужесточать дальше незачем: восьмая уже отказывается там, где откат - * выгоден (проверка «четверть держит с обеих сторон» краснеет от сдвига - * константы в любую сторону). - */ -#ifndef FL_REGION_GAIN -#define FL_REGION_GAIN (size_t)4 -#endif - -/* - * Предел глубины обхода при перекладке. Глубже область просто ОТКАЗЫВАЕТСЯ - * работать — она не имеет права ни падать по стеку, ни отказывать вычислению. - * Обход рекурсивный, кадр его тонок, тысяча кадров — сотня килобайт стека. - */ -#ifndef FL_REGION_DEPTH -#define FL_REGION_DEPTH (size_t)1024 -#endif - -/* - * ── Обмер: сколько займёт копия ──────────────────────────────────────────── - * - * Считает ровно то, что выделит перекладка, — теми же размерами и тем же - * округлением. Считает С БЮДЖЕТОМ и бросает счёт, как только бюджет перебран. - * - * Бюджет — не мелочь ради скорости, а то, чем ограничены три из четырёх - * известных изъянов приёма «копия наружу»: - * - * • копия разворачивает разделяемый подграф в дерево (один и тот же подсписок, - * положенный в результат десять раз, станет десятью копиями), и в худшем - * случае это ЭКСПОНЕНТА. С бюджетом обход такого значения обрывается на - * первых же байтах сверх бюджета — и область отказывается от отката, вместо - * того чтобы взорваться; - * • копия стоит O(размера результата), и у функции, которая возвращает много, - * а мусорит мало, это чистый убыток. Бюджет запрещает такой случай по - * построению; - * • накопление растущего результата на каждом витке даёт квадрат. Как только - * результат перерастает четверть мусора, копии прекращаются сами. - * - * Отсюда и величина бюджета: наросшее, делённое на FL_REGION_GAIN. Тогда работа - * области — и обмер, и обе перекладки — ограничена долей той работы, которую - * область уже проделала на выделении. Это и есть обещание: цена приёма не - * может превысить постоянного множителя от объёма, которым он распоряжается. - */ -static bool fl_region_take(size_t *total, size_t budget, size_t need) { - size_t rounded = 0; - /* Сперва сырой размер, потом округление: так округлять нечему переполниться — - бюджет заведомо меньше половины адресного пространства. */ - if (need > budget) { - return false; - } - rounded = fl_round_up(need); - if (rounded > budget || *total > budget - rounded) { - return false; - } - *total += rounded; - return true; -} - -/* - * ── Что откат ЗАБЕРЁТ, а что оставит на месте ────────────────────────────── - * - * Откат возвращает арене всё, что выдано ПОСЛЕ отметки, и не трогает ничего - * ниже неё. Значит подграф, целиком лежащий ниже отметки, переживёт откат сам — - * и копировать его незачем: ни обмеру считать его байты, ни перекладке их - * возить. Тот же довод уже записан у имён полей («ни одно имя не рождается во - * время расчёта»), только там он верен по построению, а здесь его надо спросить - * у арены. - * - * Спрашивается он ОБОЛОЧКОЙ: наименьший и наибольший адрес памяти, выданной - * после отметки. Оболочка, а не точный список кусков, потому что куски — это - * отдельные покупки у malloc, в адресах они не подряд, и «внутри оболочки» ещё - * не значит «выше отметки». Ошибка в эту сторону безобидна: узел просто - * скопируется, как копировался всегда. Обратной ошибки быть не может — ни один - * адрес выше отметки вне оболочки не лежит, потому что оболочка построена по - * всем таким кускам разом. - * - * Цена — два сравнения на узел вместо нуля, и обход цепочки кусков от отметки - * до текущего ОДИН раз на закрытие. Второе почти ничего не стоит: порог - * FL_REGION_MIN проходит меньше четырёх процентов закрытий (замер 23 августа: - * `check examples/wal/write-ahead-log.flang` — 5 415 из 613 703, - * `check flang/self/tags.flang` — 744 273 из 19 924 228). - */ -typedef struct fl_live { - const char *lo; - const char *hi; -} fl_live; - -static bool fl_live_in(fl_live zone, const void *p) { - const char *q = (const char *)p; - return q >= zone.lo && q < zone.hi; -} - -static fl_live fl_region_live(const fl_arena *arena, fl_mark mark) { - fl_live zone; - fl_chunk *chunk = NULL; - zone.lo = NULL; - zone.hi = NULL; - if (arena == NULL) { - return zone; - } - for (chunk = mark.chunk == NULL ? arena->chunks : mark.chunk; chunk != NULL; - chunk = chunk->next) { - const char *base = fl_chunk_data(chunk); - const char *lo = base + (chunk == mark.chunk ? mark.used : (size_t)0); - const char *hi = base + chunk->used; - if (lo < hi) { - if (zone.lo == NULL || lo < zone.lo) { - zone.lo = lo; - } - if (zone.hi == NULL || hi > zone.hi) { - zone.hi = hi; - } - } - if (chunk == arena->current) { - break; - } - } - return zone; -} - -/* - * ── Чего отсекать НЕЛЬЗЯ: списки ─────────────────────────────────────────── - * - * Записи, варианты и строки заполняет ровно одно место — выдача, — и больше их - * не правит ничто; значит у узла, лежащего ниже отметки, ниже отметки лежит и - * всё, на что он указывает: оно выдано РАНЬШЕ него. - * - * У списка это неверно. Быстрый путь `fl_b_dobavit` пишет - * `grow->items[grow->filled] = item` в уже выданный массив: массив может лежать - * ниже отметки, а положенное в него значение — выше. Признак «массив с запасом» - * отличить не помогает: `fl_list_slice` его роняет, и тот же массив приезжает - * уже без признака. Проверено прогоном на прошлой попытке: отсечение списков - * ответило «FLANG_UNKNOWN_NAME: запись не содержит поле «вид»» кодом 1. - */ - -/* - * ── Отказная памятка: обмер, который можно НЕ делать ─────────────────────── - * - * Обмер кончается отказом в 98,7 % случаев (замер на `flang check - * flang/self/tags.flang`), и стоит он треть всего расчёта, тогда как перекладка, - * ради которой обмер и делается, — полпроцента. Мерят в шестьдесят пять раз - * больше, чем перекладывают. - * - * Причина не в длине одного обхода, а в их ЧИСЛЕ и во вложенности. Области - * вложены как вызовы: внутренняя закрывается, её результат становится частью - * результата внешней, тот — частью результата следующей. И каждая мерит заново - * ВЕСЬ свой результат, то есть один и тот же подграф обходится столько раз, - * какова глубина рекурсии. Отсюда и сверхлинейность. - * - * Памятка обрывает этот повтор одним доказанным фактом. Обмер МОНОТОНЕН по - * бюджету: `fl_region_take` отказывает тем охотнее, чем бюджет меньше, а порядок - * обхода от бюджета не зависит вовсе. Значит из «значение v не уложилось в - * бюджет B» следует «v не уложится ни в какой бюджет ≤ B» — то есть отказ даёт - * НИЖНЮЮ ОЦЕНКУ: копии v нужно БОЛЬШЕ B байт. - * - * Эту оценку и запоминает арена — ровно одну, про последний отказавший - * результат. Когда обход внешней области доходит до того же узла, у него в - * запасе остаётся `budget - *total` байт; если запас не больше запомненного B, - * узел заведомо не влезет, и обход обрывается сразу, не спускаясь внутрь. - * - * Чего памятка НЕ делает: она не отбирает ни одного отката, который состоялся бы - * без неё. Обрыв происходит только там, где полный обход всё равно кончился бы - * отказом, — это следствие монотонности, а не догадка. Поэтому память остаётся - * прежней, а ведомости — прежними до знака. - * - * Узнаётся значение по тройке (метка, адрес, число элементов). Адреса живут в - * арене, и после отката тот же адрес достаётся другому значению, — поэтому - * `fl_arena_rollback` памятку снимает. Даже проспи он её, беды бы не случилось: - * ошибочное срабатывание означает лишний ОТКАЗ области, а отказать область - * вправе в любой момент, и снаружи отказ не виден ничем, кроме памяти. - */ -struct fl_deny { - const void *id; - size_t count; - size_t need; /* доказано: копии этого узла нужно БОЛЬШЕ стольких байт */ - int tag; -}; - -/* - * Памяток столько, какова обычная глубина вложенности областей на одном пути: - * каждая записывает свою, и вытеснять друг друга им незачем. Таблица заводится - * ЛЕНИВО и только у той арены, где обмер хоть раз отказал: арен в прогоне - * бывает по две на процесс, а процессов — миллион, и восемь килобайт с рождения - * стоили бы больше всей выгоды (см. `fl_arena_init_small`). - */ -#define FL_DENY_SLOTS (size_t)256 - -static void fl_region_forget(fl_arena *arena) { - if (arena != NULL && arena->deny != NULL) { - memset(arena->deny, 0, FL_DENY_SLOTS * sizeof(struct fl_deny)); - } -} - -static struct fl_deny *fl_region_slot(struct fl_deny *table, const void *id) { - /* Адреса в арене выровнены по FL_ALIGNMENT, поэтому младшие биты пусты. */ - return table + (((size_t)(const char *)id / FL_ALIGNMENT) & (FL_DENY_SLOTS - 1)); -} - -static bool fl_region_denied(const fl_arena *arena, int tag, const void *id, size_t count, - size_t budget, size_t total) { - const struct fl_deny *slot = NULL; - if (arena == NULL || arena->deny == NULL) { - return false; - } - slot = fl_region_slot(arena->deny, id); - return slot->id == id && slot->tag == tag && slot->count == count && budget - total <= slot->need; -} - -/* - * Запомнить отказ. `need` — сколько байт узлу заведомо мало: обход начался, - * когда в запасе было ровно столько, и не уложился. Оценка только РАСТЁТ: - * тот же узел, отказавший при большем запасе, знает о себе больше. - */ -static void fl_region_note(fl_arena *arena, int tag, const void *id, size_t count, size_t need) { - struct fl_deny *slot = NULL; - if (arena == NULL || id == NULL) { - return; - } - if (arena->deny == NULL) { - arena->deny = (struct fl_deny *)calloc(FL_DENY_SLOTS, sizeof(struct fl_deny)); - if (arena->deny == NULL) { - return; /* без памятки обмер просто работает как раньше */ - } - } - slot = fl_region_slot(arena->deny, id); - if (slot->id == id && slot->tag == tag && slot->count == count) { - if (slot->need < need) { - slot->need = need; - } - return; - } - slot->id = id; - slot->tag = tag; - slot->count = count; - slot->need = need; -} -static bool fl_region_fields_size(fl_arena *arena, fl_live zone, const fl_field *fields, - size_t count, size_t budget, size_t depth, size_t *total); - -static bool fl_region_size(fl_arena *arena, fl_live zone, fl_value value, size_t budget, - size_t depth, size_t *total) { - if (depth > FL_REGION_DEPTH) { - return false; - } - switch (value.tag) { - case FL_NOTHING: - case FL_NUMBER: - case FL_FLAG: - return true; - case FL_STRING: - /* Ноль на конце копия ставит всегда, хотя исходник мог быть срезом. */ - if (value.as.string.bytes == (size_t)-1) { - return false; - } - if (value.as.string.utf8 != NULL && !fl_live_in(zone, value.as.string.utf8)) { - return true; /* откат этих октетов не тронет */ - } - return fl_region_take(total, budget, value.as.string.bytes + 1); - case FL_LIST: { - size_t index = 0; - const void *id = (const void *)value.as.list.items; - const size_t entry = *total; - if (value.as.list.count == 0) { - return true; - } - if (fl_region_denied(arena, (int)FL_LIST, id, value.as.list.count, budget, entry)) { - return false; - } - if (value.as.list.count > ((size_t)-1) / sizeof(fl_value) || - !fl_region_take(total, budget, value.as.list.count * sizeof(fl_value))) { - fl_region_note(arena, (int)FL_LIST, id, value.as.list.count, budget - entry); - return false; - } - for (index = 0; index < value.as.list.count; index += 1) { - if (!fl_region_size(arena, zone, value.as.list.items[index], budget, depth + 1, total)) { - fl_region_note(arena, (int)FL_LIST, id, value.as.list.count, budget - entry); - return false; - } - } - return true; - } - case FL_RECORD: { - const void *id = (const void *)value.as.record; - const size_t entry = *total; - if (id == NULL) { - return false; - } - if (!fl_live_in(zone, id)) { - return true; /* откат эту запись не тронет */ - } - if (fl_region_denied(arena, (int)FL_RECORD, id, value.as.record->count, budget, entry)) { - return false; - } - if (!fl_region_take(total, budget, sizeof(fl_record)) || - !fl_region_fields_size(arena, zone, value.as.record->fields, value.as.record->count, budget, - depth, total)) { - fl_region_note(arena, (int)FL_RECORD, id, value.as.record->count, budget - entry); - return false; - } - return true; - } - case FL_VARIANT: { - const void *id = (const void *)value.as.variant; - const size_t entry = *total; - if (id == NULL) { - return false; - } - if (!fl_live_in(zone, id)) { - return true; /* откат этот вариант не тронет */ - } - if (fl_region_denied(arena, (int)FL_VARIANT, id, value.as.variant->count, budget, entry)) { - return false; - } - if (!fl_region_take(total, budget, sizeof(fl_variant)) || - !fl_region_fields_size(arena, zone, value.as.variant->fields, value.as.variant->count, budget, - depth, total)) { - fl_region_note(arena, (int)FL_VARIANT, id, value.as.variant->count, budget - entry); - return false; - } - return true; - } - default: - return false; - } -} - -static bool fl_region_fields_size(fl_arena *arena, fl_live zone, const fl_field *fields, - size_t count, size_t budget, size_t depth, size_t *total) { - size_t index = 0; - if (count == 0) { - return true; - } - if (fields == NULL || count > ((size_t)-1) / sizeof(fl_field)) { - return false; - } - if (!fl_region_take(total, budget, count * sizeof(fl_field))) { - return false; - } - for (index = 0; index < count; index += 1) { - if (!fl_region_size(arena, zone, fields[index].value, budget, depth + 1, total)) { - return false; - } - } - return true; -} - -/* - * ── Перекладка: копия значения в плоский буфер ───────────────────────────── - * - * Выдача — бамп указателя внутри буфера, и размер буфера посчитан обмером - * заранее ровно по тем же правилам. Значит переполниться буфер не может; если - * всё-таки переполнился, обмер разошёлся с копией, и область отказывается от - * отката вместо того, чтобы писать мимо. - * - * Имена полей и имя варианта НЕ копируются, и это не экономия, а свойство - * дерева: имя приходит из модели (`.rodata`) либо из разобранного запроса, - * который построен ДО вызова, то есть ниже любой отметки. Ни одно имя не - * рождается во время расчёта — единственный, кто их выделяет, это разбор - * запроса в прогонщике. Тот же довод записан в `fl_conc_clone`. - * - * ── `zone`: что копировать, а что взять ссылкой ──────────────────────────── - * Ровно та же граница, по которой считал обмер, — иначе буфер разошёлся бы с - * посчитанным размером. Узел ВНЕ зоны копируется не глубже самого значения: - * оно уже лежит там, где переживёт откат. - * - * Зона разная у двух перекладок одного закрытия, и это не небрежность: - * • вниз, в буфер: зона — память, выданная после отметки (её откат заберёт); - * • обратно, в арену: зона — сам буфер (всё, что не в нём, уже пережило - * откат и лежит на месте). - * Множество копируемых узлов у обеих одно и то же, потому вторая и умещается - * в тот же посчитанный размер. - */ -typedef struct fl_pack { - char *base; - size_t size; - size_t used; - fl_live zone; -} fl_pack; - -static void *fl_pack_alloc(fl_pack *pack, size_t size) { - const size_t wanted = fl_round_up(size == 0 ? 1 : size); - char *block = NULL; - if (wanted > pack->size - pack->used) { - return NULL; - } - block = pack->base + pack->used; - pack->used += wanted; - return block; -} - -static bool fl_pack_fields(fl_pack *pack, const fl_field *fields, size_t count, size_t depth, - const fl_field **out); - -static bool fl_pack_value(fl_pack *pack, fl_value value, size_t depth, fl_value *out) { - if (depth > FL_REGION_DEPTH) { - return false; - } - switch (value.tag) { - case FL_NOTHING: - case FL_NUMBER: - case FL_FLAG: - *out = value; - return true; - case FL_STRING: { - char *text = NULL; - if (value.as.string.utf8 != NULL && !fl_live_in(pack->zone, value.as.string.utf8)) { - *out = value; - return true; - } - text = (char *)fl_pack_alloc(pack, value.as.string.bytes + 1); - if (text == NULL) { - return false; - } - if (value.as.string.bytes > 0) { - memcpy(text, value.as.string.utf8, value.as.string.bytes); - } - text[value.as.string.bytes] = '\0'; - *out = fl_text_borrow(text, value.as.string.bytes, value.as.string.points); - return true; - } - case FL_LIST: { - fl_value *items = NULL; - size_t index = 0; - if (value.as.list.count == 0) { - *out = fl_list(NULL, 0); - return true; - } - items = (fl_value *)fl_pack_alloc(pack, value.as.list.count * sizeof(fl_value)); - if (items == NULL) { - return false; - } - for (index = 0; index < value.as.list.count; index += 1) { - if (!fl_pack_value(pack, value.as.list.items[index], depth + 1, &items[index])) { - return false; - } - } - /* Хвостовой запас копии не передаётся: `fl_grow` считает ячейки от базы - своего массива, а копия — другое выделение. Наблюдаемо это не меняет - ничего (flang_runtime.h: «Поле не наблюдаемо»). */ - *out = fl_list(items, value.as.list.count); - return true; - } - case FL_RECORD: { - fl_record *record = NULL; - if (!fl_live_in(pack->zone, (const void *)value.as.record)) { - *out = value; - return true; - } - record = (fl_record *)fl_pack_alloc(pack, sizeof(fl_record)); - if (record == NULL) { - return false; - } - record->count = value.as.record->count; - record->fields = NULL; - if (!fl_pack_fields(pack, value.as.record->fields, value.as.record->count, depth, &record->fields)) { - return false; - } - out->tag = FL_RECORD; - out->as.record = record; - return true; - } - case FL_VARIANT: { - fl_variant *variant = NULL; - if (!fl_live_in(pack->zone, (const void *)value.as.variant)) { - *out = value; - return true; - } - variant = (fl_variant *)fl_pack_alloc(pack, sizeof(fl_variant)); - if (variant == NULL) { - return false; - } - variant->name = value.as.variant->name; - variant->count = value.as.variant->count; - variant->fields = NULL; - if (!fl_pack_fields(pack, value.as.variant->fields, value.as.variant->count, depth, &variant->fields)) { - return false; - } - out->tag = FL_VARIANT; - out->as.variant = variant; - return true; - } - default: - return false; - } -} - -static bool fl_pack_fields(fl_pack *pack, const fl_field *fields, size_t count, size_t depth, - const fl_field **out) { - fl_field *copy = NULL; - size_t index = 0; - if (count == 0) { - *out = NULL; - return true; - } - copy = (fl_field *)fl_pack_alloc(pack, count * sizeof(fl_field)); - if (copy == NULL) { - return false; - } - for (index = 0; index < count; index += 1) { - copy[index].name = fields[index].name; - if (!fl_pack_value(pack, fields[index].value, depth + 1, ©[index].value)) { - return false; - } - } - *out = copy; - return true; -} - -/* - * Откат к отметке. Куски выше отметки ОТДАЮТСЯ СИСТЕМЕ — все, кроме первых - * FL_ARENA_KEEP: их откат оставляет себе под ближайшую выдачу. - * - * ── Здесь стоял НЕЗАМЕРЕННЫЙ довод, и замер его отменил ──────────────────── - * До 24 августа 2026 откат не отдавал НИЧЕГО: куски помечались пустыми, ровно - * как в `fl_arena_reset`, и ближайшая выдача брала их снова. Довод стоял прямо - * здесь и звучал так: арена, которую откатывают, остаётся горячей в кэше, а - * растущая покупает новое и гуляет по всей памяти, — то есть удержание кусков - * не только бережёт память, но и УСКОРЯЕТ расчёт. - * - * Довод верен по знаку и ничтожен по величине. Следствие же его дорого: - * `fl_arena_release` зовётся дважды и обе — в самом конце работы - * (`flang_cli.c`), поэтому пик прогона равен НЕ рабочему набору, а всему, что - * арена купила за жизнь. Отсюда «растёт по гигабайту в минуту и никогда не - * падает» у долгого счёта. - * - * ── Замер 24 августа 2026 ────────────────────────────────────────────────── - * `flang check flang/self/types.flang --proof`, четыре колонки ОДНИМ двоичным - * (perf на машине закрыт; режим включался переменной среды, чтобы сравнивать - * одну сборку с собою), подряд, при загрузке машины 14,7–15,2: - * - * что делает откат время пик (maxRSS) покупок у malloc - * держит весь хвост 1:34,92 6 236 536 КБ 94 022 - * отдаёт весь хвост 1:36,06 3 952 772 КБ 1 303 680 - * отдаёт, оставив 1 1:35,50 3 950 176 КБ 1 104 760 - * отдаёт, оставив 4 1:34,99 3 974 552 КБ 657 256 - * - * То есть ПАМЯТЬ −36,3 %, ВРЕМЯ +0,07 % — при разбросе повторов 0,45 %, то есть - * времени не потеряно вовсе. Ответ ведомости побайтово тот же (1 635 строк, - * одна сумма md5 на обе колонки). - * - * Почему оставлять всё-таки надо, и почему именно четыре: сразу после отката - * `fl_region_close` кладёт копию результата обратно в арену. Отдав весь хвост, - * она идёт за ним к malloc — отсюда 1,3 млн покупок вместо 94 тысяч и те - * самые +1,2 % времени. Четыре куска эту покупку перекрывают, покупок остаётся - * вдвое меньше, а пик не меняется: удержанное ограничено - * FL_ARENA_KEEP кусками на арену, а не всей историей. - * - * ── И то же самое двумя двоичными, без счётчиков ─────────────────────────── - * Счётчики стоят около процента времени сами по себе, поэтому итог снят ещё раз - * ДВУМЯ обычными сборками — стволом и этой правкой, — подряд, при загрузке 15,8: - * - * прогон ствол эта правка - * check flang/self/types.flang --proof 1:33,91 1:33,37 - * 6 235 232 КБ 3 965 748 КБ (−36,4 %) - * check flang/self/parser.flang --proof 1:42,92 1:43,76 - * 3 022 784 КБ 1 172 216 КБ (−61,2 %) - * check flang/stdlib/strings.flang 0:03,77 0:04,03 - * 211 152 КБ 89 064 КБ (−57,8 %) - * - * Вывод у всех трёх побайтово тот же и код возврата тот же. Времени правка не - * стоит: на самом тяжёлом прогоне она даже быстрее ствола на 0,6 %, на мелком - * дороже на 0,26 с. Разброс повторов по времени 0,45 %, по пику 4,5 %. - * - * Чего замер НЕ показывает: перепечатку семени им не мерили — она в этот час - * шла у соседа, а второй такой машина не выдержала бы. Мерили `check` на - * исходниках самого компилятора; устройство арены у обеих команд одно, но доля - * отката в пике у перепечатки не измерена: по замеру 21 августа память печати - * лежит в глубокой НЕхвостовой рекурсии, где область отказывается от отката - * ЗАКОННО, — а где отката нет, там и отдавать нечего. - * - * ── Долю печати В ПИКЕ ИЗМЕРИЛИ: её нет (25 августа 2026) ────────────────── - * Задача 0032 просила связать этот откат с записью: печать компилятора копит - * двадцать один мегабайт вывода в памяти и пишет его одним куском в конце, - * значит-де «пик памяти включает весь накопленный вывод». Прогон отвечает: НЕ - * ВКЛЮЧАЕТ. `flang emit flang/self/parser.flang --target c` — 5,87 МБ печати, - * четверть компилятора, — разложен по шагам замером изнутри бинарника (VmHWM и - * VmRSS из /proc/self/status на границах шагов): - * - * шаг время VmHWM после шага VmRSS после шага - * проверка 159,38 с 2 702 МиБ 976 МиБ - * печать 68,25 с 2 702 МиБ 1 379 МиБ - * запись файлов 0,00 с - * - * Пик НЕ СДВИНУЛСЯ ни на мегабайт: его ставит проверка, и ставит ДО того, как - * печать началась. Запись по ходу этого пика не снимет ничем — снимать нечего. - * Двадцать один мегабайт вывода против 250 ГБ пика перепечатки — восемь тысячных - * процента; беда была названа по догадке, а не по замеру. - * - * Что печать всё-таки стоит — ВРЕМЯ: 68,25 с из 228,4 с, то есть 30 % прогона на - * крупном файле, и весь этот срок каталог вывода ПУСТ (проверено опросом раз в - * две секунды: первые байты появляются в последнюю секунду). Вот это в задаче - * правда, и цена снятого прогона — эти 30 %, а не память. - * - * ── И то, ради чего врезка стоит именно здесь ────────────────────────────── - * Арена в СЕМЕНИ на 516 строк старше этого файла: напечатанный `bootstrap/` не - * знает ни отдачи хвоста системе, ни отказа копировать подграфы ниже отметки. - * Перепечатка идёт СЕМЕНЕМ — то есть без всего, что здесь написано. Два - * двоичных, собранных из одного `compiler_flang.c` и разных рантаймов, пущены - * ПОДРЯД при одной загрузке на одном и том же `emit parser.flang`: - * - * семя как есть 6:28,84 пик 27,2 ГиБ - * оно же с рантаймом из этого файла 3:52,77 пик 2,64 ГиБ - * - * ВРЕМЯ −40 %, ПИК −90,3 % (в 10,3 раза). Напечатанное совпало байт в байт: - * `diff -r` чист на обеих программах (`builtins.flang` и `parser.flang`), md5 - * одна на обе колонки. За сутки перепечатка умерла трижды, и один раз — - * `FLANG_MEMORY`; эти десять раз по пику ей доступны СЕЙЧАС, пересборкой - * `bootstrap/flang` с рантаймом отсюда, и перепечатки они не требуют. - * - * ── Почему хвост можно отдавать целиком ──────────────────────────────────── - * За текущим куском вся цепочка ПУСТА, и это не случай: `fl_arena_alloc` - * продвигает `current` только на кусок с `used == 0`, а новый вставляет сразу - * за ним, — значит «после текущего всё пусто» держится с рождения арены. Куски - * от отметки до текущего пустеют этим самым откатом. Стало быть после отката - * пуст весь хвост за `mark.chunk`, и отдать его — то же самое, что занулить, - * только память при этом уходит системе. - * - * ── И почему обход до конца цепочки больше не дорог ──────────────────────── - * Здесь стояло правило «обход обрывается на текущем куске, а не идёт до конца - * цепочки», и стояло оно по делу: за текущим лежали куски ВСЕХ прежних откатов, - * то есть вся история арены, и зануление их стоило 16 % времени на 665 тысячах - * откатов (`docs/zettel/subgraphs-below-the-rollback-mark-need-no-copy.md`). - * Отдача снимает саму причину: после каждого отката за отметкой остаётся не - * больше FL_ARENA_KEEP кусков, поэтому хвост — это то, что выдано ПОСЛЕ - * отметки, а не всё, что арена купила за жизнь. Истории в цепочке больше нет, и - * обрывать обход не на чем. - */ -static void fl_arena_rollback(fl_arena *arena, fl_mark mark) { - const size_t header = fl_round_up(sizeof(fl_chunk)); - fl_chunk *chunk = NULL; - fl_chunk *keep_head = NULL; - fl_chunk *keep_tail = NULL; - size_t kept = 0; - if (mark.chunk == NULL) { - /* На отметке арена была пуста: пусто всё, что после неё. */ - chunk = arena->chunks; - } else { - mark.chunk->used = mark.used; - chunk = mark.chunk->next; - } - while (chunk != NULL) { - fl_chunk *next = chunk->next; - if (kept < (size_t)FL_ARENA_KEEP) { - chunk->used = 0; - chunk->next = NULL; - if (keep_tail == NULL) { - keep_head = chunk; - } else { - keep_tail->next = chunk; - } - keep_tail = chunk; - kept += 1; - } else { - /* `reserved` — это купленное у malloc, и отданное из него вычитается: - иначе арена считала бы своим то, чего у неё уже нет. */ - arena->reserved -= header + chunk->capacity; - free(chunk); - } - chunk = next; - } - if (mark.chunk == NULL) { - arena->chunks = keep_head; - arena->current = keep_head; - } else { - mark.chunk->next = keep_head; - arena->current = mark.chunk; - } - arena->handed = mark.handed; - /* Память выше отметки сейчас достанется кому-то другому, и запомненные адреса - могли бы совпасть с чужими значениями. Памятка снимается вместе с памятью. */ - fl_region_forget(arena); -} - -/** Буфер под перекладку: живёт между вызовами, отдаётся в `fl_arena_release`. */ -static bool fl_region_staging(fl_arena *arena, size_t need) { - char *buffer = NULL; - if (arena->staging_size >= need) { - return true; - } - buffer = (char *)malloc(need); - if (buffer == NULL) { - return false; - } - free(arena->staging); - arena->staging = buffer; - arena->staging_size = need; - return true; -} - -fl_mark fl_region_open(fl_ctx *ctx) { - fl_mark mark; - fl_arena *arena = ctx == NULL ? NULL : ctx->arena; - mark.chunk = NULL; - mark.used = 0; - mark.handed = 0; - mark.guard_chunk = NULL; - mark.guard_used = 0; - if (arena == NULL) { - return mark; - } - mark.chunk = arena->current; - mark.used = arena->current == NULL ? 0 : arena->current->used; - mark.handed = arena->handed; - mark.guard_chunk = arena->guard_chunk; - mark.guard_used = arena->guard_used; - arena->guard_chunk = mark.chunk; - arena->guard_used = mark.used; - return mark; -} - -/* - * ── Закрытие области ─────────────────────────────────────────────────────── - * - * Порядок такой и другим быть не может: - * - * 1. вернуть границу объемлющей области — ВСЕГДА, каким бы ни был исход; - * 2. на отказе не трогать ничего: текст диагностики лежит в арене выше - * отметки, и откат стёр бы сообщение об ошибке; - * 3. обмерить результат с бюджетом в четверть наросшего; - * 4. переложить результат в буфер вне арены; - * 5. откатить арену; - * 6. переложить результат из буфера обратно, одним блоком. - * - * Шаги 3–6 могут не состояться — и тогда область просто НЕ ДЕЛАЕТ НИЧЕГО. - * Это главное её свойство: она вправе отказаться в любой момент до шага 5, и - * отказ не виден снаружи ничем, кроме памяти. Отказывается она, когда: - * - * • область наросла меньше куска арены (платить не за что); - * • результат больше четверти наросшего (см. FL_REGION_GAIN); - * • результат не влез в бюджет — разделяемый подграф, большой ответ, - * накопление (см. обмер выше); - * • значение глубже FL_REGION_DEPTH; - * • не дали буфера; - * • обмер разошёлся с перекладкой (такого быть не должно, но проверка - * дешевле доверия). - * - * Единственное место, где область всё-таки может отказать вычислению, — шаг 6: - * после отката память под копию уже нужна, и не дать её может только настоящее - * исчерпание. Тогда честный FLANG_MEMORY, а не тишина. - */ -fl_status fl_region_close(fl_ctx *ctx, fl_mark mark, fl_status status, fl_value *result, fl_error *error) { - fl_arena *arena = ctx == NULL ? NULL : ctx->arena; - size_t grown = 0; - size_t live = 0; - fl_pack pack; - fl_live zone; - fl_value staged = fl_nothing(); - fl_value moved = fl_nothing(); - char *block = NULL; - - if (arena == NULL) { - return status; - } - arena->guard_chunk = mark.guard_chunk; - arena->guard_used = mark.guard_used; - if (status != FL_OK || result == NULL) { - return status; - } - - grown = arena->handed - mark.handed; - if (grown < (size_t)FL_REGION_MIN) { - return FL_OK; - } - zone = fl_region_live(arena, mark); - if (!fl_region_size(arena, zone, *result, grown / FL_REGION_GAIN, 0, &live)) { - return FL_OK; - } - if (live == 0) { - /* Копировать нечего: результат — скаляр, пустой список либо целиком лежит - ниже отметки. Во всех трёх случаях он переживёт откат как есть. */ - fl_arena_rollback(arena, mark); - return FL_OK; - } - if (!fl_region_staging(arena, live)) { - return FL_OK; - } - pack.base = arena->staging; - pack.size = live; - pack.used = 0; - pack.zone = zone; - if (!fl_pack_value(&pack, *result, 0, &staged)) { - return FL_OK; - } - - fl_arena_rollback(arena, mark); - - block = (char *)fl_arena_alloc(arena, live); - if (block == NULL) { - return fl_no_memory(error); - } - pack.base = block; - pack.size = live; - pack.used = 0; - /* Обратно копируется ровно то, что легло в буфер; всё прочее уже пережило - откат и лежит на месте. */ - pack.zone.lo = arena->staging; - pack.zone.hi = arena->staging + live; - if (!fl_pack_value(&pack, staged, 0, &moved)) { - return fl_no_memory(error); - } - *result = moved; - return FL_OK; -} - -/* - * ── Область на накопитель свёртки ────────────────────────────────────────── - * - * Область на ВЫЗОВ не помогает свёртке: свёртка печатается обычным циклом C, и - * функция, внутри которой она стоит, вправе не быть рекурсивной — тогда области - * у неё нет вовсе. А накопителей за цикл получается столько же, сколько витков, - * и каждый из них живёт до конца объемлющего вызова. «Сортировка вставками» из - * `examples/rosetta/quicksort.flang` — ровно этот случай: на четырёх - * тысячах элементов живых значений четыре тысячи, а мёртвых почти восемь - * миллионов, то есть 99,95 % пика. - * - * Довод законности здесь ПРЯМЕЕ, чем у области на вызов, и виден прямо в - * напечатанном C: - * - * fl_value akk = <начальное>; ← ниже отметки - * const fl_mark m = fl_region_open(ctx); - * for (...) { - * const fl_value el = list.as.list.items[i]; ← ниже отметки: элемент - * ... akk = <тело>; ... входного списка - * FL_TRY(fl_region_recycle(ctx, m, &akk, error)); - * } - * - * После присваивания прежний накопитель не читает НИКТО: имя `akk` — единственное, - * что на него указывало, и оно уже переприсвоено; значения flang неизменяемы, - * замыканий нет, ссылок наружу нет. А входной список и его элементы построил - * вызывающий, и лежат они НИЖЕ отметки, то есть откат их не трогает. Поэтому - * «переиспользовать на месте разрешено там, где доказано, что старое значение - * больше не читается» выполняется здесь не догадкой, а видом цикла. - * - * Почему нужна отдельная функция, а не голый `fl_region_close`: тот ВСЕГДА - * возвращает границу объемлющей области (шаг 1 над ним), и со второго витка - * цикл шёл бы без сторожа. Тогда `fl_arena_extend` продлил бы на месте блок, - * лежащий ниже отметки, — а его вот-вот откатят. - * - * Отметка после отката остаётся действительной: откат восстанавливает ровно - * `mark.chunk`/`mark.used`. Если область отказалась (см. список над - * `fl_region_close`), отметка тем более действительна — арена не двигалась - * ниже неё. Поэтому взводить сторож можно в обоих исходах одинаково. - * - * Границу объемлющей области возвращает НЕ эта функция, а `fl_region_close`, - * который печатается один раз после цикла. На раннем возврате по ошибке из тела - * цикла граница остаётся взведённой на отметке свёртки — это строго - * консервативно (`fl_arena_extend` откажет там, где мог бы согласиться) и - * снимается ближайшим `fl_region_close` объемлющего вызова либо - * `fl_arena_reset`. - */ -/* - * Бюджет витка строже, чем бюджет вызова, и это ЗАМЕРЕНО. Свёрткой в дереве - * записаны две разные вещи, и им нужны разные пороги: - * - * • накопление — `свёртка ... как акк и эл → добавить эл к акк`. Здесь - * «добавить» пишет в хвостовой запас на месте (см. `fl_b_dobavit`), мусора - * почти нет, и наросшее лишь вдвое больше живого. Откат тут — чистый - * убыток: он копирует накопитель И теряет запас, так что следующее - * «добавить» идёт на копию; - * • перестройка — `свёртка ... как акк и эл → «Вставить по порядку» от эл и - * акк`. Здесь каждый виток строит новый накопитель целиком, прежний - * мгновенно мёртв, и наросшее к витку k — это все прежние накопители, то - * есть примерно k²/2 при живых k. - * - * При бюджете вызова (четверть наросшего) первый случай проходит впритык и - * платит: «Сортировка вставками» на 4 000 элементах стала 262 с вместо 151. - * При бюджете витка (одна шестнадцатая) накопление отказывается по построению, - * а перестройка проходит с k ≈ 32 — и обе меряются одним числом, а не - * догадкой о том, что написал автор. - */ -#ifndef FL_REGION_LOOP_GAIN -#define FL_REGION_LOOP_GAIN (size_t)16 -#endif - -fl_status fl_region_recycle(fl_ctx *ctx, fl_mark mark, fl_value *result, fl_error *error) { - fl_arena *arena = ctx == NULL ? NULL : ctx->arena; - size_t grown = 0; - size_t live = 0; - fl_status status = FL_OK; - - if (arena == NULL || result == NULL) { - return FL_OK; - } - grown = arena->handed - mark.handed; - if (grown < (size_t)FL_REGION_MIN) { - return FL_OK; - } - /* Другая область — счёт отступа начинается заново. Совпадение отметок у двух - разных свёрток обмеру не вредит: оно стоит лишней попытки, а не пропуска. */ - if (mark.handed != arena->recycle_mark) { - arena->recycle_mark = mark.handed; - arena->recycle_next = 0; - } - if (grown < arena->recycle_next) { - /* В прошлый раз обмер отказал, и наросшее с тех пор не удвоилось. Пробовать - снова незачем: ответ будет тот же, а стоить он будет уже дороже. */ - return FL_OK; - } - if (!fl_region_size(arena, fl_region_live(arena, mark), *result, grown / FL_REGION_LOOP_GAIN, 0, - &live)) { - /* Накопление: живого столько же, сколько наросло. Откат не окупится. */ - arena->recycle_next = grown > ((size_t)-1) / 2 ? (size_t)-1 : grown * 2; - return FL_OK; - } - arena->recycle_next = 0; - status = fl_region_close(ctx, mark, FL_OK, result, error); - if (status != FL_OK) { - return status; - } - arena->guard_chunk = mark.chunk; - arena->guard_used = mark.used; - return FL_OK; -} - -/* ═════════════════════════════ UTF-8 ═════════════════════════════ */ - -/* - * Строки flang меряются кодовыми точками (SPEC, раздел 5), поэтому весь UTF-8 - * здесь — свой, без единой сторонней библиотеки. Ведущий байт кодовой точки - * узнаётся тем же способом, что и везде: у продолжения старшие биты 10. - * - * ГДЕ НАЧИНАЕТСЯ ЗНАК — один ответ на все формы, и до 22 августа 2026 его не - * было. Первый октет строки начинает знак ВСЕГДА, даже если он октет - * продолжения: иначе строка из одних продолжений оказывалась «длиной 0» и - * «пустой», оставаясь при этом непустой по содержимому. Прогон на этой ветке: - * у строки из октетов BC D0 `длина` давала 1, а `разложить … на символы` — два - * куска, и второй кусок писался ЗА КОНЕЦ выделенного массива, потому что - * массив мерился длиной. Одна мера — значит и счёт, и нарезка, и поиск ходят по - * одному и тому же множеству начал. - */ -static bool fl_utf8_starts(const char *utf8, size_t index) { - return index == 0 || ((unsigned char)utf8[index] & 0xC0u) != 0x80u; -} - -/* Правило начала знака выписано ОДИН раз и зовётся отовсюду, включая эти два - цикла. Развернуть его здесь руками (вынести нулевой октет из цикла) — значит - завести вторую копию правила ради 1,8 % времени: замер `flang check` на - 90-килобайтном flang/stdlib/strings.flang, лучшее из семи, — 2,29 с с общим - правилом против 2,25 с с развёрнутым при 2,14 с до правки. Вторая копия - правила — это ровно тот способ, каким меры разошлись в прошлый раз. */ -static size_t fl_utf8_points(const char *utf8, size_t bytes) { - size_t points = 0; - size_t index = 0; - for (index = 0; index < bytes; index += 1) { - if (fl_utf8_starts(utf8, index)) { - points += 1; - } - } - return points; -} - -/* - * ГДЕ СОДЕРЖИМОЕ ПЕРЕСТАЁТ БЫТЬ ТЕКСТОМ. Счёт точек выше не проверяет НИЧЕГО: - * он делит байты на ведущие и продолжающие и на любом мусоре отвечает числом. - * Этого хватает строкам самого языка — они собраны из литералов и из чтения - * текста, — но не хватает хозяину, которому дали двоичный файл: 13 886 504 - * октета он назовёт 11 776 136 знаками и не скажет ни слова. Здесь спрашивается - * прямо, и живут обе стороны вопроса в ОДНОМ месте, чтобы два хозяина на C - * (`flang io` и планировщик конкурентности) не разошлись ответами. - * - * Возвращает номер (от 1) ПЕРВОГО октета, который не складывается в правильный - * UTF-8, и 0 — если складывается весь. - * - * НУЛЕВОЙ ОКТЕТ ЗДЕСЬ ЗАКОНЕН, и это решение по улике, а не недосмотр. U+0000 — - * обычная кодовая точка, и в дереве есть исходник, который её содержит: - * `flang/self/link.flang` разделяет нулём имена в ключе (октет 91 689 из - * 138 872). Запрет ловил бы ЭТОТ файл и не ловил бы ничего сверх того, что - * ловится неправильным UTF-8: в заголовке ELF первый неправильный октет — 26-й, - * а первый нулевой — 8-й, отвергаются оба файла одинаково. Опасен ноль был - * ровно там, где длину брали `strlen`ом; длину теперь берут у значения. - */ -size_t fl_utf8_not_text_at(const char *utf8, size_t bytes) { - size_t at = 0; - while (at < bytes) { - const unsigned char lead = (unsigned char)utf8[at]; - unsigned long point = 0; - size_t more = 0; - size_t step = 0; - if (lead < 0x80u) { - at += 1; - continue; - } - if ((lead & 0xE0u) == 0xC0u) { - more = 1; - point = (unsigned long)(lead & 0x1Fu); - } else if ((lead & 0xF0u) == 0xE0u) { - more = 2; - point = (unsigned long)(lead & 0x0Fu); - } else if ((lead & 0xF8u) == 0xF0u) { - more = 3; - point = (unsigned long)(lead & 0x07u); - } else { - return at + 1; - } - if (at + more >= bytes) return at + 1; - for (step = 1; step <= more; step += 1) { - const unsigned char next = (unsigned char)utf8[at + step]; - if ((next & 0xC0u) != 0x80u) return at + 1; - point = (point << 6) | (unsigned long)(next & 0x3Fu); - } - /* Пересокращённая запись, суррогат и всё выше U+10FFFF — тоже не текст: - иначе у одного знака было бы два написания, и счёт разошёлся бы. */ - if (more == 1 && point < 0x80UL) return at + 1; - if (more == 2 && point < 0x800UL) return at + 1; - if (more == 3 && point < 0x10000UL) return at + 1; - if (point > 0x10FFFFUL) return at + 1; - if (point >= 0xD800UL && point <= 0xDFFFUL) return at + 1; - at += more + 1; - } - return 0; -} - -/** Байтовое смещение кодовой точки с номером point (от нуля). */ -static size_t fl_utf8_offset(const char *utf8, size_t bytes, size_t point) { - size_t seen = 0; - size_t index = 0; - for (index = 0; index < bytes; index += 1) { - if (fl_utf8_starts(utf8, index)) { - if (seen == point) { - return index; - } - seen += 1; - } - } - return bytes; -} - -/** Стоит ли смещение на границе знака: конец строки границей считается тоже. */ -static bool fl_utf8_boundary(const char *utf8, size_t bytes, size_t index) { - return index >= bytes || fl_utf8_starts(utf8, index); -} - -static unsigned long fl_utf8_decode(const char *utf8, size_t bytes, size_t offset, size_t *width) { - const unsigned char lead = (unsigned char)utf8[offset]; - unsigned long code = lead; - size_t length = 1; - if (lead >= 0xF0u) { - length = 4; - code = lead & 0x07u; - } else if (lead >= 0xE0u) { - length = 3; - code = lead & 0x0Fu; - } else if (lead >= 0xC0u) { - length = 2; - code = lead & 0x1Fu; - } - if (offset + length > bytes) { - length = 1; - code = lead; - } else if (length > 1) { - size_t index = 1; - for (index = 1; index < length; index += 1) { - code = (code << 6) | ((unsigned char)utf8[offset + index] & 0x3Fu); - } - } - if (width != NULL) { - *width = length; - } - return code; -} - -/* Пробел по ECMAScript: WhiteSpace + LineTerminator. Список закрытый, поэтому - он здесь целиком, а не «всё, что скажет isspace» — isspace зависит от locale - и не знает про U+00A0 и U+2028. */ -static bool fl_js_space(unsigned long code) { - switch (code) { - case 0x0009u: - case 0x000Au: - case 0x000Bu: - case 0x000Cu: - case 0x000Du: - case 0x0020u: - case 0x00A0u: - case 0x1680u: - case 0x2028u: - case 0x2029u: - case 0x202Fu: - case 0x205Fu: - case 0x3000u: - case 0xFEFFu: - return true; - default: - return code >= 0x2000u && code <= 0x200Au; - } -} - -/* ═════════════════════════════ значения ═════════════════════════════ */ - -fl_value fl_nothing(void) { - fl_value value; - value.tag = FL_NOTHING; - value.as.number = 0.0; - return value; -} - -fl_value fl_number(double number) { - fl_value value; - value.tag = FL_NUMBER; - value.as.number = number; - return value; -} - -fl_value fl_flag(bool flag) { - fl_value value; - value.tag = FL_FLAG; - value.as.flag = flag; - return value; -} - -fl_value fl_text_borrow(const char *utf8, size_t bytes, size_t points) { - fl_value value; - value.tag = FL_STRING; - value.as.string.utf8 = utf8; - value.as.string.bytes = bytes; - value.as.string.points = points; - return value; -} - -/** Строка-константа рантайма («да», «нет», «ничто»): длины считаются на месте. */ -static fl_value fl_text_static(const char *utf8) { - const size_t bytes = strlen(utf8); - return fl_text_borrow(utf8, bytes, fl_utf8_points(utf8, bytes)); -} - -fl_status fl_text(fl_ctx *ctx, const char *utf8, size_t bytes, fl_value *out, fl_error *error) { - char *copy = NULL; - if (ctx == NULL || out == NULL) { - return FL_INVALID_ARGUMENT; - } - copy = (char *)fl_arena_alloc(ctx->arena, bytes + 1); - if (copy == NULL) { - return fl_no_memory(error); - } - if (bytes > 0) { - memcpy(copy, utf8, bytes); - } - copy[bytes] = '\0'; - *out = fl_text_borrow(copy, bytes, fl_utf8_points(copy, bytes)); - return FL_OK; -} - -/* - * Запас массива — с двух концов. Запись одна на массив и общая для всех - * значений списка, которые на этот массив смотрят: «добавить» занимает ячейку - * за концом, «приписать» — перед началом, а два водораздела не дают двум разным - * спискам занять одну и ту же ячейку. Целиком правило — над `fl_b_dobavit`. - */ -struct fl_grow { - fl_value *items; /* база массива; значение с запасом смотрит внутрь неё */ - size_t head; /* первая занятая кем-то ячейка; только убывает */ - size_t filled; /* ячеек занято до этой, не включая; только растёт */ - size_t capacity; /* ячеек в массиве всего */ -}; - -fl_value fl_list(const fl_value *items, size_t count) { - fl_value value; - value.tag = FL_LIST; - value.as.list.items = items; - value.as.list.count = count; - value.as.list.grow = NULL; /* запас заводит только «добавить» */ - return value; -} - -/** То же, но с запасом: единственный, кто им пользуется, — «добавить». */ -static fl_value fl_list_grown(const fl_value *items, size_t count, fl_grow *grow) { - fl_value value = fl_list(items, count); - value.as.list.grow = grow; - return value; -} - -fl_status fl_list_alloc(fl_ctx *ctx, size_t count, fl_value **items, fl_error *error) { - if (ctx == NULL || items == NULL) { - return FL_INVALID_ARGUMENT; - } - if (count == 0) { - *items = NULL; - return FL_OK; - } - if (count > ((size_t)-1) / sizeof(fl_value)) { - return fl_no_memory(error); - } - *items = (fl_value *)fl_arena_alloc(ctx->arena, count * sizeof(fl_value)); - if (*items == NULL) { - return fl_no_memory(error); - } - return FL_OK; -} - -/* - * Хвост списка — срез, а не копия. В JS «хвост» копирует, потому что массив - * нельзя разделить с суффиксом; здесь значения неизменяемы и живут в арене, - * поэтому срез наблюдаемо неотличим от копии, а рекурсия «голова и хвост» по - * длинному списку из квадратичной становится линейной. - * - * Запас срезу не передаётся (`fl_list` ставит NULL), и это обязательно: запись - * запаса считает ячейки от базы массива, а срез начинается не с неё. - */ -fl_value fl_list_slice(fl_value list, size_t from) { - if (list.tag != FL_LIST || from >= list.as.list.count) { - return fl_list(NULL, 0); - } - return fl_list(list.as.list.items + from, list.as.list.count - from); -} - -/* - * Цепочка — список ЛИБО строка: образцы «пусто» и «голова и хвост» разбирают - * обе. Причина одна на все восемь целей: у строки ровно два случая, пустая и - * «первый символ и остаток», третьего нет. До этих четырёх функций посимвольный - * проход обязан был начинаться с «разложить … на символы», и это меняло - * сигнатуру функции. - * - * Голова строки — строка из ОДНОЙ кодовой точки, а не байт: `fl_utf8_offset` - * считает точки, как «длина», «символ» и «символы». Байтовая нарезка разваливала - * бы эмодзи пополам, и разбор строки разошёлся бы с её длиной. - * - * Обе — срезы без копирования, как `fl_list_slice`: значения неизменяемы, память - * общая, и рекурсия по строке остаётся линейной. - */ -bool fl_chain_empty(fl_value value) { - if (value.tag == FL_STRING) { - return value.as.string.points == 0; - } - return value.tag == FL_LIST && value.as.list.count == 0; -} - -bool fl_chain_cons(fl_value value) { - if (value.tag == FL_STRING) { - return value.as.string.points > 0; - } - return value.tag == FL_LIST && value.as.list.count > 0; -} - -fl_value fl_chain_head(fl_value value) { - if (value.tag == FL_STRING) { - size_t stop = fl_utf8_offset(value.as.string.utf8, value.as.string.bytes, 1); - return fl_text_borrow(value.as.string.utf8, stop, 1); - } - return value.as.list.items[0]; -} - -fl_value fl_chain_tail(fl_value value) { - if (value.tag == FL_STRING) { - size_t start = fl_utf8_offset(value.as.string.utf8, value.as.string.bytes, 1); - return fl_text_borrow(value.as.string.utf8 + start, value.as.string.bytes - start, - value.as.string.points - 1); - } - return fl_list_slice(value, 1); -} - -static fl_status fl_fields_new(fl_ctx *ctx, const char *const *names, const fl_value *values, size_t count, - const fl_field **out, fl_error *error) { - fl_field *fields = NULL; - size_t index = 0; - if (count == 0) { - *out = NULL; - return FL_OK; - } - if (count > ((size_t)-1) / sizeof(fl_field)) { - return fl_no_memory(error); - } - fields = (fl_field *)fl_arena_alloc(ctx->arena, count * sizeof(fl_field)); - if (fields == NULL) { - return fl_no_memory(error); - } - for (index = 0; index < count; index += 1) { - fields[index].name = names[index]; - fields[index].value = values[index]; - } - *out = fields; - return FL_OK; -} - -fl_status fl_record_new(fl_ctx *ctx, const char *const *names, const fl_value *values, size_t count, - fl_value *out, fl_error *error) { - fl_record *record = NULL; - if (ctx == NULL || out == NULL) { - return FL_INVALID_ARGUMENT; - } - record = (fl_record *)fl_arena_alloc(ctx->arena, sizeof(fl_record)); - if (record == NULL) { - return fl_no_memory(error); - } - record->count = count; - record->fields = NULL; - FL_TRY(fl_fields_new(ctx, names, values, count, &record->fields, error)); - out->tag = FL_RECORD; - out->as.record = record; - return FL_OK; -} - -fl_status fl_variant_new(fl_ctx *ctx, const char *name, const char *const *names, const fl_value *values, - size_t count, fl_value *out, fl_error *error) { - fl_variant *variant = NULL; - if (ctx == NULL || out == NULL) { - return FL_INVALID_ARGUMENT; - } - variant = (fl_variant *)fl_arena_alloc(ctx->arena, sizeof(fl_variant)); - if (variant == NULL) { - return fl_no_memory(error); - } - variant->name = name; - variant->count = count; - variant->fields = NULL; - FL_TRY(fl_fields_new(ctx, names, values, count, &variant->fields, error)); - out->tag = FL_VARIANT; - out->as.variant = variant; - return FL_OK; -} - -bool fl_is_list(fl_value value) { - return value.tag == FL_LIST; -} - -bool fl_is_record(fl_value value) { - return value.tag == FL_RECORD; -} - -bool fl_is_variant(fl_value value) { - return value.tag == FL_VARIANT; -} - -bool fl_is_scalar(fl_value value) { - return value.tag == FL_NOTHING || value.tag == FL_NUMBER || value.tag == FL_FLAG || value.tag == FL_STRING; -} - -bool fl_variant_is(fl_value value, const char *name) { - return value.tag == FL_VARIANT && fl_name_same(value.as.variant->name, name); -} - -/* ═════════════════════════ число → текст ═════════════════════════ */ - -/* - * Правила ECMAScript Number::toString. Меньше всего хотелось писать это - * руками, но выбора нет: «к строке» от числа и половина текстов диагностик - * содержат число, а printf("%g") печатает «1e+21» как «1e+21» и «100000» как - * «100000», зато 0.1 — как «0.1» лишь по счастливой случайности выбранной - * точности. Совпадение с интерпретатором обязано быть посимвольным. - * - * Шаг первый — кратчайшая запись, читающаяся обратно тем же double: пробуем - * точности от 1 до 17 и берём первую, которую strtod возвращает без потерь. - * Шаг второй — расстановка точки и экспоненты ровно по таблице стандарта. - */ -/* - * Проб точности — от одной до семнадцати, и каждая стоит snprintf плюс strtod. - * Это самая дорогая работа среди встроенных форм на один вызов, и счётчику она - * до сих пор ничего не стоила. `probes` считает сделанные пробы; NULL — не - * считать (диагностикам счёт не нужен, они не в счёте витков программы). - */ -static size_t fl_number_text_cost(double value, char *buffer, size_t *probes) { - char probe[64]; - char digits[32]; - size_t offset = 0; - size_t count = 0; - const char *cursor = NULL; - int precision = 1; - int exponent = 0; - int n = 0; - int k = 0; - - if (isnan(value)) { - memcpy(buffer, "NaN", 4); - return 3; - } - if (value == 0.0) { - /* String(-0) === "0": знак нуля не печатается (но Object.is его различает). */ - buffer[0] = '0'; - buffer[1] = '\0'; - return 1; - } - if (value < 0.0) { - buffer[offset] = '-'; - offset += 1; - value = -value; - } - if (isinf(value)) { - memcpy(buffer + offset, "Infinity", 9); - return offset + 8; - } - - for (precision = 1; precision < 17; precision += 1) { - snprintf(probe, sizeof probe, "%.*e", precision - 1, value); - if (probes != NULL) { - *probes += 1; - } - if (strtod(probe, NULL) == value) { - break; - } - } - snprintf(probe, sizeof probe, "%.*e", precision - 1, value); - - for (cursor = probe; *cursor != '\0' && *cursor != 'e'; cursor += 1) { - if (*cursor >= '0' && *cursor <= '9') { - digits[count] = *cursor; - count += 1; - } - } - digits[count] = '\0'; - exponent = (*cursor == 'e') ? atoi(cursor + 1) : 0; - - k = (int)count; - n = exponent + 1; - - if (k <= n && n <= 21) { - memcpy(buffer + offset, digits, count); - offset += count; - while (n > k) { - buffer[offset] = '0'; - offset += 1; - n -= 1; - } - } else if (n > 0 && n <= 21) { - memcpy(buffer + offset, digits, (size_t)n); - offset += (size_t)n; - buffer[offset] = '.'; - offset += 1; - memcpy(buffer + offset, digits + n, count - (size_t)n); - offset += count - (size_t)n; - } else if (n > -6 && n <= 0) { - buffer[offset] = '0'; - buffer[offset + 1] = '.'; - offset += 2; - while (n < 0) { - buffer[offset] = '0'; - offset += 1; - n += 1; - } - memcpy(buffer + offset, digits, count); - offset += count; - } else { - buffer[offset] = digits[0]; - offset += 1; - if (k > 1) { - buffer[offset] = '.'; - offset += 1; - memcpy(buffer + offset, digits + 1, count - 1); - offset += count - 1; - } - buffer[offset] = 'e'; - offset += 1; - buffer[offset] = (n - 1) < 0 ? '-' : '+'; - offset += 1; - offset += (size_t)snprintf(buffer + offset, FL_NUMBER_TEXT_MAX - offset, "%d", n - 1 < 0 ? -(n - 1) : n - 1); - } - buffer[offset] = '\0'; - return offset; -} - -size_t fl_number_text(double value, char *buffer) { - return fl_number_text_cost(value, buffer, NULL); -} - -/* ═════════════════════════ буфер для текстов ═════════════════════════ */ - -typedef struct fl_buffer { - char *data; - size_t length; - size_t capacity; -} fl_buffer; - -static bool fl_buffer_reserve(fl_ctx *ctx, fl_buffer *buffer, size_t extra) { - size_t capacity = buffer->capacity == 0 ? 64 : buffer->capacity; - char *data = NULL; - if (buffer->length + extra + 1 <= buffer->capacity) { - return true; - } - while (capacity < buffer->length + extra + 1) { - capacity *= 2; - } - /* Арена не умеет освобождать, поэтому рост — это копирование в новый блок. - Строки диагностик короткие, а значит и копий одна-две. */ - data = (char *)fl_arena_alloc(ctx->arena, capacity); - if (data == NULL) { - return false; - } - if (buffer->length > 0) { - memcpy(data, buffer->data, buffer->length); - } - buffer->data = data; - buffer->capacity = capacity; - return true; -} - -static bool fl_buffer_add(fl_ctx *ctx, fl_buffer *buffer, const char *text, size_t bytes) { - if (!fl_buffer_reserve(ctx, buffer, bytes)) { - return false; - } - if (bytes > 0) { - memcpy(buffer->data + buffer->length, text, bytes); - } - buffer->length += bytes; - buffer->data[buffer->length] = '\0'; - return true; -} - -static bool fl_buffer_text(fl_ctx *ctx, fl_buffer *buffer, const char *text) { - return fl_buffer_add(ctx, buffer, text, strlen(text)); -} - -/* ═════════════════════════ описание значений ═════════════════════════ */ - -const char *fl_type_name(fl_ctx *ctx, fl_value value) { - switch (value.tag) { - case FL_NOTHING: - return "ничто"; - case FL_STRING: - return "строка"; - case FL_NUMBER: - return "число"; - case FL_FLAG: - return "признак"; - case FL_LIST: - return "список"; - case FL_RECORD: - return "запись"; - case FL_VARIANT: { - fl_buffer buffer; - buffer.data = NULL; - buffer.length = 0; - buffer.capacity = 0; - if (!fl_buffer_text(ctx, &buffer, "вариант «") || !fl_buffer_text(ctx, &buffer, value.as.variant->name) || - !fl_buffer_text(ctx, &buffer, "»")) { - return "вариант"; - } - return buffer.data; - } - } - return "неизвестное значение"; -} - -/** JSON.stringify для строки: диагностики интерпретатора печатают строки им. */ -static bool fl_buffer_quote(fl_ctx *ctx, fl_buffer *buffer, const char *utf8, size_t bytes) { - size_t index = 0; - if (!fl_buffer_text(ctx, buffer, "\"")) { - return false; - } - for (index = 0; index < bytes; index += 1) { - const unsigned char byte = (unsigned char)utf8[index]; - char escape[8]; - switch (byte) { - case '"': - if (!fl_buffer_text(ctx, buffer, "\\\"")) return false; - continue; - case '\\': - if (!fl_buffer_text(ctx, buffer, "\\\\")) return false; - continue; - case '\b': - if (!fl_buffer_text(ctx, buffer, "\\b")) return false; - continue; - case '\f': - if (!fl_buffer_text(ctx, buffer, "\\f")) return false; - continue; - case '\n': - if (!fl_buffer_text(ctx, buffer, "\\n")) return false; - continue; - case '\r': - if (!fl_buffer_text(ctx, buffer, "\\r")) return false; - continue; - case '\t': - if (!fl_buffer_text(ctx, buffer, "\\t")) return false; - continue; - default: - break; - } - if (byte < 0x20u) { - snprintf(escape, sizeof escape, "\\u%04x", (unsigned)byte); - if (!fl_buffer_text(ctx, buffer, escape)) return false; - continue; - } - if (!fl_buffer_add(ctx, buffer, (const char *)&byte, 1)) return false; - } - return fl_buffer_text(ctx, buffer, "\""); -} - -static bool fl_describe_into(fl_ctx *ctx, fl_buffer *buffer, fl_value value) { - char number[FL_NUMBER_TEXT_MAX]; - size_t index = 0; - switch (value.tag) { - case FL_STRING: - return fl_buffer_quote(ctx, buffer, value.as.string.utf8, value.as.string.bytes); - case FL_VARIANT: - if (!fl_buffer_text(ctx, buffer, value.as.variant->name)) return false; - if (value.as.variant->count == 0) return true; - if (!fl_buffer_text(ctx, buffer, "(")) return false; - for (index = 0; index < value.as.variant->count; index += 1) { - if (index > 0 && !fl_buffer_text(ctx, buffer, ", ")) return false; - if (!fl_buffer_text(ctx, buffer, value.as.variant->fields[index].name)) return false; - } - return fl_buffer_text(ctx, buffer, ")"); - case FL_LIST: - snprintf(number, sizeof number, "%lu", (unsigned long)value.as.list.count); - return fl_buffer_text(ctx, buffer, "список из ") && fl_buffer_text(ctx, buffer, number); - case FL_RECORD: - if (!fl_buffer_text(ctx, buffer, "запись {")) return false; - for (index = 0; index < value.as.record->count; index += 1) { - if (index > 0 && !fl_buffer_text(ctx, buffer, ", ")) return false; - if (!fl_buffer_text(ctx, buffer, value.as.record->fields[index].name)) return false; - } - return fl_buffer_text(ctx, buffer, "}"); - case FL_NOTHING: - return fl_buffer_text(ctx, buffer, "ничто"); - case FL_FLAG: - return fl_buffer_text(ctx, buffer, value.as.flag ? "да" : "нет"); - case FL_NUMBER: - fl_number_text(value.as.number, number); - return fl_buffer_text(ctx, buffer, number); - } - return fl_buffer_text(ctx, buffer, "неизвестное значение"); -} - -const char *fl_describe(fl_ctx *ctx, fl_value value) { - fl_buffer buffer; - buffer.data = NULL; - buffer.length = 0; - buffer.capacity = 0; - if (!fl_describe_into(ctx, &buffer, value)) { - return "значение"; - } - return buffer.data; -} - -/** Строка в кавычках JSON — для диагностик «к числу». */ -static const char *fl_quoted(fl_ctx *ctx, fl_value text) { - fl_buffer buffer; - buffer.data = NULL; - buffer.length = 0; - buffer.capacity = 0; - if (!fl_buffer_quote(ctx, &buffer, text.as.string.utf8, text.as.string.bytes)) { - return "\"\""; - } - return buffer.data; -} - -/* ═════════════════════════ равенство ═════════════════════════ */ - -static bool fl_same_number(double left, double right) { - /* Object.is: NaN равен NaN, 0 не равен −0. */ - if (isnan(left) || isnan(right)) { - return isnan(left) && isnan(right); - } - if (left != right) { - return false; - } - if (left == 0.0) { - return (signbit(left) != 0) == (signbit(right) != 0); - } - return true; -} - -/* - * ── ДИАГОНАЛЬ ПОПАДАЕТ ВСЕГДА: ЗАМЕР, А НЕ НАДЕЖДА ──────────────────────── - * - * Поле ищется не перебором, а сверкой на СВОЁМ месте: у двух записей одного - * типа поля напечатаны в порядке объявления, а имя поля — литерал единицы - * трансляции, и одинаковые литералы компилятор сливает. Перебор оставлен под - * `иначе` — на случай, когда поля написаны в разном порядке: порядок ключей - * равенству не важен, и это семантика языка, а не подробность. - * - * СКОЛЬКО РАЗ ПЕРЕБОР НУЖЕН НА САМОМ ДЕЛЕ, снято счётчиками двоичным нынешнего - * печатника на работе `emit flang/self/lexer.flang --target c`: - * - * зовов fl_fields_equal 14 838 502 744 - * полей просмотрено 41 262 203 904 (наибольшая запись — 13 полей) - * витков перебора 134 303 803 982 3,25 витка на поле — КВАДРАТ - * диагональ совпала бы 41 262 203 904 ВСЕ поля до единого - * промахов диагонали 0 - * - * Ноль промахов на сорока одном миллиарде полей. Значит диагональ снимает - * 134 млрд витков перебора и все зовы `fl_name_same` вместе с ними — отсюда и - * `__strcmp_avx2` в профиле перепечатки. - */ -static bool fl_fields_equal(const fl_field *left, size_t left_count, const fl_field *right, size_t right_count) { - size_t index = 0; - size_t other = 0; - if (left_count != right_count) { - return false; - } - /* Порядок ключей неважен — важен состав, как в recordsEqual интерпретатора. */ - for (index = 0; index < left_count; index += 1) { - bool found = false; - if (left[index].name == right[index].name) { - found = fl_equal(left[index].value, right[index].value); - } else { - for (other = 0; other < right_count; other += 1) { - if (fl_name_same(left[index].name, right[other].name)) { - found = fl_equal(left[index].value, right[other].value); - break; - } - } - } - if (!found) { - return false; - } - } - return true; -} - -/* - * ── ОДИН И ТОТ ЖЕ УКАЗАТЕЛЬ — ОДНО СРАВНЕНИЕ, А НЕ ОБХОД ДЕРЕВА ──────────── - * - * Записи, варианты и списки flang неизменяемы и живут в арене: `равен` над - * ними почти всегда зовётся на ОДНОМ И ТОМ ЖЕ значении — значение сверяется с - * самим собой или с собственным подузлом, приехавшим по другому пути. До этой - * правки каждый такой зов обходил всё дерево до листьев. - * - * СКОЛЬКО ИХ, снято счётчиками, врезанными в этот файл, двоичным нынешнего - * печатника (`bootstrap-pechatnyy`, тем самым, который сейчас печатает семя), - * работа `emit flang/self/lexer.flang --target c`, 8 мин 50 с: - * - * зовов fl_equal 49 112 829 764 - * запись 7 485 029 278 тот же указатель 7 480 721 921 99,942 % - * вариант 7 353 480 760 тот же указатель 7 341 572 922 99,838 % - * список 1 742 910 604 тот же указатель 1 741 380 199 99,912 % - * строка 18 368 031 077 тот же указатель 18 311 592 023 99,693 % - * - * То есть 34,9 млрд зовов из 49,1 (71 %) — это сверка значения с самим собой. - * У строк отсев уже был (`fl_bytes_same` начинается со сверки указателей); у - * записи, варианта и списка его не было, и он вносится здесь. - * - * ПОЧЕМУ ЭТО ТОЧНО, А НЕ «ПОЧТИ». Отсев верен ровно настолько, насколько - * равенство flang рефлексивно, — и оно рефлексивно на ВСЕХ значениях языка, - * включая края: `не число` (`0 делить на 0`) равен сам себе (`fl_same_number` - * возвращает истину для двух NaN, в отличие от IEEE 754), минус ноль равен - * минус нулю (знак у обоих один и тот же). Будь равенство IEEE-шным, отсев по - * указателю был бы НЕВЕРЕН на записи с `не число` внутри — поэтому здесь он - * стоит вместе со ссылкой на `fl_same_number`, а не сам по себе. - * - * У списка сверяется ПАРА «начало и длина»: один и тот же массив, взятый - * разной длины, — разные списки, и длина проверяется первой. - */ -bool fl_equal(fl_value left, fl_value right) { - if (fl_is_scalar(left) || fl_is_scalar(right)) { - if (!fl_is_scalar(left) || !fl_is_scalar(right) || left.tag != right.tag) { - return false; - } - switch (left.tag) { - case FL_NOTHING: - return true; - case FL_FLAG: - return left.as.flag == right.as.flag; - case FL_NUMBER: - return fl_same_number(left.as.number, right.as.number); - case FL_STRING: - return left.as.string.bytes == right.as.string.bytes && - fl_bytes_same(left.as.string.utf8, right.as.string.utf8, left.as.string.bytes); - default: - return false; - } - } - if (left.tag == FL_LIST && right.tag == FL_LIST) { - size_t index = 0; - if (left.as.list.count != right.as.list.count) { - return false; - } - if (left.as.list.items == right.as.list.items) { - return true; - } - for (index = 0; index < left.as.list.count; index += 1) { - if (!fl_equal(left.as.list.items[index], right.as.list.items[index])) { - return false; - } - } - return true; - } - if (left.tag == FL_VARIANT && right.tag == FL_VARIANT) { - if (left.as.variant == right.as.variant) { - return true; - } - if (!fl_name_same(left.as.variant->name, right.as.variant->name)) { - return false; - } - return fl_fields_equal(left.as.variant->fields, left.as.variant->count, right.as.variant->fields, - right.as.variant->count); - } - if (left.tag == FL_RECORD && right.tag == FL_RECORD) { - if (left.as.record == right.as.record) { - return true; - } - return fl_fields_equal(left.as.record->fields, left.as.record->count, right.as.record->fields, - right.as.record->count); - } - return false; -} - -/* ═════════════════════════ операции языка ═════════════════════════ */ - -fl_status fl_field_get(fl_ctx *ctx, fl_value target, const char *name, fl_value *out, fl_error *error) { - size_t index = 0; - if (target.tag == FL_VARIANT) { - /* Поле СУММЫ ИЗ ОДНОГО ВАРИАНТА. Что вариант ровно один, проверила проверка типов, поэтому сюда приезжает значение, у которого поле есть. Отказ ниже остаётся прежним: он про сумму из двух и более. */ - for (index = 0; index < target.as.variant->count; index += 1) { - if (target.as.variant->fields[index].name == name) { - *out = target.as.variant->fields[index].value; - return FL_OK; - } - } - for (index = 0; index < target.as.variant->count; index += 1) { - if (fl_name_same(target.as.variant->fields[index].name, name)) { - *out = target.as.variant->fields[index].value; - return FL_OK; - } - } - return fl_fail(ctx, error, FL_CODE_TYPE, "поле «%s» нельзя взять у варианта «%s» — нужен разбор", name, - target.as.variant->name); - } - if (target.tag != FL_RECORD) { - return fl_fail(ctx, error, FL_CODE_TYPE, "поле «%s» можно взять только у записи, получено %s", name, - fl_type_name(ctx, target)); - } - { - const fl_field *fields = target.as.record->fields; - size_t count = target.as.record->count; - for (index = 0; index < count; index += 1) { - if (fields[index].name == name) { - *out = fields[index].value; - return FL_OK; - } - } - for (index = 0; index < count; index += 1) { - if (fl_name_same(fields[index].name, name)) { - *out = fields[index].value; - return FL_OK; - } - } - } - return fl_fail(ctx, error, FL_CODE_UNKNOWN_NAME, "запись не содержит поле «%s»", name); -} - -fl_status fl_variant_field(fl_ctx *ctx, fl_value target, const char *name, fl_value *out, fl_error *error) { - size_t index = 0; - for (index = 0; index < target.as.variant->count; index += 1) { - if (target.as.variant->fields[index].name == name) { - *out = target.as.variant->fields[index].value; - return FL_OK; - } - } - for (index = 0; index < target.as.variant->count; index += 1) { - if (fl_name_same(target.as.variant->fields[index].name, name)) { - *out = target.as.variant->fields[index].value; - return FL_OK; - } - } - return fl_fail(ctx, error, FL_CODE_UNKNOWN_NAME, "вариант «%s» не содержит поле «%s»", target.as.variant->name, - name); -} - -fl_status fl_cond(fl_ctx *ctx, fl_value value, bool *out, fl_error *error) { - if (value.tag != FL_FLAG) { - return fl_fail(ctx, error, FL_CODE_TYPE, "условие «если» должно быть признаком, получено %s", - fl_type_name(ctx, value)); - } - *out = value.as.flag; - return FL_OK; -} - -fl_status fl_keep(fl_ctx *ctx, fl_value value, bool *out, fl_error *error) { - if (value.tag != FL_FLAG) { - return fl_fail(ctx, error, FL_CODE_TYPE, "условие «отфильтровать» должно быть признаком, получено %s", - fl_type_name(ctx, value)); - } - *out = value.as.flag; - return FL_OK; -} - -fl_status fl_post(fl_ctx *ctx, fl_value value, const char *property, const char *function, bool *out, - fl_error *error) { - if (value.tag != FL_FLAG) { - return fl_fail(ctx, error, FL_CODE_TYPE, - "постусловие «%s» функции «%s» должно давать признак, получено %s", property, function, - fl_type_name(ctx, value)); - } - *out = value.as.flag; - return FL_OK; -} - -fl_status fl_pre(fl_ctx *ctx, fl_value value, const char *property, const char *function, bool *out, - fl_error *error) { - if (value.tag != FL_FLAG) { - return fl_fail(ctx, error, FL_CODE_TYPE, - "предусловие «%s» функции «%s» должно давать признак, получено %s", property, function, - fl_type_name(ctx, value)); - } - *out = value.as.flag; - return FL_OK; -} - -fl_status fl_match_fail(fl_ctx *ctx, fl_value value, fl_error *error) { - return fl_fail(ctx, error, FL_CODE_MATCH_NOT_EXHAUSTIVE, "разбор не покрывает значение %s", - fl_describe(ctx, value)); -} - -fl_status fl_require_list(fl_ctx *ctx, fl_value value, const char *label, fl_value *out, fl_error *error) { - if (value.tag != FL_LIST) { - return fl_fail(ctx, error, FL_CODE_TYPE, "«%s» работает только со списком, получено %s", label, - fl_type_name(ctx, value)); - } - /* - * Единственная точка входа всех трёх циклов по списку — `отфильтровать`, - * `отобразить`, `свёртка`, — и единственная, где рантайму видна длина. Заряд - * снимается здесь; почему именно здесь, сказано у `fl_charge`. - * - * Заряд НЕ отказывает, хотя мог бы: у рантайма нет имени объемлющей функции, - * а отказ обязан назвать её дословно так, как называет толкователь - * («функция «Имя» исчерпала лимит шагов …»). Назвать вместо неё форму — - * «функция «отфильтровать»» — значило бы напечатать текст, которого - * толкователь не печатает никогда, то есть развести бэкенды. Поэтому предел - * ловит ближайший `fl_tick`: вход в функцию, виток хвостового цикла или - * отскок батута. Перелёт ограничен длиной ОДНОГО списка — список уже лежит в - * памяти целиком, убежать ему некуда, — и это строго лучше прежнего, где - * предел не срабатывал на этой работе вовсе. - */ - fl_charge(ctx, value.as.list.count); - *out = value; - return FL_OK; -} - -/* - * Отказ арифметики: текст живёт ЗДЕСЬ И ТОЛЬКО ЗДЕСЬ. - * - * Зовут его двое: `fl_numbers` ниже, когда проверку делает рантайм, и сам - * напечатанный код, когда проверку делает он (см. шапку в flang_runtime.h). - * Один текст на оба пути — иначе они разошлись бы молча в тот день, когда - * сообщение поправят в одном месте. - */ -fl_status fl_not_numbers(fl_ctx *ctx, const char *op, fl_value left, fl_value right, fl_error *error) { - const char *left_name = fl_type_name(ctx, left); - return fl_fail(ctx, error, FL_CODE_TYPE, "операция «%s» допустима только для чисел, получено %s и %s", op, - left_name, fl_type_name(ctx, right)); -} - -/* Сообщение дословно как в ядре (src/utility.ts, compare). */ -fl_status fl_not_order(fl_ctx *ctx, fl_value left, fl_value right, fl_error *error) { - (void)left; - (void)right; - return fl_fail(ctx, error, FL_CODE_TYPE, "%s", "сравнения порядка допустимы только для чисел"); -} - -static fl_status fl_numbers(fl_ctx *ctx, const char *op, fl_value left, fl_value right, fl_error *error) { - if (left.tag != FL_NUMBER || right.tag != FL_NUMBER) { - return fl_not_numbers(ctx, op, left, right, error); - } - return FL_OK; -} - -static fl_status fl_order(fl_ctx *ctx, fl_value left, fl_value right, fl_error *error) { - if (left.tag != FL_NUMBER || right.tag != FL_NUMBER) { - return fl_not_order(ctx, left, right, error); - } - return FL_OK; -} - -fl_status fl_add(fl_ctx *ctx, fl_value left, fl_value right, fl_value *out, fl_error *error) { - FL_TRY(fl_numbers(ctx, "add", left, right, error)); - *out = fl_number(left.as.number + right.as.number); - return FL_OK; -} - -fl_status fl_sub(fl_ctx *ctx, fl_value left, fl_value right, fl_value *out, fl_error *error) { - FL_TRY(fl_numbers(ctx, "sub", left, right, error)); - *out = fl_number(left.as.number - right.as.number); - return FL_OK; -} - -fl_status fl_mul(fl_ctx *ctx, fl_value left, fl_value right, fl_value *out, fl_error *error) { - FL_TRY(fl_numbers(ctx, "mul", left, right, error)); - *out = fl_number(left.as.number * right.as.number); - return FL_OK; -} - -/* Деление на ноль даёт Infinity — это значение IEEE-754, а не ошибка. */ -fl_status fl_div(fl_ctx *ctx, fl_value left, fl_value right, fl_value *out, fl_error *error) { - FL_TRY(fl_numbers(ctx, "div", left, right, error)); - *out = fl_number(left.as.number / right.as.number); - return FL_OK; -} - -fl_status fl_mod(fl_ctx *ctx, fl_value left, fl_value right, fl_value *out, fl_error *error) { - FL_TRY(fl_numbers(ctx, "mod", left, right, error)); - /* Оператор % в JS для чисел — это fmod: знак от делимого, без округления. */ - *out = fl_number(fmod(left.as.number, right.as.number)); - return FL_OK; -} - -/* Порядок операций ядра: (процент / 100) * значение. Переставить множители - нельзя — меняется последний бит мантиссы. */ -fl_status fl_percent(fl_ctx *ctx, fl_value left, fl_value right, fl_value *out, fl_error *error) { - FL_TRY(fl_numbers(ctx, "percent", left, right, error)); - *out = fl_number((left.as.number / 100.0) * right.as.number); - return FL_OK; -} - -fl_status fl_gt(fl_ctx *ctx, fl_value left, fl_value right, fl_value *out, fl_error *error) { - FL_TRY(fl_order(ctx, left, right, error)); - *out = fl_flag(left.as.number > right.as.number); - return FL_OK; -} - -fl_status fl_lt(fl_ctx *ctx, fl_value left, fl_value right, fl_value *out, fl_error *error) { - FL_TRY(fl_order(ctx, left, right, error)); - *out = fl_flag(left.as.number < right.as.number); - return FL_OK; -} - -fl_status fl_gte(fl_ctx *ctx, fl_value left, fl_value right, fl_value *out, fl_error *error) { - FL_TRY(fl_order(ctx, left, right, error)); - *out = fl_flag(left.as.number >= right.as.number); - return FL_OK; -} - -fl_status fl_lte(fl_ctx *ctx, fl_value left, fl_value right, fl_value *out, fl_error *error) { - FL_TRY(fl_order(ctx, left, right, error)); - *out = fl_flag(left.as.number <= right.as.number); - return FL_OK; -} - -/* - * Слипнутся ли на стыке два знака в один. Правая строка, начинающаяся октетом - * продолжения, прирастает к последнему знаку левой: два знака на входе, один на - * выходе, и `длина (соединить а с б)` перестала бы равняться сумме длин. - * Показать разницу представление не умеет, поэтому склейка ОТКАЗЫВАЕТ — тем же - * видом отказа и по той же причине, по какой отказывает склейка половин - * суррогатной пары у целей с UTF-16 (SPEC, раздел 5). - */ -static bool fl_shov_sliyaet_s(bool est_levoe, fl_value right) { - return est_levoe && right.as.string.bytes > 0 && - ((unsigned char)right.as.string.utf8[0] & 0xC0u) == 0x80u; -} - -static bool fl_shov_sliyaet(fl_value left, fl_value right) { - return fl_shov_sliyaet_s(left.as.string.bytes > 0, right); -} - -static fl_status fl_join_two(fl_ctx *ctx, fl_value left, fl_value right, fl_value *out, fl_error *error) { - char *data = NULL; - const size_t bytes = left.as.string.bytes + right.as.string.bytes; - if (fl_shov_sliyaet(left, right)) { - return fl_fail(ctx, error, FL_CODE_BUILTIN_ARGS, "%s", - "«соединить»: на стыке октет продолжения прирос бы к последнему знаку левой " - "строки — два знака слились бы в один"); - } - /* Склейка копирует оба слагаемых целиком: столько октетов и стоит. */ - fl_charge(ctx, bytes); - data = (char *)fl_arena_alloc(ctx->arena, bytes + 1); - if (data == NULL) { - return fl_no_memory(error); - } - if (left.as.string.bytes > 0) { - memcpy(data, left.as.string.utf8, left.as.string.bytes); - } - if (right.as.string.bytes > 0) { - memcpy(data + left.as.string.bytes, right.as.string.utf8, right.as.string.bytes); - } - data[bytes] = '\0'; - *out = fl_text_borrow(data, bytes, left.as.string.points + right.as.string.points); - return FL_OK; -} - -fl_status fl_concat(fl_ctx *ctx, fl_value left, fl_value right, fl_value *out, fl_error *error) { - if (left.tag != FL_STRING || right.tag != FL_STRING) { - const char *left_name = fl_type_name(ctx, left); - return fl_fail(ctx, error, FL_CODE_TYPE, "«соединить» допустимо только для строк, получено %s и %s", - left_name, fl_type_name(ctx, right)); - } - return fl_join_two(ctx, left, right, out, error); -} - -/* ═════════════════════════ проверка аргументов ═════════════════════════ */ - -static fl_status fl_expect_string(fl_ctx *ctx, const char *name, fl_value value, const char *role, - fl_error *error) { - if (value.tag != FL_STRING) { - return fl_fail(ctx, error, FL_CODE_BUILTIN_ARGS, "«%s»: %s должна быть строкой, получено %s", name, role, - fl_type_name(ctx, value)); - } - return FL_OK; -} - -static fl_status fl_expect_number(fl_ctx *ctx, const char *name, fl_value value, const char *role, - fl_error *error) { - if (value.tag != FL_NUMBER) { - return fl_fail(ctx, error, FL_CODE_BUILTIN_ARGS, "«%s»: %s должно быть числом, получено %s", name, role, - fl_type_name(ctx, value)); - } - return FL_OK; -} - -static fl_status fl_expect_integer(fl_ctx *ctx, const char *name, fl_value value, const char *role, - fl_error *error) { - char text[FL_NUMBER_TEXT_MAX]; - FL_TRY(fl_expect_number(ctx, name, value, role, error)); - /* Number.isInteger: конечное и без дробной части. */ - if (!isfinite(value.as.number) || floor(value.as.number) != value.as.number) { - fl_number_text(value.as.number, text); - return fl_fail(ctx, error, FL_CODE_BUILTIN_ARGS, "«%s»: %s должно быть целым числом, получено %s", name, - role, text); - } - return FL_OK; -} - -static fl_status fl_expect_list(fl_ctx *ctx, const char *name, fl_value value, const char *role, - fl_error *error) { - if (value.tag != FL_LIST) { - return fl_fail(ctx, error, FL_CODE_BUILTIN_ARGS, "«%s»: %s должен быть списком, получено %s", name, role, - fl_type_name(ctx, value)); - } - return FL_OK; -} - -/* ═════════════════════════ встроенные формы ═════════════════════════ */ - -fl_status fl_b_dlina(fl_ctx *ctx, fl_value value, fl_value *out, fl_error *error) { - if (value.tag == FL_STRING) { - *out = fl_number((double)value.as.string.points); - return FL_OK; - } - if (value.tag == FL_LIST) { - *out = fl_number((double)value.as.list.count); - return FL_OK; - } - return fl_fail(ctx, error, FL_CODE_BUILTIN_ARGS, "«длина»: ожидается строка или список, получено %s", - fl_type_name(ctx, value)); -} - -fl_status fl_b_simvol(fl_ctx *ctx, fl_value index, fl_value text, fl_value *out, fl_error *error) { - double at = 0.0; - size_t start = 0; - size_t stop = 0; - FL_TRY(fl_expect_integer(ctx, "символ", index, "индекс", error)); - FL_TRY(fl_expect_string(ctx, "символ", text, "строка", error)); - at = index.as.number - (double)FL_INDEX_BASE; - if (at < 0.0 || at >= (double)text.as.string.points) { - char number[FL_NUMBER_TEXT_MAX]; - fl_number_text(index.as.number, number); - return fl_fail(ctx, error, FL_CODE_BUILTIN_ARGS, "«символ»: индекс %s вне строки длиной %lu", number, - (unsigned long)text.as.string.points); - } - /* `fl_utf8_offset` идёт по знакам от начала: до N-го знака ровно N шагов. */ - fl_charge(ctx, (size_t)at + 1); - start = fl_utf8_offset(text.as.string.utf8, text.as.string.bytes, (size_t)at); - stop = fl_utf8_offset(text.as.string.utf8, text.as.string.bytes, (size_t)at + 1); - *out = fl_text_borrow(text.as.string.utf8 + start, stop - start, 1); - return FL_OK; -} - -fl_status fl_b_podstroka(fl_ctx *ctx, fl_value text, fl_value from, fl_value to, fl_value *out, - fl_error *error) { - double start = 0.0; - double end = 0.0; - size_t first = 0; - size_t last = 0; - FL_TRY(fl_expect_string(ctx, "подстрока", text, "строка", error)); - FL_TRY(fl_expect_integer(ctx, "подстрока", from, "начало", error)); - FL_TRY(fl_expect_integer(ctx, "подстрока", to, "конец", error)); - start = from.as.number - (double)FL_INDEX_BASE; - end = to.as.number; - if (start < 0.0 || start > (double)text.as.string.points) { - char number[FL_NUMBER_TEXT_MAX]; - fl_number_text(from.as.number, number); - return fl_fail(ctx, error, FL_CODE_BUILTIN_ARGS, "«подстрока»: начало %s вне строки длиной %lu", number, - (unsigned long)text.as.string.points); - } - if (end < start || end > (double)text.as.string.points) { - char first_text[FL_NUMBER_TEXT_MAX]; - char last_text[FL_NUMBER_TEXT_MAX]; - fl_number_text(to.as.number, last_text); - fl_number_text(from.as.number, first_text); - return fl_fail(ctx, error, FL_CODE_BUILTIN_ARGS, "«подстрока»: конец %s вне диапазона [%s, %lu]", - last_text, first_text, (unsigned long)text.as.string.points); - } - /* Оба края ищутся обходом от начала строки; дальний и определяет цену. */ - fl_charge(ctx, (size_t)end); - first = fl_utf8_offset(text.as.string.utf8, text.as.string.bytes, (size_t)start); - last = fl_utf8_offset(text.as.string.utf8, text.as.string.bytes, (size_t)end); - *out = fl_text_borrow(text.as.string.utf8 + first, last - first, (size_t)(end - start)); - return FL_OK; -} - -fl_status fl_b_soedinit(fl_ctx *ctx, fl_value left, fl_value right, fl_value *out, fl_error *error) { - /* Две формы: «соединить строку с строкой» и «соединить список с - разделителем». Различаем по типу первого аргумента — как builtins.mjs. */ - if (left.tag == FL_LIST) { - size_t index = 0; - size_t bytes = 0; - size_t points = 0; - char *data = NULL; - size_t offset = 0; - bool est_levoe = false; - FL_TRY(fl_expect_string(ctx, "соединить", right, "разделитель", error)); - for (index = 0; index < left.as.list.count; index += 1) { - const fl_value item = left.as.list.items[index]; - if (item.tag != FL_STRING) { - return fl_fail(ctx, error, FL_CODE_BUILTIN_ARGS, - "«соединить»: элемент %lu списка должен быть строкой, получено %s", - (unsigned long)(index + 1), fl_type_name(ctx, item)); - } - /* Проверяется КАЖДЫЙ стык, и разделитель — такой же стык, как элемент: - иначе сумма длин перестала бы быть длиной склейки посередине списка. */ - if (index > 0 && fl_shov_sliyaet_s(est_levoe, right)) { - return fl_fail(ctx, error, FL_CODE_BUILTIN_ARGS, "%s", - "«соединить»: на стыке октет продолжения прирос бы к последнему знаку " - "левой строки — два знака слились бы в один"); - } - if (index > 0 && right.as.string.bytes > 0) { - est_levoe = true; - } - if (fl_shov_sliyaet_s(est_levoe, item)) { - return fl_fail(ctx, error, FL_CODE_BUILTIN_ARGS, "%s", - "«соединить»: на стыке октет продолжения прирос бы к последнему знаку " - "левой строки — два знака слились бы в один"); - } - if (item.as.string.bytes > 0) { - est_levoe = true; - } - bytes += item.as.string.bytes; - points += item.as.string.points; - } - if (left.as.list.count > 1) { - bytes += right.as.string.bytes * (left.as.list.count - 1); - points += right.as.string.points * (left.as.list.count - 1); - } - /* Два прохода: по элементам (проверка стыков и сумма длин) и по октетам - (копия). Заряд снимается один раз, за оба. */ - fl_charge(ctx, left.as.list.count + bytes); - data = (char *)fl_arena_alloc(ctx->arena, bytes + 1); - if (data == NULL) { - return fl_no_memory(error); - } - for (index = 0; index < left.as.list.count; index += 1) { - const fl_value item = left.as.list.items[index]; - if (index > 0 && right.as.string.bytes > 0) { - memcpy(data + offset, right.as.string.utf8, right.as.string.bytes); - offset += right.as.string.bytes; - } - if (item.as.string.bytes > 0) { - memcpy(data + offset, item.as.string.utf8, item.as.string.bytes); - offset += item.as.string.bytes; - } - } - data[offset] = '\0'; - *out = fl_text_borrow(data, offset, points); - return FL_OK; - } - FL_TRY(fl_expect_string(ctx, "соединить", left, "первая строка", error)); - FL_TRY(fl_expect_string(ctx, "соединить", right, "вторая строка", error)); - return fl_join_two(ctx, left, right, out, error); -} - -/* - * Поиск подстроки — ПО ЗНАКАМ, а не по октетам. - * - * Здесь стояло «UTF-8 самосинхронизирующийся, ложных срабатываний нет», и это - * верно ровно до тех пор, пока обе строки — правильный UTF-8. Правильности - * никто не обещал: октеты приезжают снаружи (`--args`, файл, процесс, кусок - * TCP), и тогда байтовый поиск находит вхождение ПОСРЕДИ знака. Замер на - * стволе: «мама» содержит октеты BC D0 — «да», хотя ни один знак «мамы» им не - * равен и ни одна подстрока его не даёт; «мама» начинается с октета D0 — «да», - * хотя её первый знак «м»; «разделить» резало четырёхзначную «маму» на пять - * кусков по половинке знака. - * - * Лекарство то же, каким целям с UTF-16 запретили резать суррогатную пару - * (`$isBoundary` в печати JS): совпадение засчитывается, только если оба его - * края стоят на границе знака. Два сравнения октета на найденное вхождение — - * дешевле, чем обход строки, и включаются они только после удачного memcmp. - */ -static const char *fl_find(const char *haystack, size_t haystack_bytes, const char *needle, - size_t needle_bytes, size_t *scanned) { - size_t index = 0; - if (scanned != NULL) { - *scanned = 0; - } - if (needle_bytes == 0) { - return haystack; - } - if (needle_bytes > haystack_bytes) { - return NULL; - } - for (index = 0; index + needle_bytes <= haystack_bytes; index += 1) { - if (scanned != NULL) { - /* Цена — не длина стога, а пройденная его часть: на раннем совпадении - поиск обрывается, и заряжать за весь стог значило бы врать вверх. */ - *scanned = index + 1; - } - if (memcmp(haystack + index, needle, needle_bytes) == 0 && - fl_utf8_starts(haystack, index) && - fl_utf8_boundary(haystack, haystack_bytes, index + needle_bytes)) { - return haystack + index; - } - } - return NULL; -} - -/* Стоит ли разделитель на этом месте — та же мера, что у «содержит»: оба края - вхождения обязаны стоять на границе знака, иначе кусок начинался бы половиной - знака. Без этого «разделить» резало «маму» по одинокому октету D0 на пять - кусков вместо одного. */ -static bool fl_razdelit_zdes(fl_value text, fl_value separator, size_t index) { - return memcmp(text.as.string.utf8 + index, separator.as.string.utf8, separator.as.string.bytes) == 0 && - fl_utf8_starts(text.as.string.utf8, index) && - fl_utf8_boundary(text.as.string.utf8, text.as.string.bytes, index + separator.as.string.bytes); -} - -/* Само разделение, без единой проверки: обе внешние формы («разделить» и её - доказанный путь) проверяют своё и зовут это. Общее тело здесь потому, что - расхождение двух копий алгоритма было бы расхождением ОТВЕТА, а не только - сторожа. */ -static fl_status fl_razdelit_kuski(fl_ctx *ctx, fl_value text, fl_value separator, fl_value *out, fl_error *error) { - size_t count = 1; - size_t index = 0; - size_t start = 0; - fl_value *items = NULL; - - /* Строка читается ДВАЖДЫ: сперва счёт кусков, потом их нарезка. Заряд снят - за оба прохода сразу — числа витков это не меняет, а места экономит. */ - fl_charge(ctx, text.as.string.bytes * 2); - for (index = 0; index + separator.as.string.bytes <= text.as.string.bytes;) { - if (fl_razdelit_zdes(text, separator, index)) { - count += 1; - index += separator.as.string.bytes; - } else { - index += 1; - } - } - - FL_TRY(fl_list_alloc(ctx, count, &items, error)); - count = 0; - start = 0; - for (index = 0; index + separator.as.string.bytes <= text.as.string.bytes;) { - if (fl_razdelit_zdes(text, separator, index)) { - const char *piece = text.as.string.utf8 + start; - const size_t bytes = index - start; - items[count] = fl_text_borrow(piece, bytes, fl_utf8_points(piece, bytes)); - count += 1; - index += separator.as.string.bytes; - start = index; - } else { - index += 1; - } - } - { - const char *piece = text.as.string.utf8 + start; - const size_t bytes = text.as.string.bytes - start; - items[count] = fl_text_borrow(piece, bytes, fl_utf8_points(piece, bytes)); - count += 1; - } - *out = fl_list(items, count); - return FL_OK; -} - -fl_status fl_b_razdelit(fl_ctx *ctx, fl_value text, fl_value separator, fl_value *out, fl_error *error) { - FL_TRY(fl_expect_string(ctx, "разделить", text, "строка", error)); - FL_TRY(fl_expect_string(ctx, "разделить", separator, "разделитель", error)); - if (separator.as.string.bytes == 0) { - return fl_fail(ctx, error, FL_CODE_BUILTIN_ARGS, "%s", "«разделить»: разделитель не может быть пустым"); - } - return fl_razdelit_kuski(ctx, text, separator, out, error); -} - -fl_status fl_b_razdelit_dokazano(fl_ctx *ctx, fl_value text, fl_value separator, fl_value *out, fl_error *error) { - FL_TRY(fl_expect_string(ctx, "разделить", text, "строка", error)); - FL_TRY(fl_expect_string(ctx, "разделить", separator, "разделитель", error)); - return fl_razdelit_kuski(ctx, text, separator, out, error); -} - -/* - * Разложение строки в список односимвольных строк — по кодовым точкам. - * - * Куски не копируются: каждая строка списка одалживает байты исходной - * (`fl_text_borrow`), потому что исходная строка живёт в той же арене и до - * конца вызова никуда не денется. Список из n символов стоит поэтому одного - * выделения на массив, а не n выделений на строки. - * - * Пустая строка даёт пустой список — так же, как в свидетеле. - */ -fl_status fl_b_simvoly(fl_ctx *ctx, fl_value text, fl_value *out, fl_error *error) { - size_t count = 0; - size_t index = 0; - size_t start = 0; - fl_value *items = NULL; - FL_TRY(fl_expect_string(ctx, "символы", text, "строка", error)); - - /* Тоже два прохода по октетам: счёт кодовых точек и нарезка по ведущим. */ - fl_charge(ctx, text.as.string.bytes * 2); - count = fl_utf8_points(text.as.string.utf8, text.as.string.bytes); - if (count == 0) { - *out = fl_list(NULL, 0); - return FL_OK; - } - - FL_TRY(fl_list_alloc(ctx, count, &items, error)); - count = 0; - /* Ведущий байт кодовой точки — тот, у которого старшие два бита не 10. - Встретив следующий ведущий, закрываем предыдущий символ. */ - for (index = 1; index <= text.as.string.bytes; index += 1) { - const bool конец = index == text.as.string.bytes; - const bool ведущий = - !конец && ((unsigned char)text.as.string.utf8[index] & 0xC0u) != 0x80u; - if (конец || ведущий) { - items[count] = fl_text_borrow(text.as.string.utf8 + start, index - start, 1); - count += 1; - start = index; - } - } - *out = fl_list(items, count); - return FL_OK; -} - -/* «код символа»: кодовая точка первого символа строки. - - Декодер уже есть — тот же `fl_utf8_decode`, каким считаются пробелы в «к - числу»; байт utf8[0] отдал бы первую восьмёрку бит, а не символ. */ -fl_status fl_b_kod_simvola(fl_ctx *ctx, fl_value text, fl_value *out, fl_error *error) { - size_t width = 0; - FL_TRY(fl_expect_string(ctx, "код символа", text, "строка", error)); - if (text.as.string.bytes == 0) { - return fl_fail(ctx, error, FL_CODE_BUILTIN_ARGS, "%s", "«код символа»: строка пуста"); - } - *out = fl_number( - (double)fl_utf8_decode(text.as.string.utf8, text.as.string.bytes, 0, &width)); - return FL_OK; -} - -fl_status fl_b_kod_simvola_dokazano(fl_ctx *ctx, fl_value text, fl_value *out, fl_error *error) { - size_t width = 0; - FL_TRY(fl_expect_string(ctx, "код символа", text, "строка", error)); - *out = fl_number( - (double)fl_utf8_decode(text.as.string.utf8, text.as.string.bytes, 0, &width)); - return FL_OK; -} - -/* «символ по коду»: строка ровно из одного символа. - * - * Обратной к `fl_utf8_decode` в рантайме не было, и здесь она выписана — четыре - * ветки по длине записи, ровно те же границы, по которым декодер читает. Больше - * ей нигде не нужно, поэтому она стоит внутри формы, а не рядом с декодером. - * - * Суррогат отвергается ДО кодирования. Записать его в UTF-8 технически можно — - * три байта лягут, — но это была бы не UTF-8, а WTF-8, и строка перестала бы - * быть тем, чем её объявляет `flang_runtime.h`: «строка — UTF-8, длина в байтах - * и в кодовых точках». Тот же отказ дают все восемь целей. - */ -fl_status fl_b_simvol_po_kodu(fl_ctx *ctx, fl_value code, fl_value *out, fl_error *error) { - char buffer[4]; - size_t bytes = 0; - unsigned long point = 0; - FL_TRY(fl_expect_integer(ctx, "символ по коду", code, "код", error)); - if (code.as.number < 0.0 || code.as.number > 1114111.0) { - char number[FL_NUMBER_TEXT_MAX]; - fl_number_text(code.as.number, number); - return fl_fail(ctx, error, FL_CODE_BUILTIN_ARGS, - "«символ по коду»: код %s вне диапазона Unicode [0, 1114111]", number); - } - if (code.as.number >= 55296.0 && code.as.number <= 57343.0) { - char number[FL_NUMBER_TEXT_MAX]; - fl_number_text(code.as.number, number); - return fl_fail(ctx, error, FL_CODE_BUILTIN_ARGS, - "«символ по коду»: код %s — половина суррогатной пары, а не символ", number); - } - point = (unsigned long)code.as.number; - if (point < 0x80ul) { - buffer[0] = (char)point; - bytes = 1; - } else if (point < 0x800ul) { - buffer[0] = (char)(0xC0ul | (point >> 6)); - buffer[1] = (char)(0x80ul | (point & 0x3Ful)); - bytes = 2; - } else if (point < 0x10000ul) { - buffer[0] = (char)(0xE0ul | (point >> 12)); - buffer[1] = (char)(0x80ul | ((point >> 6) & 0x3Ful)); - buffer[2] = (char)(0x80ul | (point & 0x3Ful)); - bytes = 3; - } else { - buffer[0] = (char)(0xF0ul | (point >> 18)); - buffer[1] = (char)(0x80ul | ((point >> 12) & 0x3Ful)); - buffer[2] = (char)(0x80ul | ((point >> 6) & 0x3Ful)); - buffer[3] = (char)(0x80ul | (point & 0x3Ful)); - bytes = 4; - } - return fl_text(ctx, buffer, bytes, out, error); -} - -fl_status fl_b_soderzhit(fl_ctx *ctx, fl_value left, fl_value right, fl_value *out, fl_error *error) { - if (left.tag == FL_LIST) { - size_t index = 0; - for (index = 0; index < left.as.list.count; index += 1) { - if (fl_equal(left.as.list.items[index], right)) { - fl_charge(ctx, index + 1); - *out = fl_flag(true); - return FL_OK; - } - } - fl_charge(ctx, left.as.list.count); - *out = fl_flag(false); - return FL_OK; - } - { - size_t scanned = 0; - FL_TRY(fl_expect_string(ctx, "содержит", left, "строка или список", error)); - FL_TRY(fl_expect_string(ctx, "содержит", right, "искомая подстрока", error)); - *out = fl_flag(fl_find(left.as.string.utf8, left.as.string.bytes, right.as.string.utf8, - right.as.string.bytes, &scanned) != NULL); - fl_charge(ctx, scanned); - } - return FL_OK; -} - -fl_status fl_b_nachinaetsya_s(fl_ctx *ctx, fl_value text, fl_value prefix, fl_value *out, fl_error *error) { - FL_TRY(fl_expect_string(ctx, "начинается с", text, "строка", error)); - FL_TRY(fl_expect_string(ctx, "начинается с", prefix, "префикс", error)); - /* Начало у префикса и у строки одно, поэтому левый край на границе знака - всегда; сторожится правый — иначе «мама» начиналась бы с одинокого октета - D0, то есть с половины своего первого знака. */ - /* Сравнивается ровно префикс, дальше строка не читается. */ - fl_charge(ctx, prefix.as.string.bytes); - *out = fl_flag(prefix.as.string.bytes <= text.as.string.bytes && - (prefix.as.string.bytes == 0 || - (memcmp(text.as.string.utf8, prefix.as.string.utf8, prefix.as.string.bytes) == 0 && - fl_utf8_boundary(text.as.string.utf8, text.as.string.bytes, prefix.as.string.bytes)))); - return FL_OK; -} - -/* - * «к числу» — строгий разбор. Регулярное выражение интерпретатора - * /^[+-]?(\d+(\.\d+)?|\.\d+)([eE][+-]?\d+)?$/u развёрнуто в проверку вручную: - * тянуть regex.h ради одной строки нельзя (POSIX-регулярки не знают ни \d, - * ни якорей в том же смысле), а «просто strtod» пропустил бы «0x10», «Infinity» - * и «12abc» — то есть молча превратил бы мусор в значение. - */ -static bool fl_looks_like_number(const char *text, size_t bytes) { - size_t index = 0; - size_t digits = 0; - if (bytes == 0) { - return false; - } - if (text[0] == '+' || text[0] == '-') { - index = 1; - } - while (index < bytes && text[index] >= '0' && text[index] <= '9') { - index += 1; - digits += 1; - } - if (index < bytes && text[index] == '.') { - size_t fraction = 0; - index += 1; - while (index < bytes && text[index] >= '0' && text[index] <= '9') { - index += 1; - fraction += 1; - } - /* «1.» недопустимо: у целой части обязана быть дробная, если точка есть. */ - if (fraction == 0) { - return false; - } - digits += fraction; - } - if (digits == 0) { - return false; - } - if (index < bytes && (text[index] == 'e' || text[index] == 'E')) { - size_t exponent = 0; - index += 1; - if (index < bytes && (text[index] == '+' || text[index] == '-')) { - index += 1; - } - while (index < bytes && text[index] >= '0' && text[index] <= '9') { - index += 1; - exponent += 1; - } - if (exponent == 0) { - return false; - } - } - return index == bytes; -} - -fl_status fl_b_k_chislu(fl_ctx *ctx, fl_value text, fl_value *out, fl_error *error) { - size_t start = 0; - size_t stop = 0; - size_t width = 0; - char *copy = NULL; - double number = 0.0; - FL_TRY(fl_expect_string(ctx, "к числу", text, "строка", error)); - - /* Пробелы с краёв и сама проверка вида числа читают строку целиком. */ - fl_charge(ctx, text.as.string.bytes); - stop = text.as.string.bytes; - while (start < stop) { - const unsigned long code = fl_utf8_decode(text.as.string.utf8, stop, start, &width); - if (!fl_js_space(code)) { - break; - } - start += width; - } - while (stop > start) { - /* Отступаем на начало последней кодовой точки. */ - size_t back = stop - 1; - while (back > start && ((unsigned char)text.as.string.utf8[back] & 0xC0u) == 0x80u) { - back -= 1; - } - if (!fl_js_space(fl_utf8_decode(text.as.string.utf8, stop, back, &width))) { - break; - } - stop = back; - } - - if (!fl_looks_like_number(text.as.string.utf8 + start, stop - start)) { - return fl_fail(ctx, error, FL_CODE_BUILTIN_ARGS, "«к числу»: строка %s не является числом", - fl_quoted(ctx, text)); - } - copy = (char *)fl_arena_alloc(ctx->arena, (stop - start) + 1); - if (copy == NULL) { - return fl_no_memory(error); - } - memcpy(copy, text.as.string.utf8 + start, stop - start); - copy[stop - start] = '\0'; - errno = 0; - number = strtod(copy, NULL); - if (!isfinite(number)) { - return fl_fail(ctx, error, FL_CODE_BUILTIN_ARGS, "«к числу»: строка %s не является конечным числом", - fl_quoted(ctx, text)); - } - *out = fl_number(number); - return FL_OK; -} - -/* - * Отказ «к числу», ставший значением (builtins.mjs, «отказ, ставший значением»). - * - * Разбор не повторяется: он один и тот же, и вызывается прямо здесь. Иначе - * пришлось бы держать вторую копию правил разбора и второй набор текстов, и - * первое же расхождение оказалось бы незамеченным — сравнивать было бы не с - * чем. Здесь же текст отказа приходит ровно из `fl_b_k_chislu`. - * - * `inner` — свой fl_error, а не тот, что дан вызывающим: отказ разбора наружу - * не идёт вовсе, эта форма отказать не может. Сообщение живёт в арене (его - * строит fl_vformat), поэтому borrow-строкой брать его безопасно — арена та же. - */ -fl_status fl_b_k_chislu_ili_beda(fl_ctx *ctx, fl_value text, fl_value *out, fl_error *error) { - static const char *const parsed_names[] = {"значение"}; - static const char *const failed_names[] = {"код", "сообщение"}; - fl_error inner; - fl_value number = fl_nothing(); - fl_value failure[2]; - inner.code = NULL; - inner.message = NULL; - if (fl_b_k_chislu(ctx, text, &number, &inner) == FL_OK) { - return fl_variant_new(ctx, "Разобрано", parsed_names, &number, 1, out, error); - } - failure[0] = fl_text_static(inner.code == NULL ? FL_CODE_BUILTIN_ARGS : inner.code); - failure[1] = fl_text_static(inner.message == NULL ? "" : inner.message); - return fl_variant_new(ctx, "Не разобрано", failed_names, failure, 2, out, error); -} - -fl_status fl_b_k_stroke(fl_ctx *ctx, fl_value value, fl_value *out, fl_error *error) { - char text[FL_NUMBER_TEXT_MAX]; - switch (value.tag) { - case FL_STRING: - *out = value; - return FL_OK; - case FL_NUMBER: { - /* Кратчайшая запись ищется пробами: snprintf плюс strtod на пробу. Это - самая дорогая встроенная работа на один вызов, и до сих пор она шла - счётчику бесплатно. */ - size_t probes = 0; - fl_number_text_cost(value.as.number, text, &probes); - fl_charge(ctx, probes); - return fl_text(ctx, text, strlen(text), out, error); - } - case FL_FLAG: - /* Признак печатается по-русски: поверхность языка знает «да» и «нет». */ - *out = fl_text_static(value.as.flag ? "да" : "нет"); - return FL_OK; - case FL_NOTHING: - *out = fl_text_static("ничто"); - return FL_OK; - default: - return fl_fail(ctx, error, FL_CODE_BUILTIN_ARGS, "«к строке»: ожидается скаляр, получено %s", - fl_type_name(ctx, value)); - } -} - -fl_status fl_b_pusto(fl_ctx *ctx, fl_value value, fl_value *out, fl_error *error) { - if (value.tag == FL_LIST) { - *out = fl_flag(value.as.list.count == 0); - return FL_OK; - } - if (value.tag == FL_STRING) { - *out = fl_flag(value.as.string.points == 0); - return FL_OK; - } - return fl_fail(ctx, error, FL_CODE_BUILTIN_ARGS, "«пусто»: ожидается строка или список, получено %s", - fl_type_name(ctx, value)); -} - -fl_status fl_b_golova(fl_ctx *ctx, fl_value value, fl_value *out, fl_error *error) { - FL_TRY(fl_expect_list(ctx, "голова", value, "аргумент", error)); - if (value.as.list.count == 0) { - return fl_fail(ctx, error, FL_CODE_BUILTIN_ARGS, "%s", "«голова»: список пуст"); - } - *out = value.as.list.items[0]; - return FL_OK; -} - -fl_status fl_b_golova_dokazano(fl_ctx *ctx, fl_value value, fl_value *out, fl_error *error) { - FL_TRY(fl_expect_list(ctx, "голова", value, "аргумент", error)); - *out = value.as.list.items[0]; - return FL_OK; -} - -/* - * «элемент N в СПИСОК»: обращение к массиву, без обхода. - * - * Список в C — указатель на массив плюс счётчик (`fl_value.as.list`), поэтому - * N-й элемент стоит одного сложения — столько же, сколько первый. Проверка - * границ и текст отказа повторяют вычислитель дословно: они сверяются - * дифференциально, и «похоже» тут не годится. - */ -fl_status fl_b_element(fl_ctx *ctx, fl_value index, fl_value list, fl_value *out, fl_error *error) { - double at = 0.0; - FL_TRY(fl_expect_integer(ctx, "элемент", index, "индекс", error)); - FL_TRY(fl_expect_list(ctx, "элемент", list, "список", error)); - at = index.as.number - (double)FL_INDEX_BASE; - if (at < 0.0 || at >= (double)list.as.list.count) { - char number[FL_NUMBER_TEXT_MAX]; - fl_number_text(index.as.number, number); - return fl_fail(ctx, error, FL_CODE_BUILTIN_ARGS, "«элемент»: индекс %s вне списка длиной %lu", number, - (unsigned long)list.as.list.count); - } - *out = list.as.list.items[(size_t)at]; - return FL_OK; -} - -fl_status fl_b_hvost(fl_ctx *ctx, fl_value value, fl_value *out, fl_error *error) { - FL_TRY(fl_expect_list(ctx, "хвост", value, "аргумент", error)); - if (value.as.list.count == 0) { - return fl_fail(ctx, error, FL_CODE_BUILTIN_ARGS, "%s", "«хвост»: список пуст"); - } - *out = fl_list_slice(value, 1); - return FL_OK; -} - -fl_status fl_b_hvost_dokazano(fl_ctx *ctx, fl_value value, fl_value *out, fl_error *error) { - FL_TRY(fl_expect_list(ctx, "хвост", value, "аргумент", error)); - *out = fl_list_slice(value, 1); - return FL_OK; -} - -/* - * ── «добавить»: удлинение списка за постоянное время ─────────────────────── - * - * Прямая запись — выделить n+1 значений и скопировать n — верна, но в арене, - * которая ничего не отдаёт до конца вызова, стоит ~16·n² байт на список, - * собранный n вызовами: живёт не только последняя копия, а все. Под сборщиком - * мусора этого не видно (`[...list, x]` в JS копирует ровно так же, но - * промежуточные копии умирают сразу), здесь — видно: лексер компилятора на - * 35 тысячах токенов требовал 19 ГБ. - * - * ── Почему одного `fl_arena_extend` мало ─────────────────────────────────── - * Напрашивается решение проще: раз арена умеет продлить последнюю выдачу, - * пусть «добавить» её и продлевает. Померено — не работает, и вот числа - * (20 000 «добавить», байт арены на элемент): - * - * между «добавить» ничего не выделяется: 321 018 → 316 691 (−1.3%) - * между «добавить» выделяется значение: 379 882 → 379 882 (0%) - * - * Две причины, и обе неустранимы в рамках «продлить последнее». Первая — - * последним оно почти никогда не бывает: список из чего-то состоит, и это - * что-то строится перед тем, как попасть в список. Ровно так работает лексер, - * и ровно поэтому вторая строка не сдвинулась ни на байт. Вторая — как только - * массив перерастает кусок арены (64 КБ, то есть 2048 значений), он получает - * кусок ровно по себе, и продлевать его некуда; поэтому и первая строка дала - * процент, а не порядок. - * - * Работает другое: не продлевать по одной ячейке в конце, а брать запас - * заранее — тогда между двумя «добавить» арена может выдать сколько угодно - * чужого, ячейка всё равно наша. Продление на месте осталось, но как приятное - * дополнение к запасу, а не как замена ему. - * - * ── Как это не ломает неизменяемость ─────────────────────────────────────── - * Массив выделяется с запасом, а «добавить» пишет в запас на месте. - * Всё держится на одном инварианте, и он же — доказательство неизменяемости: - * - * у массива с запасом есть общая на всех запись `fl_grow`, и занятая часть - * массива в ней — полуинтервал `[head, filled)`: `filled` только растёт, - * `head` только убывает. - * - * Занять ячейку `filled` разрешено единственному — тому списку, который в этом - * массиве кончается ровно на ней (`начало + длина == filled`); после записи - * `filled` становится больше на один. Ячейку `head − 1` занимает «приписать», и - * тоже единственному — тому, кто в этом массиве НАЧИНАЕТСЯ ровно на `head`. - * Значит: - * - * • ячейка за концом списка пишется не более одного раза за всю жизнь - * арены — второе «добавить» к тому же значению видит `filled` дальше своего - * конца и уходит на копию; то же у «приписать» с `head`; - * • ячейки `head…filled−1` не трогаются вовсе. - * - * Отсюда старый список после «добавить» — тот же самый: та же длина, те же - * элементы. Разветвление - * - * пусть «а» равно (добавить 1 к «с») ← занимает ячейку n, filled = n+1 - * пусть «б» равно (добавить 2 к «с») ← count = n ≠ filled → копия - * - * даёт два независимых списка, и ни один не портит «с». - * - * Запас удваивается, поэтому за все перевыделения арена отдаёт около 4n ячеек - * вместо n²/2 — линейно по длине. А если массив ещё и последнее, что арена - * выдала, запас продлевается прямо на месте (`fl_arena_extend`), и копий нет - * вовсе: так идёт накопление в свёртке, где между двумя «добавить» не - * выделяется ничего. - */ - -/* Запас первого массива: списки чаще коротки, и платить за них страницей ни к - чему. Дальше запас равен длине, то есть удваивается. */ -#define FL_GROW_FIRST (size_t)4 - -fl_status fl_b_dobavit(fl_ctx *ctx, fl_value item, fl_value list, fl_value *out, fl_error *error) { - const size_t limit = ((size_t)-1) / sizeof(fl_value); - fl_grow *grow = NULL; - fl_value *items = NULL; - size_t count = 0; - size_t capacity = 0; - size_t start = 0; - - FL_TRY(fl_expect_list(ctx, "добавить", list, "второй аргумент", error)); - count = list.as.list.count; - grow = list.as.list.grow; - /* Начало списка внутри массива: ноль у всех, кого собрал «добавить», и - сдвинутое вперёд у тех, кого собрал «приписать». */ - if (grow != NULL) { - start = (size_t)(list.as.list.items - grow->items); - } - - /* Быстрый путь: ячейка за концом принадлежит этому массиву и свободна. */ - if (grow != NULL && start + count == grow->filled) { - bool room = grow->filled < grow->capacity; - if (!room && grow->capacity <= limit / 2) { - /* Запас исчерпан, но массив может оказаться последней выдачей арены. */ - const size_t taken = grow->capacity * sizeof(fl_value); - if (fl_arena_extend(ctx->arena, grow->items, taken, taken)) { - grow->capacity += grow->capacity; - room = true; - } - } - if (room) { - grow->items[grow->filled] = item; - grow->filled += 1; - *out = fl_list_grown(grow->items + start, count + 1, grow); - return FL_OK; - } - } - - /* Медленный путь: копия с запасом — чтобы следующие «добавить» шли на месте. - Запись запаса выделяется ПЕРЕД массивом, тогда массив остаётся последней - выдачей арены и его ещё можно будет продлить, не копируя. */ - if (count >= limit) { - return fl_no_memory(error); - } - capacity = count < FL_GROW_FIRST ? FL_GROW_FIRST : count; - if (capacity > limit - count) { - capacity = limit - count; - } - capacity += count; /* не меньше count + 1: выше count < limit */ - grow = (fl_grow *)fl_arena_alloc(ctx->arena, sizeof(fl_grow)); - if (grow == NULL) { - return fl_no_memory(error); - } - FL_TRY(fl_list_alloc(ctx, capacity, &items, error)); - if (count > 0) { - /* Заряд стоит ТОЛЬКО на медленном пути. Быстрый путь пишет одну ячейку за - концом и стоит постоянного времени — зарядить его длиной значило бы - оболгать «добавить» вверх и вернуть тот самый квадрат, ради снятия - которого запас и заведён. */ - fl_charge(ctx, count); - memcpy(items, list.as.list.items, count * sizeof(fl_value)); - } - items[count] = item; - grow->items = items; - grow->head = 0; - grow->filled = count + 1; - grow->capacity = capacity; - *out = fl_list_grown(items, count + 1, grow); - return FL_OK; -} - -/* - * ── «приписать»: то же за постоянное время, только с другого конца ───────── - * - * Приписывание в начало — зеркало «добавить», и держится оно на той же записи - * `fl_grow`: у массива есть запас СПЕРЕДИ, а `head` не даёт двум разным - * спискам занять одну и ту же ячейку перед началом. - * - * Занять ячейку `head − 1` разрешено единственному — тому списку, который - * начинается ровно на `head`; после записи `head` становится меньше на один. - * Разветвление - * - * пусть «а» равно (приписать 1 к «с») ← занимает ячейку head−1, head−− - * пусть «б» равно (приписать 2 к «с») ← начало «с» уже не равно head → копия - * - * даёт два независимых списка, и ни один не портит «с» — доказательство то же, - * что у «добавить», и другой половиной того же инварианта. - * - * ЧЕМ ЭТО ОТЛИЧАЕТСЯ ОТ «ДОБАВИТЬ». Ровно одним: продлить массив на месте - * (`fl_arena_extend`) вперёд нельзя — арена умеет отменить только последнюю - * выдачу, и растёт та в сторону старших адресов. Поэтому весь запас - * «приписать» берёт заранее: копия кладётся в КОНЕЦ свежего массива, и всё - * свободное место остаётся перед ней. Запас равен длине, то есть удваивается, - * поэтому за все перевыделения арена отдаёт около 4n ячеек вместо n²/2. - * - * ЗАЧЕМ ЭТО ВООБЩЕ. До появления формы приписывание в начало писалось - * свёрткой, дописывающей КАЖДЫЙ элемент хвоста в свежий накопитель. У неё - * своя цена — линейная по длине на каждый вызов, — и на построении списка - * спереди назад это давало квадрат там, где нужен один проход. Числа: в SPEC, - * раздел «Стоимость встроенных форм», и в комментарии над проверкой - * «приписывание в начало не стоит длины списка» (flang/test/builtins.test.mjs). - */ -fl_status fl_b_pripisat(fl_ctx *ctx, fl_value item, fl_value list, fl_value *out, fl_error *error) { - const size_t limit = ((size_t)-1) / sizeof(fl_value); - fl_grow *grow = NULL; - fl_value *items = NULL; - size_t count = 0; - size_t capacity = 0; - size_t slack = 0; - - FL_TRY(fl_expect_list(ctx, "приписать", list, "второй аргумент", error)); - count = list.as.list.count; - grow = list.as.list.grow; - - /* Быстрый путь: ячейка перед началом принадлежит этому массиву и свободна. */ - if (grow != NULL && grow->head > 0 && list.as.list.items == grow->items + grow->head) { - grow->head -= 1; - grow->items[grow->head] = item; - *out = fl_list_grown(grow->items + grow->head, count + 1, grow); - return FL_OK; - } - - /* Медленный путь: копия в конец массива, весь запас — перед ней. */ - if (count >= limit) { - return fl_no_memory(error); - } - slack = count < FL_GROW_FIRST ? FL_GROW_FIRST : count; - if (slack > limit - count) { - slack = limit - count; - } - capacity = count + slack; /* не меньше count + 1: выше count < limit, slack >= 1 */ - grow = (fl_grow *)fl_arena_alloc(ctx->arena, sizeof(fl_grow)); - if (grow == NULL) { - return fl_no_memory(error); - } - FL_TRY(fl_list_alloc(ctx, capacity, &items, error)); - if (count > 0) { - /* Как и у «добавить»: платит копия, а не запись в занятый заранее запас. */ - fl_charge(ctx, count); - memcpy(items + slack, list.as.list.items, count * sizeof(fl_value)); - } - items[slack - 1] = item; - grow->items = items; - grow->head = slack - 1; - grow->filled = capacity; - grow->capacity = capacity; - *out = fl_list_grown(items + grow->head, count + 1, grow); - return FL_OK; -} - -fl_status fl_b_ostatok_ot(fl_ctx *ctx, fl_value left, fl_value right, fl_value *out, fl_error *error) { - FL_TRY(fl_expect_number(ctx, "остаток от", left, "делимое", error)); - FL_TRY(fl_expect_number(ctx, "остаток от", right, "делитель", error)); - *out = fl_number(fmod(left.as.number, right.as.number)); - return FL_OK; -} - -fl_status fl_b_procentov_ot(fl_ctx *ctx, fl_value left, fl_value right, fl_value *out, fl_error *error) { - FL_TRY(fl_expect_number(ctx, "процентов от", left, "процент", error)); - FL_TRY(fl_expect_number(ctx, "процентов от", right, "значение", error)); - *out = fl_number((left.as.number / 100.0) * right.as.number); - return FL_OK; -} - -/* ═════════════════════════ батут ═════════════════════════ */ - -/* - * ── Область на ВИТОК батута ──────────────────────────────────────────────── - * - * Без неё батут был дырой ровно того же вида, какую у свёртки закрывает - * `fl_region_recycle`, и дырой куда крупнее. Свёртка печатается циклом C, и - * область на вызов ей не помогала; батут — ТОЖЕ цикл C, а область у него одна - * на всю раскрутку: её открывает обёртка компоненты сильной связности - * («Печать батута» в flang/self/emit-c.flang) ПЕРЕД вызовом батута и закрывает - * ПОСЛЕ. Значит мусор всех витков доживал до конца последнего. - * - * ── Улика, снятая замером, а не рассуждением ─────────────────────────────── - * Хвостовая рекурсия на самом языке, прогнанная интерпретатором: - * - * тотальная функция «Счёт» принимает «сколько», «сумма» … убывает «сколько» - * если «сколько» равен 0 то «сумма» иначе «Счёт» от («сколько» минус 1) … - * - * витков пик до пик после время до → после - * 100 000 7,99 ГиБ 0,0135 ГиБ 12,7 с → 6,9 с - * 200 000 15,98 ГиБ 0,0135 ГиБ 25,3 с → 13,5 с - * 400 000 снят пределом 20 ГиБ 0,0135 ГиБ — → 27,2 с - * - * То есть 84 килобайта на виток и рост строго линейный: считать в интерпретаторе - * миллион витков было нельзя ни на какой машине. После правки пик от числа - * витков НЕ ЗАВИСИТ ВОВСЕ — одно и то же число на всех четырёх замерах, — а - * время вдвое МЕНЬШЕ: арена перестала покупать у malloc гигабайты кусков. - * Пик снимался ядром (`memory.peak` контрольной группы), а не `ps`: тот на этих - * же прогонах занижал вдвое. - * - * Обход стека при пике `flang check flang/stdlib/scram.flang` показывал ровно - * это место: `fl_record_new` ← «Положить» ← «Вычислить пусть» ← «Шаг машины» ← - * «Виток»(шаг) ← `fl_trampoline`. Интерпретатор — сам батут, и каждый его виток - * складывал в арену новую обстановку. - * - * ── Довод законности ─────────────────────────────────────────────────────── - * Тот же, что у свёртки, и виден он прямо в цикле ниже. После отскока живы РОВНО - * `bounce.args`: значения flang неизменяемы, замыканий нет, ссылок наружу нет, а - * `buffer` перед откатом уже переписан отскоком. Входные `args` вызывающего - * лежат НИЖЕ отметки, снятой на входе в батут, и откат их не трогает. `*result` - * на витке с отскоком не читает никто: значение отдаёт тот шаг, который отскока - * не поставил, и после него отката уже нет. - * - * Отметка своя, а не обёрточная: обёртке её не передать, не поменяв подписи - * батута у всех 144 компонент. Вложенность безобидна — `fl_region_open` - * запоминает границу объемлющей области и возвращает её `fl_region_close`, - * который здесь стоит на КАЖДОМ выходе, включая отказ (на отказе он не трогает - * арену: текст диагностики лежит выше отметки). - * - * Бюджет витка — тот же `FL_REGION_LOOP_GAIN`, и по той же причине: хвостовая - * рекурсия, накапливающая список через «добавить», обязана отказаться от отката, - * иначе она потеряет хвостовой запас и выродится в копию на каждом витке. - * - * ── ЛЕСТНИЦА ПОРОГА, И ОНА ЗАМЕРЕНА, А НЕ ПРИДУМАНА ──────────────────────── - * Первая редакция звала откат на КАЖДОМ витке, как это делает свёртка. Замер - * той редакции (10 000 витков интерпретатором, 1 830 171 отскок): память - * 0,80 ГиБ → 0,0084 ГиБ, а время 2,9 с → 20,4 с, то есть в семь раз дороже. - * Счётчик показал, куда ушло: обмер обошёл 2 578 225 252 узла на 1 820 650 - * переложенных, — 1 400 узлов обмера на один отскок. - * - * Причина видна из устройства обмера: он бросает счёт, когда живое перерастает - * бюджет (наросшее/16), НО наросшее после неудачи никуда не девается, и - * следующий виток мерит то же самое ещё раз. Пока живое больше бюджета, обмер - * повторяется на каждом отскоке и каждый раз доходит до бюджета. - * - * Поэтому порог ходит лестницей: промах — вдвое выше, попадание — вдвое ниже - * сработавшего. Тогда обмер идёт геометрически редко, а порог сам садится туда, - * где откат окупается (наросшее ≈ 16 × живое), — без единой новой постоянной. - */ -fl_status fl_trampoline(fl_ctx *ctx, fl_step step, const fl_value *args, size_t count, const char *function, - fl_value *result, fl_error *error) { - fl_arena *arena = ctx == NULL ? NULL : ctx->arena; - fl_bounce bounce; - fl_value buffer[FL_MAX_TAIL_ARGS]; - fl_value carried = fl_nothing(); - fl_mark mark; - fl_status status = FL_OK; - size_t index = 0; - size_t grown = 0; - size_t before = 0; - /* Когда пробовать откат в следующий раз; про лестницу — над функцией. */ - size_t porog = (size_t)FL_REGION_MIN; - for (index = 0; index < FL_MAX_TAIL_ARGS; index += 1) { - buffer[index] = index < count ? args[index] : fl_nothing(); - } - mark = fl_region_open(ctx); - for (;;) { - bounce.next = NULL; - for (index = 0; index < FL_MAX_TAIL_ARGS; index += 1) { - bounce.args[index] = fl_nothing(); - } - status = step(ctx, buffer, &bounce, result, error); - if (status != FL_OK || bounce.next == NULL) { - break; - } - /* Отскок — виток: «Чётное»/«Нечётное» друг на друге идут в постоянной - глубине, и без этого счётчика незавершающаяся пара крутилась бы вечно. */ - status = fl_tick(ctx, function, error); - if (status != FL_OK) { - break; - } - step = bounce.next; - for (index = 0; index < FL_MAX_TAIL_ARGS; index += 1) { - buffer[index] = bounce.args[index]; - } - if (arena == NULL) { - continue; - } - grown = arena->handed - mark.handed; - if (grown < porog) { - continue; - } - /* - * Доводы витка перекладываются ОДНИМ значением: список поверх `buffer` без - * единого выделения — обмер и перекладка читают его как всякий список, а - * лежит он на стеке, то есть переживает откат по построению. - */ - before = arena->handed; - carried = fl_list(buffer, FL_MAX_TAIL_ARGS); - status = fl_region_recycle(ctx, mark, &carried, error); - if (status != FL_OK) { - break; - } - if (arena->handed < before) { - /* Откат состоялся: доводы теперь лежат копией, её и берём. Порог вниз — - вдвое ниже сработавшего, чтобы следующий цикл нашёл своё место за один - промах, а не за всю лестницу снизу. */ - if (carried.tag == FL_LIST && carried.as.list.count == FL_MAX_TAIL_ARGS) { - for (index = 0; index < FL_MAX_TAIL_ARGS; index += 1) { - buffer[index] = carried.as.list.items[index]; - } - } - porog = grown / 2 < (size_t)FL_REGION_MIN ? (size_t)FL_REGION_MIN : grown / 2; - } else { - /* Область отказалась: живого больше бюджета. Пробовать на следующем витке - снова значит платить обмером за тот же отказ — порог вверх вдвое. */ - porog = grown > ((size_t)-1) / 2 ? (size_t)-1 : grown * 2; - } - } - return fl_region_close(ctx, mark, status, result, error); -} diff --git a/core/out-c/flang_runtime.h b/core/out-c/flang_runtime.h deleted file mode 100644 index 8993e05..0000000 --- a/core/out-c/flang_runtime.h +++ /dev/null @@ -1,1002 +0,0 @@ -/* - * Сгенерировано flang (бэкенд C, flang/self/emit-c.flang). Не редактировать руками. - * Модуль flang: «Опись диска». - * Файл: рантайм: значения, арена, UTF-8, диагностики. - * Правьте исходник на flang и печатайте заново: любая правка здесь потеряется. - */ -/* Настройки этой программы; в самом рантайме те же имена объявлены через - #ifndef, поэтому он собирается и без этого блока. */ -#define FL_INDEX_BASE 1 -#define FL_MAX_DEPTH 10000 -#define FL_MAX_STEPS 1000000 -#define FL_MAX_TAIL_ARGS 1 -#define FL_MAX_ARGS 4 - -/* SPDX-FileCopyrightText: 2026 Digitable (Marat Zimnurov) */ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Рантайм flang для бэкенда C — заголовок. - * - * Этот файл печатается бэкендом как есть, байт в байт, для любой программы: - * зависящие от программы значения (база индексации строк, предел глубины) - * лежат в отдельном «flang_config.h». Так диффы между двумя сборками - * показывают изменения модели, а не шум рантайма. - * - * ── Почему рантайм вообще нужен ──────────────────────────────────────────── - * В C нет ни списков, ни строк как данных, ни сумм типов, ни сборщика мусора. - * Значения flang (SPEC, раздел 2) — скаляр, список, запись, вариант — обязаны - * получить представление, и оно обязано совпадать по наблюдаемому поведению с - * интерпретатором: те же значения, те же коды и тексты ошибок. - * - * ── Представление значения: теговое объединение ──────────────────────────── - * `fl_value` — тег плюс объединение. Строка и список лежат в самом значении - * (указатель + длина), запись и вариант — за указателем. - * - * Почему не «отдельный тип C на каждую запись и на каждую сумму»: такой код - * читался бы приятнее, но список записей, структурное равенство, сообщение - * «запись не содержит поле «цена»» и разбор значения, пришедшего снаружи, - * потребовали бы рефлексии — то есть второго, теневого представления рядом с - * первым. Два представления одного значения — два набора расхождений с - * интерпретатором. Поэтому представление одно, а типизированный слой поверх - * него бэкенд печатает функциями-конструкторами (см. заголовок модуля). - * - * Длина строки хранится и в байтах, и в кодовых точках. Кодовые точки — это - * то, чем flang меряет строки (SPEC, раздел 5: «длина строки — в кодовых - * точках»), и считать их заново на каждом обращении значило бы сделать - * «длина» линейной там, где она обязана быть мгновенной. - * - * ── Память: арена ────────────────────────────────────────────────────────── - * Программа flang — чистое вычисление: ни ввода-вывода, ни изменяемого - * состояния, ни ссылок наружу. Значит время жизни всего, что она построила, - * это время жизни вызова. Ручной malloc/free по сгенерированному коду дал бы - * утечки на каждом раннем возврате по ошибке (а возврат по ошибке возможен - * почти в каждом узле), подсчёт ссылок — атомарные счётчики и циклы проверок - * ради того же самого. Арена решает вопрос одним движением: всё выделяется - * бампом указателя, а освобождается одним `fl_arena_reset`/`fl_arena_release` - * после того, как вызывающий забрал результат. - * - * Из этого следует главное правило API: **результат живёт в арене**. Список, - * строка, запись и вариант, возвращённые функцией, указывают в память арены; - * читать их можно до ближайшего `fl_arena_reset`, а сохранить надолго — только - * скопировав в свою память. - * - * И следствие, которое дороже всех остальных: раз арена ничего не отдаёт до - * конца вызова, каждая промежуточная копия остаётся лежать. Поэтому «добавить» - * не копирует список — он дописывает в хвостовой запас массива, а неизменяемость - * держит счётчик занятых ячеек `fl_grow` (полное объяснение — над - * `fl_b_dobavit` в flang_runtime.c). Без этого список из n элементов, собранный - * n вызовами «добавить», оставлял бы ~16·n² байт неотбираемого мусора. - * - * ── Область на вызов: арена, которую откатывают ──────────────────────────── - * Одной арены на всё вычисление мало, и это ЗАМЕРЕНО, а не предположено: - * сортировка слиянием четырёх тысяч чисел брала 1 655 МиБ пика при живом - * результате в 125 КиБ. Утечки тут ни при чём (valgrind: ноль байт в ноль - * блоков) — просто ни один промежуточный список не переиспользовался. - * - * Хуже того, дело не ограничивалось «в разы». Сортировка вставками из - * `examples/rosetta/quicksort.flang` — тотальная, доказанная структурно, - * написанная прямо — росла по памяти КУБИЧЕСКИ: 250 элементов 82 МиБ, 750 — - * 2,3 ГиБ, 1 000 — 6,6 ГиБ, а при обычном пределе адресного пространства в - * 8 ГиБ полторы тысячи элементов уже давали объявленный отказ FLANG_MEMORY. - * То есть арена не «замедляла», а не давала досчитать. - * - * Лечение — `fl_region_open`/`fl_region_close` вокруг тела каждой функции, - * способной к рекурсии. Довод законности ровно тот же, которым законна арена - * на пробег процесса (`flang/conc/RESILIENCE.md`, шаг А2): значения flang - * неизменяемы, замыканий нет, ссылок наружу нет — значит в момент возврата из - * вызова достижимо ровно то, что лежит в результате, а аргументы вызова - * построил вызывающий и лежат они НИЖЕ отметки. Поэтому результат можно - * переложить, а всё остальное отдать одним движением. - * - * Полное описание правил и того, когда область отказывается работать, — - * над `fl_region_close` в flang_runtime.c. - * - * Куски, освободившиеся на откате, УХОДЯТ СИСТЕМЕ (кроме первых FL_ARENA_KEEP). - * До 24 августа 2026 они лишь помечались пустыми, и пик прогона был равен не - * рабочему набору, а всему, что арена купила за жизнь. Замер обеих цен — над - * `fl_arena_rollback` в flang_runtime.c: пик −36…61 %, время не выросло. - * - * ── Ошибки: статус плюс выходной параметр ────────────────────────────────── - * Исключений в C нет. Контракт повторяет бэкенд C для FTS-моделей - * (tools/ftsc/src/emit/c.mjs): функция либо кладёт результат в *result и - * возвращает FL_OK, либо НЕ трогает *result и возвращает FL_ERROR, заполнив - * *error. `error` можно передать NULL, если текст не нужен. - * - * `fl_error.code` — не enum, а строка: коды flang («FLANG_TYPE») перечислимы, - * но код нарушенного постусловия приезжает данными из AST («FTS_UTILITY_PROPERTY» - * у моделей FTS), и перечисление перестало бы быть источником истины ровно там, - * где важнее всего совпасть с ядром. - */ -#ifndef FLANG_RUNTIME_H -#define FLANG_RUNTIME_H - -/* - * ── Настройки, зависящие от программы ────────────────────────────────────── - * Бэкенд печатает их конкретные значения ПЕРЕД этим файлом, поэтому здесь - * стоят только запасные: файл обязан компилироваться и сам по себе, иначе его - * нельзя ни проверить компилятором в репозитории, ни взять отдельно в чужой - * проект. Отдельного «flang_config.h» намеренно нет: один файл, который и - * лежит на диске, и печатается, не может разойтись сам с собой. - */ -#ifndef FL_INDEX_BASE -/* Индексация строк — с 1 и включительно с обоих концов (SPEC, раздел 5). */ -#define FL_INDEX_BASE 1 -#endif -#ifndef FL_MAX_DEPTH -/* Как у интерпретатора: глубже — почти наверняка ошибка, а не задача. */ -#define FL_MAX_DEPTH 10000 -#endif -#ifndef FL_MAX_STEPS -/* Как у интерпретатора: дольше — почти наверняка незавершающаяся функция. */ -#define FL_MAX_STEPS 1000000 -#endif -#ifndef FL_MAX_TAIL_ARGS -/* Ширина отскока батута: наибольшая арность среди взаимно рекурсивных функций. */ -#define FL_MAX_TAIL_ARGS 4 -#endif -#ifndef FL_MAX_ARGS -/* Ширина вызова по имени: наибольшая арность функций программы. */ -#define FL_MAX_ARGS 16 -#endif - -#include -#include -#include - -/* ───────────────────────────── арена ───────────────────────────── */ - -typedef struct fl_chunk fl_chunk; - -typedef struct fl_arena { - fl_chunk *chunks; /* односвязный список всех купленных кусков */ - fl_chunk *current; /* кусок, из которого идёт выдача */ - size_t reserved; /* сколько байт всего куплено у malloc */ - /* - * Сколько байт сейчас выдано и ещё не откачено. Растёт на каждой выдаче, - * падает до отметки на откате, обнуляется на reset. Нужен ровно одному — - * `fl_region_close`, чтобы одним вычитанием узнать, сколько наросла область, - * и решить, окупится ли откат. Обходить куски ради этого было бы дороже - * самой области. - */ - size_t handed; - /* - * Нижняя граница ближайшей ОТКРЫТОЙ области: (кусок, занято в нём). NULL в - * `guard_chunk` означает «областей нет» — тогда граница не сторожит ничего. - * Смотрит на неё только `fl_arena_extend`: продлить на месте блок, лежащий - * ниже границы, нельзя (объяснение — там же). - */ - fl_chunk *guard_chunk; - size_t guard_used; - /* - * Буфер под перекладывание результата при откате. Живёт между откатами, чтобы - * не покупать его у malloc на каждом вызове, и отдаётся системе в - * `fl_arena_release` — иначе valgrind насчитал бы недостижимое. - */ - char *staging; - size_t staging_size; - /* - * Отказная памятка обмера: одно доказанное «этому значению мало столько-то». - * Заполняется `fl_region_close`/`fl_region_recycle` при отказе обмера, читается - * самим обмером. Разбор — над `fl_region_deny` в flang_runtime.c. - */ - struct fl_deny *deny; - /* - * ОТСТУП ОБМЕРА В СВЁРТКЕ: при какой наросшей величине пробовать обмер снова - * и для какой области это записано. - * - * Зачем. `fl_region_recycle` зовётся на КАЖДОМ витке свёртки и обмеряет живое - * с бюджетом «наросло ÷ 16». У свёртки-накопления обмер отказывает всегда, но - * «наросло» растёт с каждым витком — значит растёт и бюджет, и обмер на витке - * k стоит O(k). Сумма по всем виткам — КВАДРАТ, и платит его любая свёртка с - * растущим накопителем, какой бы дешёвый алгоритм в ней ни стоял. - * - * Лечение: после отказа не пробовать снова, пока наросшее не удвоится. Тогда - * попытки идут при G, 2G, 4G…, их суммарная цена — около 2N/16, то есть - * ЛИНЕЙНАЯ. Платой за это идёт отложенная уборка: между попытками область - * может вырасти вдвое сверх той, при которой откат случился бы сразу. - */ - size_t recycle_mark; - size_t recycle_next; - /* Каким куском покупать в следующий раз. Раньше это была одна константа на - все арены сразу (64 КиБ), и для главной арены она правильна: программа, - считающая список, берёт куски пачками, и мелкие куски означали бы поход к - malloc на каждый шаг. - - Для КУЧИ ПРОЦЕССА она в триста раз велика. Замер планировщика - (`docs/scheduler-benchmark.md`, раздел 2) назвал цену числом: 131 062 - байта на работающий процесс, ровно два куска по 64 КиБ — по одному на - половину кучи, — притом что типичный процесс держит запись из двух полей. - Отсюда поле вместо константы: `fl_arena_init_small` заводит арену с мелким - первым куском, а дальше кусок УДВАИВАЕТСЯ до общего минимума, поэтому - процесс, которому и правда нужны килобайты, доходит до них за десяток - покупок, а не платит за них с рождения. */ - size_t least; -} fl_arena; - -/* - * Отметка арены: точка, к которой можно вернуть всё выданное после неё. - * Снимается `fl_region_open`, предъявляется `fl_region_close`. Лежит на стеке - * вызывающего — своей памяти у отметки нет. - */ -typedef struct fl_mark { - fl_chunk *chunk; /* кусок, из которого шла выдача на отметке */ - size_t used; /* сколько в нём было занято */ - size_t handed; /* сколько всего было выдано ареной */ - fl_chunk *guard_chunk; /* граница ОБЪЕМЛЮЩЕЙ области — восстановить на выходе */ - size_t guard_used; -} fl_mark; - -/** Арену можно держать на стеке: init/release, без выделения под саму арену. */ -void fl_arena_init(fl_arena *arena); - -/** - * Та же арена, но первый кусок — `least` байт, а не общий минимум. - * - * Заведена ради куч процессов (`flang_conc.c`): их у прогона две на процесс, и - * при миллионе процессов разница между 64 КиБ и полукилобайтом — это разница - * между ста сорока шестью гигабайтами адресного пространства и одним. - * Ноль и всё, что больше общего минимума, означают обычную арену. - */ -void fl_arena_init_small(fl_arena *arena, size_t least); - -/** Выдаёт выровненный блок; NULL — кончилась память (единственная причина). */ -void *fl_arena_alloc(fl_arena *arena, size_t size); - -/** - * Продлевает на месте блок, выданный последним. Если `block` длиной `size` — - * ровно то, что арена отдала в последний раз, и в том же куске лежит ещё - * `extra` байт, блок вырастает без копирования и функция даёт true; иначе она - * не меняет ничего и даёт false. Всё, что арена умеет отменить, — это - * последняя выдача, поэтому продление и ограничено ею. - * - * Нужна ровно одному месту — «добавить» (см. fl_b_dobavit): накопление списка - * в цикле не выделяет ничего, пока хватает куска. - */ -bool fl_arena_extend(fl_arena *arena, const void *block, size_t size, size_t extra); - -/** Освобождает всё выданное, но оставляет куски себе: следующий вызов дешевле. */ -void fl_arena_reset(fl_arena *arena); - -/** Возвращает память системе. После неё арена снова пригодна к fl_arena_init. */ -void fl_arena_release(fl_arena *arena); - -/* ───────────────────────────── значения ───────────────────────────── */ - -typedef enum fl_tag { - FL_NOTHING = 0, /* «ничто» */ - FL_NUMBER, /* число — IEEE-754 double, как в ядре FTS */ - FL_FLAG, /* признак */ - FL_STRING, /* строка — UTF-8, длина в байтах и в кодовых точках */ - FL_LIST, /* список — однородный по типу, но не по представлению */ - FL_RECORD, /* запись — объект FTS */ - FL_VARIANT /* вариант — конструктор суммы типов */ -} fl_tag; - -typedef struct fl_value fl_value; -typedef struct fl_field fl_field; -typedef struct fl_record fl_record; -typedef struct fl_variant fl_variant; - -/* - * Запас массива списка — с ДВУХ концов: сколько в нём ячеек всего, сколько - * занято за концом списка и сколько свободно перед его началом. Устройство - * спрятано в flang_runtime.c — снаружи это непрозрачная пометка «этот список - * можно удлинить, не копируя»; правило, по которому удлинение остаётся - * безопасным, объяснено над `fl_b_dobavit` и работает в обе стороны: - * «добавить» занимает ячейку за концом, «приписать» — перед началом. - */ -typedef struct fl_grow fl_grow; - -struct fl_value { - fl_tag tag; - union { - double number; - bool flag; - struct { - /* НЕ обязана заканчиваться нулём: подстрока и хвост — это срезы. */ - const char *utf8; - size_t bytes; - size_t points; - } string; - struct { - const fl_value *items; - size_t count; - /* Запас за концом и перед началом; NULL — запаса нет, «добавить» и - «приписать» скопируют. Поле не наблюдаемо: равенство, длина и печать - смотрят только на items и count. Ширину значения оно не меняет — - строка и без него занимает три слова. */ - fl_grow *grow; - } list; - const fl_record *record; - const fl_variant *variant; - } as; -}; - -/* Имена полей всегда заканчиваются нулём: они приходят из модели, а не из - данных, и печатаются в диагностиках через %s. */ -struct fl_field { - const char *name; - fl_value value; -}; - -struct fl_record { - size_t count; - const fl_field *fields; -}; - -struct fl_variant { - const char *name; - size_t count; - const fl_field *fields; -}; - -/* ───────────────────────────── статус и ошибка ───────────────────────────── */ - -typedef enum fl_status { - FL_OK = 0, - /* вычисление прекращено; код и текст — в fl_error, *result не тронут */ - FL_ERROR = 1, - /* NULL там, где его быть не может: ошибка вызывающего, а не модели */ - FL_INVALID_ARGUMENT = 2 -} fl_status; - -typedef struct fl_error { - const char *code; /* «FLANG_TYPE», «FTS_UTILITY_PROPERTY», … */ - const char *message; /* текст, дословно совпадающий с интерпретатором */ -} fl_error; - -const char *fl_status_text(fl_status status); - -/* Коды диагностик (SPEC, раздел 7) — для сравнения без опечаток. */ -#define FL_CODE_TYPE "FLANG_TYPE" -#define FL_CODE_UNKNOWN_NAME "FLANG_UNKNOWN_NAME" -#define FL_CODE_MATCH_NOT_EXHAUSTIVE "FLANG_MATCH_NOT_EXHAUSTIVE" -#define FL_CODE_BUILTIN_ARGS "FLANG_BUILTIN_ARGS" -#define FL_CODE_RECURSION_LIMIT "FLANG_RECURSION_LIMIT" -#define FL_CODE_PROPERTY "FLANG_PROPERTY" -#define FL_CODE_PARSE "FLANG_PARSE" -/* Не из SPEC: у интерпретатора кончиться памяти не может, у программы на C — да. */ -#define FL_CODE_MEMORY "FLANG_MEMORY" - -/* ───────────────────────────── контекст вызова ───────────────────────────── */ - -/* - * Арена плюс два счётчика: глубина и шаги. Ни один не украшение. - * - * Глубина: нехвостовая рекурсия в C идёт по настоящему стеку, и его - * переполнение это не исключение, а падение процесса. Интерпретатор в том же - * месте даёт FLANG_RECURSION_LIMIT, и сгенерированный код обязан давать его же. - * - * Шаги: обычная (не тотальная) функция flang может не завершаться, и глубину - * при этом не растить — хвостовой самовызов и взаимная хвостовая рекурсия идут - * в постоянной глубине. Интерпретатор ловит это лимитом шагов; без такого же - * счётчика напечатанная программа в том же месте крутилась бы вечно. Ровно это - * и делала: «Вечность» из курса собиралась и зависала. - * - * Оговорка о шаге. Шаг интерпретатора — итерация его машины, а не применение - * функции: одно применение стоит там многих шагов. Здесь шагом считается вход в - * функцию, виток цикла хвостового самовызова и отскок батута. Значит счётчик - * здесь всегда МЕНЬШЕ счётчика интерпретатора на том же вычислении, и при - * одинаковом пределе интерпретатор упирается в лимит первым. Расхождение - * одностороннее и безопасное: напечатанный код не объявит исчерпанным то, что - * интерпретатор досчитал до конца. Тот же порядок выбран в бэкендах Go, Rust и - * Python — иначе три из пяти целей печати расходились бы с двумя. - * - * Стек: третье поле того же прибора, а не третий прибор. Счётчик глубины считает - * КАДРЫ, а несёт их стек, и сколько их влезет — зависит от программы: замер - * холодными процессами при стеке 8 МиБ дал 23 807 кадров у функции с одним - * параметром и 1 518 у функции с сорока связываниями (5 526 байт на кадр против - * 352 — разница шестнадцатикратная). При объявленном пределе 10 000 вторая - * умирала по SIGSEGV — без кода, без текста, без возможности перехвата, — то - * есть объявленный предел глубины пределом НЕ БЫЛ. - * Поэтому `fl_enter` — та же и единственная точка, где сходятся все пределы, — - * смотрит ещё и на то, сколько стека осталось, и переводит его исчерпание в - * объявленный FLANG_RECURSION_LIMIT. Заводить для стека отдельный механизм - * незачем: место проверки одно, вид отказа один, набор видов отказа закрыт. - */ -typedef struct fl_ctx { - fl_arena *arena; - size_t depth; - size_t max_depth; - size_t steps; - size_t max_steps; - /* - * Сторож стека. `stack_base` — отметка, снятая на входе в расчёт - * (`fl_ctx_init`); `stack_room` — сколько байт стека под ней, 0 выключает - * сторожа. `stack_step` — наибольшая измеренная толщина ОДНОГО кадра этой - * программы: запас под последним входом обязан быть не меньше нескольких - * таких кадров, иначе между двумя проверками стек кончится у программы с - * толстым кадром, а именно она и падала. - */ - const char *stack_base; - size_t stack_room; - size_t stack_seen; - size_t stack_step; -} fl_ctx; - -void fl_ctx_init(fl_ctx *ctx, fl_arena *arena); - -/* ─────────────────────────────── стек ─────────────────────────────── */ - -/* - * Сколько байт стека нужно, чтобы объявленный предел глубины БЫЛ достижим. - * - * Замер по всему корпусу репозитория (`cc -fstack-usage`, 7 896 функций из 157 - * программ): самый толстый кадр — 6 496 байт у «Замены кириллицы» из - * `flang/self/emit-c.flang`, то есть у самого компилятора flang. Считать по - * средней программе нельзя: предел обещан всем, значит стек обязан нести - * худшую. FL_STACK_PER_FRAME взят с запасом в 2,5 раза от худшего замера — - * один кадр flang это кадр функции плюс кадр её тела плюс, если вызов стоит - * внутри «отобрать»/«свёртки», кадр вложенного тела и кадр помощника рантайма. - */ -#ifndef FL_STACK_PER_FRAME -#define FL_STACK_PER_FRAME 16384 -#endif - -/* - * Потолок. Просить у системы стек под `--max-depth 100000000` бессмысленно: - * mmap откажет, и вместо диагностики выйдет отказ на ровном месте. Выше потолка - * предел просто не несётся стеком — и тогда о его недостижимости честно говорит - * сторож, а не сегфолт. Ровно так же поступает бэкенд Python (MAX_RECURSION_LIMIT). - */ -#ifndef FL_STACK_MAX -#define FL_STACK_MAX ((size_t)1024u * 1024u * 1024u) -#endif - -/* Ниже восьми мегабайт просить нечего: столько даёт система и так. */ -#ifndef FL_STACK_MIN -#define FL_STACK_MIN ((size_t)8u * 1024u * 1024u) -#endif - -/* - * Неснижаемый запас под последним входом: на нём доигрывается тело функции, - * строится текст диагностики (vsnprintf) и разматывается возврат. Самый толстый - * кадр рантайма — 240 байт, прогонщика — 416; 128 КиБ это сотни таких кадров. - * - * Число это ПОТОЛОК запаса, а не сам запас: сторож берёт меньшее из него и - * четверти всего стека. Причина названа числом: теневой стек wasm по умолчанию - * 64 КиБ, и запас в 128 КиБ там БОЛЬШЕ всего стека — сторож отказывал бы на - * первом же входе в первую функцию, то есть «починка» превратила бы одну ложь - * (молчание) в другую (отказ на пустом месте). Четверть 64 КиБ — 16 КиБ, а это - * 68 кадров рантайма по 240 байт из тех, ради которых запас и держат. - * На стеке в 8 МиБ правило не меняет ничего: четверть там 2 МиБ, потолок ниже. - */ -#ifndef FL_STACK_MARGIN -#define FL_STACK_MARGIN ((size_t)128u * 1024u) -#endif - -/* - * Сколько стека считать имеющимся там, где спросить не у кого (не POSIX). Один - * мегабайт — наименьший распространённый умолчальный стек потока. Занизить - * значит отказать раньше, чем нужно, и сказать об этом словами; завысить значит - * упасть молча. Первое — диагностика, второе — дефект, поэтому по умолчанию - * занижено. Кто знает точнее, говорит `fl_stack_room_set`. - * - * ── Почему одного занижения оказалось МАЛО, и это предъявлено прогоном ────── - * - * WebAssembly — та самая «неизвестная платформа», и на ней занижение до мегабайта - * оказалось ЗАВЫШЕНИЕМ в шестнадцать раз: `wasm-ld` отводит теневой стек 64 КиБ. - * Замер (Chrome 151, `web/wasm/`, спуск на 1000 при пределе 10 000): глубина 30 — - * объявленный FLANG_RECURSION_LIMIT, глубина 60 — СМЕРТЬ ВКЛАДКИ. Между ними - * сторож молчал, потому что считал по мегабайту, которого нет. - * - * Хуже того, в WebAssembly нет сторожевой страницы: теневой стек не падает, а - * заезжает в кучу и молча портит данные. Под `node:wasi` тот же двоичный файл - * отвечал на глубине 65 `RuntimeError: function signature mismatch` — то есть - * стек затёр таблицу вызовов. Ни один из этих исходов не входит в замкнутое - * множество отказов (`flang/src/failures.mjs`), а закрытость этого множества и - * есть то, чем слой отказов flang отличается от соседей. - * - * Поэтому на wasm запасное число НЕ ИСПОЛЬЗУЕТСЯ вовсе: там настоящий размер - * теневого стека ВИДЕН из самого модуля (`fl_wasm_room` в реализации), и знать - * его лучше, чем угадывать. Флагов сборки для этого не нужно ни одного — и это - * главное: починка, которую надо не забыть включить, на первой же чужой сборке - * не включена. - */ -#ifndef FL_STACK_ROOM_FALLBACK -#define FL_STACK_ROOM_FALLBACK ((size_t)1024u * 1024u) -#endif - -/** - * Предел шагов, с которым заводится КАЖДЫЙ новый контекст. - * По умолчанию — `FL_MAX_STEPS`, напечатанный бэкендом; `fl_max_steps_default_set` - * поднимает его на весь процесс (ключ `--предел-шагов` у `flang check`). - * Ноль в `_set` возвращает умолчание бэкенда, а не выключает счёт. - */ -size_t fl_max_steps_default(void); -void fl_max_steps_default_set(size_t steps); - -/** - * Предел ГЛУБИНЫ, с которым заводится КАЖДЫЙ новый контекст. - * По умолчанию — `FL_MAX_DEPTH`, напечатанный бэкендом; `fl_max_depth_default_set` - * поднимает его на весь процесс (ключ `--предел-глубины` у самого бинарника). - * Ноль в `_set` возвращает умолчание бэкенда, а не снимает предел. - * - * Поднявший предел обязан поднять и стек: глубину несёт он, а заводится он один - * раз на процесс (`fl_call_deep`). Ключ `--предел-глубины` делает оба движения - * разом и потому разбирается ДО разбора команды — в `main` (flang_cli.c). - */ -size_t fl_max_depth_default(void); -void fl_max_depth_default_set(size_t depth); - -/** Сколько стека просить под предел глубины `max_depth` (с учётом границ). */ -size_t fl_stack_wanted(size_t max_depth); - -/** - * Сколько стека несёт расчёт. Спрашивается один раз и запоминается: на POSIX - * это getrlimit(RLIMIT_STACK), на прочих — FL_STACK_ROOM_FALLBACK. - */ -size_t fl_stack_room(void); - -/** - * Объявить, сколько стека несёт расчёт. Так говорит `fl_call_deep` про поток, - * который сам и завёл, и так же может сказать встраивающий, если считает не на - * главном стеке. Ноль означает «забыть ответ и спросить заново», а НЕ «сторожа - * выключить»: выключается он полем `ctx->stack_room` в конкретном контексте — - * это выбор того, кто знает, что делает, и берёт падение процесса на себя. - * - * Значение одно на процесс, и это не недосмотр: контексты заводятся на каждый - * запрос, а стек у расчёта один. Встраивающему, который считает сразу на - * нескольких потоках с разными стеками, довольно поставить `ctx->stack_room` - * прямо в контексте — `fl_ctx_init` кладёт туда именно это значение. - */ -void fl_stack_room_set(size_t bytes); - -/** - * Исполняет `work(state)` на потоке с явно заданным стеком и говорит, удалось - * ли завести такой поток. Лечение взято у бэкенда Python (call_with_deep_stack) - * и по той же причине: стек главного потока задан при запуске процесса, обычно - * 8 МиБ, и объявленный предел глубины в него не влезает — а исчерпание стека C - * это не исключение, а смерть процесса, на которой диагностику не построишь. - * - * Даёт false, если потоков нет (не POSIX) или система не дала такой стек. Тогда - * `work` НЕ вызывается, и вызывающий считает на своём стеке — с тем же сторожем - * в `fl_enter`, который в этом случае и держит обещание: отказ будет - * объявленным, просто предел окажется ниже. - */ -bool fl_call_deep(size_t stack_bytes, void (*work)(void *), void *state); - -/* Вход в функцию, способную к рекурсии; на превышении даёт FLANG_RECURSION_LIMIT. */ -fl_status fl_enter(fl_ctx *ctx, const char *function, fl_error *error); -void fl_leave(fl_ctx *ctx); - -/* ─────────────────────── окно наблюдения ─────────────────────── - * - * К ИДУЩЕЙ ПРОГРАММЕ ПОДКЛЮЧАЮТСЯ СИГНАЛОМ, И ОТВЕЧАЕТ ХОЗЯИН. - * - * `kill -USR1 ` — и в дескриптор 2 уходит снимок: имя идущей функции, - * цепочка звавших, глубина, витки и занятая память. Разбор решения — - * docs/adr/0019-the-host-answers-the-signal-not-the-program.md; коротко здесь - * только то, без чего этот заголовок не читается. - * - * ПРОГРАММА В ЭТОМ НЕ УЧАСТВУЕТ. У неё нет ни нового слова, ни нового - * поручения, и — главное — нет способа заметить, что к ней подключились. Ни - * одно поле окна не лежит в `fl_ctx` и потому не видно ни одной ветке расчёта: - * наблюдаемое течёт ТОЛЬКО наружу. Иначе программа смогла бы ветвиться по - * своим виткам, два прогона дали бы разные ответы, и `тотальная` перестала бы - * что-либо значить. - * - * ЧЕГО ЭТО СТОИТ РАСЧЁТУ: одно сравнение и одна запись на ВХОД В ФУНКЦИЮ. - * `fl_tick` — самый горячий путь рантайма — не тронут ни одной командой, и - * виток цикла хвостового самовызова тоже: сигнал приносит ядро. - * - * УМОЛЧАНИЕ SIGUSR1 В POSIX — ЗАВЕРШИТЬ ПРОЦЕСС. То есть до этого окна - * `kill -USR1` не «ничего не делал», а убивал идущий расчёт. Обработчик стоит - * с SA_RESTART: без него сигнал рвал бы `read` и `waitpid` кодом EINTR. - */ - -/* Сколько имён живых кадров держать. Глубже снимок называет глубину числом, но - имён не показывает: цепочка в тысячу имён нечитаема, а буфер обработчика - обязан быть заведомо конечным. */ -#ifndef FL_WATCH_FRAMES -#define FL_WATCH_FRAMES 64 -#endif - -/** - * Открыть окно: поставить обработчик SIGUSR1 и показывать ЭТОТ расчёт. - * Зовётся из `fl_ctx_init`, то есть ровно там, где расчёт начинается. - * Ставится обработчик один раз на процесс; контекст переназначается. - */ -void fl_watch_open(fl_ctx *ctx); - -/** - * Имя шага, которым хозяин назвал бы всю работу целиком («Ведомость - * исходников», «Шаг плана»). Снимок печатает его отдельной строкой, когда оно - * отличается от имени идущей функции. NULL стирает. - */ -void fl_watch_step(const char *name); - -/** - * Спрашивали ли с прошлого раза. Читает И СБРАСЫВАЕТ: хозяин, у которого есть - * безопасное место (граница витка плана, конец шага), отвечает там подробнее, - * чем вправе обработчик сигнала. Обработчик отвечает сразу и сам — эти два - * ответа не заменяют друг друга: тесный расчёт до границы витка не дойдёт. - */ -bool fl_watch_asked(void); - -/** - * Строка «повторить» — готовый вызов, которым этот расчёт начался. - * - * Ставит её тот, кто знает команду целиком. Рантайм собирает её сам только в - * двери программы (`fl_check_entry`), и там ему известны имя и доводы, но не - * файл: файл знает хозяин. `flang run` ставит сюда всю строку, и снимок - * подключения печатает вызов, который можно вставить и повторить. - * - * NULL стирает. Длиннее буфера — обрезается, и обрезанное видно многоточием. - */ -void fl_watch_repeat_set(const char *line); - -/* ── печать без stdio: то, что законно внутри обработчика сигнала ── - * - * Общее на рантайм и на хозяев. Числа пробелами по три разряда, память — - * двоичными приставками, как её называет остальное дерево. Своего разбора у - * хозяина быть не должно: разойдись эти две печати, одно и то же число - * выглядело бы в двух строках по-разному. */ -size_t fl_say_text(char *out, size_t room, size_t at, const char *text); -size_t fl_say_number(char *out, size_t room, size_t at, unsigned long value); -size_t fl_say_size(char *out, size_t room, size_t at, unsigned long bytes); - -/** Сколько памяти держит процесс, байтами. 0 — узнать нечем (нет /proc). */ -unsigned long fl_say_resident(void); - -/** Размер страницы, снятый ВНЕ обработчика: `sysconf` внутри сигнала незаконен. */ -void fl_say_page_ready(void); - -/* ─────────────────────── область на вызов ─────────────────────── */ - -/** - * Снимает отметку арены и объявляет область открытой. Стоит ровно пять - * присваиваний: всё, что дороже, делает `fl_region_close`, и только когда - * есть за что платить. - */ -fl_mark fl_region_open(fl_ctx *ctx); - -/** - * Закрывает область: перекладывает результат вниз и отдаёт всё остальное. - * - * Возвращает `status` вызывающему как есть — область НИКОГДА не превращает - * удачный расчёт в отказ и никогда не меняет ответ. Если переложить нельзя или - * невыгодно, она просто не делает ничего (полный список случаев — над - * реализацией). Единственное исключение — FLANG_MEMORY, если памяти не хватило - * уже на самой перекладке; там отказ честнее молчания. - * - * `status` — то, чем кончилось тело. При отказе откат не делается: текст - * диагностики лежит в арене выше отметки. - */ -fl_status fl_region_close(fl_ctx *ctx, fl_mark mark, fl_status status, fl_value *result, fl_error *error); - -/** - * Закрывает область и тут же открывает её заново той же отметкой. - * - * Нужна одному месту — накопителю свёртки (см. `fl_region_recycle` в - * flang_runtime.c). Одним `fl_region_close` тут не обойтись: он возвращает - * границу ОБЪЕМЛЮЩЕЙ области, и второй виток цикла пошёл бы без сторожа — - * `fl_arena_extend` продлил бы на месте блок, лежащий ниже отметки, а его - * вот-вот откатят. - */ -fl_status fl_region_recycle(fl_ctx *ctx, fl_mark mark, fl_value *result, fl_error *error); - -/* - * Виток вычисления: вход в функцию, оборот цикла хвостового самовызова, отскок - * батута. Считается отдельно от глубины — хвостовая рекурсия глубину не растит, - * но завершаться от этого не начинает. Предел 0 отключает счёт. - * - * Имя `fl_tick`, а не `fl_step`: `fl_step` уже занят типом функции-шага батута - * ниже, и второй смысл у того же имени читался бы как ошибка. - */ -fl_status fl_tick(fl_ctx *ctx, const char *function, fl_error *error); - -/* - * Витки цикла по списку: заряд на `count` витков разом, без отказа. - * - * Зовёт её `fl_require_list` — вход всех трёх циклов по списку. Отказа здесь - * нет намеренно: имени объемлющей функции у рантайма нет, а текст отказа обязан - * называть её дословно как толкователь. Предел ловит ближайший `fl_tick`; - * подробности и замер — у `fl_charge` в flang_runtime.c. - */ -void fl_charge(fl_ctx *ctx, size_t count); - -/* - * Ранний возврат по ошибке. Сгенерированный код состоит из него на треть, - * поэтому макрос: без него каждая строка обросла бы четырьмя. - */ -#define FL_TRY(expression) \ - do { \ - const fl_status fl_try_status_ = (expression); \ - if (fl_try_status_ != FL_OK) { \ - return fl_try_status_; \ - } \ - } while (0) - -/* ───────────────────────────── конструкторы ───────────────────────────── */ - -fl_value fl_nothing(void); -fl_value fl_number(double number); -fl_value fl_flag(bool flag); - -/** Строка без копирования: годится для литералов и для срезов. */ -fl_value fl_text_borrow(const char *utf8, size_t bytes, size_t points); - -/** Строка с копированием в арену; кодовые точки считаются здесь. */ -fl_status fl_text(fl_ctx *ctx, const char *utf8, size_t bytes, fl_value *out, fl_error *error); - -/** - * Номер (от 1) первого октета, который не складывается в правильный UTF-8; 0 — - * содержимое текст. Нулевой октет законен: U+0000 — обычная кодовая точка. - * Спрашивает хозяин, которому дали файл: строка языка обязана быть UTF-8, и - * молчаливая ложь о числе знаков хуже отказа. - */ -size_t fl_utf8_not_text_at(const char *utf8, size_t bytes); - -/** Список из готового массива значений (массив обязан жить в арене). Запаса у - такого списка нет: первое же «добавить» к нему скопирует и заведёт запас. */ -fl_value fl_list(const fl_value *items, size_t count); - -/** Массив под список: заполняется вызывающим, затем оборачивается в fl_list. */ -fl_status fl_list_alloc(fl_ctx *ctx, size_t count, fl_value **items, fl_error *error); - -/** Хвост списка — срез без копирования (значения неизменяемы, память общая). */ -fl_value fl_list_slice(fl_value list, size_t from); - -/** - * Цепочка — список ЛИБО строка: образцы «пусто» и «голова и хвост» разбирают - * обе. Голова строки — одна кодовая точка, хвост — остаток; оба среза, без - * копирования. Вызываются только после `fl_chain_cons`, поэтому пустого случая - * не проверяют. - */ -bool fl_chain_empty(fl_value value); -bool fl_chain_cons(fl_value value); -fl_value fl_chain_head(fl_value value); -fl_value fl_chain_tail(fl_value value); - -fl_status fl_record_new(fl_ctx *ctx, const char *const *names, const fl_value *values, size_t count, - fl_value *out, fl_error *error); -fl_status fl_variant_new(fl_ctx *ctx, const char *name, const char *const *names, const fl_value *values, - size_t count, fl_value *out, fl_error *error); - -/* ───────────────────────────── предикаты и описание ───────────────────────────── */ - -bool fl_is_list(fl_value value); -bool fl_is_record(fl_value value); -bool fl_is_variant(fl_value value); -bool fl_is_scalar(fl_value value); -bool fl_variant_is(fl_value value, const char *name); - -/** «ничто», «строка», «число», «признак», «список», «вариант «Имя»», «запись». */ -const char *fl_type_name(fl_ctx *ctx, fl_value value); - -/** Короткое описание значения для диагностик (describeValue интерпретатора). */ -const char *fl_describe(fl_ctx *ctx, fl_value value); - -/** - * Число в текст ровно по правилам ECMAScript Number::toString — потому что - * «к строке» и тексты диагностик обязаны совпасть с интерпретатором посимвольно. - * Буфера в 40 байт хватает на любой double. - */ -#define FL_NUMBER_TEXT_MAX 40 -size_t fl_number_text(double value, char *buffer); - -/** Структурное равенство; скаляры — как Object.is (NaN равен NaN, 0 ≠ −0). */ -bool fl_equal(fl_value left, fl_value right); - -/* ───────────────────────────── ошибки ───────────────────────────── */ - -/** Всегда возвращает FL_ERROR: `return fl_fail(ctx, error, FL_CODE_TYPE, "…", …);` */ -fl_status fl_fail(fl_ctx *ctx, fl_error *error, const char *code, const char *format, ...); - -/* ───────────────────────────── граница входа ───────────────────────────── - * - * Объявленные типы параметров — ДАННЫМИ. Прогонщик сверяет по ним значения, - * пришедшие снаружи, ДО вызова функции. - * - * Зачем это здесь, а не в самих функциях. Доказательство завершения - * `тотальной` стоит НА ТИПЕ: у `неотрицательное` есть дно 0 и потолок 2^53−1, ниже - * которого `н минус 1` точно меньше `н`, и сторож убывания в такую функцию не - * печатается вовсе. Значение вне типа выносит вместе с типом и доказательство: - * 1e300 минус 1 равно 1e300, цепочка вечна, а ловить её нечем — сторожа нет. - * Поэтому дверь одна и стоит она ДО вычисления; внутри функции проверять было - * бы уже поздно и не тем. - * - * Таблица печатается бэкендом вместе с программой (см. `<модуль>_entry`), а - * строит её `flang/src/types.mjs` (`таблицаВхода`) — тем же пониманием слов - * «значение подходит типу», каким сверяется `flang run --args`. Восемь целей - * читают одну таблицу, а не заводят восемь пониманий. - */ - -typedef enum fl_type_kind { - /* Значение-функция, параметр полиморфизма и применение типа с аргументами: - одной таблицы им мало, поэтому они не сверяются вовсе. */ - FL_TYPE_UNKNOWN = 0, - FL_TYPE_NUMBER, - FL_TYPE_STRING, - FL_TYPE_FLAG, - FL_TYPE_NULL, - FL_TYPE_LIST, - FL_TYPE_RECORD, - FL_TYPE_SUM -} fl_type_kind; - -/* Поле записи или варианта: имя и индекс его типа в таблице типов. */ -typedef struct fl_type_field { - const char *name; - size_t type; -} fl_type_field; - -/* Вариант суммы: имя дискриминанта и отрезок его полей в общем массиве. */ -typedef struct fl_type_variant { - const char *name; - size_t field_from; - size_t field_count; -} fl_type_variant; - -/* - * Поля и варианты лежат СПЛОШНЫМИ ОТРЕЗКАМИ общих массивов, а тип называет - * начало и длину. Не ради экономии: массив нулевой длины в C99 незаконен, и - * отдельный массив на каждый тип означал бы россыпь имён вида `..._fields_3_2` - * — по одному на каждый вариант каждой суммы. - */ -typedef struct fl_type { - fl_type_kind kind; - const char *name; /* печатное имя типа: «неотрицательное», «список числа», «Позиция» */ - const char *owner; /* имя записи или суммы без кавычек — для текстов о полях */ - bool optional; /* «… или ничто»: отсутствие значения законно */ - bool integral; /* целое ли */ - bool bounded; /* есть ли конечный отрезок (у `число` его нет) */ - double low; - double high; - size_t of; /* тип элемента списка — индекс в той же таблице */ - size_t field_from; - size_t field_count; - size_t variant_from; - size_t variant_count; -} fl_type; - -typedef struct fl_entry_param { - const char *function; /* имя функции flang */ - const char *name; /* имя параметра */ - size_t type; /* индекс в таблице типов */ -} fl_entry_param; - -typedef struct fl_entry_table { - const fl_type *types; - size_t type_count; - const fl_type_field *fields; - size_t field_count; - const fl_type_variant *variants; - size_t variant_count; - const fl_entry_param *params; - size_t param_count; -} fl_entry_table; - -/** - * Сверка набора значений с объявленными типами параметров функции. - * - * Молчит (FL_OK) там, где сверять нечем: имени в таблице нет, число значений с - * числом параметров не сошлось (об этом скажет диспетчер своим текстом), тип - * приехал видом `неизвестно`. Тексты отказов дословно те же, что у - * `checkValue` свидетеля, потому что расхождение здесь означало бы, что у языка - * два ответа на вопрос «подходит ли значение типу». - */ -fl_status fl_check_entry(fl_ctx *ctx, const fl_entry_table *table, const char *name, const fl_value *args, - size_t count, fl_error *error); - -/* ───────────────────────────── операции языка ───────────────────────────── */ - -fl_status fl_field_get(fl_ctx *ctx, fl_value target, const char *name, fl_value *out, fl_error *error); -fl_status fl_variant_field(fl_ctx *ctx, fl_value target, const char *name, fl_value *out, fl_error *error); -fl_status fl_cond(fl_ctx *ctx, fl_value value, bool *out, fl_error *error); -fl_status fl_keep(fl_ctx *ctx, fl_value value, bool *out, fl_error *error); -fl_status fl_post(fl_ctx *ctx, fl_value value, const char *property, const char *function, bool *out, - fl_error *error); -/* Предусловие — отдельно от постусловия, а не тот же помощник со вторым текстом: - слова отказа дословно те же, что у интерпретатора (checkPreconditions в - flang/src/interpret.mjs). Зовёт это ТОЛЬКО дверь программы — вызов по имени - (prefix_call): внутри программы предусловие снял вызывающий на проверке. */ -fl_status fl_pre(fl_ctx *ctx, fl_value value, const char *property, const char *function, bool *out, - fl_error *error); -fl_status fl_match_fail(fl_ctx *ctx, fl_value value, fl_error *error); -fl_status fl_require_list(fl_ctx *ctx, fl_value value, const char *label, fl_value *out, fl_error *error); - -fl_status fl_add(fl_ctx *ctx, fl_value left, fl_value right, fl_value *out, fl_error *error); -fl_status fl_sub(fl_ctx *ctx, fl_value left, fl_value right, fl_value *out, fl_error *error); -fl_status fl_mul(fl_ctx *ctx, fl_value left, fl_value right, fl_value *out, fl_error *error); -fl_status fl_div(fl_ctx *ctx, fl_value left, fl_value right, fl_value *out, fl_error *error); -fl_status fl_mod(fl_ctx *ctx, fl_value left, fl_value right, fl_value *out, fl_error *error); -fl_status fl_percent(fl_ctx *ctx, fl_value left, fl_value right, fl_value *out, fl_error *error); -fl_status fl_gt(fl_ctx *ctx, fl_value left, fl_value right, fl_value *out, fl_error *error); -fl_status fl_lt(fl_ctx *ctx, fl_value left, fl_value right, fl_value *out, fl_error *error); -fl_status fl_gte(fl_ctx *ctx, fl_value left, fl_value right, fl_value *out, fl_error *error); -fl_status fl_lte(fl_ctx *ctx, fl_value left, fl_value right, fl_value *out, fl_error *error); -fl_status fl_concat(fl_ctx *ctx, fl_value left, fl_value right, fl_value *out, fl_error *error); - -/* - * ── Отказ без проверки: для мест, где проверку делает сам напечатанный код ── - * - * Там, где вывод типов доказал тип обоих операндов, печать считает арифметику - * выражением C, а сверку тегов ставит сама — одним `if`, который остаётся в теле - * вызывающего и потому не мешает компилятору держать значения в регистрах. - * Зовутся эти две ТОЛЬКО с ветви отказа и всегда возвращают FL_ERROR: текст - * отказа обязан быть тем же самым, что у `fl_add` и соседей, и писать его во - * второй раз значило бы завести второе место, где он может разойтись. - * - * ПОЧЕМУ ПРОВЕРКА ВСЁ-ТАКИ НУЖНА, хотя тип доказан. Доказательство верно при - * условии, что значения пришли по объявленным типам, а граница входа - * (`fl_check_entry`) сверяет НЕ ВСЁ: вид `неизвестно` — параметр полиморфизма и - * значение-функцию — она пропускает, потому что сверять их не с чем. Через эту - * дверь чужое значение доезжает до места, где тип «доказан», и без проверки - * `.as.number` прочитал бы соседнее поле объединения — то есть выдал бы ЧИСЛО - * вместо отказа. Измерено на корпусе: `«Отобразить» от Удвоить и [1, "два"]` - * давало 1.32e-309 вместо FLANG_TYPE. - */ -fl_status fl_not_numbers(fl_ctx *ctx, const char *op, fl_value left, fl_value right, fl_error *error); -fl_status fl_not_order(fl_ctx *ctx, fl_value left, fl_value right, fl_error *error); - -/* ───────────────────────────── встроенные формы ───────────────────────────── */ - -fl_status fl_b_dlina(fl_ctx *ctx, fl_value value, fl_value *out, fl_error *error); -fl_status fl_b_simvol(fl_ctx *ctx, fl_value index, fl_value text, fl_value *out, fl_error *error); -fl_status fl_b_podstroka(fl_ctx *ctx, fl_value text, fl_value from, fl_value to, fl_value *out, fl_error *error); -fl_status fl_b_soedinit(fl_ctx *ctx, fl_value left, fl_value right, fl_value *out, fl_error *error); -fl_status fl_b_razdelit(fl_ctx *ctx, fl_value text, fl_value separator, fl_value *out, fl_error *error); -fl_status fl_b_simvoly(fl_ctx *ctx, fl_value text, fl_value *out, fl_error *error); -fl_status fl_b_kod_simvola(fl_ctx *ctx, fl_value text, fl_value *out, fl_error *error); -fl_status fl_b_simvol_po_kodu(fl_ctx *ctx, fl_value code, fl_value *out, fl_error *error); -fl_status fl_b_soderzhit(fl_ctx *ctx, fl_value left, fl_value right, fl_value *out, fl_error *error); -fl_status fl_b_nachinaetsya_s(fl_ctx *ctx, fl_value text, fl_value prefix, fl_value *out, fl_error *error); -fl_status fl_b_k_chislu(fl_ctx *ctx, fl_value text, fl_value *out, fl_error *error); -/* Та же «к числу», но отказ возвращается значением «Не разобрано», а не статусом. */ -fl_status fl_b_k_chislu_ili_beda(fl_ctx *ctx, fl_value text, fl_value *out, fl_error *error); -fl_status fl_b_k_stroke(fl_ctx *ctx, fl_value value, fl_value *out, fl_error *error); -fl_status fl_b_pusto(fl_ctx *ctx, fl_value value, fl_value *out, fl_error *error); -fl_status fl_b_golova(fl_ctx *ctx, fl_value value, fl_value *out, fl_error *error); -fl_status fl_b_hvost(fl_ctx *ctx, fl_value value, fl_value *out, fl_error *error); -fl_status fl_b_element(fl_ctx *ctx, fl_value index, fl_value list, fl_value *out, fl_error *error); -fl_status fl_b_dobavit(fl_ctx *ctx, fl_value item, fl_value list, fl_value *out, fl_error *error); -fl_status fl_b_pripisat(fl_ctx *ctx, fl_value item, fl_value list, fl_value *out, fl_error *error); -fl_status fl_b_ostatok_ot(fl_ctx *ctx, fl_value left, fl_value right, fl_value *out, fl_error *error); -fl_status fl_b_procentov_ot(fl_ctx *ctx, fl_value left, fl_value right, fl_value *out, fl_error *error); - -/* ── Доказанный путь четырёх форм: то же действие без сторожа частичности ── - * - * Частичная форма отказывает не всегда, а на пустом. Там, где непустота - * ДОКАЗАНА проверкой типов (`flang/src/types.mjs`, `длинаНиз`), узел приезжает - * с отметкой `доказана`, и печать зовёт эти функции. Сверка типа остаётся: - * `fl_expect_list` ловит не пустоту, а другой вид значения. */ -fl_status fl_b_razdelit_dokazano(fl_ctx *ctx, fl_value text, fl_value separator, fl_value *out, fl_error *error); -fl_status fl_b_kod_simvola_dokazano(fl_ctx *ctx, fl_value text, fl_value *out, fl_error *error); -fl_status fl_b_golova_dokazano(fl_ctx *ctx, fl_value value, fl_value *out, fl_error *error); -fl_status fl_b_hvost_dokazano(fl_ctx *ctx, fl_value value, fl_value *out, fl_error *error); - -/* ───────────────────────────── батут ───────────────────────────── */ - -/* - * Взаимная хвостовая рекурсия. Интерпретатор переиспользует кадр возврата, - * поэтому «Чётное»/«Нечётное» на миллионе шагов работают в постоянной глубине. - * Обычный вызов C рос бы по стеку и упал бы там, где интерпретатор считает - * штатно, — то есть обещание совпадения нарушилось бы на ровном месте. - * - * Поэтому каждая функция компоненты сильной связности печатается ещё и как - * «шаг»: вместо вызова соседа он заполняет отскок и возвращается, а - * fl_trampoline крутит отскоки в цикле. - */ -typedef struct fl_bounce fl_bounce; - -typedef fl_status (*fl_step)(fl_ctx *ctx, const fl_value *args, fl_bounce *bounce, fl_value *result, - fl_error *error); - -struct fl_bounce { - fl_step next; /* NULL — значение готово, отскока нет */ - fl_value args[FL_MAX_TAIL_ARGS]; -}; - -/* - * `function` — имя flang, от чьего лица считается отскок и, если предел шагов - * исчерпан, называется в диагностике. Батут крутит всю компоненту сильной - * связности, поэтому взять имя из самого отскока нельзя: оно менялось бы на - * каждом витке, а интерпретатор называет ту функцию, с которой вычисление - * началось. - */ -fl_status fl_trampoline(fl_ctx *ctx, fl_step step, const fl_value *args, size_t count, const char *function, - fl_value *result, fl_error *error); - -#endif /* FLANG_RUNTIME_H */ diff --git a/core/out-c/opis_diska.c b/core/out-c/opis_diska.c deleted file mode 100644 index 3e33c2a..0000000 --- a/core/out-c/opis_diska.c +++ /dev/null @@ -1,3816 +0,0 @@ -/* - * Сгенерировано flang (бэкенд C, flang/self/emit-c.flang). Не редактировать руками. - * Модуль flang: «Опись диска». - * Файл: реализация. - * Правьте исходник на flang и печатайте заново: любая правка здесь потеряется. - */ -#include "opis_diska.h" - -#include - -/* Константы программы: имена полей и строковые литералы. */ -static const char *const opis_diska_names_1[] = { "путь", "размер", "возраст_дней", "вид", "доступен" }; -static const char *const opis_diska_names_2[] = { "разряд", "якорь", "цепь" }; -static const char *const opis_diska_names_3[] = { "разряд", "приговор", "вес" }; -static const char *const opis_diska_names_4[] = { "путь", "решение" }; -static const char *const opis_diska_names_5[] = { "кэш", "журнал", "сборка", "загрузка", "крупное", "неизвестное", "освободить" }; -static const fl_value opis_diska_text_6 = { FL_STRING, { .string = { "/", 1, 1 } } }; -static const fl_value opis_diska_text_7 = { FL_STRING, { .string = { "", 0, 0 } } }; -static const fl_value opis_diska_text_8 = { FL_STRING, { .string = { "0123456789abcdefABCDEF", 22, 22 } } }; -static const fl_value opis_diska_text_9 = { FL_STRING, { .string = { "site-packages", 13, 13 } } }; -static const fl_value opis_diska_text_10 = { FL_STRING, { .string = { "dist-packages", 13, 13 } } }; -static const fl_value opis_diska_text_11 = { FL_STRING, { .string = { ".git", 4, 4 } } }; -static const fl_value opis_diska_text_12 = { FL_STRING, { .string = { "/tmp/", 5, 5 } } }; -static const fl_value opis_diska_text_13 = { FL_STRING, { .string = { "/var/tmp/", 9, 9 } } }; -static const fl_value opis_diska_text_14 = { FL_STRING, { .string = { ".cache", 6, 6 } } }; -static const fl_value opis_diska_text_15 = { FL_STRING, { .string = { "cache", 5, 5 } } }; -static const fl_value opis_diska_text_16 = { FL_STRING, { .string = { "Caches", 6, 6 } } }; -static const fl_value opis_diska_text_17 = { FL_STRING, { .string = { ".log", 4, 4 } } }; -static const fl_value opis_diska_text_18 = { FL_STRING, { .string = { "log", 3, 3 } } }; -static const fl_value opis_diska_text_19 = { FL_STRING, { .string = { "logs", 4, 4 } } }; -static const fl_value opis_diska_text_20 = { FL_STRING, { .string = { "node_modules", 12, 12 } } }; -static const fl_value opis_diska_text_21 = { FL_STRING, { .string = { "target", 6, 6 } } }; -static const fl_value opis_diska_text_22 = { FL_STRING, { .string = { "build", 5, 5 } } }; -static const fl_value opis_diska_text_23 = { FL_STRING, { .string = { "_build", 6, 6 } } }; -static const fl_value opis_diska_text_24 = { FL_STRING, { .string = { ".gradle", 7, 7 } } }; -static const fl_value opis_diska_text_25 = { FL_STRING, { .string = { "Downloads", 9, 9 } } }; -static const fl_value opis_diska_text_26 = { FL_STRING, { .string = { "Загрузки", 16, 8 } } }; -static const fl_value opis_diska_text_27 = { FL_STRING, { .string = { ".go", 3, 3 } } }; -static const fl_value opis_diska_text_28 = { FL_STRING, { .string = { ".c", 2, 2 } } }; -static const fl_value opis_diska_text_29 = { FL_STRING, { .string = { ".h", 2, 2 } } }; -static const fl_value opis_diska_text_30 = { FL_STRING, { .string = { ".cc", 3, 3 } } }; -static const fl_value opis_diska_text_31 = { FL_STRING, { .string = { ".cpp", 4, 4 } } }; -static const fl_value opis_diska_text_32 = { FL_STRING, { .string = { ".hpp", 4, 4 } } }; -static const fl_value opis_diska_text_33 = { FL_STRING, { .string = { ".rs", 3, 3 } } }; -static const fl_value opis_diska_text_34 = { FL_STRING, { .string = { ".py", 3, 3 } } }; -static const fl_value opis_diska_text_35 = { FL_STRING, { .string = { ".js", 3, 3 } } }; -static const fl_value opis_diska_text_36 = { FL_STRING, { .string = { ".ts", 3, 3 } } }; -static const fl_value opis_diska_text_37 = { FL_STRING, { .string = { ".java", 5, 5 } } }; -static const fl_value opis_diska_text_38 = { FL_STRING, { .string = { ".kt", 3, 3 } } }; -static const fl_value opis_diska_text_39 = { FL_STRING, { .string = { ".rb", 3, 3 } } }; -static const fl_value opis_diska_text_40 = { FL_STRING, { .string = { ".php", 4, 4 } } }; -static const fl_value opis_diska_text_41 = { FL_STRING, { .string = { ".cs", 3, 3 } } }; -static const fl_value opis_diska_text_42 = { FL_STRING, { .string = { ".swift", 6, 6 } } }; -static const fl_value opis_diska_text_43 = { FL_STRING, { .string = { ".ex", 3, 3 } } }; -static const fl_value opis_diska_text_44 = { FL_STRING, { .string = { ".exs", 4, 4 } } }; -static const fl_value opis_diska_text_45 = { FL_STRING, { .string = { ".erl", 4, 4 } } }; -static const fl_value opis_diska_text_46 = { FL_STRING, { .string = { ".lua", 4, 4 } } }; -static const fl_value opis_diska_text_47 = { FL_STRING, { .string = { ".pl", 3, 3 } } }; -static const fl_value opis_diska_text_48 = { FL_STRING, { .string = { ".sh", 3, 3 } } }; -static const fl_value opis_diska_text_49 = { FL_STRING, { .string = { ".sql", 4, 4 } } }; -static const fl_value opis_diska_text_50 = { FL_STRING, { .string = { ".flang", 6, 6 } } }; -static const fl_value opis_diska_text_51 = { FL_STRING, { .string = { ".md", 3, 3 } } }; -static const fl_value opis_diska_text_52 = { FL_STRING, { .string = { ".txt", 4, 4 } } }; -static const fl_value opis_diska_text_53 = { FL_STRING, { .string = { ".rst", 4, 4 } } }; -static const fl_value opis_diska_text_54 = { FL_STRING, { .string = { ".tex", 4, 4 } } }; -static const fl_value opis_diska_text_55 = { FL_STRING, { .string = { ".html", 5, 5 } } }; -static const fl_value opis_diska_text_56 = { FL_STRING, { .string = { ".css", 4, 4 } } }; -static const fl_value opis_diska_text_57 = { FL_STRING, { .string = { ".hg", 3, 3 } } }; -static const fl_value opis_diska_text_58 = { FL_STRING, { .string = { ".svn", 4, 4 } } }; - - -/* Фабрика записи FTS «Находка». */ -fl_status opis_diska_sozdat_nahodka(fl_ctx *ctx, fl_value put, fl_value razmer, fl_value vozrast_dney, fl_value vid, fl_value dostupen, fl_value *out, fl_error *error) { - fl_value values[5]; - values[0] = put; /* «путь» */ - values[1] = razmer; /* «размер» */ - values[2] = vozrast_dney; /* «возраст_дней» */ - values[3] = vid; /* «вид» */ - values[4] = dostupen; /* «доступен» */ - return fl_record_new(ctx, opis_diska_names_1, values, 5, out, error); -} - -/* Фабрика записи FTS «Место». */ -fl_status opis_diska_sozdat_mesto(fl_ctx *ctx, fl_value razryad, fl_value yakor, fl_value cep, fl_value *out, fl_error *error) { - fl_value values[3]; - values[0] = razryad; /* «разряд» */ - values[1] = yakor; /* «якорь» */ - values[2] = cep; /* «цепь» */ - return fl_record_new(ctx, opis_diska_names_2, values, 3, out, error); -} - -/* Фабрика записи FTS «Решение». */ -fl_status opis_diska_sozdat_reshenie(fl_ctx *ctx, fl_value razryad, fl_value prigovor, fl_value ves, fl_value *out, fl_error *error) { - fl_value values[3]; - values[0] = razryad; /* «разряд» */ - values[1] = prigovor; /* «приговор» */ - values[2] = ves; /* «вес» */ - return fl_record_new(ctx, opis_diska_names_3, values, 3, out, error); -} - -/* Фабрика записи FTS «Строка отчёта». */ -fl_status opis_diska_sozdat_stroka_otchyota(fl_ctx *ctx, fl_value put, fl_value reshenie, fl_value *out, fl_error *error) { - fl_value values[2]; - values[0] = put; /* «путь» */ - values[1] = reshenie; /* «решение» */ - return fl_record_new(ctx, opis_diska_names_4, values, 2, out, error); -} - -/* Фабрика записи FTS «Свод». */ -fl_status opis_diska_sozdat_svod(fl_ctx *ctx, fl_value kesh, fl_value zhurnal, fl_value sborka, fl_value zagruzka, fl_value krupnoe, fl_value neizvestnoe, fl_value osvobodit, fl_value *out, fl_error *error) { - fl_value values[7]; - values[0] = kesh; /* «кэш» */ - values[1] = zhurnal; /* «журнал» */ - values[2] = sborka; /* «сборка» */ - values[3] = zagruzka; /* «загрузка» */ - values[4] = krupnoe; /* «крупное» */ - values[5] = neizvestnoe; /* «неизвестное» */ - values[6] = osvobodit; /* «освободить» */ - return fl_record_new(ctx, opis_diska_names_5, values, 7, out, error); -} - -/* Конструктор варианта «Файл» суммы «Вид». */ -fl_status opis_diska_variant_fayl(fl_ctx *ctx, fl_value *out, fl_error *error) { - return fl_variant_new(ctx, "Файл", NULL, NULL, 0, out, error); -} - -/* Конструктор варианта «Каталог» суммы «Вид». */ -fl_status opis_diska_variant_katalog(fl_ctx *ctx, fl_value *out, fl_error *error) { - return fl_variant_new(ctx, "Каталог", NULL, NULL, 0, out, error); -} - -/* Конструктор варианта «Ссылка» суммы «Вид». */ -fl_status opis_diska_variant_ssylka(fl_ctx *ctx, fl_value *out, fl_error *error) { - return fl_variant_new(ctx, "Ссылка", NULL, NULL, 0, out, error); -} - -/* Конструктор варианта «Кэш» суммы «Разряд». */ -fl_status opis_diska_variant_kesh(fl_ctx *ctx, fl_value *out, fl_error *error) { - return fl_variant_new(ctx, "Кэш", NULL, NULL, 0, out, error); -} - -/* Конструктор варианта «Журнал» суммы «Разряд». */ -fl_status opis_diska_variant_zhurnal(fl_ctx *ctx, fl_value *out, fl_error *error) { - return fl_variant_new(ctx, "Журнал", NULL, NULL, 0, out, error); -} - -/* Конструктор варианта «Сборка» суммы «Разряд». */ -fl_status opis_diska_variant_sborka(fl_ctx *ctx, fl_value *out, fl_error *error) { - return fl_variant_new(ctx, "Сборка", NULL, NULL, 0, out, error); -} - -/* Конструктор варианта «Загрузка» суммы «Разряд». */ -fl_status opis_diska_variant_zagruzka(fl_ctx *ctx, fl_value *out, fl_error *error) { - return fl_variant_new(ctx, "Загрузка", NULL, NULL, 0, out, error); -} - -/* Конструктор варианта «Крупное» суммы «Разряд». */ -fl_status opis_diska_variant_krupnoe(fl_ctx *ctx, fl_value *out, fl_error *error) { - return fl_variant_new(ctx, "Крупное", NULL, NULL, 0, out, error); -} - -/* Конструктор варианта «Неизвестное» суммы «Разряд». */ -fl_status opis_diska_variant_neizvestnoe(fl_ctx *ctx, fl_value *out, fl_error *error) { - return fl_variant_new(ctx, "Неизвестное", NULL, NULL, 0, out, error); -} - -/* Конструктор варианта «МожноУбрать» суммы «Приговор». */ -fl_status opis_diska_variant_mozhnoubrat(fl_ctx *ctx, fl_value *out, fl_error *error) { - return fl_variant_new(ctx, "МожноУбрать", NULL, NULL, 0, out, error); -} - -/* Конструктор варианта «Спросить» суммы «Приговор». */ -fl_status opis_diska_variant_sprosit(fl_ctx *ctx, fl_value *out, fl_error *error) { - return fl_variant_new(ctx, "Спросить", NULL, NULL, 0, out, error); -} - -/* Конструктор варианта «НеТрогать» суммы «Приговор». */ -fl_status opis_diska_variant_netrogat(fl_ctx *ctx, fl_value *out, fl_error *error) { - return fl_variant_new(ctx, "НеТрогать", NULL, NULL, 0, out, error); -} - -/* Конструктор варианта «ОтКорня» суммы «Якорь». */ -fl_status opis_diska_variant_otkornya(fl_ctx *ctx, fl_value *out, fl_error *error) { - return fl_variant_new(ctx, "ОтКорня", NULL, NULL, 0, out, error); -} - -/* Конструктор варианта «ГдеУгодно» суммы «Якорь». */ -fl_status opis_diska_variant_gdeugodno(fl_ctx *ctx, fl_value *out, fl_error *error) { - return fl_variant_new(ctx, "ГдеУгодно", NULL, NULL, 0, out, error); -} - -/* Конструктор варианта «Мусор» суммы «Природа». */ -fl_status opis_diska_variant_musor(fl_ctx *ctx, fl_value *out, fl_error *error) { - return fl_variant_new(ctx, "Мусор", NULL, NULL, 0, out, error); -} - -/* Конструктор варианта «Свежее» суммы «Природа». */ -fl_status opis_diska_variant_svezhee(fl_ctx *ctx, fl_value *out, fl_error *error) { - return fl_variant_new(ctx, "Свежее", NULL, NULL, 0, out, error); -} - -/* Конструктор варианта «Исходники» суммы «Природа». */ -fl_status opis_diska_variant_ishodniki(fl_ctx *ctx, fl_value *out, fl_error *error) { - return fl_variant_new(ctx, "Исходники", NULL, NULL, 0, out, error); -} - -/* Конструктор варианта «Личное» суммы «Природа». */ -fl_status opis_diska_variant_lichnoe(fl_ctx *ctx, fl_value *out, fl_error *error) { - return fl_variant_new(ctx, "Личное", NULL, NULL, 0, out, error); -} - -/* Конструктор варианта «Хранилище» суммы «Природа». */ -fl_status opis_diska_variant_hranilische(fl_ctx *ctx, fl_value *out, fl_error *error) { - return fl_variant_new(ctx, "Хранилище", NULL, NULL, 0, out, error); -} - -/* Конструктор варианта «ПодПрисмотром» суммы «Природа». */ -fl_status opis_diska_variant_podprismotrom(fl_ctx *ctx, fl_value *out, fl_error *error) { - return fl_variant_new(ctx, "ПодПрисмотром", NULL, NULL, 0, out, error); -} - -/* - * Функция flang «Порог крупного». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @return значение: число - */ -fl_status opis_diska_porog_krupnogo(fl_ctx *ctx, fl_value *result, fl_error *error) { - (void)ctx; - (void)error; - *result = fl_number(1073741824.0); - return FL_OK; -} - -/* - * Функция flang «Порог кэша». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @return значение: число - */ -fl_status opis_diska_porog_kesha(fl_ctx *ctx, fl_value *result, fl_error *error) { - (void)ctx; - (void)error; - *result = fl_number(7.0); - return FL_OK; -} - -/* - * Функция flang «Порог журнала». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @return значение: число - */ -fl_status opis_diska_porog_zhurnala(fl_ctx *ctx, fl_value *result, fl_error *error) { - (void)ctx; - (void)error; - *result = fl_number(30.0); - return FL_OK; -} - -/* - * Функция flang «Порог загрузки». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @return значение: число - */ -fl_status opis_diska_porog_zagruzki(fl_ctx *ctx, fl_value *result, fl_error *error) { - (void)ctx; - (void)error; - *result = fl_number(180.0); - return FL_OK; -} - -/* - * Функция flang «Составляющие пути». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param put — «путь»: строка - * @return значение: список: строка - */ -fl_status opis_diska_sostavlyayuschie_puti(fl_ctx *ctx, fl_value put, fl_value *result, fl_error *error) { - fl_value fl_t1 = fl_nothing(); /* «разделить» */ - FL_TRY(fl_b_razdelit_dokazano(ctx, put, opis_diska_text_6, &fl_t1, error)); - *result = fl_t1; - return FL_OK; -} - -/* - * Функция flang «Имя в пути». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param put — «путь»: строка - * @return значение: строка - */ -fl_status opis_diska_imya_v_puti(fl_ctx *ctx, fl_value put, fl_value *result, fl_error *error) { - fl_value fl_t2 = fl_nothing(); - FL_TRY(opis_diska_sostavlyayuschie_puti(ctx, put, &fl_t2, error)); - fl_value fl_t3 = fl_nothing(); - FL_TRY(fl_require_list(ctx, fl_t2, "свёртка", &fl_t3, error)); - fl_value sobrano = opis_diska_text_7; /* «собрано» */ - const fl_mark fl_t5 = fl_region_open(ctx); - for (size_t fl_t4 = 0; fl_t4 < fl_t3.as.list.count; fl_t4 += 1) { - const fl_value chast = fl_t3.as.list.items[fl_t4]; /* «часть» */ - sobrano = chast; - FL_TRY(fl_region_recycle(ctx, fl_t5, &sobrano, error)); - } - FL_TRY(fl_region_close(ctx, fl_t5, FL_OK, &sobrano, error)); - *result = sobrano; - return FL_OK; -} - -/* - * Функция flang «Есть составляющая». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param put — «путь»: строка - * @param imya — «имя»: строка - * @return значение - */ -fl_status opis_diska_est_sostavlyayuschaya(fl_ctx *ctx, fl_value put, fl_value imya, fl_value *result, fl_error *error) { - fl_value fl_t6 = fl_nothing(); - FL_TRY(opis_diska_sostavlyayuschie_puti(ctx, put, &fl_t6, error)); - fl_value fl_t7 = fl_nothing(); /* «содержит» */ - FL_TRY(fl_b_soderzhit(ctx, fl_t6, imya, &fl_t7, error)); - *result = fl_t7; - return FL_OK; -} - -/* - * Функция flang «Оканчивается на». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param tekst — «текст»: строка - * @param hvost — «хвост»: строка - * @return значение - */ -fl_status opis_diska_okanchivaetsya_na(fl_ctx *ctx, fl_value tekst, fl_value hvost, fl_value *result, fl_error *error) { - fl_value fl_t8 = fl_nothing(); /* «длина» */ - FL_TRY(fl_b_dlina(ctx, tekst, &fl_t8, error)); - fl_value fl_t9 = fl_nothing(); /* «длина» */ - FL_TRY(fl_b_dlina(ctx, hvost, &fl_t9, error)); - if (fl_t8.tag != FL_NUMBER || fl_t9.tag != FL_NUMBER) FL_TRY(fl_not_order(ctx, fl_t8, fl_t9, error)); - bool fl_t10 = false; - FL_TRY(fl_cond(ctx, fl_flag(fl_t8.as.number < fl_t9.as.number), &fl_t10, error)); - if (fl_t10) { - *result = fl_flag(false); - return FL_OK; - } else { - fl_value fl_t11 = fl_nothing(); /* «длина» */ - FL_TRY(fl_b_dlina(ctx, tekst, &fl_t11, error)); - fl_value fl_t12 = fl_nothing(); /* «длина» */ - FL_TRY(fl_b_dlina(ctx, hvost, &fl_t12, error)); - if (fl_t11.tag != FL_NUMBER || fl_t12.tag != FL_NUMBER) FL_TRY(fl_not_numbers(ctx, "sub", fl_t11, fl_t12, error)); - fl_value fl_t13 = fl_nothing(); /* «длина» */ - FL_TRY(fl_b_dlina(ctx, tekst, &fl_t13, error)); - fl_value fl_t14 = fl_nothing(); /* «подстрока» */ - FL_TRY(fl_b_podstroka(ctx, tekst, fl_number((fl_t11.as.number - fl_t12.as.number) + 1.0), fl_t13, &fl_t14, error)); - *result = fl_flag(fl_equal(fl_t14, hvost)); - return FL_OK; - } -} - -/* - * Функция flang «След пути». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param put — «путь»: строка - * @return значение: строка - */ -fl_status opis_diska_sled_puti(fl_ctx *ctx, fl_value put, fl_value *result, fl_error *error) { - fl_value fl_t15 = fl_nothing(); - FL_TRY(fl_concat(ctx, put, opis_diska_text_6, &fl_t15, error)); - *result = fl_t15; - return FL_OK; -} - -/* - * Функция flang «Цепь ограничена». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param cep — «цепь»: строка - * @return значение - */ -fl_status opis_diska_cep_ogranichena(fl_ctx *ctx, fl_value cep, fl_value *result, fl_error *error) { - fl_value fl_t16 = fl_nothing(); /* «начинается с» */ - FL_TRY(fl_b_nachinaetsya_s(ctx, cep, opis_diska_text_6, &fl_t16, error)); - bool fl_t17 = false; - FL_TRY(fl_cond(ctx, fl_t16, &fl_t17, error)); - fl_value fl_t18 = fl_nothing(); - if (fl_t17) { - fl_value fl_t19 = fl_nothing(); - FL_TRY(opis_diska_okanchivaetsya_na(ctx, cep, opis_diska_text_6, &fl_t19, error)); - fl_t18 = fl_t19; - } else { - fl_t18 = fl_flag(false); - } - bool fl_t20 = false; - FL_TRY(fl_cond(ctx, fl_t18, &fl_t20, error)); - if (fl_t20) { - fl_value fl_t21 = fl_nothing(); /* «длина» */ - FL_TRY(fl_b_dlina(ctx, cep, &fl_t21, error)); - if (fl_t21.tag != FL_NUMBER) FL_TRY(fl_not_order(ctx, fl_t21, fl_number(1.0), error)); - *result = fl_flag(fl_t21.as.number > 1.0); - return FL_OK; - } else { - *result = fl_flag(false); - return FL_OK; - } -} - -/* - * Функция flang «Справочник ограничен». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param spravochnik — «справочник»: список: «Место» - * @return значение - */ -fl_status opis_diska_spravochnik_ogranichen(fl_ctx *ctx, fl_value spravochnik, fl_value *result, fl_error *error) { - fl_value fl_t22 = fl_nothing(); - FL_TRY(fl_require_list(ctx, spravochnik, "свёртка", &fl_t22, error)); - fl_value akk = fl_flag(true); /* «акк» */ - const fl_mark fl_t24 = fl_region_open(ctx); - for (size_t fl_t23 = 0; fl_t23 < fl_t22.as.list.count; fl_t23 += 1) { - const fl_value mesto = fl_t22.as.list.items[fl_t23]; /* «место» */ - bool fl_t25 = false; - FL_TRY(fl_cond(ctx, akk, &fl_t25, error)); - fl_value fl_t26 = fl_nothing(); - if (fl_t25) { - fl_value fl_t27 = fl_nothing(); - FL_TRY(fl_field_get(ctx, mesto, "цепь", &fl_t27, error)); - fl_value fl_t28 = fl_nothing(); - FL_TRY(opis_diska_cep_ogranichena(ctx, fl_t27, &fl_t28, error)); - fl_t26 = fl_t28; - } else { - fl_t26 = fl_flag(false); - } - akk = fl_t26; - FL_TRY(fl_region_recycle(ctx, fl_t24, &akk, error)); - } - FL_TRY(fl_region_close(ctx, fl_t24, FL_OK, &akk, error)); - *result = akk; - return FL_OK; -} - -/* - * Функция flang «Разряд места допустим». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param razryad — «разряд»: «Разряд» - * @return значение - */ -fl_status opis_diska_razryad_mesta_dopustim(fl_ctx *ctx, fl_value razryad, fl_value *result, fl_error *error) { - if (fl_variant_is(razryad, "Кэш")) { - *result = fl_flag(true); - return FL_OK; - } else if (fl_variant_is(razryad, "Журнал")) { - *result = fl_flag(true); - return FL_OK; - } else if (fl_variant_is(razryad, "Сборка")) { - *result = fl_flag(true); - return FL_OK; - } else if (fl_variant_is(razryad, "Загрузка")) { - *result = fl_flag(true); - return FL_OK; - } else if (fl_variant_is(razryad, "Крупное")) { - *result = fl_flag(false); - return FL_OK; - } else if (fl_variant_is(razryad, "Неизвестное")) { - *result = fl_flag(false); - return FL_OK; - } else { - return fl_match_fail(ctx, razryad, error); - } -} - -/* - * Функция flang «Место подходит». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param sled — «след»: строка - * @param mesto — «место»: «Место» - * @return значение - */ -fl_status opis_diska_mesto_podhodit(fl_ctx *ctx, fl_value sled, fl_value mesto, fl_value *result, fl_error *error) { - fl_value fl_t29 = fl_nothing(); - FL_TRY(fl_field_get(ctx, mesto, "якорь", &fl_t29, error)); - if (fl_variant_is(fl_t29, "ОтКорня")) { - fl_value fl_t30 = fl_nothing(); - FL_TRY(fl_field_get(ctx, mesto, "цепь", &fl_t30, error)); - fl_value fl_t31 = fl_nothing(); /* «начинается с» */ - FL_TRY(fl_b_nachinaetsya_s(ctx, sled, fl_t30, &fl_t31, error)); - *result = fl_t31; - return FL_OK; - } else if (fl_variant_is(fl_t29, "ГдеУгодно")) { - fl_value fl_t32 = fl_nothing(); - FL_TRY(fl_field_get(ctx, mesto, "цепь", &fl_t32, error)); - fl_value fl_t33 = fl_nothing(); /* «содержит» */ - FL_TRY(fl_b_soderzhit(ctx, sled, fl_t32, &fl_t33, error)); - *result = fl_t33; - return FL_OK; - } else { - return fl_match_fail(ctx, fl_t29, error); - } -} - -/* - * Функция flang «Это неизвестное». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param razryad — «разряд»: «Разряд» - * @return значение - */ -fl_status opis_diska_eto_neizvestnoe(fl_ctx *ctx, fl_value razryad, fl_value *result, fl_error *error) { - if (fl_variant_is(razryad, "Неизвестное")) { - *result = fl_flag(true); - return FL_OK; - } else if (fl_variant_is(razryad, "Кэш")) { - *result = fl_flag(false); - return FL_OK; - } else if (fl_variant_is(razryad, "Журнал")) { - *result = fl_flag(false); - return FL_OK; - } else if (fl_variant_is(razryad, "Сборка")) { - *result = fl_flag(false); - return FL_OK; - } else if (fl_variant_is(razryad, "Загрузка")) { - *result = fl_flag(false); - return FL_OK; - } else if (fl_variant_is(razryad, "Крупное")) { - *result = fl_flag(false); - return FL_OK; - } else { - return fl_match_fail(ctx, razryad, error); - } -} - -/* - * Функция flang «Номер разряда». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param razryad — «разряд»: «Разряд» - * @return значение: число - */ -fl_status opis_diska_nomer_razryada(fl_ctx *ctx, fl_value razryad, fl_value *result, fl_error *error) { - if (fl_variant_is(razryad, "Кэш")) { - *result = fl_number(1.0); - return FL_OK; - } else if (fl_variant_is(razryad, "Журнал")) { - *result = fl_number(2.0); - return FL_OK; - } else if (fl_variant_is(razryad, "Сборка")) { - *result = fl_number(3.0); - return FL_OK; - } else if (fl_variant_is(razryad, "Загрузка")) { - *result = fl_number(4.0); - return FL_OK; - } else if (fl_variant_is(razryad, "Крупное")) { - *result = fl_number(5.0); - return FL_OK; - } else if (fl_variant_is(razryad, "Неизвестное")) { - *result = fl_number(6.0); - return FL_OK; - } else { - return fl_match_fail(ctx, razryad, error); - } -} - -/* - * Функция flang «Тот же разряд». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param pervyy — «первый»: «Разряд» - * @param vtoroy — «второй»: «Разряд» - * @return значение - */ -fl_status opis_diska_tot_zhe_razryad(fl_ctx *ctx, fl_value pervyy, fl_value vtoroy, fl_value *result, fl_error *error) { - fl_value fl_t34 = fl_nothing(); - FL_TRY(opis_diska_nomer_razryada(ctx, pervyy, &fl_t34, error)); - fl_value fl_t35 = fl_nothing(); - FL_TRY(opis_diska_nomer_razryada(ctx, vtoroy, &fl_t35, error)); - *result = fl_flag(fl_equal(fl_t34, fl_t35)); - return FL_OK; -} - -/* - * Функция flang «Место обосновано». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param put — «путь»: строка - * @param spravochnik — «справочник»: список: «Место» - * @param razryad — «разряд»: «Разряд» - * @return значение - */ -fl_status opis_diska_mesto_obosnovano(fl_ctx *ctx, fl_value put, fl_value spravochnik, fl_value razryad, fl_value *result, fl_error *error) { - fl_value fl_t36 = fl_nothing(); - FL_TRY(opis_diska_eto_neizvestnoe(ctx, razryad, &fl_t36, error)); - bool fl_t37 = false; - FL_TRY(fl_cond(ctx, fl_t36, &fl_t37, error)); - if (fl_t37) { - *result = fl_flag(true); - return FL_OK; - } else { - fl_value fl_t38 = fl_nothing(); - FL_TRY(opis_diska_razryad_mesta_dopustim(ctx, razryad, &fl_t38, error)); - bool fl_t39 = false; - FL_TRY(fl_cond(ctx, fl_t38, &fl_t39, error)); - if (fl_t39) { - fl_value fl_t40 = fl_nothing(); - FL_TRY(fl_require_list(ctx, spravochnik, "отфильтровать", &fl_t40, error)); - fl_value *fl_t41 = NULL; - size_t fl_t42 = 0; - FL_TRY(fl_list_alloc(ctx, fl_t40.as.list.count, &fl_t41, error)); - for (size_t fl_t43 = 0; fl_t43 < fl_t40.as.list.count; fl_t43 += 1) { - const fl_value mesto = fl_t40.as.list.items[fl_t43]; /* «место» */ - fl_value fl_t44 = fl_nothing(); - FL_TRY(opis_diska_sled_puti(ctx, put, &fl_t44, error)); - fl_value fl_t45 = fl_nothing(); - FL_TRY(opis_diska_mesto_podhodit(ctx, fl_t44, mesto, &fl_t45, error)); - bool fl_t46 = false; - FL_TRY(fl_cond(ctx, fl_t45, &fl_t46, error)); - fl_value fl_t47 = fl_nothing(); - if (fl_t46) { - fl_value fl_t48 = fl_nothing(); - FL_TRY(fl_field_get(ctx, mesto, "разряд", &fl_t48, error)); - fl_value fl_t49 = fl_nothing(); - FL_TRY(opis_diska_tot_zhe_razryad(ctx, fl_t48, razryad, &fl_t49, error)); - fl_t47 = fl_t49; - } else { - fl_t47 = fl_flag(false); - } - bool fl_t50 = false; - FL_TRY(fl_keep(ctx, fl_t47, &fl_t50, error)); - if (fl_t50) { - fl_t41[fl_t42] = mesto; - fl_t42 += 1; - } - } - fl_value fl_t51 = fl_nothing(); /* «длина» */ - FL_TRY(fl_b_dlina(ctx, fl_list(fl_t41, fl_t42), &fl_t51, error)); - if (fl_t51.tag != FL_NUMBER) FL_TRY(fl_not_order(ctx, fl_t51, fl_number(0.0), error)); - *result = fl_flag(fl_t51.as.number > 0.0); - return FL_OK; - } else { - *result = fl_flag(false); - return FL_OK; - } - } -} - -/* - * Функция flang «Разряд по справочнику». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param put — «путь»: строка - * @param spravochnik — «справочник»: список: «Место» - * @return значение: «Разряд» - */ -fl_status opis_diska_razryad_po_spravochniku(fl_ctx *ctx, fl_value put, fl_value spravochnik, fl_value *result, fl_error *error) { - fl_value fl_t52 = fl_nothing(); - FL_TRY(opis_diska_sled_puti(ctx, put, &fl_t52, error)); - const fl_value sled = fl_t52; /* пусть «след» */ - fl_value fl_t53 = fl_nothing(); - FL_TRY(fl_require_list(ctx, spravochnik, "свёртка", &fl_t53, error)); - fl_value fl_t54 = fl_nothing(); - FL_TRY(fl_variant_new(ctx, "Неизвестное", NULL, NULL, 0, &fl_t54, error)); - fl_value naydeno = fl_t54; /* «найдено» */ - const fl_mark fl_t56 = fl_region_open(ctx); - for (size_t fl_t55 = 0; fl_t55 < fl_t53.as.list.count; fl_t55 += 1) { - const fl_value mesto = fl_t53.as.list.items[fl_t55]; /* «место» */ - fl_value fl_t57 = fl_nothing(); - FL_TRY(opis_diska_eto_neizvestnoe(ctx, naydeno, &fl_t57, error)); - bool fl_t58 = false; - FL_TRY(fl_cond(ctx, fl_t57, &fl_t58, error)); - fl_value fl_t59 = fl_nothing(); - if (fl_t58) { - fl_value fl_t60 = fl_nothing(); - FL_TRY(opis_diska_mesto_podhodit(ctx, sled, mesto, &fl_t60, error)); - fl_t59 = fl_t60; - } else { - fl_t59 = fl_flag(false); - } - bool fl_t61 = false; - FL_TRY(fl_cond(ctx, fl_t59, &fl_t61, error)); - fl_value fl_t62 = fl_nothing(); - if (fl_t61) { - fl_value fl_t63 = fl_nothing(); - FL_TRY(fl_field_get(ctx, mesto, "разряд", &fl_t63, error)); - fl_t62 = fl_t63; - } else { - fl_t62 = naydeno; - } - naydeno = fl_t62; - FL_TRY(fl_region_recycle(ctx, fl_t56, &naydeno, error)); - } - FL_TRY(fl_region_close(ctx, fl_t56, FL_OK, &naydeno, error)); - const fl_value fl_t64 = naydeno; - fl_value fl_t65 = fl_nothing(); - FL_TRY(opis_diska_mesto_obosnovano(ctx, put, spravochnik, fl_t64, &fl_t65, error)); - /* постусловие «Место обосновано записью справочника» */ - bool fl_t66 = false; - FL_TRY(fl_post(ctx, fl_t65, "Место обосновано записью справочника", "Разряд по справочнику", &fl_t66, error)); - if (!fl_t66) { - return fl_fail(ctx, error, "FLANG_PROPERTY", "%s", "нарушено свойство «Место обосновано записью справочника» функции «Разряд по справочнику»"); - } - *result = fl_t64; - return FL_OK; -} - -/* - * Функция flang «Шестнадцатеричный знак». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param znak — «знак»: строка - * @return значение - */ -fl_status opis_diska_shestnadcaterichnyy_znak(fl_ctx *ctx, fl_value znak, fl_value *result, fl_error *error) { - fl_value fl_t67 = fl_nothing(); /* «содержит» */ - FL_TRY(fl_b_soderzhit(ctx, opis_diska_text_8, znak, &fl_t67, error)); - *result = fl_t67; - return FL_OK; -} - -/* - * Функция flang «Похоже на отпечаток». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param chast — «часть»: строка - * @return значение - */ -fl_status opis_diska_pohozhe_na_otpechatok(fl_ctx *ctx, fl_value chast, fl_value *result, fl_error *error) { - fl_value fl_t68 = fl_nothing(); /* «длина» */ - FL_TRY(fl_b_dlina(ctx, chast, &fl_t68, error)); - if (fl_t68.tag != FL_NUMBER) FL_TRY(fl_not_order(ctx, fl_t68, fl_number(32.0), error)); - bool fl_t69 = false; - FL_TRY(fl_cond(ctx, fl_flag(fl_t68.as.number < 32.0), &fl_t69, error)); - if (fl_t69) { - *result = fl_flag(false); - return FL_OK; - } else { - fl_value fl_t70 = fl_nothing(); /* «символы» */ - FL_TRY(fl_b_simvoly(ctx, chast, &fl_t70, error)); - fl_value fl_t71 = fl_nothing(); - FL_TRY(fl_require_list(ctx, fl_t70, "свёртка", &fl_t71, error)); - fl_value sobrano = fl_flag(true); /* «собрано» */ - const fl_mark fl_t73 = fl_region_open(ctx); - for (size_t fl_t72 = 0; fl_t72 < fl_t71.as.list.count; fl_t72 += 1) { - const fl_value znak = fl_t71.as.list.items[fl_t72]; /* «знак» */ - bool fl_t74 = false; - FL_TRY(fl_cond(ctx, sobrano, &fl_t74, error)); - fl_value fl_t75 = fl_nothing(); - if (fl_t74) { - fl_value fl_t76 = fl_nothing(); - FL_TRY(opis_diska_shestnadcaterichnyy_znak(ctx, znak, &fl_t76, error)); - fl_t75 = fl_t76; - } else { - fl_t75 = fl_flag(false); - } - sobrano = fl_t75; - FL_TRY(fl_region_recycle(ctx, fl_t73, &sobrano, error)); - } - FL_TRY(fl_region_close(ctx, fl_t73, FL_OK, &sobrano, error)); - *result = sobrano; - return FL_OK; - } -} - -/* - * Функция flang «Адресуется содержимым». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param put — «путь»: строка - * @return значение - */ -fl_status opis_diska_adresuetsya_soderzhimym(fl_ctx *ctx, fl_value put, fl_value *result, fl_error *error) { - fl_value fl_t77 = fl_nothing(); - FL_TRY(opis_diska_sostavlyayuschie_puti(ctx, put, &fl_t77, error)); - fl_value fl_t78 = fl_nothing(); - FL_TRY(fl_require_list(ctx, fl_t77, "отфильтровать", &fl_t78, error)); - fl_value *fl_t79 = NULL; - size_t fl_t80 = 0; - FL_TRY(fl_list_alloc(ctx, fl_t78.as.list.count, &fl_t79, error)); - for (size_t fl_t81 = 0; fl_t81 < fl_t78.as.list.count; fl_t81 += 1) { - const fl_value chast = fl_t78.as.list.items[fl_t81]; /* «часть» */ - fl_value fl_t82 = fl_nothing(); - FL_TRY(opis_diska_pohozhe_na_otpechatok(ctx, chast, &fl_t82, error)); - bool fl_t83 = false; - FL_TRY(fl_keep(ctx, fl_t82, &fl_t83, error)); - if (fl_t83) { - fl_t79[fl_t80] = chast; - fl_t80 += 1; - } - } - fl_value fl_t84 = fl_nothing(); /* «длина» */ - FL_TRY(fl_b_dlina(ctx, fl_list(fl_t79, fl_t80), &fl_t84, error)); - if (fl_t84.tag != FL_NUMBER) FL_TRY(fl_not_order(ctx, fl_t84, fl_number(0.0), error)); - bool fl_t85 = false; - FL_TRY(fl_cond(ctx, fl_flag(fl_t84.as.number > 0.0), &fl_t85, error)); - fl_value fl_t86 = fl_nothing(); - if (fl_t85) { - fl_t86 = fl_flag(true); - } else { - fl_value fl_t87 = fl_nothing(); - FL_TRY(opis_diska_est_sostavlyayuschaya(ctx, put, opis_diska_text_9, &fl_t87, error)); - fl_t86 = fl_t87; - } - bool fl_t88 = false; - FL_TRY(fl_cond(ctx, fl_t86, &fl_t88, error)); - fl_value fl_t89 = fl_nothing(); - if (fl_t88) { - fl_t89 = fl_flag(true); - } else { - fl_value fl_t90 = fl_nothing(); - FL_TRY(opis_diska_est_sostavlyayuschaya(ctx, put, opis_diska_text_10, &fl_t90, error)); - fl_t89 = fl_t90; - } - bool fl_t91 = false; - FL_TRY(fl_cond(ctx, fl_t89, &fl_t91, error)); - if (fl_t91) { - *result = fl_flag(true); - return FL_OK; - } else { - return opis_diska_est_sostavlyayuschaya(ctx, put, opis_diska_text_11, result, error); - } -} - -/* - * Функция flang «Под системным временным». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param put — «путь»: строка - * @return значение - */ -fl_status opis_diska_pod_sistemnym_vremennym(fl_ctx *ctx, fl_value put, fl_value *result, fl_error *error) { - fl_value fl_t92 = fl_nothing(); /* «начинается с» */ - FL_TRY(fl_b_nachinaetsya_s(ctx, put, opis_diska_text_12, &fl_t92, error)); - bool fl_t93 = false; - FL_TRY(fl_cond(ctx, fl_t92, &fl_t93, error)); - if (fl_t93) { - *result = fl_flag(true); - return FL_OK; - } else { - fl_value fl_t94 = fl_nothing(); /* «начинается с» */ - FL_TRY(fl_b_nachinaetsya_s(ctx, put, opis_diska_text_13, &fl_t94, error)); - *result = fl_t94; - return FL_OK; - } -} - -/* - * Функция flang «Примета кэша». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param put — «путь»: строка - * @return значение - */ -fl_status opis_diska_primeta_kesha(fl_ctx *ctx, fl_value put, fl_value *result, fl_error *error) { - fl_value fl_t95 = fl_nothing(); - FL_TRY(opis_diska_est_sostavlyayuschaya(ctx, put, opis_diska_text_14, &fl_t95, error)); - bool fl_t96 = false; - FL_TRY(fl_cond(ctx, fl_t95, &fl_t96, error)); - fl_value fl_t97 = fl_nothing(); - if (fl_t96) { - fl_t97 = fl_flag(true); - } else { - fl_value fl_t98 = fl_nothing(); - FL_TRY(opis_diska_est_sostavlyayuschaya(ctx, put, opis_diska_text_15, &fl_t98, error)); - fl_t97 = fl_t98; - } - bool fl_t99 = false; - FL_TRY(fl_cond(ctx, fl_t97, &fl_t99, error)); - fl_value fl_t100 = fl_nothing(); - if (fl_t99) { - fl_t100 = fl_flag(true); - } else { - fl_value fl_t101 = fl_nothing(); - FL_TRY(opis_diska_est_sostavlyayuschaya(ctx, put, opis_diska_text_16, &fl_t101, error)); - fl_t100 = fl_t101; - } - bool fl_t102 = false; - FL_TRY(fl_cond(ctx, fl_t100, &fl_t102, error)); - if (fl_t102) { - *result = fl_flag(true); - return FL_OK; - } else { - return opis_diska_pod_sistemnym_vremennym(ctx, put, result, error); - } -} - -/* - * Функция flang «Примета журнала». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param put — «путь»: строка - * @return значение - */ -fl_status opis_diska_primeta_zhurnala(fl_ctx *ctx, fl_value put, fl_value *result, fl_error *error) { - fl_value fl_t103 = fl_nothing(); - FL_TRY(opis_diska_imya_v_puti(ctx, put, &fl_t103, error)); - fl_value fl_t104 = fl_nothing(); - FL_TRY(opis_diska_okanchivaetsya_na(ctx, fl_t103, opis_diska_text_17, &fl_t104, error)); - bool fl_t105 = false; - FL_TRY(fl_cond(ctx, fl_t104, &fl_t105, error)); - fl_value fl_t106 = fl_nothing(); - if (fl_t105) { - fl_t106 = fl_flag(true); - } else { - fl_value fl_t107 = fl_nothing(); - FL_TRY(opis_diska_est_sostavlyayuschaya(ctx, put, opis_diska_text_18, &fl_t107, error)); - fl_t106 = fl_t107; - } - bool fl_t108 = false; - FL_TRY(fl_cond(ctx, fl_t106, &fl_t108, error)); - if (fl_t108) { - *result = fl_flag(true); - return FL_OK; - } else { - return opis_diska_est_sostavlyayuschaya(ctx, put, opis_diska_text_19, result, error); - } -} - -/* - * Функция flang «Примета сборки». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param put — «путь»: строка - * @return значение - */ -fl_status opis_diska_primeta_sborki(fl_ctx *ctx, fl_value put, fl_value *result, fl_error *error) { - fl_value fl_t109 = fl_nothing(); - FL_TRY(opis_diska_est_sostavlyayuschaya(ctx, put, opis_diska_text_20, &fl_t109, error)); - bool fl_t110 = false; - FL_TRY(fl_cond(ctx, fl_t109, &fl_t110, error)); - fl_value fl_t111 = fl_nothing(); - if (fl_t110) { - fl_t111 = fl_flag(true); - } else { - fl_value fl_t112 = fl_nothing(); - FL_TRY(opis_diska_est_sostavlyayuschaya(ctx, put, opis_diska_text_21, &fl_t112, error)); - fl_t111 = fl_t112; - } - bool fl_t113 = false; - FL_TRY(fl_cond(ctx, fl_t111, &fl_t113, error)); - fl_value fl_t114 = fl_nothing(); - if (fl_t113) { - fl_t114 = fl_flag(true); - } else { - fl_value fl_t115 = fl_nothing(); - FL_TRY(opis_diska_est_sostavlyayuschaya(ctx, put, opis_diska_text_22, &fl_t115, error)); - fl_t114 = fl_t115; - } - bool fl_t116 = false; - FL_TRY(fl_cond(ctx, fl_t114, &fl_t116, error)); - fl_value fl_t117 = fl_nothing(); - if (fl_t116) { - fl_t117 = fl_flag(true); - } else { - fl_value fl_t118 = fl_nothing(); - FL_TRY(opis_diska_est_sostavlyayuschaya(ctx, put, opis_diska_text_23, &fl_t118, error)); - fl_t117 = fl_t118; - } - bool fl_t119 = false; - FL_TRY(fl_cond(ctx, fl_t117, &fl_t119, error)); - if (fl_t119) { - *result = fl_flag(true); - return FL_OK; - } else { - return opis_diska_est_sostavlyayuschaya(ctx, put, opis_diska_text_24, result, error); - } -} - -/* - * Функция flang «Примета загрузки». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param put — «путь»: строка - * @return значение - */ -fl_status opis_diska_primeta_zagruzki(fl_ctx *ctx, fl_value put, fl_value *result, fl_error *error) { - fl_value fl_t120 = fl_nothing(); - FL_TRY(opis_diska_est_sostavlyayuschaya(ctx, put, opis_diska_text_25, &fl_t120, error)); - bool fl_t121 = false; - FL_TRY(fl_cond(ctx, fl_t120, &fl_t121, error)); - if (fl_t121) { - *result = fl_flag(true); - return FL_OK; - } else { - return opis_diska_est_sostavlyayuschaya(ctx, put, opis_diska_text_26, result, error); - } -} - -/* - * Функция flang «Есть примета». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param put — «путь»: строка - * @param mesto — «место»: «Разряд» - * @return значение - */ -fl_status opis_diska_est_primeta(fl_ctx *ctx, fl_value put, fl_value mesto, fl_value *result, fl_error *error) { - fl_value fl_t122 = fl_nothing(); - FL_TRY(opis_diska_eto_neizvestnoe(ctx, mesto, &fl_t122, error)); - bool fl_t123 = false; - FL_TRY(fl_cond(ctx, fl_t122, &fl_t123, error)); - fl_value fl_t124 = fl_nothing(); - if (fl_t123) { - fl_t124 = fl_flag(false); - } else { - fl_t124 = fl_flag(true); - } - bool fl_t125 = false; - FL_TRY(fl_cond(ctx, fl_t124, &fl_t125, error)); - fl_value fl_t126 = fl_nothing(); - if (fl_t125) { - fl_t126 = fl_flag(true); - } else { - fl_value fl_t127 = fl_nothing(); - FL_TRY(opis_diska_primeta_kesha(ctx, put, &fl_t127, error)); - fl_t126 = fl_t127; - } - bool fl_t128 = false; - FL_TRY(fl_cond(ctx, fl_t126, &fl_t128, error)); - fl_value fl_t129 = fl_nothing(); - if (fl_t128) { - fl_t129 = fl_flag(true); - } else { - fl_value fl_t130 = fl_nothing(); - FL_TRY(opis_diska_primeta_zhurnala(ctx, put, &fl_t130, error)); - fl_t129 = fl_t130; - } - bool fl_t131 = false; - FL_TRY(fl_cond(ctx, fl_t129, &fl_t131, error)); - fl_value fl_t132 = fl_nothing(); - if (fl_t131) { - fl_t132 = fl_flag(true); - } else { - fl_value fl_t133 = fl_nothing(); - FL_TRY(opis_diska_primeta_sborki(ctx, put, &fl_t133, error)); - fl_t132 = fl_t133; - } - bool fl_t134 = false; - FL_TRY(fl_cond(ctx, fl_t132, &fl_t134, error)); - if (fl_t134) { - *result = fl_flag(true); - return FL_OK; - } else { - return opis_diska_primeta_zagruzki(ctx, put, result, error); - } -} - -/* - * Функция flang «Разряд решён размером». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param razryad — «разряд»: «Разряд» - * @return значение - */ -fl_status opis_diska_razryad_reshyon_razmerom(fl_ctx *ctx, fl_value razryad, fl_value *result, fl_error *error) { - if (fl_variant_is(razryad, "Крупное")) { - *result = fl_flag(true); - return FL_OK; - } else if (fl_variant_is(razryad, "Неизвестное")) { - *result = fl_flag(true); - return FL_OK; - } else if (fl_variant_is(razryad, "Кэш")) { - *result = fl_flag(false); - return FL_OK; - } else if (fl_variant_is(razryad, "Журнал")) { - *result = fl_flag(false); - return FL_OK; - } else if (fl_variant_is(razryad, "Сборка")) { - *result = fl_flag(false); - return FL_OK; - } else if (fl_variant_is(razryad, "Загрузка")) { - *result = fl_flag(false); - return FL_OK; - } else { - return fl_match_fail(ctx, razryad, error); - } -} - -/* - * Функция flang «Разряд находки». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param nahodka — «находка»: «Находка» - * @param mesto — «место»: «Разряд» - * @return значение: «Разряд» - */ -fl_status opis_diska_razryad_nahodki(fl_ctx *ctx, fl_value nahodka, fl_value mesto, fl_value *result, fl_error *error) { - fl_value fl_t135 = fl_nothing(); - FL_TRY(opis_diska_eto_neizvestnoe(ctx, mesto, &fl_t135, error)); - bool fl_t136 = false; - FL_TRY(fl_cond(ctx, fl_t135, &fl_t136, error)); - fl_value fl_t137 = fl_nothing(); - if (fl_t136) { - fl_t137 = fl_flag(false); - } else { - fl_t137 = fl_flag(true); - } - bool fl_t138 = false; - FL_TRY(fl_cond(ctx, fl_t137, &fl_t138, error)); - fl_value fl_t139 = fl_nothing(); - if (fl_t138) { - fl_t139 = mesto; - } else { - fl_value fl_t140 = fl_nothing(); - FL_TRY(fl_field_get(ctx, nahodka, "путь", &fl_t140, error)); - fl_value fl_t141 = fl_nothing(); - FL_TRY(opis_diska_primeta_kesha(ctx, fl_t140, &fl_t141, error)); - bool fl_t142 = false; - FL_TRY(fl_cond(ctx, fl_t141, &fl_t142, error)); - fl_value fl_t143 = fl_nothing(); - if (fl_t142) { - fl_value fl_t144 = fl_nothing(); - FL_TRY(fl_variant_new(ctx, "Кэш", NULL, NULL, 0, &fl_t144, error)); - fl_t143 = fl_t144; - } else { - fl_value fl_t145 = fl_nothing(); - FL_TRY(fl_field_get(ctx, nahodka, "путь", &fl_t145, error)); - fl_value fl_t146 = fl_nothing(); - FL_TRY(opis_diska_primeta_zhurnala(ctx, fl_t145, &fl_t146, error)); - bool fl_t147 = false; - FL_TRY(fl_cond(ctx, fl_t146, &fl_t147, error)); - fl_value fl_t148 = fl_nothing(); - if (fl_t147) { - fl_value fl_t149 = fl_nothing(); - FL_TRY(fl_variant_new(ctx, "Журнал", NULL, NULL, 0, &fl_t149, error)); - fl_t148 = fl_t149; - } else { - fl_value fl_t150 = fl_nothing(); - FL_TRY(fl_field_get(ctx, nahodka, "путь", &fl_t150, error)); - fl_value fl_t151 = fl_nothing(); - FL_TRY(opis_diska_primeta_sborki(ctx, fl_t150, &fl_t151, error)); - bool fl_t152 = false; - FL_TRY(fl_cond(ctx, fl_t151, &fl_t152, error)); - fl_value fl_t153 = fl_nothing(); - if (fl_t152) { - fl_value fl_t154 = fl_nothing(); - FL_TRY(fl_variant_new(ctx, "Сборка", NULL, NULL, 0, &fl_t154, error)); - fl_t153 = fl_t154; - } else { - fl_value fl_t155 = fl_nothing(); - FL_TRY(fl_field_get(ctx, nahodka, "путь", &fl_t155, error)); - fl_value fl_t156 = fl_nothing(); - FL_TRY(opis_diska_primeta_zagruzki(ctx, fl_t155, &fl_t156, error)); - bool fl_t157 = false; - FL_TRY(fl_cond(ctx, fl_t156, &fl_t157, error)); - fl_value fl_t158 = fl_nothing(); - if (fl_t157) { - fl_value fl_t159 = fl_nothing(); - FL_TRY(fl_variant_new(ctx, "Загрузка", NULL, NULL, 0, &fl_t159, error)); - fl_t158 = fl_t159; - } else { - fl_value fl_t160 = fl_nothing(); - FL_TRY(fl_field_get(ctx, nahodka, "размер", &fl_t160, error)); - fl_value fl_t161 = fl_nothing(); - FL_TRY(opis_diska_porog_krupnogo(ctx, &fl_t161, error)); - if (fl_t160.tag != FL_NUMBER || fl_t161.tag != FL_NUMBER) FL_TRY(fl_not_order(ctx, fl_t160, fl_t161, error)); - bool fl_t162 = false; - FL_TRY(fl_cond(ctx, fl_flag(fl_t160.as.number >= fl_t161.as.number), &fl_t162, error)); - fl_value fl_t163 = fl_nothing(); - if (fl_t162) { - fl_value fl_t164 = fl_nothing(); - FL_TRY(fl_variant_new(ctx, "Крупное", NULL, NULL, 0, &fl_t164, error)); - fl_t163 = fl_t164; - } else { - fl_value fl_t165 = fl_nothing(); - FL_TRY(fl_variant_new(ctx, "Неизвестное", NULL, NULL, 0, &fl_t165, error)); - fl_t163 = fl_t165; - } - fl_t158 = fl_t163; - } - fl_t153 = fl_t158; - } - fl_t148 = fl_t153; - } - fl_t143 = fl_t148; - } - fl_t139 = fl_t143; - } - const fl_value fl_t166 = fl_t139; - fl_value fl_t167 = fl_nothing(); - FL_TRY(opis_diska_razryad_obosnovan(ctx, nahodka, mesto, fl_t166, &fl_t167, error)); - /* постусловие «Разряд обоснован приметой или местом» */ - bool fl_t168 = false; - FL_TRY(fl_post(ctx, fl_t167, "Разряд обоснован приметой или местом", "Разряд находки", &fl_t168, error)); - if (!fl_t168) { - return fl_fail(ctx, error, "FLANG_PROPERTY", "%s", "нарушено свойство «Разряд обоснован приметой или местом» функции «Разряд находки»"); - } - fl_value fl_t169 = fl_nothing(); - FL_TRY(fl_field_get(ctx, nahodka, "путь", &fl_t169, error)); - fl_value fl_t170 = fl_nothing(); - FL_TRY(opis_diska_est_primeta(ctx, fl_t169, mesto, &fl_t170, error)); - bool fl_t171 = false; - FL_TRY(fl_cond(ctx, fl_t170, &fl_t171, error)); - fl_value fl_t172 = fl_nothing(); - if (fl_t171) { - fl_t172 = fl_flag(false); - } else { - fl_t172 = fl_flag(true); - } - bool fl_t173 = false; - FL_TRY(fl_cond(ctx, fl_t172, &fl_t173, error)); - fl_value fl_t174 = fl_nothing(); - if (fl_t173) { - fl_value fl_t175 = fl_nothing(); - FL_TRY(opis_diska_razryad_reshyon_razmerom(ctx, fl_t166, &fl_t175, error)); - fl_t174 = fl_t175; - } else { - fl_t174 = fl_flag(true); - } - /* постусловие «И4: без приметы-составляющей и без места разряд решает размер» */ - bool fl_t176 = false; - FL_TRY(fl_post(ctx, fl_t174, "И4: без приметы-составляющей и без места разряд решает размер", "Разряд находки", &fl_t176, error)); - if (!fl_t176) { - return fl_fail(ctx, error, "FLANG_PROPERTY", "%s", "нарушено свойство «И4: без приметы-составляющей и без места разряд решает размер» функции «Разряд находки»"); - } - *result = fl_t166; - return FL_OK; -} - -/* - * Функция flang «Крупное не мельче порога». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param nahodka — «находка»: «Находка» - * @param razryad — «разряд»: «Разряд» - * @return значение - */ -fl_status opis_diska_krupnoe_ne_melche_poroga(fl_ctx *ctx, fl_value nahodka, fl_value razryad, fl_value *result, fl_error *error) { - if (fl_variant_is(razryad, "Крупное")) { - fl_value fl_t177 = fl_nothing(); - FL_TRY(fl_field_get(ctx, nahodka, "размер", &fl_t177, error)); - fl_value fl_t178 = fl_nothing(); - FL_TRY(opis_diska_porog_krupnogo(ctx, &fl_t178, error)); - if (fl_t177.tag != FL_NUMBER || fl_t178.tag != FL_NUMBER) FL_TRY(fl_not_order(ctx, fl_t177, fl_t178, error)); - *result = fl_flag(fl_t177.as.number >= fl_t178.as.number); - return FL_OK; - } else if (fl_variant_is(razryad, "Кэш")) { - *result = fl_flag(true); - return FL_OK; - } else if (fl_variant_is(razryad, "Журнал")) { - *result = fl_flag(true); - return FL_OK; - } else if (fl_variant_is(razryad, "Сборка")) { - *result = fl_flag(true); - return FL_OK; - } else if (fl_variant_is(razryad, "Загрузка")) { - *result = fl_flag(true); - return FL_OK; - } else if (fl_variant_is(razryad, "Неизвестное")) { - *result = fl_flag(true); - return FL_OK; - } else { - return fl_match_fail(ctx, razryad, error); - } -} - -/* - * Функция flang «Каталог». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param nahodka — «находка»: «Находка» - * @return значение - */ -fl_status opis_diska_katalog(fl_ctx *ctx, fl_value nahodka, fl_value *result, fl_error *error) { - fl_value fl_t179 = fl_nothing(); - FL_TRY(fl_field_get(ctx, nahodka, "вид", &fl_t179, error)); - if (fl_variant_is(fl_t179, "Каталог")) { - *result = fl_flag(true); - return FL_OK; - } else if (fl_variant_is(fl_t179, "Файл")) { - *result = fl_flag(false); - return FL_OK; - } else if (fl_variant_is(fl_t179, "Ссылка")) { - *result = fl_flag(false); - return FL_OK; - } else { - return fl_match_fail(ctx, fl_t179, error); - } -} - -/* - * Функция flang «Ссылка». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param nahodka — «находка»: «Находка» - * @return значение - */ -fl_status opis_diska_ssylka(fl_ctx *ctx, fl_value nahodka, fl_value *result, fl_error *error) { - fl_value fl_t180 = fl_nothing(); - FL_TRY(fl_field_get(ctx, nahodka, "вид", &fl_t180, error)); - if (fl_variant_is(fl_t180, "Ссылка")) { - *result = fl_flag(true); - return FL_OK; - } else if (fl_variant_is(fl_t180, "Файл")) { - *result = fl_flag(false); - return FL_OK; - } else if (fl_variant_is(fl_t180, "Каталог")) { - *result = fl_flag(false); - return FL_OK; - } else { - return fl_match_fail(ctx, fl_t180, error); - } -} - -/* - * Функция flang «Приговор мусора». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param nahodka — «находка»: «Находка» - * @param porog — «порог»: число - * @return значение: «Приговор» - */ -fl_status opis_diska_prigovor_musora(fl_ctx *ctx, fl_value nahodka, fl_value porog, fl_value *result, fl_error *error) { - fl_value fl_t181 = fl_nothing(); - FL_TRY(opis_diska_katalog(ctx, nahodka, &fl_t181, error)); - bool fl_t182 = false; - FL_TRY(fl_cond(ctx, fl_t181, &fl_t182, error)); - fl_value fl_t183 = fl_nothing(); - if (fl_t182) { - fl_value fl_t184 = fl_nothing(); - FL_TRY(fl_variant_new(ctx, "Спросить", NULL, NULL, 0, &fl_t184, error)); - fl_t183 = fl_t184; - } else { - fl_value fl_t185 = fl_nothing(); - FL_TRY(fl_field_get(ctx, nahodka, "возраст_дней", &fl_t185, error)); - if (fl_t185.tag != FL_NUMBER || porog.tag != FL_NUMBER) FL_TRY(fl_not_order(ctx, fl_t185, porog, error)); - bool fl_t186 = false; - FL_TRY(fl_cond(ctx, fl_flag(fl_t185.as.number >= porog.as.number), &fl_t186, error)); - fl_value fl_t187 = fl_nothing(); - if (fl_t186) { - fl_value fl_t188 = fl_nothing(); - FL_TRY(fl_variant_new(ctx, "МожноУбрать", NULL, NULL, 0, &fl_t188, error)); - fl_t187 = fl_t188; - } else { - fl_value fl_t189 = fl_nothing(); - FL_TRY(fl_variant_new(ctx, "Спросить", NULL, NULL, 0, &fl_t189, error)); - fl_t187 = fl_t189; - } - fl_t183 = fl_t187; - } - const fl_value fl_t190 = fl_t183; - fl_value fl_t191 = fl_nothing(); - FL_TRY(opis_diska_katalog(ctx, nahodka, &fl_t191, error)); - bool fl_t192 = false; - FL_TRY(fl_cond(ctx, fl_t191, &fl_t192, error)); - fl_value fl_t193 = fl_nothing(); - if (fl_t192) { - fl_value fl_t194 = fl_nothing(); - FL_TRY(opis_diska_eto_mozhnoubrat(ctx, fl_t190, &fl_t194, error)); - bool fl_t195 = false; - FL_TRY(fl_cond(ctx, fl_t194, &fl_t195, error)); - fl_value fl_t196 = fl_nothing(); - if (fl_t195) { - fl_t196 = fl_flag(false); - } else { - fl_t196 = fl_flag(true); - } - fl_t193 = fl_t196; - } else { - fl_t193 = fl_flag(true); - } - /* постусловие «Каталог не убирается» */ - bool fl_t197 = false; - FL_TRY(fl_post(ctx, fl_t193, "Каталог не убирается", "Приговор мусора", &fl_t197, error)); - if (!fl_t197) { - return fl_fail(ctx, error, "FLANG_PROPERTY", "%s", "нарушено свойство «Каталог не убирается» функции «Приговор мусора»"); - } - *result = fl_t190; - return FL_OK; -} - -/* - * Функция flang «Приговор находки». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param nahodka — «находка»: «Находка» - * @param razryad — «разряд»: «Разряд» - * @return значение: «Приговор» - */ -fl_status opis_diska_prigovor_nahodki(fl_ctx *ctx, fl_value nahodka, fl_value razryad, fl_value *result, fl_error *error) { - fl_value fl_t198 = fl_nothing(); - FL_TRY(fl_field_get(ctx, nahodka, "доступен", &fl_t198, error)); - bool fl_t199 = false; - FL_TRY(fl_cond(ctx, fl_t198, &fl_t199, error)); - fl_value fl_t200 = fl_nothing(); - if (fl_t199) { - fl_t200 = fl_flag(false); - } else { - fl_t200 = fl_flag(true); - } - bool fl_t201 = false; - FL_TRY(fl_cond(ctx, fl_t200, &fl_t201, error)); - fl_value fl_t202 = fl_nothing(); - if (fl_t201) { - fl_value fl_t203 = fl_nothing(); - FL_TRY(fl_variant_new(ctx, "НеТрогать", NULL, NULL, 0, &fl_t203, error)); - fl_t202 = fl_t203; - } else { - fl_value fl_t204 = fl_nothing(); - FL_TRY(opis_diska_ssylka(ctx, nahodka, &fl_t204, error)); - bool fl_t205 = false; - FL_TRY(fl_cond(ctx, fl_t204, &fl_t205, error)); - fl_value fl_t206 = fl_nothing(); - if (fl_t205) { - fl_value fl_t207 = fl_nothing(); - FL_TRY(fl_variant_new(ctx, "НеТрогать", NULL, NULL, 0, &fl_t207, error)); - fl_t206 = fl_t207; - } else { - fl_value fl_t208 = fl_nothing(); - FL_TRY(fl_field_get(ctx, nahodka, "путь", &fl_t208, error)); - fl_value fl_t209 = fl_nothing(); - FL_TRY(opis_diska_adresuetsya_soderzhimym(ctx, fl_t208, &fl_t209, error)); - bool fl_t210 = false; - FL_TRY(fl_cond(ctx, fl_t209, &fl_t210, error)); - fl_value fl_t211 = fl_nothing(); - if (fl_t210) { - fl_value fl_t212 = fl_nothing(); - FL_TRY(fl_variant_new(ctx, "НеТрогать", NULL, NULL, 0, &fl_t212, error)); - fl_t211 = fl_t212; - } else { - fl_value fl_t213 = fl_nothing(); - if (fl_variant_is(razryad, "Кэш")) { - fl_value fl_t214 = fl_nothing(); - FL_TRY(opis_diska_porog_kesha(ctx, &fl_t214, error)); - fl_value fl_t215 = fl_nothing(); - FL_TRY(opis_diska_prigovor_musora(ctx, nahodka, fl_t214, &fl_t215, error)); - fl_t213 = fl_t215; - } else if (fl_variant_is(razryad, "Сборка")) { - fl_value fl_t216 = fl_nothing(); - FL_TRY(opis_diska_porog_kesha(ctx, &fl_t216, error)); - fl_value fl_t217 = fl_nothing(); - FL_TRY(opis_diska_prigovor_musora(ctx, nahodka, fl_t216, &fl_t217, error)); - fl_t213 = fl_t217; - } else if (fl_variant_is(razryad, "Журнал")) { - fl_value fl_t218 = fl_nothing(); - FL_TRY(opis_diska_porog_zhurnala(ctx, &fl_t218, error)); - fl_value fl_t219 = fl_nothing(); - FL_TRY(opis_diska_prigovor_musora(ctx, nahodka, fl_t218, &fl_t219, error)); - fl_t213 = fl_t219; - } else if (fl_variant_is(razryad, "Загрузка")) { - fl_value fl_t220 = fl_nothing(); - FL_TRY(fl_field_get(ctx, nahodka, "возраст_дней", &fl_t220, error)); - fl_value fl_t221 = fl_nothing(); - FL_TRY(opis_diska_porog_zagruzki(ctx, &fl_t221, error)); - if (fl_t220.tag != FL_NUMBER || fl_t221.tag != FL_NUMBER) FL_TRY(fl_not_order(ctx, fl_t220, fl_t221, error)); - bool fl_t222 = false; - FL_TRY(fl_cond(ctx, fl_flag(fl_t220.as.number >= fl_t221.as.number), &fl_t222, error)); - fl_value fl_t223 = fl_nothing(); - if (fl_t222) { - fl_value fl_t224 = fl_nothing(); - FL_TRY(fl_variant_new(ctx, "Спросить", NULL, NULL, 0, &fl_t224, error)); - fl_t223 = fl_t224; - } else { - fl_value fl_t225 = fl_nothing(); - FL_TRY(fl_variant_new(ctx, "НеТрогать", NULL, NULL, 0, &fl_t225, error)); - fl_t223 = fl_t225; - } - fl_t213 = fl_t223; - } else if (fl_variant_is(razryad, "Крупное")) { - fl_value fl_t226 = fl_nothing(); - FL_TRY(fl_variant_new(ctx, "Спросить", NULL, NULL, 0, &fl_t226, error)); - fl_t213 = fl_t226; - } else if (fl_variant_is(razryad, "Неизвестное")) { - fl_value fl_t227 = fl_nothing(); - FL_TRY(fl_variant_new(ctx, "НеТрогать", NULL, NULL, 0, &fl_t227, error)); - fl_t213 = fl_t227; - } else { - return fl_match_fail(ctx, razryad, error); - } - fl_t211 = fl_t213; - } - fl_t206 = fl_t211; - } - fl_t202 = fl_t206; - } - const fl_value fl_t228 = fl_t202; - fl_value fl_t229 = fl_nothing(); - FL_TRY(opis_diska_prigovor_obosnovan(ctx, nahodka, razryad, fl_t228, &fl_t229, error)); - /* постусловие «Приговор обоснован» */ - bool fl_t230 = false; - FL_TRY(fl_post(ctx, fl_t229, "Приговор обоснован", "Приговор находки", &fl_t230, error)); - if (!fl_t230) { - return fl_fail(ctx, error, "FLANG_PROPERTY", "%s", "нарушено свойство «Приговор обоснован» функции «Приговор находки»"); - } - fl_value fl_t231 = fl_nothing(); - FL_TRY(fl_field_get(ctx, nahodka, "путь", &fl_t231, error)); - fl_value fl_t232 = fl_nothing(); - FL_TRY(opis_diska_adresuetsya_soderzhimym(ctx, fl_t231, &fl_t232, error)); - bool fl_t233 = false; - FL_TRY(fl_cond(ctx, fl_t232, &fl_t233, error)); - fl_value fl_t234 = fl_nothing(); - if (fl_t233) { - fl_value fl_t235 = fl_nothing(); - FL_TRY(opis_diska_eto_netrogat(ctx, fl_t228, &fl_t235, error)); - fl_t234 = fl_t235; - } else { - fl_t234 = fl_flag(true); - } - /* постусловие «И3: адресуемое содержимым не убирается» */ - bool fl_t236 = false; - FL_TRY(fl_post(ctx, fl_t234, "И3: адресуемое содержимым не убирается", "Приговор находки", &fl_t236, error)); - if (!fl_t236) { - return fl_fail(ctx, error, "FLANG_PROPERTY", "%s", "нарушено свойство «И3: адресуемое содержимым не убирается» функции «Приговор находки»"); - } - *result = fl_t228; - return FL_OK; -} - -/* - * Функция flang «Вес находки». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param nahodka — «находка»: «Находка» - * @param prigovor — «приговор»: «Приговор» - * @return значение: число - */ -fl_status opis_diska_ves_nahodki(fl_ctx *ctx, fl_value nahodka, fl_value prigovor, fl_value *result, fl_error *error) { - fl_value fl_t237 = fl_nothing(); - if (fl_variant_is(prigovor, "НеТрогать")) { - fl_t237 = fl_number(0.0); - } else if (fl_variant_is(prigovor, "МожноУбрать")) { - fl_value fl_t238 = fl_nothing(); - FL_TRY(fl_field_get(ctx, nahodka, "размер", &fl_t238, error)); - fl_t237 = fl_t238; - } else if (fl_variant_is(prigovor, "Спросить")) { - fl_value fl_t239 = fl_nothing(); - FL_TRY(fl_field_get(ctx, nahodka, "размер", &fl_t239, error)); - fl_t237 = fl_t239; - } else { - return fl_match_fail(ctx, prigovor, error); - } - const fl_value fl_t240 = fl_t237; - fl_value fl_t241 = fl_nothing(); - FL_TRY(opis_diska_ves_obosnovan(ctx, nahodka, prigovor, fl_t240, &fl_t241, error)); - /* постусловие «Вес обоснован» */ - bool fl_t242 = false; - FL_TRY(fl_post(ctx, fl_t241, "Вес обоснован", "Вес находки", &fl_t242, error)); - if (!fl_t242) { - return fl_fail(ctx, error, "FLANG_PROPERTY", "%s", "нарушено свойство «Вес обоснован» функции «Вес находки»"); - } - *result = fl_t240; - return FL_OK; -} - -/* - * Функция flang «Вес в границах». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param nahodka — «находка»: «Находка» - * @param ves — «вес»: число - * @return значение - */ -fl_status opis_diska_ves_v_granicah(fl_ctx *ctx, fl_value nahodka, fl_value ves, fl_value *result, fl_error *error) { - if (ves.tag != FL_NUMBER) FL_TRY(fl_not_order(ctx, ves, fl_number(0.0), error)); - bool fl_t243 = false; - FL_TRY(fl_cond(ctx, fl_flag(ves.as.number >= 0.0), &fl_t243, error)); - if (fl_t243) { - fl_value fl_t244 = fl_nothing(); - FL_TRY(fl_field_get(ctx, nahodka, "размер", &fl_t244, error)); - if (ves.tag != FL_NUMBER || fl_t244.tag != FL_NUMBER) FL_TRY(fl_not_order(ctx, ves, fl_t244, error)); - *result = fl_flag(ves.as.number <= fl_t244.as.number); - return FL_OK; - } else { - *result = fl_flag(false); - return FL_OK; - } -} - -/* - * Функция flang «Решить находку». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param nahodka — «находка»: «Находка» - * @param spravochnik — «справочник»: список: «Место» - * @return значение: «Решение» - */ -fl_status opis_diska_reshit_nahodku(fl_ctx *ctx, fl_value nahodka, fl_value spravochnik, fl_value *result, fl_error *error) { - fl_value fl_t245 = fl_nothing(); - FL_TRY(fl_field_get(ctx, nahodka, "путь", &fl_t245, error)); - fl_value fl_t246 = fl_nothing(); - FL_TRY(opis_diska_razryad_po_spravochniku(ctx, fl_t245, spravochnik, &fl_t246, error)); - const fl_value mesto = fl_t246; /* пусть «место» */ - fl_value fl_t247 = fl_nothing(); - FL_TRY(opis_diska_razryad_nahodki(ctx, nahodka, mesto, &fl_t247, error)); - const fl_value razryad = fl_t247; /* пусть «разряд» */ - fl_value fl_t248 = fl_nothing(); - FL_TRY(opis_diska_prigovor_nahodki(ctx, nahodka, razryad, &fl_t248, error)); - const fl_value prigovor = fl_t248; /* пусть «приговор» */ - fl_value fl_t249 = fl_nothing(); - FL_TRY(opis_diska_ves_nahodki(ctx, nahodka, prigovor, &fl_t249, error)); - fl_value fl_t251[3]; - fl_t251[0] = razryad; /* «разряд» */ - fl_t251[1] = prigovor; /* «приговор» */ - fl_t251[2] = fl_t249; /* «вес» */ - fl_value fl_t250 = fl_nothing(); - FL_TRY(fl_record_new(ctx, opis_diska_names_3, fl_t251, 3, &fl_t250, error)); - const fl_value fl_t252 = fl_t250; - fl_value fl_t253 = fl_nothing(); - FL_TRY(opis_diska_i1_derzhitsya(ctx, fl_t252, &fl_t253, error)); - /* постусловие «И1: убрать можно только мусор» */ - bool fl_t254 = false; - FL_TRY(fl_post(ctx, fl_t253, "И1: убрать можно только мусор", "Решить находку", &fl_t254, error)); - if (!fl_t254) { - return fl_fail(ctx, error, "FLANG_PROPERTY", "%s", "нарушено свойство «И1: убрать можно только мусор» функции «Решить находку»"); - } - *result = fl_t252; - return FL_OK; -} - -/* - * Функция flang «Решить всё». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param zapisi — «записи»: список: «Находка» - * @param spravochnik — «справочник»: список: «Место» - * @return значение: список: «Решение» - */ -fl_status opis_diska_reshit_vsyo(fl_ctx *ctx, fl_value zapisi, fl_value spravochnik, fl_value *result, fl_error *error) { - fl_value fl_t255 = fl_nothing(); - FL_TRY(fl_require_list(ctx, zapisi, "отобразить", &fl_t255, error)); - fl_value *fl_t256 = NULL; - size_t fl_t257 = 0; - FL_TRY(fl_list_alloc(ctx, fl_t255.as.list.count, &fl_t256, error)); - for (size_t fl_t258 = 0; fl_t258 < fl_t255.as.list.count; fl_t258 += 1) { - const fl_value nahodka = fl_t255.as.list.items[fl_t258]; /* «находка» */ - fl_value fl_t259 = fl_nothing(); - FL_TRY(opis_diska_reshit_nahodku(ctx, nahodka, spravochnik, &fl_t259, error)); - fl_t256[fl_t257] = fl_t259; - fl_t257 += 1; - } - const fl_value fl_t260 = fl_list(fl_t256, fl_t257); - fl_value fl_t261 = fl_nothing(); - FL_TRY(opis_diska_i1_derzhitsya_vsyudu(ctx, fl_t260, &fl_t261, error)); - /* постусловие «И1 всюду: убрать можно только мусор» */ - bool fl_t262 = false; - FL_TRY(fl_post(ctx, fl_t261, "И1 всюду: убрать можно только мусор", "Решить всё", &fl_t262, error)); - if (!fl_t262) { - return fl_fail(ctx, error, "FLANG_PROPERTY", "%s", "нарушено свойство «И1 всюду: убрать можно только мусор» функции «Решить всё»"); - } - *result = fl_t260; - return FL_OK; -} - -/* - * Функция flang «Расширения исходников». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @return значение: список: строка - */ -fl_status opis_diska_rasshireniya_ishodnikov(fl_ctx *ctx, fl_value *result, fl_error *error) { - fl_value *fl_t263 = NULL; - FL_TRY(fl_list_alloc(ctx, 30, &fl_t263, error)); - fl_t263[0] = opis_diska_text_27; - fl_t263[1] = opis_diska_text_28; - fl_t263[2] = opis_diska_text_29; - fl_t263[3] = opis_diska_text_30; - fl_t263[4] = opis_diska_text_31; - fl_t263[5] = opis_diska_text_32; - fl_t263[6] = opis_diska_text_33; - fl_t263[7] = opis_diska_text_34; - fl_t263[8] = opis_diska_text_35; - fl_t263[9] = opis_diska_text_36; - fl_t263[10] = opis_diska_text_37; - fl_t263[11] = opis_diska_text_38; - fl_t263[12] = opis_diska_text_39; - fl_t263[13] = opis_diska_text_40; - fl_t263[14] = opis_diska_text_41; - fl_t263[15] = opis_diska_text_42; - fl_t263[16] = opis_diska_text_43; - fl_t263[17] = opis_diska_text_44; - fl_t263[18] = opis_diska_text_45; - fl_t263[19] = opis_diska_text_46; - fl_t263[20] = opis_diska_text_47; - fl_t263[21] = opis_diska_text_48; - fl_t263[22] = opis_diska_text_49; - fl_t263[23] = opis_diska_text_50; - fl_t263[24] = opis_diska_text_51; - fl_t263[25] = opis_diska_text_52; - fl_t263[26] = opis_diska_text_53; - fl_t263[27] = opis_diska_text_54; - fl_t263[28] = opis_diska_text_55; - fl_t263[29] = opis_diska_text_56; - *result = fl_list(fl_t263, 30); - return FL_OK; -} - -/* - * Функция flang «Примета исходника». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param put — «путь»: строка - * @return значение - */ -fl_status opis_diska_primeta_ishodnika(fl_ctx *ctx, fl_value put, fl_value *result, fl_error *error) { - fl_value fl_t264 = fl_nothing(); - FL_TRY(opis_diska_imya_v_puti(ctx, put, &fl_t264, error)); - const fl_value imya = fl_t264; /* пусть «имя» */ - fl_value fl_t265 = fl_nothing(); - FL_TRY(opis_diska_rasshireniya_ishodnikov(ctx, &fl_t265, error)); - fl_value fl_t266 = fl_nothing(); - FL_TRY(fl_require_list(ctx, fl_t265, "отфильтровать", &fl_t266, error)); - fl_value *fl_t267 = NULL; - size_t fl_t268 = 0; - FL_TRY(fl_list_alloc(ctx, fl_t266.as.list.count, &fl_t267, error)); - for (size_t fl_t269 = 0; fl_t269 < fl_t266.as.list.count; fl_t269 += 1) { - const fl_value hvost = fl_t266.as.list.items[fl_t269]; /* «хвост» */ - fl_value fl_t270 = fl_nothing(); - FL_TRY(opis_diska_okanchivaetsya_na(ctx, imya, hvost, &fl_t270, error)); - bool fl_t271 = false; - FL_TRY(fl_keep(ctx, fl_t270, &fl_t271, error)); - if (fl_t271) { - fl_t267[fl_t268] = hvost; - fl_t268 += 1; - } - } - fl_value fl_t272 = fl_nothing(); /* «длина» */ - FL_TRY(fl_b_dlina(ctx, fl_list(fl_t267, fl_t268), &fl_t272, error)); - if (fl_t272.tag != FL_NUMBER) FL_TRY(fl_not_order(ctx, fl_t272, fl_number(0.0), error)); - *result = fl_flag(fl_t272.as.number > 0.0); - return FL_OK; -} - -/* - * Функция flang «Под присмотром системы версий». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param put — «путь»: строка - * @return значение - */ -fl_status opis_diska_pod_prismotrom_sistemy_versiy(fl_ctx *ctx, fl_value put, fl_value *result, fl_error *error) { - fl_value fl_t273 = fl_nothing(); - FL_TRY(opis_diska_est_sostavlyayuschaya(ctx, put, opis_diska_text_11, &fl_t273, error)); - bool fl_t274 = false; - FL_TRY(fl_cond(ctx, fl_t273, &fl_t274, error)); - fl_value fl_t275 = fl_nothing(); - if (fl_t274) { - fl_t275 = fl_flag(true); - } else { - fl_value fl_t276 = fl_nothing(); - FL_TRY(opis_diska_est_sostavlyayuschaya(ctx, put, opis_diska_text_57, &fl_t276, error)); - fl_t275 = fl_t276; - } - bool fl_t277 = false; - FL_TRY(fl_cond(ctx, fl_t275, &fl_t277, error)); - if (fl_t277) { - *result = fl_flag(true); - return FL_OK; - } else { - return opis_diska_est_sostavlyayuschaya(ctx, put, opis_diska_text_58, result, error); - } -} - -/* - * Функция flang «Мусорный разряд». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param razryad — «разряд»: «Разряд» - * @return значение - */ -fl_status opis_diska_musornyy_razryad(fl_ctx *ctx, fl_value razryad, fl_value *result, fl_error *error) { - if (fl_variant_is(razryad, "Кэш")) { - *result = fl_flag(true); - return FL_OK; - } else if (fl_variant_is(razryad, "Журнал")) { - *result = fl_flag(true); - return FL_OK; - } else if (fl_variant_is(razryad, "Сборка")) { - *result = fl_flag(true); - return FL_OK; - } else if (fl_variant_is(razryad, "Загрузка")) { - *result = fl_flag(false); - return FL_OK; - } else if (fl_variant_is(razryad, "Крупное")) { - *result = fl_flag(false); - return FL_OK; - } else if (fl_variant_is(razryad, "Неизвестное")) { - *result = fl_flag(false); - return FL_OK; - } else { - return fl_match_fail(ctx, razryad, error); - } -} - -/* - * Функция flang «Природа находки». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param nahodka — «находка»: «Находка» - * @param razryad — «разряд»: «Разряд» - * @param prigovor — «приговор»: «Приговор» - * @return значение: «Природа» - */ -fl_status opis_diska_priroda_nahodki(fl_ctx *ctx, fl_value nahodka, fl_value razryad, fl_value prigovor, fl_value *result, fl_error *error) { - fl_value fl_t278 = fl_nothing(); - FL_TRY(fl_field_get(ctx, nahodka, "путь", &fl_t278, error)); - fl_value fl_t279 = fl_nothing(); - FL_TRY(opis_diska_pod_prismotrom_sistemy_versiy(ctx, fl_t278, &fl_t279, error)); - bool fl_t280 = false; - FL_TRY(fl_cond(ctx, fl_t279, &fl_t280, error)); - fl_value fl_t281 = fl_nothing(); - if (fl_t280) { - fl_value fl_t282 = fl_nothing(); - FL_TRY(fl_variant_new(ctx, "ПодПрисмотром", NULL, NULL, 0, &fl_t282, error)); - fl_t281 = fl_t282; - } else { - fl_value fl_t283 = fl_nothing(); - FL_TRY(fl_field_get(ctx, nahodka, "путь", &fl_t283, error)); - fl_value fl_t284 = fl_nothing(); - FL_TRY(opis_diska_adresuetsya_soderzhimym(ctx, fl_t283, &fl_t284, error)); - bool fl_t285 = false; - FL_TRY(fl_cond(ctx, fl_t284, &fl_t285, error)); - fl_value fl_t286 = fl_nothing(); - if (fl_t285) { - fl_value fl_t287 = fl_nothing(); - FL_TRY(fl_variant_new(ctx, "Хранилище", NULL, NULL, 0, &fl_t287, error)); - fl_t286 = fl_t287; - } else { - fl_value fl_t288 = fl_nothing(); - FL_TRY(opis_diska_eto_mozhnoubrat(ctx, prigovor, &fl_t288, error)); - bool fl_t289 = false; - FL_TRY(fl_cond(ctx, fl_t288, &fl_t289, error)); - fl_value fl_t290 = fl_nothing(); - if (fl_t289) { - fl_value fl_t291 = fl_nothing(); - FL_TRY(fl_variant_new(ctx, "Мусор", NULL, NULL, 0, &fl_t291, error)); - fl_t290 = fl_t291; - } else { - fl_value fl_t292 = fl_nothing(); - FL_TRY(fl_field_get(ctx, nahodka, "путь", &fl_t292, error)); - fl_value fl_t293 = fl_nothing(); - FL_TRY(opis_diska_primeta_ishodnika(ctx, fl_t292, &fl_t293, error)); - bool fl_t294 = false; - FL_TRY(fl_cond(ctx, fl_t293, &fl_t294, error)); - fl_value fl_t295 = fl_nothing(); - if (fl_t294) { - fl_value fl_t296 = fl_nothing(); - FL_TRY(fl_variant_new(ctx, "Исходники", NULL, NULL, 0, &fl_t296, error)); - fl_t295 = fl_t296; - } else { - fl_value fl_t297 = fl_nothing(); - FL_TRY(opis_diska_musornyy_razryad(ctx, razryad, &fl_t297, error)); - bool fl_t298 = false; - FL_TRY(fl_cond(ctx, fl_t297, &fl_t298, error)); - fl_value fl_t299 = fl_nothing(); - if (fl_t298) { - fl_value fl_t300 = fl_nothing(); - FL_TRY(fl_variant_new(ctx, "Свежее", NULL, NULL, 0, &fl_t300, error)); - fl_t299 = fl_t300; - } else { - fl_value fl_t301 = fl_nothing(); - FL_TRY(fl_variant_new(ctx, "Личное", NULL, NULL, 0, &fl_t301, error)); - fl_t299 = fl_t301; - } - fl_t295 = fl_t299; - } - fl_t290 = fl_t295; - } - fl_t286 = fl_t290; - } - fl_t281 = fl_t286; - } - const fl_value fl_t302 = fl_t281; - fl_value fl_t303 = fl_nothing(); - FL_TRY(opis_diska_priroda_obosnovana(ctx, nahodka, razryad, prigovor, fl_t302, &fl_t303, error)); - /* постусловие «Природа обоснована» */ - bool fl_t304 = false; - FL_TRY(fl_post(ctx, fl_t303, "Природа обоснована", "Природа находки", &fl_t304, error)); - if (!fl_t304) { - return fl_fail(ctx, error, "FLANG_PROPERTY", "%s", "нарушено свойство «Природа обоснована» функции «Природа находки»"); - } - *result = fl_t302; - return FL_OK; -} - -/* - * Функция flang «Природа обоснована». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param nahodka — «находка»: «Находка» - * @param razryad — «разряд»: «Разряд» - * @param prigovor — «приговор»: «Приговор» - * @param priroda — «природа»: «Природа» - * @return значение - */ -fl_status opis_diska_priroda_obosnovana(fl_ctx *ctx, fl_value nahodka, fl_value razryad, fl_value prigovor, fl_value priroda, fl_value *result, fl_error *error) { - if (fl_variant_is(priroda, "Мусор")) { - return opis_diska_eto_mozhnoubrat(ctx, prigovor, result, error); - } else if (fl_variant_is(priroda, "ПодПрисмотром")) { - fl_value fl_t305 = fl_nothing(); - FL_TRY(fl_field_get(ctx, nahodka, "путь", &fl_t305, error)); - return opis_diska_pod_prismotrom_sistemy_versiy(ctx, fl_t305, result, error); - } else if (fl_variant_is(priroda, "Хранилище")) { - fl_value fl_t306 = fl_nothing(); - FL_TRY(fl_field_get(ctx, nahodka, "путь", &fl_t306, error)); - fl_value fl_t307 = fl_nothing(); - FL_TRY(opis_diska_adresuetsya_soderzhimym(ctx, fl_t306, &fl_t307, error)); - bool fl_t308 = false; - FL_TRY(fl_cond(ctx, fl_t307, &fl_t308, error)); - if (fl_t308) { - fl_value fl_t309 = fl_nothing(); - FL_TRY(fl_field_get(ctx, nahodka, "путь", &fl_t309, error)); - fl_value fl_t310 = fl_nothing(); - FL_TRY(opis_diska_pod_prismotrom_sistemy_versiy(ctx, fl_t309, &fl_t310, error)); - bool fl_t311 = false; - FL_TRY(fl_cond(ctx, fl_t310, &fl_t311, error)); - if (fl_t311) { - *result = fl_flag(false); - return FL_OK; - } else { - *result = fl_flag(true); - return FL_OK; - } - } else { - *result = fl_flag(false); - return FL_OK; - } - } else if (fl_variant_is(priroda, "Исходники")) { - fl_value fl_t312 = fl_nothing(); - FL_TRY(fl_field_get(ctx, nahodka, "путь", &fl_t312, error)); - fl_value fl_t313 = fl_nothing(); - FL_TRY(opis_diska_primeta_ishodnika(ctx, fl_t312, &fl_t313, error)); - bool fl_t314 = false; - FL_TRY(fl_cond(ctx, fl_t313, &fl_t314, error)); - if (fl_t314) { - fl_value fl_t315 = fl_nothing(); - FL_TRY(opis_diska_eto_mozhnoubrat(ctx, prigovor, &fl_t315, error)); - bool fl_t316 = false; - FL_TRY(fl_cond(ctx, fl_t315, &fl_t316, error)); - if (fl_t316) { - *result = fl_flag(false); - return FL_OK; - } else { - *result = fl_flag(true); - return FL_OK; - } - } else { - *result = fl_flag(false); - return FL_OK; - } - } else if (fl_variant_is(priroda, "Свежее")) { - fl_value fl_t317 = fl_nothing(); - FL_TRY(opis_diska_musornyy_razryad(ctx, razryad, &fl_t317, error)); - bool fl_t318 = false; - FL_TRY(fl_cond(ctx, fl_t317, &fl_t318, error)); - if (fl_t318) { - fl_value fl_t319 = fl_nothing(); - FL_TRY(opis_diska_eto_mozhnoubrat(ctx, prigovor, &fl_t319, error)); - bool fl_t320 = false; - FL_TRY(fl_cond(ctx, fl_t319, &fl_t320, error)); - if (fl_t320) { - *result = fl_flag(false); - return FL_OK; - } else { - *result = fl_flag(true); - return FL_OK; - } - } else { - *result = fl_flag(false); - return FL_OK; - } - } else if (fl_variant_is(priroda, "Личное")) { - fl_value fl_t321 = fl_nothing(); - FL_TRY(opis_diska_eto_mozhnoubrat(ctx, prigovor, &fl_t321, error)); - bool fl_t322 = false; - FL_TRY(fl_cond(ctx, fl_t321, &fl_t322, error)); - if (fl_t322) { - *result = fl_flag(false); - return FL_OK; - } else { - *result = fl_flag(true); - return FL_OK; - } - } else { - return fl_match_fail(ctx, priroda, error); - } -} - -/* - * Функция flang «Природа по находке». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param nahodka — «находка»: «Находка» - * @param spravochnik — «справочник»: список: «Место» - * @return значение: «Природа» - */ -fl_status opis_diska_priroda_po_nahodke(fl_ctx *ctx, fl_value nahodka, fl_value spravochnik, fl_value *result, fl_error *error) { - fl_value fl_t323 = fl_nothing(); - FL_TRY(fl_field_get(ctx, nahodka, "путь", &fl_t323, error)); - fl_value fl_t324 = fl_nothing(); - FL_TRY(opis_diska_razryad_po_spravochniku(ctx, fl_t323, spravochnik, &fl_t324, error)); - const fl_value mesto = fl_t324; /* пусть «место» */ - fl_value fl_t325 = fl_nothing(); - FL_TRY(opis_diska_razryad_nahodki(ctx, nahodka, mesto, &fl_t325, error)); - const fl_value razryad = fl_t325; /* пусть «разряд» */ - fl_value fl_t326 = fl_nothing(); - FL_TRY(opis_diska_prigovor_nahodki(ctx, nahodka, razryad, &fl_t326, error)); - const fl_value prigovor = fl_t326; /* пусть «приговор» */ - return opis_diska_priroda_nahodki(ctx, nahodka, razryad, prigovor, result, error); -} - -/* - * Функция flang «Строгость». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param priroda — «природа»: «Природа» - * @return значение: число - */ -fl_status opis_diska_strogost(fl_ctx *ctx, fl_value priroda, fl_value *result, fl_error *error) { - if (fl_variant_is(priroda, "Мусор")) { - *result = fl_number(1.0); - return FL_OK; - } else if (fl_variant_is(priroda, "Свежее")) { - *result = fl_number(2.0); - return FL_OK; - } else if (fl_variant_is(priroda, "Личное")) { - *result = fl_number(2.0); - return FL_OK; - } else if (fl_variant_is(priroda, "Исходники")) { - *result = fl_number(2.0); - return FL_OK; - } else if (fl_variant_is(priroda, "Хранилище")) { - *result = fl_number(3.0); - return FL_OK; - } else if (fl_variant_is(priroda, "ПодПрисмотром")) { - *result = fl_number(3.0); - return FL_OK; - } else { - return fl_match_fail(ctx, priroda, error); - } -} - -/* - * Функция flang «И1 держится». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param reshenie — «решение»: «Решение» - * @return значение - */ -fl_status opis_diska_i1_derzhitsya(fl_ctx *ctx, fl_value reshenie, fl_value *result, fl_error *error) { - fl_value fl_t327 = fl_nothing(); - FL_TRY(fl_field_get(ctx, reshenie, "приговор", &fl_t327, error)); - if (fl_variant_is(fl_t327, "МожноУбрать")) { - fl_value fl_t328 = fl_nothing(); - FL_TRY(fl_field_get(ctx, reshenie, "разряд", &fl_t328, error)); - if (fl_variant_is(fl_t328, "Кэш")) { - *result = fl_flag(true); - return FL_OK; - } else if (fl_variant_is(fl_t328, "Журнал")) { - *result = fl_flag(true); - return FL_OK; - } else if (fl_variant_is(fl_t328, "Сборка")) { - *result = fl_flag(true); - return FL_OK; - } else if (fl_variant_is(fl_t328, "Загрузка")) { - *result = fl_flag(false); - return FL_OK; - } else if (fl_variant_is(fl_t328, "Крупное")) { - *result = fl_flag(false); - return FL_OK; - } else if (fl_variant_is(fl_t328, "Неизвестное")) { - *result = fl_flag(false); - return FL_OK; - } else { - return fl_match_fail(ctx, fl_t328, error); - } - } else if (fl_variant_is(fl_t327, "Спросить")) { - *result = fl_flag(true); - return FL_OK; - } else if (fl_variant_is(fl_t327, "НеТрогать")) { - *result = fl_flag(true); - return FL_OK; - } else { - return fl_match_fail(ctx, fl_t327, error); - } -} - -/* - * Функция flang «И1 держится всюду». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param resheniya — «решения»: список: «Решение» - * @return значение - */ -fl_status opis_diska_i1_derzhitsya_vsyudu(fl_ctx *ctx, fl_value resheniya, fl_value *result, fl_error *error) { - fl_value fl_t329 = fl_nothing(); - FL_TRY(fl_require_list(ctx, resheniya, "свёртка", &fl_t329, error)); - fl_value akk = fl_flag(true); /* «акк» */ - const fl_mark fl_t331 = fl_region_open(ctx); - for (size_t fl_t330 = 0; fl_t330 < fl_t329.as.list.count; fl_t330 += 1) { - const fl_value reshenie = fl_t329.as.list.items[fl_t330]; /* «решение» */ - bool fl_t332 = false; - FL_TRY(fl_cond(ctx, akk, &fl_t332, error)); - fl_value fl_t333 = fl_nothing(); - if (fl_t332) { - fl_value fl_t334 = fl_nothing(); - FL_TRY(opis_diska_i1_derzhitsya(ctx, reshenie, &fl_t334, error)); - fl_t333 = fl_t334; - } else { - fl_t333 = fl_flag(false); - } - akk = fl_t333; - FL_TRY(fl_region_recycle(ctx, fl_t331, &akk, error)); - } - FL_TRY(fl_region_close(ctx, fl_t331, FL_OK, &akk, error)); - *result = akk; - return FL_OK; -} - -/* - * Функция flang «Пустой свод». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @return значение: «Свод» - */ -fl_status opis_diska_pustoy_svod(fl_ctx *ctx, fl_value *result, fl_error *error) { - fl_value fl_t336[7]; - fl_t336[0] = fl_number(0.0); /* «кэш» */ - fl_t336[1] = fl_number(0.0); /* «журнал» */ - fl_t336[2] = fl_number(0.0); /* «сборка» */ - fl_t336[3] = fl_number(0.0); /* «загрузка» */ - fl_t336[4] = fl_number(0.0); /* «крупное» */ - fl_t336[5] = fl_number(0.0); /* «неизвестное» */ - fl_t336[6] = fl_number(0.0); /* «освободить» */ - fl_value fl_t335 = fl_nothing(); - FL_TRY(fl_record_new(ctx, opis_diska_names_5, fl_t336, 7, &fl_t335, error)); - *result = fl_t335; - return FL_OK; -} - -/* - * Функция flang «Прибавить решение». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param svod — «свод»: «Свод» - * @param reshenie — «решение»: «Решение» - * @return значение: «Свод» - */ -fl_status opis_diska_pribavit_reshenie(fl_ctx *ctx, fl_value svod, fl_value reshenie, fl_value *result, fl_error *error) { - fl_value fl_t337 = fl_nothing(); - fl_value fl_t338 = fl_nothing(); - FL_TRY(fl_field_get(ctx, reshenie, "приговор", &fl_t338, error)); - if (fl_variant_is(fl_t338, "МожноУбрать")) { - fl_value fl_t339 = fl_nothing(); - FL_TRY(fl_field_get(ctx, reshenie, "вес", &fl_t339, error)); - fl_t337 = fl_t339; - } else if (fl_variant_is(fl_t338, "Спросить")) { - fl_t337 = fl_number(0.0); - } else if (fl_variant_is(fl_t338, "НеТрогать")) { - fl_t337 = fl_number(0.0); - } else { - return fl_match_fail(ctx, fl_t338, error); - } - const fl_value ubrat = fl_t337; /* пусть «убрать» */ - fl_value fl_t340 = fl_nothing(); - FL_TRY(fl_field_get(ctx, reshenie, "разряд", &fl_t340, error)); - if (fl_variant_is(fl_t340, "Кэш")) { - fl_value fl_t341 = fl_nothing(); - FL_TRY(fl_field_get(ctx, svod, "кэш", &fl_t341, error)); - fl_value fl_t342 = fl_nothing(); - FL_TRY(fl_field_get(ctx, reshenie, "вес", &fl_t342, error)); - if (fl_t341.tag != FL_NUMBER || fl_t342.tag != FL_NUMBER) FL_TRY(fl_not_numbers(ctx, "add", fl_t341, fl_t342, error)); - fl_value fl_t343 = fl_nothing(); - FL_TRY(fl_field_get(ctx, svod, "журнал", &fl_t343, error)); - fl_value fl_t344 = fl_nothing(); - FL_TRY(fl_field_get(ctx, svod, "сборка", &fl_t344, error)); - fl_value fl_t345 = fl_nothing(); - FL_TRY(fl_field_get(ctx, svod, "загрузка", &fl_t345, error)); - fl_value fl_t346 = fl_nothing(); - FL_TRY(fl_field_get(ctx, svod, "крупное", &fl_t346, error)); - fl_value fl_t347 = fl_nothing(); - FL_TRY(fl_field_get(ctx, svod, "неизвестное", &fl_t347, error)); - fl_value fl_t348 = fl_nothing(); - FL_TRY(fl_field_get(ctx, svod, "освободить", &fl_t348, error)); - if (fl_t348.tag != FL_NUMBER || ubrat.tag != FL_NUMBER) FL_TRY(fl_not_numbers(ctx, "add", fl_t348, ubrat, error)); - fl_value fl_t350[7]; - fl_t350[0] = fl_number(fl_t341.as.number + fl_t342.as.number); /* «кэш» */ - fl_t350[1] = fl_t343; /* «журнал» */ - fl_t350[2] = fl_t344; /* «сборка» */ - fl_t350[3] = fl_t345; /* «загрузка» */ - fl_t350[4] = fl_t346; /* «крупное» */ - fl_t350[5] = fl_t347; /* «неизвестное» */ - fl_t350[6] = fl_number(fl_t348.as.number + ubrat.as.number); /* «освободить» */ - fl_value fl_t349 = fl_nothing(); - FL_TRY(fl_record_new(ctx, opis_diska_names_5, fl_t350, 7, &fl_t349, error)); - *result = fl_t349; - return FL_OK; - } else if (fl_variant_is(fl_t340, "Журнал")) { - fl_value fl_t351 = fl_nothing(); - FL_TRY(fl_field_get(ctx, svod, "кэш", &fl_t351, error)); - fl_value fl_t352 = fl_nothing(); - FL_TRY(fl_field_get(ctx, svod, "журнал", &fl_t352, error)); - fl_value fl_t353 = fl_nothing(); - FL_TRY(fl_field_get(ctx, reshenie, "вес", &fl_t353, error)); - if (fl_t352.tag != FL_NUMBER || fl_t353.tag != FL_NUMBER) FL_TRY(fl_not_numbers(ctx, "add", fl_t352, fl_t353, error)); - fl_value fl_t354 = fl_nothing(); - FL_TRY(fl_field_get(ctx, svod, "сборка", &fl_t354, error)); - fl_value fl_t355 = fl_nothing(); - FL_TRY(fl_field_get(ctx, svod, "загрузка", &fl_t355, error)); - fl_value fl_t356 = fl_nothing(); - FL_TRY(fl_field_get(ctx, svod, "крупное", &fl_t356, error)); - fl_value fl_t357 = fl_nothing(); - FL_TRY(fl_field_get(ctx, svod, "неизвестное", &fl_t357, error)); - fl_value fl_t358 = fl_nothing(); - FL_TRY(fl_field_get(ctx, svod, "освободить", &fl_t358, error)); - if (fl_t358.tag != FL_NUMBER || ubrat.tag != FL_NUMBER) FL_TRY(fl_not_numbers(ctx, "add", fl_t358, ubrat, error)); - fl_value fl_t360[7]; - fl_t360[0] = fl_t351; /* «кэш» */ - fl_t360[1] = fl_number(fl_t352.as.number + fl_t353.as.number); /* «журнал» */ - fl_t360[2] = fl_t354; /* «сборка» */ - fl_t360[3] = fl_t355; /* «загрузка» */ - fl_t360[4] = fl_t356; /* «крупное» */ - fl_t360[5] = fl_t357; /* «неизвестное» */ - fl_t360[6] = fl_number(fl_t358.as.number + ubrat.as.number); /* «освободить» */ - fl_value fl_t359 = fl_nothing(); - FL_TRY(fl_record_new(ctx, opis_diska_names_5, fl_t360, 7, &fl_t359, error)); - *result = fl_t359; - return FL_OK; - } else if (fl_variant_is(fl_t340, "Сборка")) { - fl_value fl_t361 = fl_nothing(); - FL_TRY(fl_field_get(ctx, svod, "кэш", &fl_t361, error)); - fl_value fl_t362 = fl_nothing(); - FL_TRY(fl_field_get(ctx, svod, "журнал", &fl_t362, error)); - fl_value fl_t363 = fl_nothing(); - FL_TRY(fl_field_get(ctx, svod, "сборка", &fl_t363, error)); - fl_value fl_t364 = fl_nothing(); - FL_TRY(fl_field_get(ctx, reshenie, "вес", &fl_t364, error)); - if (fl_t363.tag != FL_NUMBER || fl_t364.tag != FL_NUMBER) FL_TRY(fl_not_numbers(ctx, "add", fl_t363, fl_t364, error)); - fl_value fl_t365 = fl_nothing(); - FL_TRY(fl_field_get(ctx, svod, "загрузка", &fl_t365, error)); - fl_value fl_t366 = fl_nothing(); - FL_TRY(fl_field_get(ctx, svod, "крупное", &fl_t366, error)); - fl_value fl_t367 = fl_nothing(); - FL_TRY(fl_field_get(ctx, svod, "неизвестное", &fl_t367, error)); - fl_value fl_t368 = fl_nothing(); - FL_TRY(fl_field_get(ctx, svod, "освободить", &fl_t368, error)); - if (fl_t368.tag != FL_NUMBER || ubrat.tag != FL_NUMBER) FL_TRY(fl_not_numbers(ctx, "add", fl_t368, ubrat, error)); - fl_value fl_t370[7]; - fl_t370[0] = fl_t361; /* «кэш» */ - fl_t370[1] = fl_t362; /* «журнал» */ - fl_t370[2] = fl_number(fl_t363.as.number + fl_t364.as.number); /* «сборка» */ - fl_t370[3] = fl_t365; /* «загрузка» */ - fl_t370[4] = fl_t366; /* «крупное» */ - fl_t370[5] = fl_t367; /* «неизвестное» */ - fl_t370[6] = fl_number(fl_t368.as.number + ubrat.as.number); /* «освободить» */ - fl_value fl_t369 = fl_nothing(); - FL_TRY(fl_record_new(ctx, opis_diska_names_5, fl_t370, 7, &fl_t369, error)); - *result = fl_t369; - return FL_OK; - } else if (fl_variant_is(fl_t340, "Загрузка")) { - fl_value fl_t371 = fl_nothing(); - FL_TRY(fl_field_get(ctx, svod, "кэш", &fl_t371, error)); - fl_value fl_t372 = fl_nothing(); - FL_TRY(fl_field_get(ctx, svod, "журнал", &fl_t372, error)); - fl_value fl_t373 = fl_nothing(); - FL_TRY(fl_field_get(ctx, svod, "сборка", &fl_t373, error)); - fl_value fl_t374 = fl_nothing(); - FL_TRY(fl_field_get(ctx, svod, "загрузка", &fl_t374, error)); - fl_value fl_t375 = fl_nothing(); - FL_TRY(fl_field_get(ctx, reshenie, "вес", &fl_t375, error)); - if (fl_t374.tag != FL_NUMBER || fl_t375.tag != FL_NUMBER) FL_TRY(fl_not_numbers(ctx, "add", fl_t374, fl_t375, error)); - fl_value fl_t376 = fl_nothing(); - FL_TRY(fl_field_get(ctx, svod, "крупное", &fl_t376, error)); - fl_value fl_t377 = fl_nothing(); - FL_TRY(fl_field_get(ctx, svod, "неизвестное", &fl_t377, error)); - fl_value fl_t378 = fl_nothing(); - FL_TRY(fl_field_get(ctx, svod, "освободить", &fl_t378, error)); - if (fl_t378.tag != FL_NUMBER || ubrat.tag != FL_NUMBER) FL_TRY(fl_not_numbers(ctx, "add", fl_t378, ubrat, error)); - fl_value fl_t380[7]; - fl_t380[0] = fl_t371; /* «кэш» */ - fl_t380[1] = fl_t372; /* «журнал» */ - fl_t380[2] = fl_t373; /* «сборка» */ - fl_t380[3] = fl_number(fl_t374.as.number + fl_t375.as.number); /* «загрузка» */ - fl_t380[4] = fl_t376; /* «крупное» */ - fl_t380[5] = fl_t377; /* «неизвестное» */ - fl_t380[6] = fl_number(fl_t378.as.number + ubrat.as.number); /* «освободить» */ - fl_value fl_t379 = fl_nothing(); - FL_TRY(fl_record_new(ctx, opis_diska_names_5, fl_t380, 7, &fl_t379, error)); - *result = fl_t379; - return FL_OK; - } else if (fl_variant_is(fl_t340, "Крупное")) { - fl_value fl_t381 = fl_nothing(); - FL_TRY(fl_field_get(ctx, svod, "кэш", &fl_t381, error)); - fl_value fl_t382 = fl_nothing(); - FL_TRY(fl_field_get(ctx, svod, "журнал", &fl_t382, error)); - fl_value fl_t383 = fl_nothing(); - FL_TRY(fl_field_get(ctx, svod, "сборка", &fl_t383, error)); - fl_value fl_t384 = fl_nothing(); - FL_TRY(fl_field_get(ctx, svod, "загрузка", &fl_t384, error)); - fl_value fl_t385 = fl_nothing(); - FL_TRY(fl_field_get(ctx, svod, "крупное", &fl_t385, error)); - fl_value fl_t386 = fl_nothing(); - FL_TRY(fl_field_get(ctx, reshenie, "вес", &fl_t386, error)); - if (fl_t385.tag != FL_NUMBER || fl_t386.tag != FL_NUMBER) FL_TRY(fl_not_numbers(ctx, "add", fl_t385, fl_t386, error)); - fl_value fl_t387 = fl_nothing(); - FL_TRY(fl_field_get(ctx, svod, "неизвестное", &fl_t387, error)); - fl_value fl_t388 = fl_nothing(); - FL_TRY(fl_field_get(ctx, svod, "освободить", &fl_t388, error)); - if (fl_t388.tag != FL_NUMBER || ubrat.tag != FL_NUMBER) FL_TRY(fl_not_numbers(ctx, "add", fl_t388, ubrat, error)); - fl_value fl_t390[7]; - fl_t390[0] = fl_t381; /* «кэш» */ - fl_t390[1] = fl_t382; /* «журнал» */ - fl_t390[2] = fl_t383; /* «сборка» */ - fl_t390[3] = fl_t384; /* «загрузка» */ - fl_t390[4] = fl_number(fl_t385.as.number + fl_t386.as.number); /* «крупное» */ - fl_t390[5] = fl_t387; /* «неизвестное» */ - fl_t390[6] = fl_number(fl_t388.as.number + ubrat.as.number); /* «освободить» */ - fl_value fl_t389 = fl_nothing(); - FL_TRY(fl_record_new(ctx, opis_diska_names_5, fl_t390, 7, &fl_t389, error)); - *result = fl_t389; - return FL_OK; - } else if (fl_variant_is(fl_t340, "Неизвестное")) { - fl_value fl_t391 = fl_nothing(); - FL_TRY(fl_field_get(ctx, svod, "кэш", &fl_t391, error)); - fl_value fl_t392 = fl_nothing(); - FL_TRY(fl_field_get(ctx, svod, "журнал", &fl_t392, error)); - fl_value fl_t393 = fl_nothing(); - FL_TRY(fl_field_get(ctx, svod, "сборка", &fl_t393, error)); - fl_value fl_t394 = fl_nothing(); - FL_TRY(fl_field_get(ctx, svod, "загрузка", &fl_t394, error)); - fl_value fl_t395 = fl_nothing(); - FL_TRY(fl_field_get(ctx, svod, "крупное", &fl_t395, error)); - fl_value fl_t396 = fl_nothing(); - FL_TRY(fl_field_get(ctx, svod, "неизвестное", &fl_t396, error)); - fl_value fl_t397 = fl_nothing(); - FL_TRY(fl_field_get(ctx, reshenie, "вес", &fl_t397, error)); - if (fl_t396.tag != FL_NUMBER || fl_t397.tag != FL_NUMBER) FL_TRY(fl_not_numbers(ctx, "add", fl_t396, fl_t397, error)); - fl_value fl_t398 = fl_nothing(); - FL_TRY(fl_field_get(ctx, svod, "освободить", &fl_t398, error)); - if (fl_t398.tag != FL_NUMBER || ubrat.tag != FL_NUMBER) FL_TRY(fl_not_numbers(ctx, "add", fl_t398, ubrat, error)); - fl_value fl_t400[7]; - fl_t400[0] = fl_t391; /* «кэш» */ - fl_t400[1] = fl_t392; /* «журнал» */ - fl_t400[2] = fl_t393; /* «сборка» */ - fl_t400[3] = fl_t394; /* «загрузка» */ - fl_t400[4] = fl_t395; /* «крупное» */ - fl_t400[5] = fl_number(fl_t396.as.number + fl_t397.as.number); /* «неизвестное» */ - fl_t400[6] = fl_number(fl_t398.as.number + ubrat.as.number); /* «освободить» */ - fl_value fl_t399 = fl_nothing(); - FL_TRY(fl_record_new(ctx, opis_diska_names_5, fl_t400, 7, &fl_t399, error)); - *result = fl_t399; - return FL_OK; - } else { - return fl_match_fail(ctx, fl_t340, error); - } -} - -/* - * Функция flang «Свести». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param zapisi — «записи»: список: «Находка» - * @param spravochnik — «справочник»: список: «Место» - * @return значение: «Свод» - */ -fl_status opis_diska_svesti(fl_ctx *ctx, fl_value zapisi, fl_value spravochnik, fl_value *result, fl_error *error) { - fl_value fl_t401 = fl_nothing(); - FL_TRY(opis_diska_reshit_vsyo(ctx, zapisi, spravochnik, &fl_t401, error)); - fl_value fl_t402 = fl_nothing(); - FL_TRY(fl_require_list(ctx, fl_t401, "свёртка", &fl_t402, error)); - fl_value fl_t403 = fl_nothing(); - FL_TRY(opis_diska_pustoy_svod(ctx, &fl_t403, error)); - fl_value svod = fl_t403; /* «свод» */ - const fl_mark fl_t405 = fl_region_open(ctx); - for (size_t fl_t404 = 0; fl_t404 < fl_t402.as.list.count; fl_t404 += 1) { - const fl_value reshenie = fl_t402.as.list.items[fl_t404]; /* «решение» */ - fl_value fl_t406 = fl_nothing(); - FL_TRY(opis_diska_pribavit_reshenie(ctx, svod, reshenie, &fl_t406, error)); - svod = fl_t406; - FL_TRY(fl_region_recycle(ctx, fl_t405, &svod, error)); - } - FL_TRY(fl_region_close(ctx, fl_t405, FL_OK, &svod, error)); - const fl_value fl_t407 = svod; - fl_value fl_t408 = fl_nothing(); - FL_TRY(opis_diska_i2_derzhitsya(ctx, zapisi, spravochnik, fl_t407, &fl_t408, error)); - /* постусловие «И2: освобождаемое не больше убираемого» */ - bool fl_t409 = false; - FL_TRY(fl_post(ctx, fl_t408, "И2: освобождаемое не больше убираемого", "Свести", &fl_t409, error)); - if (!fl_t409) { - return fl_fail(ctx, error, "FLANG_PROPERTY", "%s", "нарушено свойство «И2: освобождаемое не больше убираемого» функции «Свести»"); - } - *result = fl_t407; - return FL_OK; -} - -/* - * Функция flang «Сумма размеров убираемых». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param zapisi — «записи»: список: «Находка» - * @param spravochnik — «справочник»: список: «Место» - * @return значение: число - */ -fl_status opis_diska_summa_razmerov_ubiraemyh(fl_ctx *ctx, fl_value zapisi, fl_value spravochnik, fl_value *result, fl_error *error) { - fl_value fl_t410 = fl_nothing(); - FL_TRY(fl_require_list(ctx, zapisi, "свёртка", &fl_t410, error)); - fl_value akk = fl_number(0.0); /* «акк» */ - const fl_mark fl_t412 = fl_region_open(ctx); - for (size_t fl_t411 = 0; fl_t411 < fl_t410.as.list.count; fl_t411 += 1) { - const fl_value nahodka = fl_t410.as.list.items[fl_t411]; /* «находка» */ - fl_value fl_t413 = fl_nothing(); - fl_value fl_t414 = fl_nothing(); - FL_TRY(opis_diska_reshit_nahodku(ctx, nahodka, spravochnik, &fl_t414, error)); - fl_value fl_t415 = fl_nothing(); - FL_TRY(fl_field_get(ctx, fl_t414, "приговор", &fl_t415, error)); - if (fl_variant_is(fl_t415, "МожноУбрать")) { - fl_value fl_t416 = fl_nothing(); - FL_TRY(fl_field_get(ctx, nahodka, "размер", &fl_t416, error)); - if (akk.tag != FL_NUMBER || fl_t416.tag != FL_NUMBER) FL_TRY(fl_not_numbers(ctx, "add", akk, fl_t416, error)); - fl_t413 = fl_number(akk.as.number + fl_t416.as.number); - } else if (fl_variant_is(fl_t415, "Спросить")) { - fl_t413 = akk; - } else if (fl_variant_is(fl_t415, "НеТрогать")) { - fl_t413 = akk; - } else { - return fl_match_fail(ctx, fl_t415, error); - } - akk = fl_t413; - FL_TRY(fl_region_recycle(ctx, fl_t412, &akk, error)); - } - FL_TRY(fl_region_close(ctx, fl_t412, FL_OK, &akk, error)); - *result = akk; - return FL_OK; -} - -/* - * Функция flang «И2 держится». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param zapisi — «записи»: список: «Находка» - * @param spravochnik — «справочник»: список: «Место» - * @param svod — «свод»: «Свод» - * @return значение - */ -fl_status opis_diska_i2_derzhitsya(fl_ctx *ctx, fl_value zapisi, fl_value spravochnik, fl_value svod, fl_value *result, fl_error *error) { - fl_value fl_t417 = fl_nothing(); - FL_TRY(fl_field_get(ctx, svod, "освободить", &fl_t417, error)); - fl_value fl_t418 = fl_nothing(); - FL_TRY(opis_diska_summa_razmerov_ubiraemyh(ctx, zapisi, spravochnik, &fl_t418, error)); - if (fl_t417.tag != FL_NUMBER || fl_t418.tag != FL_NUMBER) FL_TRY(fl_not_order(ctx, fl_t417, fl_t418, error)); - *result = fl_flag(fl_t417.as.number <= fl_t418.as.number); - return FL_OK; -} - -/* - * Функция flang «Строку отчёта». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param nahodka — «находка»: «Находка» - * @param spravochnik — «справочник»: список: «Место» - * @return значение: «Строка отчёта» - */ -fl_status opis_diska_stroku_otchyota(fl_ctx *ctx, fl_value nahodka, fl_value spravochnik, fl_value *result, fl_error *error) { - fl_value fl_t419 = fl_nothing(); - FL_TRY(fl_field_get(ctx, nahodka, "путь", &fl_t419, error)); - fl_value fl_t420 = fl_nothing(); - FL_TRY(opis_diska_reshit_nahodku(ctx, nahodka, spravochnik, &fl_t420, error)); - fl_value fl_t422[2]; - fl_t422[0] = fl_t419; /* «путь» */ - fl_t422[1] = fl_t420; /* «решение» */ - fl_value fl_t421 = fl_nothing(); - FL_TRY(fl_record_new(ctx, opis_diska_names_4, fl_t422, 2, &fl_t421, error)); - *result = fl_t421; - return FL_OK; -} - -/* Тело «Вставить по весу»; глубину считает обёртка ниже. */ -static fl_status opis_diska_vstavit_po_vesu_body(fl_ctx *ctx, fl_value stroka, fl_value stroki, fl_value *result, fl_error *error) { - if (fl_chain_empty(stroki)) { - fl_value *fl_t423 = NULL; - FL_TRY(fl_list_alloc(ctx, 1, &fl_t423, error)); - fl_t423[0] = stroka; - *result = fl_list(fl_t423, 1); - return FL_OK; - } else if (fl_chain_cons(stroki)) { - const fl_value golova = fl_chain_head(stroki); /* голова «голова» */ - const fl_value hvost = fl_chain_tail(stroki); /* хвост «хвост» */ - fl_value fl_t424 = fl_nothing(); - FL_TRY(fl_field_get(ctx, stroka, "решение", &fl_t424, error)); - fl_value fl_t425 = fl_nothing(); - FL_TRY(fl_field_get(ctx, fl_t424, "вес", &fl_t425, error)); - fl_value fl_t426 = fl_nothing(); - FL_TRY(fl_field_get(ctx, golova, "решение", &fl_t426, error)); - fl_value fl_t427 = fl_nothing(); - FL_TRY(fl_field_get(ctx, fl_t426, "вес", &fl_t427, error)); - if (fl_t425.tag != FL_NUMBER || fl_t427.tag != FL_NUMBER) FL_TRY(fl_not_order(ctx, fl_t425, fl_t427, error)); - bool fl_t428 = false; - FL_TRY(fl_cond(ctx, fl_flag(fl_t425.as.number >= fl_t427.as.number), &fl_t428, error)); - if (fl_t428) { - return opis_diska_pripisat_stroku_otchyota(ctx, stroka, stroki, result, error); - } else { - fl_value fl_t429 = fl_nothing(); - FL_TRY(opis_diska_vstavit_po_vesu(ctx, stroka, hvost, &fl_t429, error)); - return opis_diska_pripisat_stroku_otchyota(ctx, golova, fl_t429, result, error); - } - } else { - return fl_match_fail(ctx, stroki, error); - } -} - -/* - * Функция flang «Вставить по весу». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * - * Рекурсивная: считает глубину, на превышении — FLANG_RECURSION_LIMIT. - * @param stroka — «строка»: «Строка отчёта» - * @param stroki — «строки»: список: «Строка отчёта» - * @return значение: список: «Строка отчёта» - */ -fl_status opis_diska_vstavit_po_vesu(fl_ctx *ctx, fl_value stroka, fl_value stroki, fl_value *result, fl_error *error) { - FL_TRY(fl_enter(ctx, "Вставить по весу", error)); - { - const fl_mark region = fl_region_open(ctx); - const fl_status status = opis_diska_vstavit_po_vesu_body(ctx, stroka, stroki, result, error); - fl_leave(ctx); - return fl_region_close(ctx, region, status, result, error); - } -} - -/* - * Функция flang «Приписать строку отчёта». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param pervaya — «первая»: «Строка отчёта» - * @param stroki — «строки»: список: «Строка отчёта» - * @return значение: список: «Строка отчёта» - */ -fl_status opis_diska_pripisat_stroku_otchyota(fl_ctx *ctx, fl_value pervaya, fl_value stroki, fl_value *result, fl_error *error) { - fl_value fl_t430 = fl_nothing(); - FL_TRY(fl_require_list(ctx, stroki, "свёртка", &fl_t430, error)); - fl_value *fl_t431 = NULL; - FL_TRY(fl_list_alloc(ctx, 1, &fl_t431, error)); - fl_t431[0] = pervaya; - fl_value akk = fl_list(fl_t431, 1); /* «акк» */ - const fl_mark fl_t433 = fl_region_open(ctx); - for (size_t fl_t432 = 0; fl_t432 < fl_t430.as.list.count; fl_t432 += 1) { - const fl_value el = fl_t430.as.list.items[fl_t432]; /* «эл» */ - fl_value fl_t434 = fl_nothing(); /* «добавить» */ - FL_TRY(fl_b_dobavit(ctx, el, akk, &fl_t434, error)); - akk = fl_t434; - FL_TRY(fl_region_recycle(ctx, fl_t433, &akk, error)); - } - FL_TRY(fl_region_close(ctx, fl_t433, FL_OK, &akk, error)); - *result = akk; - return FL_OK; -} - -/* - * Функция flang «Отчёт». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param zapisi — «записи»: список: «Находка» - * @param spravochnik — «справочник»: список: «Место» - * @return значение: список: «Строка отчёта» - */ -fl_status opis_diska_otchyot(fl_ctx *ctx, fl_value zapisi, fl_value spravochnik, fl_value *result, fl_error *error) { - fl_value fl_t435 = fl_nothing(); - FL_TRY(fl_require_list(ctx, zapisi, "свёртка", &fl_t435, error)); - fl_value akk = fl_list(NULL, 0); /* «акк» */ - const fl_mark fl_t437 = fl_region_open(ctx); - for (size_t fl_t436 = 0; fl_t436 < fl_t435.as.list.count; fl_t436 += 1) { - const fl_value nahodka = fl_t435.as.list.items[fl_t436]; /* «находка» */ - fl_value fl_t438 = fl_nothing(); - FL_TRY(opis_diska_stroku_otchyota(ctx, nahodka, spravochnik, &fl_t438, error)); - fl_value fl_t439 = fl_nothing(); - FL_TRY(opis_diska_vstavit_po_vesu(ctx, fl_t438, akk, &fl_t439, error)); - akk = fl_t439; - FL_TRY(fl_region_recycle(ctx, fl_t437, &akk, error)); - } - FL_TRY(fl_region_close(ctx, fl_t437, FL_OK, &akk, error)); - const fl_value fl_t440 = akk; - fl_value fl_t441 = fl_nothing(); - FL_TRY(opis_diska_otchyot_toy_zhe_dliny(ctx, zapisi, fl_t440, &fl_t441, error)); - /* постусловие «Отчёт той же длины» */ - bool fl_t442 = false; - FL_TRY(fl_post(ctx, fl_t441, "Отчёт той же длины", "Отчёт", &fl_t442, error)); - if (!fl_t442) { - return fl_fail(ctx, error, "FLANG_PROPERTY", "%s", "нарушено свойство «Отчёт той же длины» функции «Отчёт»"); - } - *result = fl_t440; - return FL_OK; -} - -/* - * Функция flang «Отчёт той же длины». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param zapisi — «записи»: список: «Находка» - * @param stroki — «строки»: список: «Строка отчёта» - * @return значение - */ -fl_status opis_diska_otchyot_toy_zhe_dliny(fl_ctx *ctx, fl_value zapisi, fl_value stroki, fl_value *result, fl_error *error) { - fl_value fl_t443 = fl_nothing(); /* «длина» */ - FL_TRY(fl_b_dlina(ctx, stroki, &fl_t443, error)); - fl_value fl_t444 = fl_nothing(); /* «длина» */ - FL_TRY(fl_b_dlina(ctx, zapisi, &fl_t444, error)); - *result = fl_flag(fl_equal(fl_t443, fl_t444)); - return FL_OK; -} - -/* - * Функция flang «Это МожноУбрать». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param prigovor — «приговор»: «Приговор» - * @return значение - */ -fl_status opis_diska_eto_mozhnoubrat(fl_ctx *ctx, fl_value prigovor, fl_value *result, fl_error *error) { - if (fl_variant_is(prigovor, "МожноУбрать")) { - *result = fl_flag(true); - return FL_OK; - } else if (fl_variant_is(prigovor, "Спросить")) { - *result = fl_flag(false); - return FL_OK; - } else if (fl_variant_is(prigovor, "НеТрогать")) { - *result = fl_flag(false); - return FL_OK; - } else { - return fl_match_fail(ctx, prigovor, error); - } -} - -/* - * Функция flang «Это НеТрогать». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param prigovor — «приговор»: «Приговор» - * @return значение - */ -fl_status opis_diska_eto_netrogat(fl_ctx *ctx, fl_value prigovor, fl_value *result, fl_error *error) { - if (fl_variant_is(prigovor, "НеТрогать")) { - *result = fl_flag(true); - return FL_OK; - } else if (fl_variant_is(prigovor, "Спросить")) { - *result = fl_flag(false); - return FL_OK; - } else if (fl_variant_is(prigovor, "МожноУбрать")) { - *result = fl_flag(false); - return FL_OK; - } else { - return fl_match_fail(ctx, prigovor, error); - } -} - -/* - * Функция flang «И1 на паре». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param razryad — «разряд»: «Разряд» - * @param prigovor — «приговор»: «Приговор» - * @return значение - */ -fl_status opis_diska_i1_na_pare(fl_ctx *ctx, fl_value razryad, fl_value prigovor, fl_value *result, fl_error *error) { - fl_value fl_t445 = fl_nothing(); - FL_TRY(opis_diska_eto_mozhnoubrat(ctx, prigovor, &fl_t445, error)); - bool fl_t446 = false; - FL_TRY(fl_cond(ctx, fl_t445, &fl_t446, error)); - fl_value fl_t447 = fl_nothing(); - if (fl_t446) { - fl_t447 = fl_flag(false); - } else { - fl_t447 = fl_flag(true); - } - bool fl_t448 = false; - FL_TRY(fl_cond(ctx, fl_t447, &fl_t448, error)); - if (fl_t448) { - *result = fl_flag(true); - return FL_OK; - } else { - if (fl_variant_is(razryad, "Кэш")) { - *result = fl_flag(true); - return FL_OK; - } else if (fl_variant_is(razryad, "Журнал")) { - *result = fl_flag(true); - return FL_OK; - } else if (fl_variant_is(razryad, "Сборка")) { - *result = fl_flag(true); - return FL_OK; - } else if (fl_variant_is(razryad, "Загрузка")) { - *result = fl_flag(false); - return FL_OK; - } else if (fl_variant_is(razryad, "Крупное")) { - *result = fl_flag(false); - return FL_OK; - } else if (fl_variant_is(razryad, "Неизвестное")) { - *result = fl_flag(false); - return FL_OK; - } else { - return fl_match_fail(ctx, razryad, error); - } - } -} - -/* - * Функция flang «Порог разряда». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param razryad — «разряд»: «Разряд» - * @return значение: число - */ -fl_status opis_diska_porog_razryada(fl_ctx *ctx, fl_value razryad, fl_value *result, fl_error *error) { - if (fl_variant_is(razryad, "Кэш")) { - return opis_diska_porog_kesha(ctx, result, error); - } else if (fl_variant_is(razryad, "Сборка")) { - return opis_diska_porog_kesha(ctx, result, error); - } else if (fl_variant_is(razryad, "Журнал")) { - return opis_diska_porog_zhurnala(ctx, result, error); - } else if (fl_variant_is(razryad, "Загрузка")) { - return opis_diska_porog_zagruzki(ctx, result, error); - } else if (fl_variant_is(razryad, "Крупное")) { - *result = fl_number(0.0); - return FL_OK; - } else if (fl_variant_is(razryad, "Неизвестное")) { - *result = fl_number(0.0); - return FL_OK; - } else { - return fl_match_fail(ctx, razryad, error); - } -} - -/* - * Функция flang «Разряд обоснован». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param nahodka — «находка»: «Находка» - * @param mesto — «место»: «Разряд» - * @param razryad — «разряд»: «Разряд» - * @return значение - */ -fl_status opis_diska_razryad_obosnovan(fl_ctx *ctx, fl_value nahodka, fl_value mesto, fl_value razryad, fl_value *result, fl_error *error) { - fl_value fl_t449 = fl_nothing(); - FL_TRY(opis_diska_eto_neizvestnoe(ctx, mesto, &fl_t449, error)); - bool fl_t450 = false; - FL_TRY(fl_cond(ctx, fl_t449, &fl_t450, error)); - fl_value fl_t451 = fl_nothing(); - if (fl_t450) { - fl_t451 = fl_flag(false); - } else { - fl_t451 = fl_flag(true); - } - bool fl_t452 = false; - FL_TRY(fl_cond(ctx, fl_t451, &fl_t452, error)); - if (fl_t452) { - return opis_diska_tot_zhe_razryad(ctx, razryad, mesto, result, error); - } else { - return opis_diska_razryad_obosnovan_primetoy(ctx, nahodka, razryad, result, error); - } -} - -/* - * Функция flang «Разряд обоснован приметой». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param nahodka — «находка»: «Находка» - * @param razryad — «разряд»: «Разряд» - * @return значение - */ -fl_status opis_diska_razryad_obosnovan_primetoy(fl_ctx *ctx, fl_value nahodka, fl_value razryad, fl_value *result, fl_error *error) { - fl_value fl_t453 = fl_nothing(); - FL_TRY(fl_field_get(ctx, nahodka, "путь", &fl_t453, error)); - fl_value fl_t454 = fl_nothing(); - FL_TRY(opis_diska_primeta_kesha(ctx, fl_t453, &fl_t454, error)); - const fl_value kesh = fl_t454; /* пусть «кэш» */ - fl_value fl_t455 = fl_nothing(); - FL_TRY(fl_field_get(ctx, nahodka, "путь", &fl_t455, error)); - fl_value fl_t456 = fl_nothing(); - FL_TRY(opis_diska_primeta_zhurnala(ctx, fl_t455, &fl_t456, error)); - const fl_value zhurnal = fl_t456; /* пусть «журнал» */ - fl_value fl_t457 = fl_nothing(); - FL_TRY(fl_field_get(ctx, nahodka, "путь", &fl_t457, error)); - fl_value fl_t458 = fl_nothing(); - FL_TRY(opis_diska_primeta_sborki(ctx, fl_t457, &fl_t458, error)); - const fl_value sborka = fl_t458; /* пусть «сборка» */ - fl_value fl_t459 = fl_nothing(); - FL_TRY(fl_field_get(ctx, nahodka, "путь", &fl_t459, error)); - fl_value fl_t460 = fl_nothing(); - FL_TRY(opis_diska_primeta_zagruzki(ctx, fl_t459, &fl_t460, error)); - const fl_value zagruzka = fl_t460; /* пусть «загрузка» */ - fl_value fl_t461 = fl_nothing(); - FL_TRY(fl_field_get(ctx, nahodka, "размер", &fl_t461, error)); - fl_value fl_t462 = fl_nothing(); - FL_TRY(opis_diska_porog_krupnogo(ctx, &fl_t462, error)); - if (fl_t461.tag != FL_NUMBER || fl_t462.tag != FL_NUMBER) FL_TRY(fl_not_order(ctx, fl_t461, fl_t462, error)); - const fl_value krupnoe = fl_flag(fl_t461.as.number >= fl_t462.as.number); /* пусть «крупное» */ - if (fl_variant_is(razryad, "Кэш")) { - *result = kesh; - return FL_OK; - } else if (fl_variant_is(razryad, "Журнал")) { - bool fl_t463 = false; - FL_TRY(fl_cond(ctx, zhurnal, &fl_t463, error)); - if (fl_t463) { - bool fl_t464 = false; - FL_TRY(fl_cond(ctx, kesh, &fl_t464, error)); - if (fl_t464) { - *result = fl_flag(false); - return FL_OK; - } else { - *result = fl_flag(true); - return FL_OK; - } - } else { - *result = fl_flag(false); - return FL_OK; - } - } else if (fl_variant_is(razryad, "Сборка")) { - bool fl_t465 = false; - FL_TRY(fl_cond(ctx, sborka, &fl_t465, error)); - fl_value fl_t466 = fl_nothing(); - if (fl_t465) { - bool fl_t467 = false; - FL_TRY(fl_cond(ctx, kesh, &fl_t467, error)); - fl_value fl_t468 = fl_nothing(); - if (fl_t467) { - fl_t468 = fl_flag(false); - } else { - fl_t468 = fl_flag(true); - } - fl_t466 = fl_t468; - } else { - fl_t466 = fl_flag(false); - } - bool fl_t469 = false; - FL_TRY(fl_cond(ctx, fl_t466, &fl_t469, error)); - if (fl_t469) { - bool fl_t470 = false; - FL_TRY(fl_cond(ctx, zhurnal, &fl_t470, error)); - if (fl_t470) { - *result = fl_flag(false); - return FL_OK; - } else { - *result = fl_flag(true); - return FL_OK; - } - } else { - *result = fl_flag(false); - return FL_OK; - } - } else if (fl_variant_is(razryad, "Загрузка")) { - bool fl_t471 = false; - FL_TRY(fl_cond(ctx, zagruzka, &fl_t471, error)); - fl_value fl_t472 = fl_nothing(); - if (fl_t471) { - bool fl_t473 = false; - FL_TRY(fl_cond(ctx, kesh, &fl_t473, error)); - fl_value fl_t474 = fl_nothing(); - if (fl_t473) { - fl_t474 = fl_flag(false); - } else { - fl_t474 = fl_flag(true); - } - fl_t472 = fl_t474; - } else { - fl_t472 = fl_flag(false); - } - bool fl_t475 = false; - FL_TRY(fl_cond(ctx, fl_t472, &fl_t475, error)); - fl_value fl_t476 = fl_nothing(); - if (fl_t475) { - bool fl_t477 = false; - FL_TRY(fl_cond(ctx, zhurnal, &fl_t477, error)); - fl_value fl_t478 = fl_nothing(); - if (fl_t477) { - fl_t478 = fl_flag(false); - } else { - fl_t478 = fl_flag(true); - } - fl_t476 = fl_t478; - } else { - fl_t476 = fl_flag(false); - } - bool fl_t479 = false; - FL_TRY(fl_cond(ctx, fl_t476, &fl_t479, error)); - if (fl_t479) { - bool fl_t480 = false; - FL_TRY(fl_cond(ctx, sborka, &fl_t480, error)); - if (fl_t480) { - *result = fl_flag(false); - return FL_OK; - } else { - *result = fl_flag(true); - return FL_OK; - } - } else { - *result = fl_flag(false); - return FL_OK; - } - } else if (fl_variant_is(razryad, "Крупное")) { - bool fl_t481 = false; - FL_TRY(fl_cond(ctx, krupnoe, &fl_t481, error)); - fl_value fl_t482 = fl_nothing(); - if (fl_t481) { - bool fl_t483 = false; - FL_TRY(fl_cond(ctx, kesh, &fl_t483, error)); - fl_value fl_t484 = fl_nothing(); - if (fl_t483) { - fl_t484 = fl_flag(false); - } else { - fl_t484 = fl_flag(true); - } - fl_t482 = fl_t484; - } else { - fl_t482 = fl_flag(false); - } - bool fl_t485 = false; - FL_TRY(fl_cond(ctx, fl_t482, &fl_t485, error)); - fl_value fl_t486 = fl_nothing(); - if (fl_t485) { - bool fl_t487 = false; - FL_TRY(fl_cond(ctx, zhurnal, &fl_t487, error)); - fl_value fl_t488 = fl_nothing(); - if (fl_t487) { - fl_t488 = fl_flag(false); - } else { - fl_t488 = fl_flag(true); - } - fl_t486 = fl_t488; - } else { - fl_t486 = fl_flag(false); - } - bool fl_t489 = false; - FL_TRY(fl_cond(ctx, fl_t486, &fl_t489, error)); - fl_value fl_t490 = fl_nothing(); - if (fl_t489) { - bool fl_t491 = false; - FL_TRY(fl_cond(ctx, sborka, &fl_t491, error)); - fl_value fl_t492 = fl_nothing(); - if (fl_t491) { - fl_t492 = fl_flag(false); - } else { - fl_t492 = fl_flag(true); - } - fl_t490 = fl_t492; - } else { - fl_t490 = fl_flag(false); - } - bool fl_t493 = false; - FL_TRY(fl_cond(ctx, fl_t490, &fl_t493, error)); - if (fl_t493) { - bool fl_t494 = false; - FL_TRY(fl_cond(ctx, zagruzka, &fl_t494, error)); - if (fl_t494) { - *result = fl_flag(false); - return FL_OK; - } else { - *result = fl_flag(true); - return FL_OK; - } - } else { - *result = fl_flag(false); - return FL_OK; - } - } else if (fl_variant_is(razryad, "Неизвестное")) { - bool fl_t495 = false; - FL_TRY(fl_cond(ctx, kesh, &fl_t495, error)); - fl_value fl_t496 = fl_nothing(); - if (fl_t495) { - fl_t496 = fl_flag(false); - } else { - fl_t496 = fl_flag(true); - } - bool fl_t497 = false; - FL_TRY(fl_cond(ctx, fl_t496, &fl_t497, error)); - fl_value fl_t498 = fl_nothing(); - if (fl_t497) { - bool fl_t499 = false; - FL_TRY(fl_cond(ctx, zhurnal, &fl_t499, error)); - fl_value fl_t500 = fl_nothing(); - if (fl_t499) { - fl_t500 = fl_flag(false); - } else { - fl_t500 = fl_flag(true); - } - fl_t498 = fl_t500; - } else { - fl_t498 = fl_flag(false); - } - bool fl_t501 = false; - FL_TRY(fl_cond(ctx, fl_t498, &fl_t501, error)); - fl_value fl_t502 = fl_nothing(); - if (fl_t501) { - bool fl_t503 = false; - FL_TRY(fl_cond(ctx, sborka, &fl_t503, error)); - fl_value fl_t504 = fl_nothing(); - if (fl_t503) { - fl_t504 = fl_flag(false); - } else { - fl_t504 = fl_flag(true); - } - fl_t502 = fl_t504; - } else { - fl_t502 = fl_flag(false); - } - bool fl_t505 = false; - FL_TRY(fl_cond(ctx, fl_t502, &fl_t505, error)); - fl_value fl_t506 = fl_nothing(); - if (fl_t505) { - bool fl_t507 = false; - FL_TRY(fl_cond(ctx, zagruzka, &fl_t507, error)); - fl_value fl_t508 = fl_nothing(); - if (fl_t507) { - fl_t508 = fl_flag(false); - } else { - fl_t508 = fl_flag(true); - } - fl_t506 = fl_t508; - } else { - fl_t506 = fl_flag(false); - } - bool fl_t509 = false; - FL_TRY(fl_cond(ctx, fl_t506, &fl_t509, error)); - if (fl_t509) { - bool fl_t510 = false; - FL_TRY(fl_cond(ctx, krupnoe, &fl_t510, error)); - if (fl_t510) { - *result = fl_flag(false); - return FL_OK; - } else { - *result = fl_flag(true); - return FL_OK; - } - } else { - *result = fl_flag(false); - return FL_OK; - } - } else { - return fl_match_fail(ctx, razryad, error); - } -} - -/* - * Функция flang «Приговор обоснован». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param nahodka — «находка»: «Находка» - * @param razryad — «разряд»: «Разряд» - * @param prigovor — «приговор»: «Приговор» - * @return значение - */ -fl_status opis_diska_prigovor_obosnovan(fl_ctx *ctx, fl_value nahodka, fl_value razryad, fl_value prigovor, fl_value *result, fl_error *error) { - fl_value fl_t511 = fl_nothing(); - FL_TRY(fl_field_get(ctx, nahodka, "доступен", &fl_t511, error)); - bool fl_t512 = false; - FL_TRY(fl_cond(ctx, fl_t511, &fl_t512, error)); - fl_value fl_t513 = fl_nothing(); - if (fl_t512) { - fl_t513 = fl_flag(false); - } else { - fl_t513 = fl_flag(true); - } - bool fl_t514 = false; - FL_TRY(fl_cond(ctx, fl_t513, &fl_t514, error)); - if (fl_t514) { - return opis_diska_eto_netrogat(ctx, prigovor, result, error); - } else { - fl_value fl_t515 = fl_nothing(); - FL_TRY(opis_diska_ssylka(ctx, nahodka, &fl_t515, error)); - bool fl_t516 = false; - FL_TRY(fl_cond(ctx, fl_t515, &fl_t516, error)); - if (fl_t516) { - return opis_diska_eto_netrogat(ctx, prigovor, result, error); - } else { - fl_value fl_t517 = fl_nothing(); - FL_TRY(opis_diska_eto_mozhnoubrat(ctx, prigovor, &fl_t517, error)); - bool fl_t518 = false; - FL_TRY(fl_cond(ctx, fl_t517, &fl_t518, error)); - if (fl_t518) { - fl_value fl_t519 = fl_nothing(); - FL_TRY(opis_diska_i1_na_pare(ctx, razryad, prigovor, &fl_t519, error)); - bool fl_t520 = false; - FL_TRY(fl_cond(ctx, fl_t519, &fl_t520, error)); - fl_value fl_t521 = fl_nothing(); - if (fl_t520) { - fl_value fl_t522 = fl_nothing(); - FL_TRY(opis_diska_katalog(ctx, nahodka, &fl_t522, error)); - bool fl_t523 = false; - FL_TRY(fl_cond(ctx, fl_t522, &fl_t523, error)); - fl_value fl_t524 = fl_nothing(); - if (fl_t523) { - fl_t524 = fl_flag(false); - } else { - fl_t524 = fl_flag(true); - } - fl_t521 = fl_t524; - } else { - fl_t521 = fl_flag(false); - } - bool fl_t525 = false; - FL_TRY(fl_cond(ctx, fl_t521, &fl_t525, error)); - if (fl_t525) { - fl_value fl_t526 = fl_nothing(); - FL_TRY(fl_field_get(ctx, nahodka, "возраст_дней", &fl_t526, error)); - fl_value fl_t527 = fl_nothing(); - FL_TRY(opis_diska_porog_razryada(ctx, razryad, &fl_t527, error)); - if (fl_t526.tag != FL_NUMBER || fl_t527.tag != FL_NUMBER) FL_TRY(fl_not_order(ctx, fl_t526, fl_t527, error)); - *result = fl_flag(fl_t526.as.number >= fl_t527.as.number); - return FL_OK; - } else { - *result = fl_flag(false); - return FL_OK; - } - } else { - *result = fl_flag(true); - return FL_OK; - } - } - } -} - -/* - * Функция flang «Вес обоснован». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param nahodka — «находка»: «Находка» - * @param prigovor — «приговор»: «Приговор» - * @param ves — «вес»: число - * @return значение - */ -fl_status opis_diska_ves_obosnovan(fl_ctx *ctx, fl_value nahodka, fl_value prigovor, fl_value ves, fl_value *result, fl_error *error) { - fl_value fl_t528 = fl_nothing(); - FL_TRY(opis_diska_eto_netrogat(ctx, prigovor, &fl_t528, error)); - bool fl_t529 = false; - FL_TRY(fl_cond(ctx, fl_t528, &fl_t529, error)); - if (fl_t529) { - *result = fl_flag(fl_equal(ves, fl_number(0.0))); - return FL_OK; - } else { - fl_value fl_t530 = fl_nothing(); - FL_TRY(fl_field_get(ctx, nahodka, "размер", &fl_t530, error)); - bool fl_t531 = false; - FL_TRY(fl_cond(ctx, fl_flag(fl_equal(ves, fl_t530)), &fl_t531, error)); - if (fl_t531) { - return opis_diska_ves_v_granicah(ctx, nahodka, ves, result, error); - } else { - *result = fl_flag(false); - return FL_OK; - } - } -} - -/* - * Вызов по исходному имени flang. Коды и тексты — те же, что у - * интерпретатора: «не найдена функция …» и «функция … принимает N аргум.». - */ -fl_status opis_diska_call(fl_ctx *ctx, const char *name, const fl_value *args, size_t count, - fl_value *result, fl_error *error) { - if (strcmp(name, "Порог крупного") == 0) { - if (count != 0) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Порог крупного", (unsigned long)0, (unsigned long)count); - } - return opis_diska_porog_krupnogo(ctx, result, error); - } - if (strcmp(name, "Порог кэша") == 0) { - if (count != 0) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Порог кэша", (unsigned long)0, (unsigned long)count); - } - return opis_diska_porog_kesha(ctx, result, error); - } - if (strcmp(name, "Порог журнала") == 0) { - if (count != 0) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Порог журнала", (unsigned long)0, (unsigned long)count); - } - return opis_diska_porog_zhurnala(ctx, result, error); - } - if (strcmp(name, "Порог загрузки") == 0) { - if (count != 0) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Порог загрузки", (unsigned long)0, (unsigned long)count); - } - return opis_diska_porog_zagruzki(ctx, result, error); - } - if (strcmp(name, "Составляющие пути") == 0) { - if (count != 1) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Составляющие пути", (unsigned long)1, (unsigned long)count); - } - return opis_diska_sostavlyayuschie_puti(ctx, args[0], result, error); - } - if (strcmp(name, "Имя в пути") == 0) { - if (count != 1) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Имя в пути", (unsigned long)1, (unsigned long)count); - } - return opis_diska_imya_v_puti(ctx, args[0], result, error); - } - if (strcmp(name, "Есть составляющая") == 0) { - if (count != 2) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Есть составляющая", (unsigned long)2, (unsigned long)count); - } - return opis_diska_est_sostavlyayuschaya(ctx, args[0], args[1], result, error); - } - if (strcmp(name, "Оканчивается на") == 0) { - if (count != 2) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Оканчивается на", (unsigned long)2, (unsigned long)count); - } - return opis_diska_okanchivaetsya_na(ctx, args[0], args[1], result, error); - } - if (strcmp(name, "След пути") == 0) { - if (count != 1) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "След пути", (unsigned long)1, (unsigned long)count); - } - return opis_diska_sled_puti(ctx, args[0], result, error); - } - if (strcmp(name, "Цепь ограничена") == 0) { - if (count != 1) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Цепь ограничена", (unsigned long)1, (unsigned long)count); - } - return opis_diska_cep_ogranichena(ctx, args[0], result, error); - } - if (strcmp(name, "Справочник ограничен") == 0) { - if (count != 1) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Справочник ограничен", (unsigned long)1, (unsigned long)count); - } - return opis_diska_spravochnik_ogranichen(ctx, args[0], result, error); - } - if (strcmp(name, "Разряд места допустим") == 0) { - if (count != 1) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Разряд места допустим", (unsigned long)1, (unsigned long)count); - } - return opis_diska_razryad_mesta_dopustim(ctx, args[0], result, error); - } - if (strcmp(name, "Место подходит") == 0) { - if (count != 2) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Место подходит", (unsigned long)2, (unsigned long)count); - } - return opis_diska_mesto_podhodit(ctx, args[0], args[1], result, error); - } - if (strcmp(name, "Это неизвестное") == 0) { - if (count != 1) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Это неизвестное", (unsigned long)1, (unsigned long)count); - } - return opis_diska_eto_neizvestnoe(ctx, args[0], result, error); - } - if (strcmp(name, "Номер разряда") == 0) { - if (count != 1) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Номер разряда", (unsigned long)1, (unsigned long)count); - } - return opis_diska_nomer_razryada(ctx, args[0], result, error); - } - if (strcmp(name, "Тот же разряд") == 0) { - if (count != 2) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Тот же разряд", (unsigned long)2, (unsigned long)count); - } - return opis_diska_tot_zhe_razryad(ctx, args[0], args[1], result, error); - } - if (strcmp(name, "Место обосновано") == 0) { - if (count != 3) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Место обосновано", (unsigned long)3, (unsigned long)count); - } - return opis_diska_mesto_obosnovano(ctx, args[0], args[1], args[2], result, error); - } - if (strcmp(name, "Разряд по справочнику") == 0) { - if (count != 2) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Разряд по справочнику", (unsigned long)2, (unsigned long)count); - } - return opis_diska_razryad_po_spravochniku(ctx, args[0], args[1], result, error); - } - if (strcmp(name, "Шестнадцатеричный знак") == 0) { - if (count != 1) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Шестнадцатеричный знак", (unsigned long)1, (unsigned long)count); - } - return opis_diska_shestnadcaterichnyy_znak(ctx, args[0], result, error); - } - if (strcmp(name, "Похоже на отпечаток") == 0) { - if (count != 1) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Похоже на отпечаток", (unsigned long)1, (unsigned long)count); - } - return opis_diska_pohozhe_na_otpechatok(ctx, args[0], result, error); - } - if (strcmp(name, "Адресуется содержимым") == 0) { - if (count != 1) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Адресуется содержимым", (unsigned long)1, (unsigned long)count); - } - return opis_diska_adresuetsya_soderzhimym(ctx, args[0], result, error); - } - if (strcmp(name, "Под системным временным") == 0) { - if (count != 1) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Под системным временным", (unsigned long)1, (unsigned long)count); - } - return opis_diska_pod_sistemnym_vremennym(ctx, args[0], result, error); - } - if (strcmp(name, "Примета кэша") == 0) { - if (count != 1) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Примета кэша", (unsigned long)1, (unsigned long)count); - } - return opis_diska_primeta_kesha(ctx, args[0], result, error); - } - if (strcmp(name, "Примета журнала") == 0) { - if (count != 1) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Примета журнала", (unsigned long)1, (unsigned long)count); - } - return opis_diska_primeta_zhurnala(ctx, args[0], result, error); - } - if (strcmp(name, "Примета сборки") == 0) { - if (count != 1) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Примета сборки", (unsigned long)1, (unsigned long)count); - } - return opis_diska_primeta_sborki(ctx, args[0], result, error); - } - if (strcmp(name, "Примета загрузки") == 0) { - if (count != 1) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Примета загрузки", (unsigned long)1, (unsigned long)count); - } - return opis_diska_primeta_zagruzki(ctx, args[0], result, error); - } - if (strcmp(name, "Есть примета") == 0) { - if (count != 2) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Есть примета", (unsigned long)2, (unsigned long)count); - } - return opis_diska_est_primeta(ctx, args[0], args[1], result, error); - } - if (strcmp(name, "Разряд решён размером") == 0) { - if (count != 1) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Разряд решён размером", (unsigned long)1, (unsigned long)count); - } - return opis_diska_razryad_reshyon_razmerom(ctx, args[0], result, error); - } - if (strcmp(name, "Разряд находки") == 0) { - if (count != 2) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Разряд находки", (unsigned long)2, (unsigned long)count); - } - return opis_diska_razryad_nahodki(ctx, args[0], args[1], result, error); - } - if (strcmp(name, "Крупное не мельче порога") == 0) { - if (count != 2) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Крупное не мельче порога", (unsigned long)2, (unsigned long)count); - } - return opis_diska_krupnoe_ne_melche_poroga(ctx, args[0], args[1], result, error); - } - if (strcmp(name, "Каталог") == 0) { - if (count != 1) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Каталог", (unsigned long)1, (unsigned long)count); - } - return opis_diska_katalog(ctx, args[0], result, error); - } - if (strcmp(name, "Ссылка") == 0) { - if (count != 1) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Ссылка", (unsigned long)1, (unsigned long)count); - } - return opis_diska_ssylka(ctx, args[0], result, error); - } - if (strcmp(name, "Приговор мусора") == 0) { - if (count != 2) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Приговор мусора", (unsigned long)2, (unsigned long)count); - } - return opis_diska_prigovor_musora(ctx, args[0], args[1], result, error); - } - if (strcmp(name, "Приговор находки") == 0) { - if (count != 2) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Приговор находки", (unsigned long)2, (unsigned long)count); - } - return opis_diska_prigovor_nahodki(ctx, args[0], args[1], result, error); - } - if (strcmp(name, "Вес находки") == 0) { - if (count != 2) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Вес находки", (unsigned long)2, (unsigned long)count); - } - return opis_diska_ves_nahodki(ctx, args[0], args[1], result, error); - } - if (strcmp(name, "Вес в границах") == 0) { - if (count != 2) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Вес в границах", (unsigned long)2, (unsigned long)count); - } - return opis_diska_ves_v_granicah(ctx, args[0], args[1], result, error); - } - if (strcmp(name, "Решить находку") == 0) { - if (count != 2) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Решить находку", (unsigned long)2, (unsigned long)count); - } - return opis_diska_reshit_nahodku(ctx, args[0], args[1], result, error); - } - if (strcmp(name, "Решить всё") == 0) { - if (count != 2) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Решить всё", (unsigned long)2, (unsigned long)count); - } - return opis_diska_reshit_vsyo(ctx, args[0], args[1], result, error); - } - if (strcmp(name, "Расширения исходников") == 0) { - if (count != 0) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Расширения исходников", (unsigned long)0, (unsigned long)count); - } - return opis_diska_rasshireniya_ishodnikov(ctx, result, error); - } - if (strcmp(name, "Примета исходника") == 0) { - if (count != 1) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Примета исходника", (unsigned long)1, (unsigned long)count); - } - return opis_diska_primeta_ishodnika(ctx, args[0], result, error); - } - if (strcmp(name, "Под присмотром системы версий") == 0) { - if (count != 1) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Под присмотром системы версий", (unsigned long)1, (unsigned long)count); - } - return opis_diska_pod_prismotrom_sistemy_versiy(ctx, args[0], result, error); - } - if (strcmp(name, "Мусорный разряд") == 0) { - if (count != 1) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Мусорный разряд", (unsigned long)1, (unsigned long)count); - } - return opis_diska_musornyy_razryad(ctx, args[0], result, error); - } - if (strcmp(name, "Природа находки") == 0) { - if (count != 3) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Природа находки", (unsigned long)3, (unsigned long)count); - } - return opis_diska_priroda_nahodki(ctx, args[0], args[1], args[2], result, error); - } - if (strcmp(name, "Природа обоснована") == 0) { - if (count != 4) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Природа обоснована", (unsigned long)4, (unsigned long)count); - } - return opis_diska_priroda_obosnovana(ctx, args[0], args[1], args[2], args[3], result, error); - } - if (strcmp(name, "Природа по находке") == 0) { - if (count != 2) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Природа по находке", (unsigned long)2, (unsigned long)count); - } - return opis_diska_priroda_po_nahodke(ctx, args[0], args[1], result, error); - } - if (strcmp(name, "Строгость") == 0) { - if (count != 1) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Строгость", (unsigned long)1, (unsigned long)count); - } - return opis_diska_strogost(ctx, args[0], result, error); - } - if (strcmp(name, "И1 держится") == 0) { - if (count != 1) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "И1 держится", (unsigned long)1, (unsigned long)count); - } - return opis_diska_i1_derzhitsya(ctx, args[0], result, error); - } - if (strcmp(name, "И1 держится всюду") == 0) { - if (count != 1) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "И1 держится всюду", (unsigned long)1, (unsigned long)count); - } - return opis_diska_i1_derzhitsya_vsyudu(ctx, args[0], result, error); - } - if (strcmp(name, "Пустой свод") == 0) { - if (count != 0) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Пустой свод", (unsigned long)0, (unsigned long)count); - } - return opis_diska_pustoy_svod(ctx, result, error); - } - if (strcmp(name, "Прибавить решение") == 0) { - if (count != 2) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Прибавить решение", (unsigned long)2, (unsigned long)count); - } - return opis_diska_pribavit_reshenie(ctx, args[0], args[1], result, error); - } - if (strcmp(name, "Свести") == 0) { - if (count != 2) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Свести", (unsigned long)2, (unsigned long)count); - } - return opis_diska_svesti(ctx, args[0], args[1], result, error); - } - if (strcmp(name, "Сумма размеров убираемых") == 0) { - if (count != 2) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Сумма размеров убираемых", (unsigned long)2, (unsigned long)count); - } - return opis_diska_summa_razmerov_ubiraemyh(ctx, args[0], args[1], result, error); - } - if (strcmp(name, "И2 держится") == 0) { - if (count != 3) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "И2 держится", (unsigned long)3, (unsigned long)count); - } - return opis_diska_i2_derzhitsya(ctx, args[0], args[1], args[2], result, error); - } - if (strcmp(name, "Строку отчёта") == 0) { - if (count != 2) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Строку отчёта", (unsigned long)2, (unsigned long)count); - } - return opis_diska_stroku_otchyota(ctx, args[0], args[1], result, error); - } - if (strcmp(name, "Вставить по весу") == 0) { - if (count != 2) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Вставить по весу", (unsigned long)2, (unsigned long)count); - } - return opis_diska_vstavit_po_vesu(ctx, args[0], args[1], result, error); - } - if (strcmp(name, "Приписать строку отчёта") == 0) { - if (count != 2) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Приписать строку отчёта", (unsigned long)2, (unsigned long)count); - } - return opis_diska_pripisat_stroku_otchyota(ctx, args[0], args[1], result, error); - } - if (strcmp(name, "Отчёт") == 0) { - if (count != 2) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Отчёт", (unsigned long)2, (unsigned long)count); - } - return opis_diska_otchyot(ctx, args[0], args[1], result, error); - } - if (strcmp(name, "Отчёт той же длины") == 0) { - if (count != 2) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Отчёт той же длины", (unsigned long)2, (unsigned long)count); - } - return opis_diska_otchyot_toy_zhe_dliny(ctx, args[0], args[1], result, error); - } - if (strcmp(name, "Это МожноУбрать") == 0) { - if (count != 1) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Это МожноУбрать", (unsigned long)1, (unsigned long)count); - } - return opis_diska_eto_mozhnoubrat(ctx, args[0], result, error); - } - if (strcmp(name, "Это НеТрогать") == 0) { - if (count != 1) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Это НеТрогать", (unsigned long)1, (unsigned long)count); - } - return opis_diska_eto_netrogat(ctx, args[0], result, error); - } - if (strcmp(name, "И1 на паре") == 0) { - if (count != 2) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "И1 на паре", (unsigned long)2, (unsigned long)count); - } - return opis_diska_i1_na_pare(ctx, args[0], args[1], result, error); - } - if (strcmp(name, "Порог разряда") == 0) { - if (count != 1) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Порог разряда", (unsigned long)1, (unsigned long)count); - } - return opis_diska_porog_razryada(ctx, args[0], result, error); - } - if (strcmp(name, "Разряд обоснован") == 0) { - if (count != 3) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Разряд обоснован", (unsigned long)3, (unsigned long)count); - } - return opis_diska_razryad_obosnovan(ctx, args[0], args[1], args[2], result, error); - } - if (strcmp(name, "Разряд обоснован приметой") == 0) { - if (count != 2) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Разряд обоснован приметой", (unsigned long)2, (unsigned long)count); - } - return opis_diska_razryad_obosnovan_primetoy(ctx, args[0], args[1], result, error); - } - if (strcmp(name, "Приговор обоснован") == 0) { - if (count != 3) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Приговор обоснован", (unsigned long)3, (unsigned long)count); - } - return opis_diska_prigovor_obosnovan(ctx, args[0], args[1], args[2], result, error); - } - if (strcmp(name, "Вес обоснован") == 0) { - if (count != 3) { - return fl_fail(ctx, error, FL_CODE_TYPE, "функция «%s» принимает %lu аргум., получено %lu", - "Вес обоснован", (unsigned long)3, (unsigned long)count); - } - return opis_diska_ves_obosnovan(ctx, args[0], args[1], args[2], result, error); - } - return fl_fail(ctx, error, FL_CODE_UNKNOWN_NAME, "не найдена функция «%s»", name); -} - -/* - * ТА ЖЕ ДВЕРЬ, НО С ГРАНИЦЕЙ ВХОДА: сначала объявленные типы параметров - * (fl_check_entry по таблице внизу файла), потом вызов. Зовите ЭТУ, если - * значения пришли снаружи — из JSON, из другого языка, от человека. - * - * Почему не сверяет сам `_call`. Он обязан отвечать значение в значение так - * же, как `interpret` у свидетеля, а тот объявленных типов не сверяет тоже: - * сверяет их `flang run`. Здесь ровно та же пара — `_call` вычислитель, - * `_enter` дверь, — и разойдись они, у языка стало бы два ответа на вопрос - * «подходит ли значение типу». - */ -fl_status opis_diska_enter(fl_ctx *ctx, const char *name, const fl_value *args, size_t count, - fl_value *result, fl_error *error) { - FL_TRY(fl_check_entry(ctx, opis_diska_entry(), name, args, count, error)); - return opis_diska_call(ctx, name, args, count, result, error); -} - -/* - * Граница входа: объявленные типы параметров данными. Прогонщик сверяет по - * ним значения, пришедшие снаружи, ДО вызова (fl_check_entry). - * - * Виды `неизвестно` (значение-функция, параметр полиморфизма, применение - * типа с аргументами) не сверяются — ровно как молчит о них проверка - * значений свидетеля. - */ -static const fl_type_field opis_diska_entry_fields[] = { - { "разряд", 3 }, - { "якорь", 4 }, - { "цепь", 0 }, - { "путь", 0 }, - { "размер", 6 }, - { "возраст_дней", 6 }, - { "вид", 7 }, - { "доступен", 8 }, - { "разряд", 3 }, - { "приговор", 9 }, - { "вес", 6 }, - { "кэш", 6 }, - { "журнал", 6 }, - { "сборка", 6 }, - { "загрузка", 6 }, - { "крупное", 6 }, - { "неизвестное", 6 }, - { "освободить", 6 }, - { "путь", 0 }, - { "решение", 12 }, -}; - -static const fl_type_variant opis_diska_entry_variants[] = { - { "Кэш", 0, 0 }, - { "Журнал", 0, 0 }, - { "Сборка", 0, 0 }, - { "Загрузка", 0, 0 }, - { "Крупное", 0, 0 }, - { "Неизвестное", 0, 0 }, - { "ОтКорня", 0, 0 }, - { "ГдеУгодно", 0, 0 }, - { "Файл", 3, 0 }, - { "Каталог", 3, 0 }, - { "Ссылка", 3, 0 }, - { "МожноУбрать", 8, 0 }, - { "Спросить", 8, 0 }, - { "НеТрогать", 8, 0 }, - { "Мусор", 8, 0 }, - { "Свежее", 8, 0 }, - { "Исходники", 8, 0 }, - { "Личное", 8, 0 }, - { "Хранилище", 8, 0 }, - { "ПодПрисмотром", 8, 0 }, -}; - -static const fl_type opis_diska_entry_types[] = { - { FL_TYPE_STRING, "строка", "", false, false, false, 0.0, 0.0, 0, 0, 0, 0, 0 }, - { FL_TYPE_LIST, "список «Место»", "", false, false, false, 0.0, 0.0, 2, 0, 0, 0, 0 }, - { FL_TYPE_RECORD, "«Место»", "Место", false, false, false, 0.0, 0.0, 0, 0, 3, 0, 0 }, - { FL_TYPE_SUM, "«Разряд»", "Разряд", false, false, false, 0.0, 0.0, 0, 0, 0, 0, 6 }, - { FL_TYPE_SUM, "«Якорь»", "Якорь", false, false, false, 0.0, 0.0, 0, 0, 0, 6, 2 }, - { FL_TYPE_RECORD, "«Находка»", "Находка", false, false, false, 0.0, 0.0, 0, 3, 5, 0, 0 }, - { FL_TYPE_NUMBER, "число", "", false, false, false, 0.0, 0.0, 0, 0, 0, 0, 0 }, - { FL_TYPE_SUM, "«Вид»", "Вид", false, false, false, 0.0, 0.0, 0, 0, 0, 8, 3 }, - { FL_TYPE_FLAG, "признак", "", false, false, false, 0.0, 0.0, 0, 0, 0, 0, 0 }, - { FL_TYPE_SUM, "«Приговор»", "Приговор", false, false, false, 0.0, 0.0, 0, 0, 0, 11, 3 }, - { FL_TYPE_LIST, "список «Находка»", "", false, false, false, 0.0, 0.0, 5, 0, 0, 0, 0 }, - { FL_TYPE_SUM, "«Природа»", "Природа", false, false, false, 0.0, 0.0, 0, 0, 0, 14, 6 }, - { FL_TYPE_RECORD, "«Решение»", "Решение", false, false, false, 0.0, 0.0, 0, 8, 3, 0, 0 }, - { FL_TYPE_LIST, "список «Решение»", "", false, false, false, 0.0, 0.0, 12, 0, 0, 0, 0 }, - { FL_TYPE_RECORD, "«Свод»", "Свод", false, false, false, 0.0, 0.0, 0, 11, 7, 0, 0 }, - { FL_TYPE_RECORD, "«Строка отчёта»", "Строка отчёта", false, false, false, 0.0, 0.0, 0, 18, 2, 0, 0 }, - { FL_TYPE_LIST, "список «Строка отчёта»", "", false, false, false, 0.0, 0.0, 15, 0, 0, 0, 0 }, -}; - -static const fl_entry_param opis_diska_entry_params[] = { - { "Составляющие пути", "путь", 0 }, - { "Имя в пути", "путь", 0 }, - { "Есть составляющая", "путь", 0 }, - { "Есть составляющая", "имя", 0 }, - { "Оканчивается на", "текст", 0 }, - { "Оканчивается на", "хвост", 0 }, - { "След пути", "путь", 0 }, - { "Цепь ограничена", "цепь", 0 }, - { "Справочник ограничен", "справочник", 1 }, - { "Разряд места допустим", "разряд", 3 }, - { "Место подходит", "след", 0 }, - { "Место подходит", "место", 2 }, - { "Это неизвестное", "разряд", 3 }, - { "Номер разряда", "разряд", 3 }, - { "Тот же разряд", "первый", 3 }, - { "Тот же разряд", "второй", 3 }, - { "Место обосновано", "путь", 0 }, - { "Место обосновано", "справочник", 1 }, - { "Место обосновано", "разряд", 3 }, - { "Разряд по справочнику", "путь", 0 }, - { "Разряд по справочнику", "справочник", 1 }, - { "Шестнадцатеричный знак", "знак", 0 }, - { "Похоже на отпечаток", "часть", 0 }, - { "Адресуется содержимым", "путь", 0 }, - { "Под системным временным", "путь", 0 }, - { "Примета кэша", "путь", 0 }, - { "Примета журнала", "путь", 0 }, - { "Примета сборки", "путь", 0 }, - { "Примета загрузки", "путь", 0 }, - { "Есть примета", "путь", 0 }, - { "Есть примета", "место", 3 }, - { "Разряд решён размером", "разряд", 3 }, - { "Разряд находки", "находка", 5 }, - { "Разряд находки", "место", 3 }, - { "Крупное не мельче порога", "находка", 5 }, - { "Крупное не мельче порога", "разряд", 3 }, - { "Каталог", "находка", 5 }, - { "Ссылка", "находка", 5 }, - { "Приговор мусора", "находка", 5 }, - { "Приговор мусора", "порог", 6 }, - { "Приговор находки", "находка", 5 }, - { "Приговор находки", "разряд", 3 }, - { "Вес находки", "находка", 5 }, - { "Вес находки", "приговор", 9 }, - { "Вес в границах", "находка", 5 }, - { "Вес в границах", "вес", 6 }, - { "Решить находку", "находка", 5 }, - { "Решить находку", "справочник", 1 }, - { "Решить всё", "записи", 10 }, - { "Решить всё", "справочник", 1 }, - { "Примета исходника", "путь", 0 }, - { "Под присмотром системы версий", "путь", 0 }, - { "Мусорный разряд", "разряд", 3 }, - { "Природа находки", "находка", 5 }, - { "Природа находки", "разряд", 3 }, - { "Природа находки", "приговор", 9 }, - { "Природа обоснована", "находка", 5 }, - { "Природа обоснована", "разряд", 3 }, - { "Природа обоснована", "приговор", 9 }, - { "Природа обоснована", "природа", 11 }, - { "Природа по находке", "находка", 5 }, - { "Природа по находке", "справочник", 1 }, - { "Строгость", "природа", 11 }, - { "И1 держится", "решение", 12 }, - { "И1 держится всюду", "решения", 13 }, - { "Прибавить решение", "свод", 14 }, - { "Прибавить решение", "решение", 12 }, - { "Свести", "записи", 10 }, - { "Свести", "справочник", 1 }, - { "Сумма размеров убираемых", "записи", 10 }, - { "Сумма размеров убираемых", "справочник", 1 }, - { "И2 держится", "записи", 10 }, - { "И2 держится", "справочник", 1 }, - { "И2 держится", "свод", 14 }, - { "Строку отчёта", "находка", 5 }, - { "Строку отчёта", "справочник", 1 }, - { "Вставить по весу", "строка", 15 }, - { "Вставить по весу", "строки", 16 }, - { "Приписать строку отчёта", "первая", 15 }, - { "Приписать строку отчёта", "строки", 16 }, - { "Отчёт", "записи", 10 }, - { "Отчёт", "справочник", 1 }, - { "Отчёт той же длины", "записи", 10 }, - { "Отчёт той же длины", "строки", 16 }, - { "Это МожноУбрать", "приговор", 9 }, - { "Это НеТрогать", "приговор", 9 }, - { "И1 на паре", "разряд", 3 }, - { "И1 на паре", "приговор", 9 }, - { "Порог разряда", "разряд", 3 }, - { "Разряд обоснован", "находка", 5 }, - { "Разряд обоснован", "место", 3 }, - { "Разряд обоснован", "разряд", 3 }, - { "Разряд обоснован приметой", "находка", 5 }, - { "Разряд обоснован приметой", "разряд", 3 }, - { "Приговор обоснован", "находка", 5 }, - { "Приговор обоснован", "разряд", 3 }, - { "Приговор обоснован", "приговор", 9 }, - { "Вес обоснован", "находка", 5 }, - { "Вес обоснован", "приговор", 9 }, - { "Вес обоснован", "вес", 6 }, -}; - -static const fl_entry_table opis_diska_entry_table = { - opis_diska_entry_types, 17, - opis_diska_entry_fields, 20, - opis_diska_entry_variants, 20, - opis_diska_entry_params, 100 -}; - -const fl_entry_table *opis_diska_entry(void) { - return &opis_diska_entry_table; -} diff --git a/core/out-c/opis_diska.h b/core/out-c/opis_diska.h deleted file mode 100644 index 9ecdd74..0000000 --- a/core/out-c/opis_diska.h +++ /dev/null @@ -1,736 +0,0 @@ -/* - * Сгенерировано flang (бэкенд C, flang/self/emit-c.flang). Не редактировать руками. - * Модуль flang: «Опись диска». - * Файл: объявления: конструкторы значений и функции программы. - * Правьте исходник на flang и печатайте заново: любая правка здесь потеряется. - */ -#ifndef OPIS_DISKA_H -#define OPIS_DISKA_H - -#include "flang_runtime.h" - -/* - * Контракт вызова: функция кладёт результат в *result и возвращает FL_OK - * либо НЕ трогает *result и возвращает FL_ERROR, заполнив *error (его можно - * передать NULL). Результат живёт в арене контекста — до ближайшего - * fl_arena_reset; чтобы сохранить его надолго, скопируйте в свою память. - * - * fl_arena arena; - * fl_ctx ctx; - * fl_error error; - * fl_value result; - * fl_arena_init(&arena); - * fl_ctx_init(&ctx, &arena); - * if (…(&ctx, …, &result, &error) != FL_OK) { … error.code, error.message … } - * fl_arena_release(&arena); - */ - -/* Запись FTS «Находка»: «путь», «размер», «возраст_дней», «вид», «доступен». */ -/* Запись flang тотальна: пропущенное поле — это «ничто», а не дырка. */ -fl_status opis_diska_sozdat_nahodka(fl_ctx *ctx, fl_value put, fl_value razmer, fl_value vozrast_dney, fl_value vid, fl_value dostupen, fl_value *out, fl_error *error); - -/* Запись FTS «Место»: «разряд», «якорь», «цепь». */ -/* Запись flang тотальна: пропущенное поле — это «ничто», а не дырка. */ -fl_status opis_diska_sozdat_mesto(fl_ctx *ctx, fl_value razryad, fl_value yakor, fl_value cep, fl_value *out, fl_error *error); - -/* Запись FTS «Решение»: «разряд», «приговор», «вес». */ -/* Запись flang тотальна: пропущенное поле — это «ничто», а не дырка. */ -fl_status opis_diska_sozdat_reshenie(fl_ctx *ctx, fl_value razryad, fl_value prigovor, fl_value ves, fl_value *out, fl_error *error); - -/* Запись FTS «Строка отчёта»: «путь», «решение». */ -/* Запись flang тотальна: пропущенное поле — это «ничто», а не дырка. */ -fl_status opis_diska_sozdat_stroka_otchyota(fl_ctx *ctx, fl_value put, fl_value reshenie, fl_value *out, fl_error *error); - -/* Запись FTS «Свод»: «кэш», «журнал», «сборка», «загрузка», «крупное», «неизвестное», «освободить». */ -/* Запись flang тотальна: пропущенное поле — это «ничто», а не дырка. */ -fl_status opis_diska_sozdat_svod(fl_ctx *ctx, fl_value kesh, fl_value zhurnal, fl_value sborka, fl_value zagruzka, fl_value krupnoe, fl_value neizvestnoe, fl_value osvobodit, fl_value *out, fl_error *error); - -/* Сумма типов FTS «Вид»: «Файл» | «Каталог» | «Ссылка». */ -/* Дискриминант — имя варианта; проверяется через fl_variant_is(значение, "Имя"). */ -fl_status opis_diska_variant_fayl(fl_ctx *ctx, fl_value *out, fl_error *error); -fl_status opis_diska_variant_katalog(fl_ctx *ctx, fl_value *out, fl_error *error); -fl_status opis_diska_variant_ssylka(fl_ctx *ctx, fl_value *out, fl_error *error); - -/* Сумма типов FTS «Разряд»: «Кэш» | «Журнал» | «Сборка» | «Загрузка» | «Крупное» | «Неизвестное». */ -/* Дискриминант — имя варианта; проверяется через fl_variant_is(значение, "Имя"). */ -fl_status opis_diska_variant_kesh(fl_ctx *ctx, fl_value *out, fl_error *error); -fl_status opis_diska_variant_zhurnal(fl_ctx *ctx, fl_value *out, fl_error *error); -fl_status opis_diska_variant_sborka(fl_ctx *ctx, fl_value *out, fl_error *error); -fl_status opis_diska_variant_zagruzka(fl_ctx *ctx, fl_value *out, fl_error *error); -fl_status opis_diska_variant_krupnoe(fl_ctx *ctx, fl_value *out, fl_error *error); -fl_status opis_diska_variant_neizvestnoe(fl_ctx *ctx, fl_value *out, fl_error *error); - -/* Сумма типов FTS «Приговор»: «МожноУбрать» | «Спросить» | «НеТрогать». */ -/* Дискриминант — имя варианта; проверяется через fl_variant_is(значение, "Имя"). */ -fl_status opis_diska_variant_mozhnoubrat(fl_ctx *ctx, fl_value *out, fl_error *error); -fl_status opis_diska_variant_sprosit(fl_ctx *ctx, fl_value *out, fl_error *error); -fl_status opis_diska_variant_netrogat(fl_ctx *ctx, fl_value *out, fl_error *error); - -/* Сумма типов FTS «Якорь»: «ОтКорня» | «ГдеУгодно». */ -/* Дискриминант — имя варианта; проверяется через fl_variant_is(значение, "Имя"). */ -fl_status opis_diska_variant_otkornya(fl_ctx *ctx, fl_value *out, fl_error *error); -fl_status opis_diska_variant_gdeugodno(fl_ctx *ctx, fl_value *out, fl_error *error); - -/* Сумма типов FTS «Природа»: «Мусор» | «Свежее» | «Исходники» | «Личное» | «Хранилище» | «ПодПрисмотром». */ -/* Дискриминант — имя варианта; проверяется через fl_variant_is(значение, "Имя"). */ -fl_status opis_diska_variant_musor(fl_ctx *ctx, fl_value *out, fl_error *error); -fl_status opis_diska_variant_svezhee(fl_ctx *ctx, fl_value *out, fl_error *error); -fl_status opis_diska_variant_ishodniki(fl_ctx *ctx, fl_value *out, fl_error *error); -fl_status opis_diska_variant_lichnoe(fl_ctx *ctx, fl_value *out, fl_error *error); -fl_status opis_diska_variant_hranilische(fl_ctx *ctx, fl_value *out, fl_error *error); -fl_status opis_diska_variant_podprismotrom(fl_ctx *ctx, fl_value *out, fl_error *error); - -/* - * Функция flang «Порог крупного». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @return значение: число - */ -fl_status opis_diska_porog_krupnogo(fl_ctx *ctx, fl_value *result, fl_error *error); - -/* - * Функция flang «Порог кэша». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @return значение: число - */ -fl_status opis_diska_porog_kesha(fl_ctx *ctx, fl_value *result, fl_error *error); - -/* - * Функция flang «Порог журнала». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @return значение: число - */ -fl_status opis_diska_porog_zhurnala(fl_ctx *ctx, fl_value *result, fl_error *error); - -/* - * Функция flang «Порог загрузки». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @return значение: число - */ -fl_status opis_diska_porog_zagruzki(fl_ctx *ctx, fl_value *result, fl_error *error); - -/* - * Функция flang «Составляющие пути». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param put — «путь»: строка - * @return значение: список: строка - */ -fl_status opis_diska_sostavlyayuschie_puti(fl_ctx *ctx, fl_value put, fl_value *result, fl_error *error); - -/* - * Функция flang «Имя в пути». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param put — «путь»: строка - * @return значение: строка - */ -fl_status opis_diska_imya_v_puti(fl_ctx *ctx, fl_value put, fl_value *result, fl_error *error); - -/* - * Функция flang «Есть составляющая». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param put — «путь»: строка - * @param imya — «имя»: строка - * @return значение - */ -fl_status opis_diska_est_sostavlyayuschaya(fl_ctx *ctx, fl_value put, fl_value imya, fl_value *result, fl_error *error); - -/* - * Функция flang «Оканчивается на». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param tekst — «текст»: строка - * @param hvost — «хвост»: строка - * @return значение - */ -fl_status opis_diska_okanchivaetsya_na(fl_ctx *ctx, fl_value tekst, fl_value hvost, fl_value *result, fl_error *error); - -/* - * Функция flang «След пути». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param put — «путь»: строка - * @return значение: строка - */ -fl_status opis_diska_sled_puti(fl_ctx *ctx, fl_value put, fl_value *result, fl_error *error); - -/* - * Функция flang «Цепь ограничена». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param cep — «цепь»: строка - * @return значение - */ -fl_status opis_diska_cep_ogranichena(fl_ctx *ctx, fl_value cep, fl_value *result, fl_error *error); - -/* - * Функция flang «Справочник ограничен». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param spravochnik — «справочник»: список: «Место» - * @return значение - */ -fl_status opis_diska_spravochnik_ogranichen(fl_ctx *ctx, fl_value spravochnik, fl_value *result, fl_error *error); - -/* - * Функция flang «Разряд места допустим». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param razryad — «разряд»: «Разряд» - * @return значение - */ -fl_status opis_diska_razryad_mesta_dopustim(fl_ctx *ctx, fl_value razryad, fl_value *result, fl_error *error); - -/* - * Функция flang «Место подходит». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param sled — «след»: строка - * @param mesto — «место»: «Место» - * @return значение - */ -fl_status opis_diska_mesto_podhodit(fl_ctx *ctx, fl_value sled, fl_value mesto, fl_value *result, fl_error *error); - -/* - * Функция flang «Это неизвестное». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param razryad — «разряд»: «Разряд» - * @return значение - */ -fl_status opis_diska_eto_neizvestnoe(fl_ctx *ctx, fl_value razryad, fl_value *result, fl_error *error); - -/* - * Функция flang «Номер разряда». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param razryad — «разряд»: «Разряд» - * @return значение: число - */ -fl_status opis_diska_nomer_razryada(fl_ctx *ctx, fl_value razryad, fl_value *result, fl_error *error); - -/* - * Функция flang «Тот же разряд». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param pervyy — «первый»: «Разряд» - * @param vtoroy — «второй»: «Разряд» - * @return значение - */ -fl_status opis_diska_tot_zhe_razryad(fl_ctx *ctx, fl_value pervyy, fl_value vtoroy, fl_value *result, fl_error *error); - -/* - * Функция flang «Место обосновано». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param put — «путь»: строка - * @param spravochnik — «справочник»: список: «Место» - * @param razryad — «разряд»: «Разряд» - * @return значение - */ -fl_status opis_diska_mesto_obosnovano(fl_ctx *ctx, fl_value put, fl_value spravochnik, fl_value razryad, fl_value *result, fl_error *error); - -/* - * Функция flang «Разряд по справочнику». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param put — «путь»: строка - * @param spravochnik — «справочник»: список: «Место» - * @return значение: «Разряд» - */ -fl_status opis_diska_razryad_po_spravochniku(fl_ctx *ctx, fl_value put, fl_value spravochnik, fl_value *result, fl_error *error); - -/* - * Функция flang «Шестнадцатеричный знак». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param znak — «знак»: строка - * @return значение - */ -fl_status opis_diska_shestnadcaterichnyy_znak(fl_ctx *ctx, fl_value znak, fl_value *result, fl_error *error); - -/* - * Функция flang «Похоже на отпечаток». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param chast — «часть»: строка - * @return значение - */ -fl_status opis_diska_pohozhe_na_otpechatok(fl_ctx *ctx, fl_value chast, fl_value *result, fl_error *error); - -/* - * Функция flang «Адресуется содержимым». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param put — «путь»: строка - * @return значение - */ -fl_status opis_diska_adresuetsya_soderzhimym(fl_ctx *ctx, fl_value put, fl_value *result, fl_error *error); - -/* - * Функция flang «Под системным временным». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param put — «путь»: строка - * @return значение - */ -fl_status opis_diska_pod_sistemnym_vremennym(fl_ctx *ctx, fl_value put, fl_value *result, fl_error *error); - -/* - * Функция flang «Примета кэша». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param put — «путь»: строка - * @return значение - */ -fl_status opis_diska_primeta_kesha(fl_ctx *ctx, fl_value put, fl_value *result, fl_error *error); - -/* - * Функция flang «Примета журнала». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param put — «путь»: строка - * @return значение - */ -fl_status opis_diska_primeta_zhurnala(fl_ctx *ctx, fl_value put, fl_value *result, fl_error *error); - -/* - * Функция flang «Примета сборки». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param put — «путь»: строка - * @return значение - */ -fl_status opis_diska_primeta_sborki(fl_ctx *ctx, fl_value put, fl_value *result, fl_error *error); - -/* - * Функция flang «Примета загрузки». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param put — «путь»: строка - * @return значение - */ -fl_status opis_diska_primeta_zagruzki(fl_ctx *ctx, fl_value put, fl_value *result, fl_error *error); - -/* - * Функция flang «Есть примета». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param put — «путь»: строка - * @param mesto — «место»: «Разряд» - * @return значение - */ -fl_status opis_diska_est_primeta(fl_ctx *ctx, fl_value put, fl_value mesto, fl_value *result, fl_error *error); - -/* - * Функция flang «Разряд решён размером». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param razryad — «разряд»: «Разряд» - * @return значение - */ -fl_status opis_diska_razryad_reshyon_razmerom(fl_ctx *ctx, fl_value razryad, fl_value *result, fl_error *error); - -/* - * Функция flang «Разряд находки». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param nahodka — «находка»: «Находка» - * @param mesto — «место»: «Разряд» - * @return значение: «Разряд» - */ -fl_status opis_diska_razryad_nahodki(fl_ctx *ctx, fl_value nahodka, fl_value mesto, fl_value *result, fl_error *error); - -/* - * Функция flang «Крупное не мельче порога». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param nahodka — «находка»: «Находка» - * @param razryad — «разряд»: «Разряд» - * @return значение - */ -fl_status opis_diska_krupnoe_ne_melche_poroga(fl_ctx *ctx, fl_value nahodka, fl_value razryad, fl_value *result, fl_error *error); - -/* - * Функция flang «Каталог». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param nahodka — «находка»: «Находка» - * @return значение - */ -fl_status opis_diska_katalog(fl_ctx *ctx, fl_value nahodka, fl_value *result, fl_error *error); - -/* - * Функция flang «Ссылка». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param nahodka — «находка»: «Находка» - * @return значение - */ -fl_status opis_diska_ssylka(fl_ctx *ctx, fl_value nahodka, fl_value *result, fl_error *error); - -/* - * Функция flang «Приговор мусора». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param nahodka — «находка»: «Находка» - * @param porog — «порог»: число - * @return значение: «Приговор» - */ -fl_status opis_diska_prigovor_musora(fl_ctx *ctx, fl_value nahodka, fl_value porog, fl_value *result, fl_error *error); - -/* - * Функция flang «Приговор находки». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param nahodka — «находка»: «Находка» - * @param razryad — «разряд»: «Разряд» - * @return значение: «Приговор» - */ -fl_status opis_diska_prigovor_nahodki(fl_ctx *ctx, fl_value nahodka, fl_value razryad, fl_value *result, fl_error *error); - -/* - * Функция flang «Вес находки». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param nahodka — «находка»: «Находка» - * @param prigovor — «приговор»: «Приговор» - * @return значение: число - */ -fl_status opis_diska_ves_nahodki(fl_ctx *ctx, fl_value nahodka, fl_value prigovor, fl_value *result, fl_error *error); - -/* - * Функция flang «Вес в границах». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param nahodka — «находка»: «Находка» - * @param ves — «вес»: число - * @return значение - */ -fl_status opis_diska_ves_v_granicah(fl_ctx *ctx, fl_value nahodka, fl_value ves, fl_value *result, fl_error *error); - -/* - * Функция flang «Решить находку». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param nahodka — «находка»: «Находка» - * @param spravochnik — «справочник»: список: «Место» - * @return значение: «Решение» - */ -fl_status opis_diska_reshit_nahodku(fl_ctx *ctx, fl_value nahodka, fl_value spravochnik, fl_value *result, fl_error *error); - -/* - * Функция flang «Решить всё». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param zapisi — «записи»: список: «Находка» - * @param spravochnik — «справочник»: список: «Место» - * @return значение: список: «Решение» - */ -fl_status opis_diska_reshit_vsyo(fl_ctx *ctx, fl_value zapisi, fl_value spravochnik, fl_value *result, fl_error *error); - -/* - * Функция flang «Расширения исходников». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @return значение: список: строка - */ -fl_status opis_diska_rasshireniya_ishodnikov(fl_ctx *ctx, fl_value *result, fl_error *error); - -/* - * Функция flang «Примета исходника». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param put — «путь»: строка - * @return значение - */ -fl_status opis_diska_primeta_ishodnika(fl_ctx *ctx, fl_value put, fl_value *result, fl_error *error); - -/* - * Функция flang «Под присмотром системы версий». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param put — «путь»: строка - * @return значение - */ -fl_status opis_diska_pod_prismotrom_sistemy_versiy(fl_ctx *ctx, fl_value put, fl_value *result, fl_error *error); - -/* - * Функция flang «Мусорный разряд». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param razryad — «разряд»: «Разряд» - * @return значение - */ -fl_status opis_diska_musornyy_razryad(fl_ctx *ctx, fl_value razryad, fl_value *result, fl_error *error); - -/* - * Функция flang «Природа находки». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param nahodka — «находка»: «Находка» - * @param razryad — «разряд»: «Разряд» - * @param prigovor — «приговор»: «Приговор» - * @return значение: «Природа» - */ -fl_status opis_diska_priroda_nahodki(fl_ctx *ctx, fl_value nahodka, fl_value razryad, fl_value prigovor, fl_value *result, fl_error *error); - -/* - * Функция flang «Природа обоснована». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param nahodka — «находка»: «Находка» - * @param razryad — «разряд»: «Разряд» - * @param prigovor — «приговор»: «Приговор» - * @param priroda — «природа»: «Природа» - * @return значение - */ -fl_status opis_diska_priroda_obosnovana(fl_ctx *ctx, fl_value nahodka, fl_value razryad, fl_value prigovor, fl_value priroda, fl_value *result, fl_error *error); - -/* - * Функция flang «Природа по находке». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param nahodka — «находка»: «Находка» - * @param spravochnik — «справочник»: список: «Место» - * @return значение: «Природа» - */ -fl_status opis_diska_priroda_po_nahodke(fl_ctx *ctx, fl_value nahodka, fl_value spravochnik, fl_value *result, fl_error *error); - -/* - * Функция flang «Строгость». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param priroda — «природа»: «Природа» - * @return значение: число - */ -fl_status opis_diska_strogost(fl_ctx *ctx, fl_value priroda, fl_value *result, fl_error *error); - -/* - * Функция flang «И1 держится». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param reshenie — «решение»: «Решение» - * @return значение - */ -fl_status opis_diska_i1_derzhitsya(fl_ctx *ctx, fl_value reshenie, fl_value *result, fl_error *error); - -/* - * Функция flang «И1 держится всюду». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param resheniya — «решения»: список: «Решение» - * @return значение - */ -fl_status opis_diska_i1_derzhitsya_vsyudu(fl_ctx *ctx, fl_value resheniya, fl_value *result, fl_error *error); - -/* - * Функция flang «Пустой свод». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @return значение: «Свод» - */ -fl_status opis_diska_pustoy_svod(fl_ctx *ctx, fl_value *result, fl_error *error); - -/* - * Функция flang «Прибавить решение». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param svod — «свод»: «Свод» - * @param reshenie — «решение»: «Решение» - * @return значение: «Свод» - */ -fl_status opis_diska_pribavit_reshenie(fl_ctx *ctx, fl_value svod, fl_value reshenie, fl_value *result, fl_error *error); - -/* - * Функция flang «Свести». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param zapisi — «записи»: список: «Находка» - * @param spravochnik — «справочник»: список: «Место» - * @return значение: «Свод» - */ -fl_status opis_diska_svesti(fl_ctx *ctx, fl_value zapisi, fl_value spravochnik, fl_value *result, fl_error *error); - -/* - * Функция flang «Сумма размеров убираемых». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param zapisi — «записи»: список: «Находка» - * @param spravochnik — «справочник»: список: «Место» - * @return значение: число - */ -fl_status opis_diska_summa_razmerov_ubiraemyh(fl_ctx *ctx, fl_value zapisi, fl_value spravochnik, fl_value *result, fl_error *error); - -/* - * Функция flang «И2 держится». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param zapisi — «записи»: список: «Находка» - * @param spravochnik — «справочник»: список: «Место» - * @param svod — «свод»: «Свод» - * @return значение - */ -fl_status opis_diska_i2_derzhitsya(fl_ctx *ctx, fl_value zapisi, fl_value spravochnik, fl_value svod, fl_value *result, fl_error *error); - -/* - * Функция flang «Строку отчёта». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param nahodka — «находка»: «Находка» - * @param spravochnik — «справочник»: список: «Место» - * @return значение: «Строка отчёта» - */ -fl_status opis_diska_stroku_otchyota(fl_ctx *ctx, fl_value nahodka, fl_value spravochnik, fl_value *result, fl_error *error); - -/* - * Функция flang «Вставить по весу». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * - * Рекурсивная: считает глубину, на превышении — FLANG_RECURSION_LIMIT. - * @param stroka — «строка»: «Строка отчёта» - * @param stroki — «строки»: список: «Строка отчёта» - * @return значение: список: «Строка отчёта» - */ -fl_status opis_diska_vstavit_po_vesu(fl_ctx *ctx, fl_value stroka, fl_value stroki, fl_value *result, fl_error *error); - -/* - * Функция flang «Приписать строку отчёта». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param pervaya — «первая»: «Строка отчёта» - * @param stroki — «строки»: список: «Строка отчёта» - * @return значение: список: «Строка отчёта» - */ -fl_status opis_diska_pripisat_stroku_otchyota(fl_ctx *ctx, fl_value pervaya, fl_value stroki, fl_value *result, fl_error *error); - -/* - * Функция flang «Отчёт». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param zapisi — «записи»: список: «Находка» - * @param spravochnik — «справочник»: список: «Место» - * @return значение: список: «Строка отчёта» - */ -fl_status opis_diska_otchyot(fl_ctx *ctx, fl_value zapisi, fl_value spravochnik, fl_value *result, fl_error *error); - -/* - * Функция flang «Отчёт той же длины». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param zapisi — «записи»: список: «Находка» - * @param stroki — «строки»: список: «Строка отчёта» - * @return значение - */ -fl_status opis_diska_otchyot_toy_zhe_dliny(fl_ctx *ctx, fl_value zapisi, fl_value stroki, fl_value *result, fl_error *error); - -/* - * Функция flang «Это МожноУбрать». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param prigovor — «приговор»: «Приговор» - * @return значение - */ -fl_status opis_diska_eto_mozhnoubrat(fl_ctx *ctx, fl_value prigovor, fl_value *result, fl_error *error); - -/* - * Функция flang «Это НеТрогать». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param prigovor — «приговор»: «Приговор» - * @return значение - */ -fl_status opis_diska_eto_netrogat(fl_ctx *ctx, fl_value prigovor, fl_value *result, fl_error *error); - -/* - * Функция flang «И1 на паре». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param razryad — «разряд»: «Разряд» - * @param prigovor — «приговор»: «Приговор» - * @return значение - */ -fl_status opis_diska_i1_na_pare(fl_ctx *ctx, fl_value razryad, fl_value prigovor, fl_value *result, fl_error *error); - -/* - * Функция flang «Порог разряда». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param razryad — «разряд»: «Разряд» - * @return значение: число - */ -fl_status opis_diska_porog_razryada(fl_ctx *ctx, fl_value razryad, fl_value *result, fl_error *error); - -/* - * Функция flang «Разряд обоснован». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param nahodka — «находка»: «Находка» - * @param mesto — «место»: «Разряд» - * @param razryad — «разряд»: «Разряд» - * @return значение - */ -fl_status opis_diska_razryad_obosnovan(fl_ctx *ctx, fl_value nahodka, fl_value mesto, fl_value razryad, fl_value *result, fl_error *error); - -/* - * Функция flang «Разряд обоснован приметой». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param nahodka — «находка»: «Находка» - * @param razryad — «разряд»: «Разряд» - * @return значение - */ -fl_status opis_diska_razryad_obosnovan_primetoy(fl_ctx *ctx, fl_value nahodka, fl_value razryad, fl_value *result, fl_error *error); - -/* - * Функция flang «Приговор обоснован». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param nahodka — «находка»: «Находка» - * @param razryad — «разряд»: «Разряд» - * @param prigovor — «приговор»: «Приговор» - * @return значение - */ -fl_status opis_diska_prigovor_obosnovan(fl_ctx *ctx, fl_value nahodka, fl_value razryad, fl_value prigovor, fl_value *result, fl_error *error); - -/* - * Функция flang «Вес обоснован». - * - * Тотальная: завершение доказано анализом завершаемости (totality.mjs). - * @param nahodka — «находка»: «Находка» - * @param prigovor — «приговор»: «Приговор» - * @param ves — «вес»: число - * @return значение - */ -fl_status opis_diska_ves_obosnovan(fl_ctx *ctx, fl_value nahodka, fl_value prigovor, fl_value ves, fl_value *result, fl_error *error); - -/* - * Вызов функции по её исходному имени flang. Нужен прогонщику и всякому, - * кто связывает программу с внешним миром динамически (скрипт, FFI, тест). - */ -fl_status opis_diska_call(fl_ctx *ctx, const char *name, const fl_value *args, size_t count, - fl_value *result, fl_error *error); - -/* - * ТО ЖЕ, НО ЧЕРЕЗ ГРАНИЦУ ВХОДА: объявленные типы сверяются ДО вызова. - * Значения, пришедшие снаружи — из JSON, из другого языка, от человека, — - * обязаны заходить ЗДЕСЬ: `_call` их не сверяет, а доказательство завершения - * `тотальной` стоит НА ТИПЕ и вместе с типом теряется. - */ -fl_status opis_diska_enter(fl_ctx *ctx, const char *name, const fl_value *args, size_t count, - fl_value *result, fl_error *error); - -/* - * Объявленные типы параметров — данными. Прогонщик сверяет по ним значения, - * пришедшие снаружи, ДО вызова: доказательство завершения `тотальной` стоит - * на типе, и значение вне типа выносит вместе с типом и доказательство. - */ -const fl_entry_table *opis_diska_entry(void); - -#endif /* OPIS_DISKA_H */ diff --git a/ui-flang/README.md b/ui-flang/README.md index 3040edc..59fbd74 100644 --- a/ui-flang/README.md +++ b/ui-flang/README.md @@ -51,10 +51,14 @@ FLANG=<путь> make -C ui-flang проверка # те же 590 приме flang emit flang-tui/flang/screen.flang --target go --out out-go/screen ``` -**Зачем закоммичено, если это вывод.** Ровно затем же, зачем закоммичен -[`core/out-go`](../core/README.md): чтобы сборка из чистого клона требовала -только Go. Кто клонировал digitdisk и хочет собрать двоичный файл с признаком -`flangui`, не обязан ставить компилятор flang. +**Зачем закоммичено, если это вывод.** Довод тот же, что у +[`core/out-go`](../core/README.md), и он тоже назван по признаку сборки, а не +вообще: 18 053 строки в git покупают признак `flangui` и — что важнее — +`tools/sverka-ui.sh`, единственный прогон, доказывающий, что две реализации +раскладки рисуют один экран. Он собирает дерево дважды и в своей же шапке +говорит: «Компилятор flang НЕ нужен — печать лежит в дереве». Уберите печать — +и этот прогон начинается со сборки семени: 1 мин 54 с при `-j4`, 1,7 ГиБ +памяти. Обычный `go build ./...` не читает отсюда ничего. **Цена.** Правка руками внутри `out-go/` — **дефект, а не правка:** `make -C ui-flang печать` начинается с `rm -rf out-go` и сотрёт её молча. Меняется