@algosail/lang 0.2.12 → 0.5.1

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 +12 -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 +34 -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,390 @@
1
+ /**
2
+ * Tokenization aligned with RFC-lex-0.1 (comments, literals, path, regexp vs word_ref).
3
+ */
4
+
5
+ import * as diag from './diagnostics.js'
6
+
7
+ const RE_MODULE = /^\+([A-Z][a-zA-Z0-9_]*)/
8
+ const RE_EFFECT_ADD = /^\+(Async|Fail)\b/
9
+ const RE_EFFECT_SUB = /^-(Async|Fail)\b/
10
+ const RE_WORD_DEF = /^@([a-z][a-zA-Z0-9_]*)/
11
+ const RE_WORD_REF = /^\/([a-z][a-zA-Z0-9_]*)/
12
+ const RE_STACK_VAR = /^~([a-z][a-zA-Z0-9_]*)/
13
+ const RE_MOD_WORD = /^~([A-Z][a-zA-Z0-9_]*)\/([a-z][a-zA-Z0-9_]*)/
14
+ const RE_MOD_TYPE = /^~([A-Z][a-zA-Z0-9_]*)\/([A-Z][a-zA-Z0-9_]*)/
15
+ const RE_IDENT = /^([a-zA-Z_][a-zA-Z0-9_]*)/
16
+ const RE_NUM = /^([0-9][0-9_]*(\.[0-9_]+)?([eE][+-]?[0-9_]+)?|0[xX][0-9a-fA-F_]+|0[oO][0-7_]+|0[bB][01_]+)\b/
17
+ const RE_BIGINT = /^([0-9][0-9_]*|0[xX][0-9a-fA-F_]+)n\b/
18
+ const RE_PATH = /^([^\s()]+)/
19
+
20
+ function isWs (c) {
21
+ return c === ' ' || c === '\t' || c === '\n' || c === '\r'
22
+ }
23
+
24
+ function advancePos (state, n) {
25
+ for (let k = 0; k < n; k++) {
26
+ const c = state.source[state.i + k]
27
+ if (c === '\n') {
28
+ state.line++
29
+ state.col = 1
30
+ } else {
31
+ state.col++
32
+ }
33
+ }
34
+ state.i += n
35
+ }
36
+
37
+ export function skipWs (state) {
38
+ while (state.i < state.source.length && isWs(state.source[state.i])) {
39
+ advancePos(state, 1)
40
+ }
41
+ }
42
+
43
+ function pos (state) {
44
+ return { offset: state.i, line: state.line, column: state.col }
45
+ }
46
+
47
+ /**
48
+ * Блочный комментарий `-- … --` как токен (текст между парами) для doc_block в парсере.
49
+ * @returns {{ type: 'COMMENT', value: string, offset: number, line: number, column: number } | null}
50
+ */
51
+ function readCommentToken (state, diagnostics) {
52
+ const start = pos(state)
53
+ advancePos(state, 2)
54
+ const from = state.i
55
+ const rest = state.source.slice(from)
56
+ const endRel = rest.indexOf('--')
57
+ if (endRel === -1) {
58
+ diagnostics.push({
59
+ code: 'E1002',
60
+ message: diag.e1002UnclosedComment(),
61
+ offset: start.offset,
62
+ line: start.line,
63
+ column: start.column
64
+ })
65
+ state.i = state.source.length
66
+ return null
67
+ }
68
+ const inner = rest.slice(0, endRel)
69
+ for (let k = 0; k < endRel; k++) {
70
+ const c = rest[k]
71
+ if (c === '\n') {
72
+ state.line++
73
+ state.col = 1
74
+ } else {
75
+ state.col++
76
+ }
77
+ }
78
+ state.i = from + endRel
79
+ advancePos(state, 2)
80
+ return {
81
+ type: 'COMMENT',
82
+ value: inner,
83
+ offset: start.offset,
84
+ line: start.line,
85
+ column: start.column
86
+ }
87
+ }
88
+
89
+ function tryReadRegexp (state, start) {
90
+ const s = state.source
91
+ let j = state.i + 1
92
+ let inClass = false
93
+ while (j < s.length) {
94
+ const c = s[j]
95
+ if (c === '\\') {
96
+ j += 2
97
+ continue
98
+ }
99
+ if (inClass) {
100
+ if (c === ']') inClass = false
101
+ j++
102
+ continue
103
+ }
104
+ if (c === '[') {
105
+ inClass = true
106
+ j++
107
+ continue
108
+ }
109
+ if (c === '\n') return null
110
+ if (c === '/') {
111
+ j++
112
+ while (j < s.length && /[gimsuy]/.test(s[j])) {
113
+ if (s[j] === 'm' && /[a-zA-Z]/.test(s[j + 1] || '')) break
114
+ j++
115
+ }
116
+ const raw = s.slice(state.i, j)
117
+ const n = j - state.i
118
+ advancePos(state, n)
119
+ return {
120
+ type: 'REGEXP',
121
+ value: raw,
122
+ offset: start.offset,
123
+ line: start.line,
124
+ column: start.column
125
+ }
126
+ }
127
+ j++
128
+ }
129
+ return null
130
+ }
131
+
132
+ function readString (state, quote, diagnostics) {
133
+ const start = pos(state)
134
+ let j = state.i + 1
135
+ const src = state.source
136
+ while (j < src.length) {
137
+ const c = src[j]
138
+ if (c === '\\') {
139
+ j += 2
140
+ continue
141
+ }
142
+ if (c === quote) {
143
+ const n = j + 1 - state.i
144
+ advancePos(state, n)
145
+ return {
146
+ type: 'STRING',
147
+ value: src.slice(start.offset, state.i),
148
+ offset: start.offset,
149
+ line: start.line,
150
+ column: start.column
151
+ }
152
+ }
153
+ if (c === '\n') break
154
+ j++
155
+ }
156
+ diagnostics.push({
157
+ code: 'E1001',
158
+ message: diag.e1001UnexpectedToken(quote),
159
+ offset: start.offset,
160
+ line: start.line,
161
+ column: start.column
162
+ })
163
+ state.i = src.length
164
+ return null
165
+ }
166
+
167
+ export function nextToken (state, ctx) {
168
+ skipWs(state)
169
+ if (state.i >= state.source.length) {
170
+ return { type: 'EOF', offset: state.i, line: state.line, column: state.col }
171
+ }
172
+
173
+ const s = state.source.slice(state.i)
174
+ const start = pos(state)
175
+
176
+ if (s.startsWith('--')) {
177
+ const ct = readCommentToken(state, ctx.diagnostics)
178
+ if (ct) return ct
179
+ return { type: 'EOF', offset: state.i, line: state.line, column: state.col }
180
+ }
181
+
182
+ if (s.startsWith('->')) {
183
+ advancePos(state, 2)
184
+ return { type: 'ARROW', value: '->', offset: start.offset, line: start.line, column: start.column }
185
+ }
186
+
187
+ const c0 = s[0]
188
+ if (c0 === '&') {
189
+ advancePos(state, 1)
190
+ return { type: 'AMP', value: '&', offset: start.offset, line: start.line, column: start.column }
191
+ }
192
+
193
+ if (c0 === '(' || c0 === ')' || c0 === '[' || c0 === ']' || c0 === '|' || c0 === ':' || c0 === ';') {
194
+ advancePos(state, 1)
195
+ return { type: c0, value: c0, offset: start.offset, line: start.line, column: start.column }
196
+ }
197
+
198
+ if (c0 === '"' || c0 === "'") {
199
+ const str = readString(state, c0, ctx.diagnostics)
200
+ if (str) return str
201
+ return { type: 'EOF', offset: state.i, line: state.line, column: state.col }
202
+ }
203
+
204
+ if (c0 === '/') {
205
+ if (ctx.regexpOk) {
206
+ const r = tryReadRegexp(state, start)
207
+ if (r) return r
208
+ }
209
+ const m = s.match(RE_WORD_REF)
210
+ if (m) {
211
+ advancePos(state, m[0].length)
212
+ return {
213
+ type: 'WORD_REF',
214
+ value: m[1],
215
+ offset: start.offset,
216
+ line: start.line,
217
+ column: start.column
218
+ }
219
+ }
220
+ ctx.diagnostics.push({
221
+ code: 'E1001',
222
+ message: diag.e1001UnexpectedToken('/'),
223
+ offset: start.offset,
224
+ line: start.line,
225
+ column: start.column
226
+ })
227
+ advancePos(state, 1)
228
+ return nextToken(state, ctx)
229
+ }
230
+
231
+ if (s.startsWith('nil') && !/[a-zA-Z0-9_]/.test(s[3] || '')) {
232
+ advancePos(state, 3)
233
+ return { type: 'NIL', value: 'nil', offset: start.offset, line: start.line, column: start.column }
234
+ }
235
+ if (s.startsWith('true') && !/[a-zA-Z0-9_]/.test(s[4] || '')) {
236
+ advancePos(state, 4)
237
+ return { type: 'TRUE', value: 'true', offset: start.offset, line: start.line, column: start.column }
238
+ }
239
+ if (s.startsWith('false') && !/[a-zA-Z0-9_]/.test(s[5] || '')) {
240
+ advancePos(state, 5)
241
+ return { type: 'FALSE', value: 'false', offset: start.offset, line: start.line, column: start.column }
242
+ }
243
+
244
+ let m = s.match(RE_BIGINT)
245
+ if (m) {
246
+ advancePos(state, m[0].length)
247
+ return { type: 'BIGINT', value: m[0], offset: start.offset, line: start.line, column: start.column }
248
+ }
249
+ m = s.match(RE_NUM)
250
+ if (m) {
251
+ advancePos(state, m[0].length)
252
+ return { type: 'NUMBER', value: m[0], offset: start.offset, line: start.line, column: start.column }
253
+ }
254
+
255
+ m = s.match(RE_EFFECT_ADD)
256
+ if (m) {
257
+ advancePos(state, m[0].length)
258
+ return {
259
+ type: 'EFFECT_ADD',
260
+ value: m[0],
261
+ offset: start.offset,
262
+ line: start.line,
263
+ column: start.column
264
+ }
265
+ }
266
+ m = s.match(RE_EFFECT_SUB)
267
+ if (m) {
268
+ advancePos(state, m[0].length)
269
+ return {
270
+ type: 'EFFECT_SUB',
271
+ value: m[0],
272
+ offset: start.offset,
273
+ line: start.line,
274
+ column: start.column
275
+ }
276
+ }
277
+
278
+ m = s.match(RE_MODULE)
279
+ if (m) {
280
+ advancePos(state, m[0].length)
281
+ return {
282
+ type: 'MODULE',
283
+ value: m[1],
284
+ offset: start.offset,
285
+ line: start.line,
286
+ column: start.column
287
+ }
288
+ }
289
+
290
+ m = s.match(RE_WORD_DEF)
291
+ if (m) {
292
+ advancePos(state, m[0].length)
293
+ return {
294
+ type: 'WORD_DEF',
295
+ value: m[1],
296
+ offset: start.offset,
297
+ line: start.line,
298
+ column: start.column
299
+ }
300
+ }
301
+
302
+ m = s.match(RE_MOD_WORD)
303
+ if (m) {
304
+ advancePos(state, m[0].length)
305
+ return {
306
+ type: 'MODULE_WORD_REF',
307
+ module: m[1],
308
+ word: m[2],
309
+ offset: start.offset,
310
+ line: start.line,
311
+ column: start.column
312
+ }
313
+ }
314
+ m = s.match(RE_MOD_TYPE)
315
+ if (m) {
316
+ advancePos(state, m[0].length)
317
+ return {
318
+ type: 'MODULE_TYPE_REF',
319
+ module: m[1],
320
+ typeName: m[2],
321
+ offset: start.offset,
322
+ line: start.line,
323
+ column: start.column
324
+ }
325
+ }
326
+ m = s.match(RE_STACK_VAR)
327
+ if (m) {
328
+ advancePos(state, m[0].length)
329
+ return {
330
+ type: 'STACK_VAR',
331
+ value: m[1],
332
+ offset: start.offset,
333
+ line: start.line,
334
+ column: start.column
335
+ }
336
+ }
337
+
338
+ m = s.match(RE_IDENT)
339
+ if (m) {
340
+ const word = m[1]
341
+ advancePos(state, m[0].length)
342
+ if (/^[A-Z]/.test(word)) {
343
+ return {
344
+ type: 'TYPE_NAME',
345
+ value: word,
346
+ offset: start.offset,
347
+ line: start.line,
348
+ column: start.column
349
+ }
350
+ }
351
+ return {
352
+ type: 'LOWER_IDENT',
353
+ value: word,
354
+ offset: start.offset,
355
+ line: start.line,
356
+ column: start.column
357
+ }
358
+ }
359
+
360
+ ctx.diagnostics.push({
361
+ code: 'E1001',
362
+ message: diag.e1001UnexpectedToken(c0),
363
+ offset: start.offset,
364
+ line: start.line,
365
+ column: start.column
366
+ })
367
+ advancePos(state, 1)
368
+ return nextToken(state, ctx)
369
+ }
370
+
371
+ export function readPathToken (state, ctx) {
372
+ skipWs(state)
373
+ if (state.i >= state.source.length) return null
374
+ const start = pos(state)
375
+ const s = state.source.slice(state.i)
376
+ const m = s.match(RE_PATH)
377
+ if (!m || m[1] === '') return null
378
+ advancePos(state, m[1].length)
379
+ return {
380
+ type: 'PATH',
381
+ value: m[1],
382
+ offset: start.offset,
383
+ line: start.line,
384
+ column: start.column
385
+ }
386
+ }
387
+
388
+ export function createLexerState (source) {
389
+ return { source, i: 0, line: 1, col: 1 }
390
+ }