@algosail/lang 0.2.11 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/sail.mjs +4 -0
- package/cli/run-cli.js +176 -0
- package/index.js +11 -2
- package/lib/codegen/README.md +230 -0
- package/lib/codegen/codegen-diagnostics.js +164 -0
- package/lib/codegen/compile-graph.js +107 -0
- package/lib/codegen/emit-adt.js +177 -0
- package/lib/codegen/emit-body.js +1265 -0
- package/lib/codegen/emit-builtin.js +371 -0
- package/lib/codegen/emit-jsdoc-sail.js +383 -0
- package/lib/codegen/emit-module.js +498 -0
- package/lib/codegen/esm-imports.js +26 -0
- package/lib/codegen/index.js +69 -0
- package/lib/codegen/out-layout.js +102 -0
- package/lib/ffi/extract-jsdoc-sail.js +34 -0
- package/lib/io-node/index.js +4 -0
- package/lib/io-node/package-root.js +18 -0
- package/lib/io-node/read-file.js +12 -0
- package/lib/io-node/resolve-package.js +24 -0
- package/lib/io-node/resolve-sail-names-from-disk.js +21 -0
- package/lib/ir/assert-json-serializable.js +30 -0
- package/lib/ir/attach-call-effects.js +108 -0
- package/lib/ir/bind-values.js +594 -0
- package/lib/ir/build-module-ir.js +290 -0
- package/lib/ir/index.js +31 -0
- package/lib/ir/lower-body-steps.js +170 -0
- package/lib/ir/module-metadata.js +65 -0
- package/lib/ir/schema-version.js +15 -0
- package/lib/ir/serialize.js +202 -0
- package/lib/ir/stitch-types.js +92 -0
- package/lib/names/adt-autogen.js +22 -0
- package/lib/names/import-path.js +28 -0
- package/lib/names/index.js +1 -0
- package/lib/names/local-declarations.js +127 -0
- package/lib/names/lower-first.js +6 -0
- package/lib/names/module-scope.js +120 -0
- package/lib/names/resolve-sail.js +365 -0
- package/lib/names/walk-ast-refs.js +91 -0
- package/lib/parse/ast-build.js +51 -0
- package/lib/parse/ast-spec.js +212 -0
- package/lib/parse/builtins-set.js +12 -0
- package/lib/parse/diagnostics.js +180 -0
- package/lib/parse/index.js +46 -0
- package/lib/parse/lexer.js +390 -0
- package/lib/parse/parse-source.js +912 -0
- package/lib/typecheck/adt-autogen-sigs.js +345 -0
- package/lib/typecheck/build-type-env.js +148 -0
- package/lib/typecheck/builtin-signatures.js +183 -0
- package/lib/typecheck/check-word-body.js +1021 -0
- package/lib/typecheck/effect-decl.js +124 -0
- package/lib/typecheck/index.js +55 -0
- package/lib/typecheck/normalize-sig.js +369 -0
- package/lib/typecheck/stack-step-snapshots.js +56 -0
- package/lib/typecheck/unify-type.js +665 -0
- package/lib/typecheck/validate-adt.js +201 -0
- package/package.json +4 -9
- package/scripts/regen-demo-full-syntax-ast.mjs +22 -0
- package/test/cli/sail-cli.test.js +64 -0
- package/test/codegen/compile-bracket-ffi-e2e.test.js +64 -0
- package/test/codegen/compile-stage0.test.js +128 -0
- package/test/codegen/compile-stage4-layout.test.js +124 -0
- package/test/codegen/e2e-prelude-ffi-adt/app/extra.sail +6 -0
- package/test/codegen/e2e-prelude-ffi-adt/app/lib.sail +33 -0
- package/test/codegen/e2e-prelude-ffi-adt/app/main.sail +28 -0
- package/test/codegen/e2e-prelude-ffi-adt/artifacts/.gitignore +2 -0
- package/test/codegen/e2e-prelude-ffi-adt/ffi/helpers.js +27 -0
- package/test/codegen/e2e-prelude-ffi-adt.test.js +100 -0
- package/test/codegen/emit-adt-stage6.test.js +168 -0
- package/test/codegen/emit-async-stage5.test.js +164 -0
- package/test/codegen/emit-body-stage2.test.js +139 -0
- package/test/codegen/emit-body.test.js +163 -0
- package/test/codegen/emit-builtins-stage7.test.js +258 -0
- package/test/codegen/emit-diagnostics-stage9.test.js +90 -0
- package/test/codegen/emit-jsdoc-stage8.test.js +113 -0
- package/test/codegen/emit-module-stage3.test.js +78 -0
- package/test/conformance/conformance-ir-l4.test.js +38 -0
- package/test/conformance/conformance-l5-codegen.test.js +111 -0
- package/test/conformance/conformance-runner.js +91 -0
- package/test/conformance/conformance-suite-l3.test.js +32 -0
- package/test/ffi/prelude-jsdoc.test.js +49 -0
- package/test/fixtures/demo-full-syntax.ast.json +1471 -0
- package/test/fixtures/demo-full-syntax.sail +35 -0
- package/test/fixtures/io-node-ffi-adt/ffi.js +7 -0
- package/test/fixtures/io-node-ffi-adt/use.sail +4 -0
- package/test/fixtures/io-node-mini/dep.sail +2 -0
- package/test/fixtures/io-node-mini/entry.sail +4 -0
- package/test/fixtures/io-node-prelude/entry.sail +4 -0
- package/test/fixtures/io-node-reexport-chain/a.sail +4 -0
- package/test/fixtures/io-node-reexport-chain/b.sail +2 -0
- package/test/fixtures/io-node-reexport-chain/c.sail +2 -0
- package/test/io-node/resolve-disk.test.js +59 -0
- package/test/ir/bind-values.test.js +84 -0
- package/test/ir/build-module-ir.test.js +100 -0
- package/test/ir/call-effects.test.js +97 -0
- package/test/ir/ffi-bracket-ir.test.js +59 -0
- package/test/ir/full-ir-document.test.js +51 -0
- package/test/ir/ir-document-assert.js +67 -0
- package/test/ir/lower-body-steps.test.js +90 -0
- package/test/ir/module-metadata.test.js +42 -0
- package/test/ir/serialization-model.test.js +172 -0
- package/test/ir/stitch-types.test.js +74 -0
- package/test/names/l2-resolve-adt-autogen.test.js +155 -0
- package/test/names/l2-resolve-bracket-ffi.test.js +108 -0
- package/test/names/l2-resolve-declaration-and-bracket-errors.test.js +276 -0
- package/test/names/l2-resolve-graph.test.js +105 -0
- package/test/names/l2-resolve-single-file.test.js +79 -0
- package/test/parse/ast-spec.test.js +56 -0
- package/test/parse/ast.test.js +476 -0
- package/test/parse/contract.test.js +37 -0
- package/test/parse/fixtures-full-syntax.test.js +24 -0
- package/test/parse/helpers.js +27 -0
- package/test/parse/l0-lex-diagnostics-matrix.test.js +59 -0
- package/test/parse/l0-lex.test.js +40 -0
- package/test/parse/l1-diagnostics.test.js +77 -0
- package/test/parse/l1-import.test.js +28 -0
- package/test/parse/l1-parse-diagnostics-matrix.test.js +32 -0
- package/test/parse/l1-top-level.test.js +47 -0
- package/test/parse/l1-types.test.js +31 -0
- package/test/parse/l1-words.test.js +49 -0
- package/test/parse/l2-diagnostics-contract.test.js +67 -0
- package/test/parse/l3-diagnostics-contract.test.js +66 -0
- package/test/typecheck/adt-decl-stage2.test.js +83 -0
- package/test/typecheck/container-contract-e1309.test.js +258 -0
- package/test/typecheck/ffi-bracket-l3.test.js +61 -0
- package/test/typecheck/l3-diagnostics-matrix.test.js +248 -0
- package/test/typecheck/l3-partial-pipeline.test.js +74 -0
- package/test/typecheck/opaque-ffi-type.test.js +78 -0
- package/test/typecheck/sig-type-stage3.test.js +190 -0
- package/test/typecheck/stack-check-stage4.test.js +149 -0
- package/test/typecheck/stack-check-stage5.test.js +74 -0
- package/test/typecheck/stack-check-stage6.test.js +56 -0
- package/test/typecheck/stack-check-stage7.test.js +160 -0
- package/test/typecheck/stack-check-stage8.test.js +146 -0
- package/test/typecheck/stack-check-stage9.test.js +105 -0
- package/test/typecheck/typecheck-env.test.js +53 -0
- package/test/typecheck/typecheck-pipeline.test.js +37 -0
- package/README.md +0 -37
- package/cli/sail.js +0 -151
- package/cli/typecheck.js +0 -39
- package/docs/ARCHITECTURE.md +0 -50
- package/docs/CHANGELOG.md +0 -18
- package/docs/FFI-GUIDE.md +0 -65
- package/docs/RELEASE.md +0 -36
- package/docs/TESTING.md +0 -86
- package/test/integration.test.js +0 -61
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* L5: обход модулей после успешного typecheck; эмиссия ESM по модулю (RFC-compile §4, §9).
|
|
3
|
+
*/
|
|
4
|
+
import path from 'node:path'
|
|
5
|
+
import { e5301ReadFileRequired, e5302CannotReadFfi } from './codegen-diagnostics.js'
|
|
6
|
+
import { emitModuleEsmSource } from './emit-module.js'
|
|
7
|
+
import {
|
|
8
|
+
isPathInsideSourceRoot,
|
|
9
|
+
projectJsFileToOutputPath,
|
|
10
|
+
sailModuleToOutputJsPath
|
|
11
|
+
} from './out-layout.js'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Проектные FFI `.js` для зеркала в `out-dir`: только импорты с относительным `path` в `.sail`
|
|
15
|
+
* (`./` / `../`). Пакеты npm (`@scope/pkg/…`, bare) не копируются — в ESM остаётся тот же
|
|
16
|
+
* спецификатор, резолв через `node_modules` (RFC-compile §9.2).
|
|
17
|
+
*
|
|
18
|
+
* @param {object} env
|
|
19
|
+
* @param {string} sourceRoot
|
|
20
|
+
* @returns {Set<string>}
|
|
21
|
+
*/
|
|
22
|
+
export function collectProjectFfiJsPathsForCodegenCopy (env, sourceRoot) {
|
|
23
|
+
/** @type {Set<string>} */
|
|
24
|
+
const set = new Set()
|
|
25
|
+
for (const modPath of env.modulePathsOrdered) {
|
|
26
|
+
const normMod = path.normalize(modPath)
|
|
27
|
+
if (path.extname(normMod).toLowerCase() !== '.sail') continue
|
|
28
|
+
const snap = env.snapshots.get(normMod)
|
|
29
|
+
const scope = env.scopeByPath.get(normMod)
|
|
30
|
+
if (!snap?.ok || !Array.isArray(snap.items) || !scope?.importMap) continue
|
|
31
|
+
for (const item of snap.items) {
|
|
32
|
+
if (item.kind !== 'import') continue
|
|
33
|
+
const spec = item.path
|
|
34
|
+
if (typeof spec !== 'string') continue
|
|
35
|
+
if (!spec.startsWith('./') && !spec.startsWith('../')) continue
|
|
36
|
+
const dep = scope.importMap.get(item.module)
|
|
37
|
+
const p =
|
|
38
|
+
dep != null && typeof dep === 'object' && typeof dep.path === 'string'
|
|
39
|
+
? dep.path
|
|
40
|
+
: null
|
|
41
|
+
if (p == null) continue
|
|
42
|
+
const abs = path.normalize(p)
|
|
43
|
+
if (path.extname(abs).toLowerCase() !== '.js') continue
|
|
44
|
+
if (!isPathInsideSourceRoot(abs, sourceRoot)) continue
|
|
45
|
+
set.add(abs)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return set
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @param {object} env — `TypecheckEnv` после успешного `typecheckSail` (`modulePathsOrdered`, …)
|
|
53
|
+
* @param {{ outDir: string, sourceRoot: string, entryPath: string }} layout — {@link resolveCompileLayout}
|
|
54
|
+
* @param {{ mkdir: (p: string, opts?: { recursive?: boolean }) => Promise<void>, writeFile: (p: string, data: string) => Promise<void> }} fsSink
|
|
55
|
+
* @param {(p: string) => string | null | undefined} readFile — для копирования проектных FFI `.js` (RFC-compile §9.1)
|
|
56
|
+
* @returns {Promise<{ ok: true, diagnostics: [], emitted: string[] } | { ok: false, diagnostics: object[], emitted: [] }>}
|
|
57
|
+
*/
|
|
58
|
+
export async function emitCodegenStage0Stub (env, layout, fsSink, readFile) {
|
|
59
|
+
/** @type {string[]} */
|
|
60
|
+
const emitted = []
|
|
61
|
+
const sourceRoot = layout.sourceRoot
|
|
62
|
+
|
|
63
|
+
const ffiSet = collectProjectFfiJsPathsForCodegenCopy(env, sourceRoot)
|
|
64
|
+
const ffiSorted = [...ffiSet].sort()
|
|
65
|
+
if (ffiSorted.length > 0 && typeof readFile !== 'function') {
|
|
66
|
+
return {
|
|
67
|
+
ok: false,
|
|
68
|
+
diagnostics: [e5301ReadFileRequired()],
|
|
69
|
+
emitted: []
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
for (const jsPath of ffiSorted) {
|
|
74
|
+
const mapped = projectJsFileToOutputPath(layout, jsPath)
|
|
75
|
+
if (!mapped.ok) {
|
|
76
|
+
return { ok: false, diagnostics: [mapped.diagnostic], emitted: [] }
|
|
77
|
+
}
|
|
78
|
+
const text = readFile(jsPath)
|
|
79
|
+
if (text == null || text === undefined) {
|
|
80
|
+
return {
|
|
81
|
+
ok: false,
|
|
82
|
+
diagnostics: [e5302CannotReadFfi(jsPath)],
|
|
83
|
+
emitted: []
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
await fsSink.mkdir(path.dirname(mapped.path), { recursive: true })
|
|
87
|
+
await fsSink.writeFile(mapped.path, text)
|
|
88
|
+
emitted.push(mapped.path)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
for (const modPath of env.modulePathsOrdered) {
|
|
92
|
+
const norm = path.normalize(modPath)
|
|
93
|
+
if (path.extname(norm).toLowerCase() !== '.sail') continue
|
|
94
|
+
const mapped = sailModuleToOutputJsPath(layout, norm)
|
|
95
|
+
if (!mapped.ok) {
|
|
96
|
+
return { ok: false, diagnostics: [mapped.diagnostic], emitted: [] }
|
|
97
|
+
}
|
|
98
|
+
const gen = emitModuleEsmSource({ env, modulePath: norm, layout })
|
|
99
|
+
if (!gen.ok) {
|
|
100
|
+
return { ok: false, diagnostics: gen.diagnostics, emitted: [] }
|
|
101
|
+
}
|
|
102
|
+
await fsSink.mkdir(path.dirname(mapped.path), { recursive: true })
|
|
103
|
+
await fsSink.writeFile(mapped.path, gen.source)
|
|
104
|
+
emitted.push(mapped.path)
|
|
105
|
+
}
|
|
106
|
+
return { ok: true, diagnostics: [], emitted }
|
|
107
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* L5 этап 6: тела автоген-слов ADT (RFC-0.1 §7, RFC-compile §8).
|
|
3
|
+
* Без синтетического IR в buildModuleIr: только строка тела для локальной `function`.
|
|
4
|
+
*/
|
|
5
|
+
import { lowerFirst } from '../names/lower-first.js'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {import('../typecheck/normalize-sig.js').NormalizedSignature} NormalizedSignature
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @param {string} wordName
|
|
13
|
+
* @param {{ kind: string, ast: object }} adtEntry
|
|
14
|
+
* @returns {{
|
|
15
|
+
* kind: 'sum_elim'|'sum_ctor'|'prod_ctor'|'prod_get'|'prod_with'
|
|
16
|
+
* ast: object
|
|
17
|
+
* tag?: object
|
|
18
|
+
* field?: object
|
|
19
|
+
* } | null}
|
|
20
|
+
*/
|
|
21
|
+
export function classifyAutogenWord (wordName, adtEntry) {
|
|
22
|
+
const ast = adtEntry.ast
|
|
23
|
+
if (adtEntry.kind === 'sum') {
|
|
24
|
+
const elim = lowerFirst(ast.name)
|
|
25
|
+
if (wordName === elim) return { kind: 'sum_elim', ast }
|
|
26
|
+
for (const tag of ast.tags ?? []) {
|
|
27
|
+
if (lowerFirst(tag.name) === wordName) {
|
|
28
|
+
return { kind: 'sum_ctor', ast, tag }
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
} else if (adtEntry.kind === 'product') {
|
|
32
|
+
if (lowerFirst(ast.name) === wordName) return { kind: 'prod_ctor', ast }
|
|
33
|
+
const withN = lowerFirst(`with${ast.name}`)
|
|
34
|
+
if (wordName === withN) return { kind: 'prod_with', ast }
|
|
35
|
+
for (const f of ast.fields ?? []) {
|
|
36
|
+
if (lowerFirst(f.name + ast.name) === wordName) {
|
|
37
|
+
return { kind: 'prod_get', ast, field: f }
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return null
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @param {import('../typecheck/build-type-env.js').TypecheckEnv} env
|
|
46
|
+
* @param {string} modulePath
|
|
47
|
+
* @param {string} wordName
|
|
48
|
+
* @returns {ReturnType<typeof classifyAutogenWord>}
|
|
49
|
+
*/
|
|
50
|
+
export function findAutogenRoleInModule (env, modulePath, wordName) {
|
|
51
|
+
const adtMap = env.adtByPath?.get(modulePath)
|
|
52
|
+
if (!(adtMap instanceof Map)) return null
|
|
53
|
+
for (const entry of adtMap.values()) {
|
|
54
|
+
const role = classifyAutogenWord(wordName, entry)
|
|
55
|
+
if (role) return role
|
|
56
|
+
}
|
|
57
|
+
return null
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* @param {object} sumAst
|
|
62
|
+
*/
|
|
63
|
+
function emitSumEliminatorBody (sumAst) {
|
|
64
|
+
const tags = sumAst.tags ?? []
|
|
65
|
+
const n = tags.length
|
|
66
|
+
const pNames = tags.map((_, i) => `p${i + 1}`)
|
|
67
|
+
const declP = pNames.length ? `, ${pNames.join(', ')}` : ''
|
|
68
|
+
/** @type {string[]} */
|
|
69
|
+
const lines = [
|
|
70
|
+
`const nB = ${n}`,
|
|
71
|
+
`let s0, d${declP}`,
|
|
72
|
+
'if (arguments.length === nB + 1) {',
|
|
73
|
+
' s0 = undefined',
|
|
74
|
+
' d = arguments[0]'
|
|
75
|
+
]
|
|
76
|
+
for (let i = 0; i < n; i++) {
|
|
77
|
+
lines.push(` ${pNames[i]} = arguments[${i + 1}]`)
|
|
78
|
+
}
|
|
79
|
+
lines.push(
|
|
80
|
+
'} else if (arguments.length === nB + 2) {',
|
|
81
|
+
' s0 = arguments[0]',
|
|
82
|
+
' d = arguments[1]'
|
|
83
|
+
)
|
|
84
|
+
for (let i = 0; i < n; i++) {
|
|
85
|
+
lines.push(` ${pNames[i]} = arguments[${i + 2}]`)
|
|
86
|
+
}
|
|
87
|
+
lines.push(
|
|
88
|
+
'} else {',
|
|
89
|
+
` throw new Error(${JSON.stringify('ADT eliminator: неверное число аргументов')})`,
|
|
90
|
+
'}'
|
|
91
|
+
)
|
|
92
|
+
for (let i = 0; i < n; i++) {
|
|
93
|
+
const tag = tags[i]
|
|
94
|
+
const tname = JSON.stringify(tag.name)
|
|
95
|
+
if (tag.payload) {
|
|
96
|
+
lines.push(`if (d.tag === ${tname}) return ${pNames[i]}(s0, d.value);`)
|
|
97
|
+
} else {
|
|
98
|
+
lines.push(`if (d.tag === ${tname}) return ${pNames[i]}(s0);`)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
lines.push(
|
|
102
|
+
`throw new Error(${JSON.stringify('ADT eliminator: неизвестный tag')});`
|
|
103
|
+
)
|
|
104
|
+
return lines.join('\n')
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Список формальных параметров для `function name(…)`: eliminator — без параметров (`arguments`).
|
|
109
|
+
*
|
|
110
|
+
* @param {{ kind: string }} role
|
|
111
|
+
* @param {NormalizedSignature | null | undefined} normalizedSig
|
|
112
|
+
*/
|
|
113
|
+
export function formatAutogenFormalParameters (role, normalizedSig) {
|
|
114
|
+
if (role.kind === 'sum_elim') return ''
|
|
115
|
+
const left = normalizedSig != null && Array.isArray(normalizedSig.left)
|
|
116
|
+
? normalizedSig.left
|
|
117
|
+
: []
|
|
118
|
+
if (left.length === 0) return ''
|
|
119
|
+
return Array.from({ length: left.length }, (_, i) => `p${i}`).join(', ')
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* @param {{ kind: string, ast: object, tag?: object, field?: object }} role
|
|
124
|
+
* @param {NormalizedSignature | null | undefined} _normalizedSig зарезервировано для будущих проверок
|
|
125
|
+
*/
|
|
126
|
+
export function emitAdtAutogenFunctionBody (role, _normalizedSig) {
|
|
127
|
+
switch (role.kind) {
|
|
128
|
+
case 'sum_elim':
|
|
129
|
+
return emitSumEliminatorBody(role.ast)
|
|
130
|
+
case 'sum_ctor': {
|
|
131
|
+
const tag = /** @type {{ name: string, payload?: unknown }} */ (role.tag)
|
|
132
|
+
const tname = JSON.stringify(tag.name)
|
|
133
|
+
if (tag.payload) {
|
|
134
|
+
return `return { tag: ${tname}, value: p0 };`
|
|
135
|
+
}
|
|
136
|
+
return `return { tag: ${tname} };`
|
|
137
|
+
}
|
|
138
|
+
case 'prod_ctor': {
|
|
139
|
+
const ast = role.ast
|
|
140
|
+
const fields = ast.fields ?? []
|
|
141
|
+
/** @type {string[]} */
|
|
142
|
+
const parts = []
|
|
143
|
+
for (let ix = 0; ix < fields.length; ix++) {
|
|
144
|
+
const f = /** @type {{ name: string }} */ (fields[ix])
|
|
145
|
+
parts.push(`${JSON.stringify(f.name)}: p${ix}`)
|
|
146
|
+
}
|
|
147
|
+
return `return { ${parts.join(', ')} };`
|
|
148
|
+
}
|
|
149
|
+
case 'prod_get': {
|
|
150
|
+
const field = /** @type {{ name: string }} */ (role.field)
|
|
151
|
+
const k = JSON.stringify(field.name)
|
|
152
|
+
return `return p0[${k}];`
|
|
153
|
+
}
|
|
154
|
+
case 'prod_with': {
|
|
155
|
+
const ast = role.ast
|
|
156
|
+
const fields = ast.fields ?? []
|
|
157
|
+
const acc = fields.map((/** @type {{ name: string }} */ f) => {
|
|
158
|
+
return `p0[${JSON.stringify(f.name)}]`
|
|
159
|
+
})
|
|
160
|
+
return `return p1(undefined, ${acc.join(', ')});`
|
|
161
|
+
}
|
|
162
|
+
default:
|
|
163
|
+
throw new Error(`emit-adt: неизвестный kind роли ${String(/** @type {{ kind?: string }} */ (role).kind)}`)
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Имя вспомогательной функции для квалифицированного автогена (`~M/w` → не экспортируется из M).
|
|
169
|
+
*
|
|
170
|
+
* @param {string} moduleAlias
|
|
171
|
+
* @param {string} wordName
|
|
172
|
+
*/
|
|
173
|
+
export function mangleQualifiedAutogenName (moduleAlias, wordName) {
|
|
174
|
+
const safeA = moduleAlias.replace(/[^A-Za-z0-9_$]/g, '_')
|
|
175
|
+
const safeW = wordName.replace(/[^A-Za-z0-9_$]/g, '_')
|
|
176
|
+
return `__adt_${safeA}__${safeW}`
|
|
177
|
+
}
|