@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.
Files changed (145) hide show
  1. package/bin/sail.mjs +4 -0
  2. package/cli/run-cli.js +176 -0
  3. package/index.js +11 -2
  4. package/lib/codegen/README.md +230 -0
  5. package/lib/codegen/codegen-diagnostics.js +164 -0
  6. package/lib/codegen/compile-graph.js +107 -0
  7. package/lib/codegen/emit-adt.js +177 -0
  8. package/lib/codegen/emit-body.js +1265 -0
  9. package/lib/codegen/emit-builtin.js +371 -0
  10. package/lib/codegen/emit-jsdoc-sail.js +383 -0
  11. package/lib/codegen/emit-module.js +498 -0
  12. package/lib/codegen/esm-imports.js +26 -0
  13. package/lib/codegen/index.js +69 -0
  14. package/lib/codegen/out-layout.js +102 -0
  15. package/lib/ffi/extract-jsdoc-sail.js +34 -0
  16. package/lib/io-node/index.js +4 -0
  17. package/lib/io-node/package-root.js +18 -0
  18. package/lib/io-node/read-file.js +12 -0
  19. package/lib/io-node/resolve-package.js +24 -0
  20. package/lib/io-node/resolve-sail-names-from-disk.js +21 -0
  21. package/lib/ir/assert-json-serializable.js +30 -0
  22. package/lib/ir/attach-call-effects.js +108 -0
  23. package/lib/ir/bind-values.js +594 -0
  24. package/lib/ir/build-module-ir.js +290 -0
  25. package/lib/ir/index.js +31 -0
  26. package/lib/ir/lower-body-steps.js +170 -0
  27. package/lib/ir/module-metadata.js +65 -0
  28. package/lib/ir/schema-version.js +15 -0
  29. package/lib/ir/serialize.js +202 -0
  30. package/lib/ir/stitch-types.js +92 -0
  31. package/lib/names/adt-autogen.js +22 -0
  32. package/lib/names/import-path.js +28 -0
  33. package/lib/names/index.js +1 -0
  34. package/lib/names/local-declarations.js +127 -0
  35. package/lib/names/lower-first.js +6 -0
  36. package/lib/names/module-scope.js +120 -0
  37. package/lib/names/resolve-sail.js +365 -0
  38. package/lib/names/walk-ast-refs.js +91 -0
  39. package/lib/parse/ast-build.js +51 -0
  40. package/lib/parse/ast-spec.js +212 -0
  41. package/lib/parse/builtins-set.js +12 -0
  42. package/lib/parse/diagnostics.js +180 -0
  43. package/lib/parse/index.js +46 -0
  44. package/lib/parse/lexer.js +390 -0
  45. package/lib/parse/parse-source.js +912 -0
  46. package/lib/typecheck/adt-autogen-sigs.js +345 -0
  47. package/lib/typecheck/build-type-env.js +148 -0
  48. package/lib/typecheck/builtin-signatures.js +183 -0
  49. package/lib/typecheck/check-word-body.js +1021 -0
  50. package/lib/typecheck/effect-decl.js +124 -0
  51. package/lib/typecheck/index.js +55 -0
  52. package/lib/typecheck/normalize-sig.js +369 -0
  53. package/lib/typecheck/stack-step-snapshots.js +56 -0
  54. package/lib/typecheck/unify-type.js +665 -0
  55. package/lib/typecheck/validate-adt.js +201 -0
  56. package/package.json +4 -9
  57. package/scripts/regen-demo-full-syntax-ast.mjs +22 -0
  58. package/test/cli/sail-cli.test.js +64 -0
  59. package/test/codegen/compile-bracket-ffi-e2e.test.js +64 -0
  60. package/test/codegen/compile-stage0.test.js +128 -0
  61. package/test/codegen/compile-stage4-layout.test.js +124 -0
  62. package/test/codegen/e2e-prelude-ffi-adt/app/extra.sail +6 -0
  63. package/test/codegen/e2e-prelude-ffi-adt/app/lib.sail +33 -0
  64. package/test/codegen/e2e-prelude-ffi-adt/app/main.sail +28 -0
  65. package/test/codegen/e2e-prelude-ffi-adt/artifacts/.gitignore +2 -0
  66. package/test/codegen/e2e-prelude-ffi-adt/ffi/helpers.js +27 -0
  67. package/test/codegen/e2e-prelude-ffi-adt.test.js +100 -0
  68. package/test/codegen/emit-adt-stage6.test.js +168 -0
  69. package/test/codegen/emit-async-stage5.test.js +164 -0
  70. package/test/codegen/emit-body-stage2.test.js +139 -0
  71. package/test/codegen/emit-body.test.js +163 -0
  72. package/test/codegen/emit-builtins-stage7.test.js +258 -0
  73. package/test/codegen/emit-diagnostics-stage9.test.js +90 -0
  74. package/test/codegen/emit-jsdoc-stage8.test.js +113 -0
  75. package/test/codegen/emit-module-stage3.test.js +78 -0
  76. package/test/conformance/conformance-ir-l4.test.js +38 -0
  77. package/test/conformance/conformance-l5-codegen.test.js +111 -0
  78. package/test/conformance/conformance-runner.js +91 -0
  79. package/test/conformance/conformance-suite-l3.test.js +32 -0
  80. package/test/ffi/prelude-jsdoc.test.js +49 -0
  81. package/test/fixtures/demo-full-syntax.ast.json +1471 -0
  82. package/test/fixtures/demo-full-syntax.sail +35 -0
  83. package/test/fixtures/io-node-ffi-adt/ffi.js +7 -0
  84. package/test/fixtures/io-node-ffi-adt/use.sail +4 -0
  85. package/test/fixtures/io-node-mini/dep.sail +2 -0
  86. package/test/fixtures/io-node-mini/entry.sail +4 -0
  87. package/test/fixtures/io-node-prelude/entry.sail +4 -0
  88. package/test/fixtures/io-node-reexport-chain/a.sail +4 -0
  89. package/test/fixtures/io-node-reexport-chain/b.sail +2 -0
  90. package/test/fixtures/io-node-reexport-chain/c.sail +2 -0
  91. package/test/io-node/resolve-disk.test.js +59 -0
  92. package/test/ir/bind-values.test.js +84 -0
  93. package/test/ir/build-module-ir.test.js +100 -0
  94. package/test/ir/call-effects.test.js +97 -0
  95. package/test/ir/ffi-bracket-ir.test.js +59 -0
  96. package/test/ir/full-ir-document.test.js +51 -0
  97. package/test/ir/ir-document-assert.js +67 -0
  98. package/test/ir/lower-body-steps.test.js +90 -0
  99. package/test/ir/module-metadata.test.js +42 -0
  100. package/test/ir/serialization-model.test.js +172 -0
  101. package/test/ir/stitch-types.test.js +74 -0
  102. package/test/names/l2-resolve-adt-autogen.test.js +155 -0
  103. package/test/names/l2-resolve-bracket-ffi.test.js +108 -0
  104. package/test/names/l2-resolve-declaration-and-bracket-errors.test.js +276 -0
  105. package/test/names/l2-resolve-graph.test.js +105 -0
  106. package/test/names/l2-resolve-single-file.test.js +79 -0
  107. package/test/parse/ast-spec.test.js +56 -0
  108. package/test/parse/ast.test.js +476 -0
  109. package/test/parse/contract.test.js +37 -0
  110. package/test/parse/fixtures-full-syntax.test.js +24 -0
  111. package/test/parse/helpers.js +27 -0
  112. package/test/parse/l0-lex-diagnostics-matrix.test.js +59 -0
  113. package/test/parse/l0-lex.test.js +40 -0
  114. package/test/parse/l1-diagnostics.test.js +77 -0
  115. package/test/parse/l1-import.test.js +28 -0
  116. package/test/parse/l1-parse-diagnostics-matrix.test.js +32 -0
  117. package/test/parse/l1-top-level.test.js +47 -0
  118. package/test/parse/l1-types.test.js +31 -0
  119. package/test/parse/l1-words.test.js +49 -0
  120. package/test/parse/l2-diagnostics-contract.test.js +67 -0
  121. package/test/parse/l3-diagnostics-contract.test.js +66 -0
  122. package/test/typecheck/adt-decl-stage2.test.js +83 -0
  123. package/test/typecheck/container-contract-e1309.test.js +258 -0
  124. package/test/typecheck/ffi-bracket-l3.test.js +61 -0
  125. package/test/typecheck/l3-diagnostics-matrix.test.js +248 -0
  126. package/test/typecheck/l3-partial-pipeline.test.js +74 -0
  127. package/test/typecheck/opaque-ffi-type.test.js +78 -0
  128. package/test/typecheck/sig-type-stage3.test.js +190 -0
  129. package/test/typecheck/stack-check-stage4.test.js +149 -0
  130. package/test/typecheck/stack-check-stage5.test.js +74 -0
  131. package/test/typecheck/stack-check-stage6.test.js +56 -0
  132. package/test/typecheck/stack-check-stage7.test.js +160 -0
  133. package/test/typecheck/stack-check-stage8.test.js +146 -0
  134. package/test/typecheck/stack-check-stage9.test.js +105 -0
  135. package/test/typecheck/typecheck-env.test.js +53 -0
  136. package/test/typecheck/typecheck-pipeline.test.js +37 -0
  137. package/README.md +0 -37
  138. package/cli/sail.js +0 -151
  139. package/cli/typecheck.js +0 -39
  140. package/docs/ARCHITECTURE.md +0 -50
  141. package/docs/CHANGELOG.md +0 -18
  142. package/docs/FFI-GUIDE.md +0 -65
  143. package/docs/RELEASE.md +0 -36
  144. package/docs/TESTING.md +0 -86
  145. package/test/integration.test.js +0 -61
@@ -0,0 +1,371 @@
1
+ /**
2
+ * L5 этап 7: комбинаторы `builtin_word` RFC-builtins-0.1 §4.4–4.5 (dip, keep, bi, tri, spread, both).
3
+ * Перестановки §4.1–4.2 — в emit-body.js (`BUILTIN_SHUFFLE_EXPR`).
4
+ *
5
+ * Расширение: новый builtin в RFC → `builtins-set.js`, `builtin-signatures.js`,
6
+ * при необходимости `bind-values.js`, затем маппинг здесь или в shuffle-таблице.
7
+ */
8
+
9
+ /**
10
+ * @typedef {{
11
+ * idToJs: Map<string, string>
12
+ * lines: string[]
13
+ * genName: () => string
14
+ * genQuoteName: () => string
15
+ * quoteMetaByValueId: Map<string, import('./emit-body.js').QuoteEmitMeta>
16
+ * slotExprByName: Map<string, string>
17
+ * strict: boolean
18
+ * wordLabel: string
19
+ * entryParamCounter: { n: number }
20
+ * resolveQualifiedCallee?: (moduleAlias: string, wordName: string) => string
21
+ * callerAsync: boolean
22
+ * calleeAsyncResolver?: import('./emit-body.js').CalleeAsyncResolver | null
23
+ * sumEliminatorNames?: Set<string> | null
24
+ * }} CombinatorEmitEnv
25
+ */
26
+
27
+ export const COMBINATOR_BUILTIN_NAMES = new Set([
28
+ 'dip',
29
+ 'keep',
30
+ 'bi',
31
+ 'tri',
32
+ 'spread',
33
+ 'both'
34
+ ])
35
+
36
+ /**
37
+ * Изолированное исполнение тела quotation: как inline-ветка `call` (RFC-compile §6.3).
38
+ *
39
+ * @param {Record<string, unknown>[]} innerSteps
40
+ * @param {string[]} innerEntryStackIds
41
+ * @param {string[]} innerArgIds id значений снизу вверх под quotation
42
+ * @param {string[]} topResultIds id верхней части пост-стека под результат inner (дно→вершина)
43
+ * @param {CombinatorEmitEnv} env
44
+ * @param {(steps: Record<string, unknown>[], e: object) => string[]} processIrSteps
45
+ */
46
+ export function mapInlineQuoteResults (
47
+ innerSteps,
48
+ innerEntryStackIds,
49
+ innerArgIds,
50
+ topResultIds,
51
+ env,
52
+ processIrSteps
53
+ ) {
54
+ if (innerArgIds.length !== innerEntryStackIds.length) {
55
+ throw new Error(
56
+ `emit-builtin[${env.wordLabel}]: несовпадение длины innerArgIds и innerEntryStackIds`
57
+ )
58
+ }
59
+ const merged = new Map(env.idToJs)
60
+ for (let i = 0; i < innerEntryStackIds.length; i++) {
61
+ const aid = innerArgIds[i]
62
+ const j = env.idToJs.get(aid)
63
+ if (j == null) {
64
+ throw new Error(`emit-builtin[${env.wordLabel}]: нет JS для inner arg ${aid}`)
65
+ }
66
+ merged.set(innerEntryStackIds[i], j)
67
+ }
68
+ const innerEnv = {
69
+ idToJs: merged,
70
+ lines: env.lines,
71
+ genName: env.genName,
72
+ genQuoteName: env.genQuoteName,
73
+ quoteMetaByValueId: env.quoteMetaByValueId,
74
+ slotExprByName: env.slotExprByName,
75
+ strict: env.strict,
76
+ wordLabel: env.wordLabel,
77
+ entryParamCounter: env.entryParamCounter,
78
+ resolveQualifiedCallee: env.resolveQualifiedCallee,
79
+ callerAsync: env.callerAsync,
80
+ calleeAsyncResolver: env.calleeAsyncResolver,
81
+ sumEliminatorNames: env.sumEliminatorNames ?? null,
82
+ initialStackIds:
83
+ innerEntryStackIds.length > 0 ? [...innerEntryStackIds] : undefined
84
+ }
85
+ const innerFinalIds = processIrSteps(innerSteps, innerEnv)
86
+ if (innerFinalIds.length !== topResultIds.length) {
87
+ throw new Error(
88
+ `emit-builtin[${env.wordLabel}]: внутри quotation ожидалось ${topResultIds.length} выходов, есть ${innerFinalIds.length}`
89
+ )
90
+ }
91
+ for (let i = 0; i < topResultIds.length; i++) {
92
+ const rid = topResultIds[i]
93
+ const innerId = innerFinalIds[i]
94
+ const j = merged.get(innerId)
95
+ if (j == null) {
96
+ throw new Error(
97
+ `emit-builtin[${env.wordLabel}]: нет JS для внутреннего id ${innerId}`
98
+ )
99
+ }
100
+ env.idToJs.set(rid, j)
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Quote как значение (после вызова слова): изолированный вызов как у `call` invoke (RFC-builtins §4.6 Iso).
106
+ *
107
+ * @param {string} quoteId
108
+ * @param {string[]} innerArgIds
109
+ * @param {string[]} topTargetIds
110
+ * @param {number} innerOutLen
111
+ * @param {CombinatorEmitEnv} env
112
+ * @param {boolean} calleeAsyncInvoke
113
+ */
114
+ function mapInvokeCombinatorQuote (
115
+ quoteId,
116
+ innerArgIds,
117
+ topTargetIds,
118
+ innerOutLen,
119
+ env,
120
+ calleeAsyncInvoke
121
+ ) {
122
+ const quoteJs = env.idToJs.get(quoteId)
123
+ if (quoteJs == null) {
124
+ throw new Error(
125
+ `emit-builtin[${env.wordLabel}]: invoke — нет JS для quote id ${quoteId}`
126
+ )
127
+ }
128
+ const callArgExprs = innerArgIds.map((id) => {
129
+ const j = env.idToJs.get(id)
130
+ if (j == null) {
131
+ throw new Error(`emit-builtin[${env.wordLabel}]: invoke — нет JS для arg ${id}`)
132
+ }
133
+ return j
134
+ })
135
+ const paren = callArgExprs.length ? `(${callArgExprs.join(', ')})` : '()'
136
+ let callExpr = `${quoteJs}${paren}`
137
+ const useAwait = env.callerAsync && calleeAsyncInvoke
138
+ if (useAwait) {
139
+ callExpr = `await ${callExpr}`
140
+ }
141
+ const m = innerOutLen
142
+ if (m !== topTargetIds.length) {
143
+ throw new Error(
144
+ `emit-builtin[${env.wordLabel}]: invoke — несовпадение m и topTargetIds (${m} vs ${topTargetIds.length})`
145
+ )
146
+ }
147
+ if (m === 0) {
148
+ env.lines.push(`${callExpr};`)
149
+ } else if (m === 1) {
150
+ const nm = env.genName()
151
+ env.lines.push(`const ${nm} = ${callExpr};`)
152
+ env.idToJs.set(topTargetIds[0], nm)
153
+ } else {
154
+ const names = topTargetIds.map(() => env.genName())
155
+ env.lines.push(`const [${names.join(', ')}] = ${callExpr};`)
156
+ for (let i = 0; i < m; i++) {
157
+ env.idToJs.set(topTargetIds[i], names[i])
158
+ }
159
+ }
160
+ }
161
+
162
+ /**
163
+ * @param {object} part
164
+ * @param {string} quoteId
165
+ * @param {string[]} innerArgIds
166
+ * @param {string[]} topTargetIds
167
+ * @param {CombinatorEmitEnv} env
168
+ * @param {(steps: Record<string, unknown>[], e: object) => string[]} processIrSteps
169
+ * @param {Record<string, unknown>} node
170
+ */
171
+ function runCombinatorQuoteBranch (
172
+ part,
173
+ quoteId,
174
+ innerArgIds,
175
+ topTargetIds,
176
+ env,
177
+ processIrSteps,
178
+ node
179
+ ) {
180
+ const k = /** @type {{ kind?: string }} */ (part).kind
181
+ if (k === 'invoke') {
182
+ const p = /** @type {{ innerInLen: number, innerOutLen: number }} */ (part)
183
+ if (innerArgIds.length !== p.innerInLen) {
184
+ throw new Error(
185
+ `emit-builtin[${env.wordLabel}]: invoke — ожидалось ${p.innerInLen} argIds`
186
+ )
187
+ }
188
+ const calleeAsyncInvoke = node.calleeAsync === true
189
+ mapInvokeCombinatorQuote(
190
+ quoteId,
191
+ innerArgIds,
192
+ topTargetIds,
193
+ p.innerOutLen,
194
+ env,
195
+ calleeAsyncInvoke
196
+ )
197
+ return
198
+ }
199
+ const inl = /** @type {{ innerSteps: Record<string, unknown>[], innerEntryStackIds: string[] }} */ (
200
+ part
201
+ )
202
+ mapInlineQuoteResults(
203
+ inl.innerSteps,
204
+ inl.innerEntryStackIds,
205
+ innerArgIds,
206
+ topTargetIds,
207
+ env,
208
+ processIrSteps
209
+ )
210
+ }
211
+
212
+ /**
213
+ * @param {string} name
214
+ * @param {Record<string, unknown>} node
215
+ * @param {{ argIds: string[], resultIds: string[] }} vb
216
+ * @param {number} L
217
+ * @param {CombinatorEmitEnv} env
218
+ * @param {(steps: Record<string, unknown>[], e: object) => string[]} processIrSteps
219
+ * @returns {boolean}
220
+ */
221
+ export function tryEmitCombinatorBuiltin (
222
+ name,
223
+ node,
224
+ vb,
225
+ L,
226
+ env,
227
+ processIrSteps
228
+ ) {
229
+ if (!COMBINATOR_BUILTIN_NAMES.has(name)) {
230
+ return false
231
+ }
232
+
233
+ const cmeta = node.combinatorInlineMeta
234
+ if (cmeta == null || typeof cmeta !== 'object') {
235
+ throw new Error(`emit-builtin[${env.wordLabel}]: комбинатор без combinatorInlineMeta`)
236
+ }
237
+ const parts = /** @type {{ parts?: object[] }} */ (cmeta).parts
238
+ if (!Array.isArray(parts)) {
239
+ throw new Error(`emit-builtin[${env.wordLabel}]: combinatorInlineMeta без parts`)
240
+ }
241
+
242
+ const baseLen = L - vb.argIds.length
243
+ const topOutIds = vb.resultIds.slice(baseLen)
244
+ const a = vb.argIds
245
+
246
+ switch (name) {
247
+ case 'dip': {
248
+ if (a.length !== 3 || topOutIds.length !== 2 || parts.length !== 1) {
249
+ throw new Error(`emit-builtin[${env.wordLabel}]: dip — неверные привязки`)
250
+ }
251
+ runCombinatorQuoteBranch(
252
+ parts[0],
253
+ a[2],
254
+ [a[0]],
255
+ [topOutIds[0]],
256
+ env,
257
+ processIrSteps,
258
+ node
259
+ )
260
+ break
261
+ }
262
+ case 'keep': {
263
+ if (a.length !== 3 || topOutIds.length !== 2 || parts.length !== 1) {
264
+ throw new Error(`emit-builtin[${env.wordLabel}]: keep — неверные привязки`)
265
+ }
266
+ runCombinatorQuoteBranch(
267
+ parts[0],
268
+ a[2],
269
+ [a[0], a[1]],
270
+ [topOutIds[0]],
271
+ env,
272
+ processIrSteps,
273
+ node
274
+ )
275
+ break
276
+ }
277
+ case 'bi': {
278
+ if (a.length !== 4 || topOutIds.length !== 3 || parts.length !== 2) {
279
+ throw new Error(`emit-builtin[${env.wordLabel}]: bi — неверные привязки`)
280
+ }
281
+ runCombinatorQuoteBranch(
282
+ parts[0],
283
+ a[2],
284
+ [a[1]],
285
+ [topOutIds[1]],
286
+ env,
287
+ processIrSteps,
288
+ node
289
+ )
290
+ runCombinatorQuoteBranch(
291
+ parts[1],
292
+ a[3],
293
+ [a[1]],
294
+ [topOutIds[2]],
295
+ env,
296
+ processIrSteps,
297
+ node
298
+ )
299
+ break
300
+ }
301
+ case 'tri': {
302
+ if (a.length !== 5 || topOutIds.length !== 4 || parts.length !== 3) {
303
+ throw new Error(`emit-builtin[${env.wordLabel}]: tri — неверные привязки`)
304
+ }
305
+ for (let k = 0; k < 3; k++) {
306
+ runCombinatorQuoteBranch(
307
+ parts[k],
308
+ a[2 + k],
309
+ [a[1]],
310
+ [topOutIds[1 + k]],
311
+ env,
312
+ processIrSteps,
313
+ node
314
+ )
315
+ }
316
+ break
317
+ }
318
+ case 'spread': {
319
+ if (a.length !== 5 || topOutIds.length !== 3 || parts.length !== 2) {
320
+ throw new Error(`emit-builtin[${env.wordLabel}]: spread — неверные привязки`)
321
+ }
322
+ runCombinatorQuoteBranch(
323
+ parts[0],
324
+ a[3],
325
+ [a[1]],
326
+ [topOutIds[1]],
327
+ env,
328
+ processIrSteps,
329
+ node
330
+ )
331
+ runCombinatorQuoteBranch(
332
+ parts[1],
333
+ a[4],
334
+ [a[2]],
335
+ [topOutIds[2]],
336
+ env,
337
+ processIrSteps,
338
+ node
339
+ )
340
+ break
341
+ }
342
+ case 'both': {
343
+ if (a.length !== 4 || topOutIds.length !== 3 || parts.length !== 1) {
344
+ throw new Error(`emit-builtin[${env.wordLabel}]: both — неверные привязки`)
345
+ }
346
+ runCombinatorQuoteBranch(
347
+ parts[0],
348
+ a[3],
349
+ [a[1]],
350
+ [topOutIds[1]],
351
+ env,
352
+ processIrSteps,
353
+ node
354
+ )
355
+ runCombinatorQuoteBranch(
356
+ parts[0],
357
+ a[3],
358
+ [a[2]],
359
+ [topOutIds[2]],
360
+ env,
361
+ processIrSteps,
362
+ node
363
+ )
364
+ break
365
+ }
366
+ default:
367
+ return false
368
+ }
369
+
370
+ return true
371
+ }