aontu 0.63.0 → 0.65.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/README.md +8 -8
- package/dist/agentsmd.js +3 -3
- package/dist/agentsmd.js.map +1 -1
- package/dist/aontu.d.ts +3 -3
- package/dist/aontu.js +4 -6
- package/dist/aontu.js.map +1 -1
- package/dist/aontumodel.js +6 -12
- package/dist/aontumodel.js.map +1 -1
- package/dist/casing.d.ts +5 -0
- package/dist/casing.js +90 -0
- package/dist/casing.js.map +1 -0
- package/dist/cli.d.ts +16 -3
- package/dist/cli.js +653 -455
- package/dist/cli.js.map +1 -1
- package/dist/compat.d.ts +7 -0
- package/dist/compat.js +113 -0
- package/dist/compat.js.map +1 -0
- package/dist/helpdoc.js +5 -5
- package/dist/helpdoc.js.map +1 -1
- package/dist/hints.js +14 -5
- package/dist/hints.js.map +1 -1
- package/dist/lang.d.ts +1 -0
- package/dist/lang.js +8 -0
- package/dist/lang.js.map +1 -1
- package/dist/lsp.d.ts +1 -1
- package/dist/mcp.js +0 -36
- package/dist/mcp.js.map +1 -1
- package/dist/mod.d.ts +18 -3
- package/dist/mod.js +117 -46
- package/dist/mod.js.map +1 -1
- package/dist/pkg-net.d.ts +227 -0
- package/dist/pkg-net.js +1598 -0
- package/dist/pkg-net.js.map +1 -0
- package/dist/pkg-zip.d.ts +8 -0
- package/dist/pkg-zip.js +196 -0
- package/dist/pkg-zip.js.map +1 -0
- package/dist/pkg.d.ts +159 -0
- package/dist/pkg.js +767 -0
- package/dist/pkg.js.map +1 -0
- package/dist/profile.d.ts +9 -0
- package/dist/profile.js +28 -0
- package/dist/profile.js.map +1 -0
- package/dist/report-sarif.js +1 -1
- package/dist/report-sarif.js.map +1 -1
- package/dist/subsume.d.ts +3 -1
- package/dist/subsume.js +2 -0
- package/dist/subsume.js.map +1 -1
- package/dist/trace.d.ts +21 -0
- package/dist/trace.js +107 -0
- package/dist/trace.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/unify.js +1 -6
- package/dist/unify.js.map +1 -1
- package/dist/val/CmpFuncVal.d.ts +2 -0
- package/dist/val/CmpFuncVal.js +47 -13
- package/dist/val/CmpFuncVal.js.map +1 -1
- package/dist/val/NomFuncVal.js +11 -13
- package/dist/val/NomFuncVal.js.map +1 -1
- package/dist/val/ReferFuncVal.js +3 -0
- package/dist/val/ReferFuncVal.js.map +1 -1
- package/package.json +1 -1
- package/skill/SKILL.md +3 -3
- package/skill/error-codes.md +4 -4
- package/skill/examples.md +4 -4
- package/skill/grammar-card.md +1 -1
- package/skill/init/check.sh +2 -2
- package/skill/tasks.md +14 -12
- package/src/agentsmd.ts +3 -3
- package/src/aontu.ts +3 -8
- package/src/aontumodel.ts +6 -12
- package/src/casing.ts +95 -0
- package/src/cli.ts +758 -495
- package/src/compat.ts +148 -0
- package/src/helpdoc.ts +5 -5
- package/src/hints.ts +14 -7
- package/src/lang.ts +9 -1
- package/src/mcp.ts +0 -39
- package/src/mod.ts +142 -51
- package/src/pkg-net.ts +1945 -0
- package/src/pkg-zip.ts +228 -0
- package/src/pkg.ts +1018 -0
- package/src/profile.ts +42 -0
- package/src/report-sarif.ts +1 -1
- package/src/subsume.ts +2 -2
- package/src/trace.ts +157 -0
- package/src/unify.ts +1 -6
- package/src/val/CmpFuncVal.ts +66 -20
- package/src/val/NomFuncVal.ts +3 -5
- package/src/val/ReferFuncVal.ts +4 -0
- package/dist/lower.d.ts +0 -23
- package/dist/lower.js +0 -528
- package/dist/lower.js.map +0 -1
- package/dist/mod-tool.d.ts +0 -58
- package/dist/mod-tool.js +0 -403
- package/dist/mod-tool.js.map +0 -1
- package/dist/render.d.ts +0 -53
- package/dist/render.js +0 -432
- package/dist/render.js.map +0 -1
- package/src/lower.ts +0 -586
- package/src/mod-tool.ts +0 -561
- package/src/render.ts +0 -580
package/src/lower.ts
DELETED
|
@@ -1,586 +0,0 @@
|
|
|
1
|
-
/* Copyright (c) 2025 Richard Rodger, MIT License */
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import { exactJSON } from './exactjson'
|
|
5
|
-
import type { RenderLoss } from './render'
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
// The lowering's view of one unit: the merged profile, the family it
|
|
9
|
-
// names, and the report the losses go to.
|
|
10
|
-
export type LowerCtx = {
|
|
11
|
-
profile: any
|
|
12
|
-
family: string
|
|
13
|
-
unit: string
|
|
14
|
-
lossy: RenderLoss[]
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
function ln(at: number, text: string): any {
|
|
19
|
-
return { k: 'line', at, n: [text] }
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const BLANK = { k: 'blank' }
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
// --- identifiers -------------------------------------------------------
|
|
26
|
-
|
|
27
|
-
function isUpper(c: string): boolean {
|
|
28
|
-
return 'A' <= c && c <= 'Z'
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function isLower(c: string): boolean {
|
|
32
|
-
return 'a' <= c && c <= 'z'
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function isDigit(c: string): boolean {
|
|
36
|
-
return '0' <= c && c <= '9'
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function isASCII(c: string): boolean {
|
|
40
|
-
return (c.codePointAt(0) as number) < 0x80
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export function splitWords(name: string): string[] {
|
|
44
|
-
const words: string[] = []
|
|
45
|
-
let cur = ''
|
|
46
|
-
const chars = Array.from(name)
|
|
47
|
-
for (let i = 0; i < chars.length; i++) {
|
|
48
|
-
const c = chars[i]
|
|
49
|
-
if ('_' === c || '-' === c || ' ' === c) {
|
|
50
|
-
if ('' !== cur) {
|
|
51
|
-
words.push(cur)
|
|
52
|
-
}
|
|
53
|
-
cur = ''
|
|
54
|
-
continue
|
|
55
|
-
}
|
|
56
|
-
if ('' !== cur) {
|
|
57
|
-
const prev = chars[i - 1]
|
|
58
|
-
const next = chars[i + 1]
|
|
59
|
-
const boundary = isASCII(c) && isASCII(prev) && (
|
|
60
|
-
(isUpper(c) && (isLower(prev) || isDigit(prev))) ||
|
|
61
|
-
(isDigit(c) !== isDigit(prev)) ||
|
|
62
|
-
(isUpper(c) && isUpper(prev) && undefined !== next && isLower(next)))
|
|
63
|
-
if (boundary) {
|
|
64
|
-
words.push(cur)
|
|
65
|
-
cur = ''
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
cur += c
|
|
69
|
-
}
|
|
70
|
-
if ('' !== cur) {
|
|
71
|
-
words.push(cur)
|
|
72
|
-
}
|
|
73
|
-
return words
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export function lowerASCII(s: string): string {
|
|
77
|
-
return Array.from(s).map((c) =>
|
|
78
|
-
isUpper(c) ? String.fromCharCode(c.charCodeAt(0) + 32) : c).join('')
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export function upperASCII(s: string): string {
|
|
82
|
-
return Array.from(s).map((c) =>
|
|
83
|
-
isLower(c) ? String.fromCharCode(c.charCodeAt(0) - 32) : c).join('')
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// A word capitalised: the acronym set wins, so `id` is `ID` under a
|
|
87
|
-
// profile that lists it and `Id` under one that does not.
|
|
88
|
-
export function capitalise(word: string, acronyms: string[]): string {
|
|
89
|
-
const low = lowerASCII(word)
|
|
90
|
-
for (const a of acronyms) {
|
|
91
|
-
if (lowerASCII(a) === low) {
|
|
92
|
-
return a
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
const chars = Array.from(low)
|
|
96
|
-
return upperASCII(chars[0]) + chars.slice(1).join('')
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// THE CASE STYLES of aontu:render's %case, over the words.
|
|
100
|
-
export function caseName(name: string, style: string, acronyms: string[]): string {
|
|
101
|
-
if ('as-is' === style) {
|
|
102
|
-
return name
|
|
103
|
-
}
|
|
104
|
-
const words = splitWords(name)
|
|
105
|
-
if (0 === words.length) {
|
|
106
|
-
return name
|
|
107
|
-
}
|
|
108
|
-
if ('snake' === style) {
|
|
109
|
-
return words.map(lowerASCII).join('_')
|
|
110
|
-
}
|
|
111
|
-
if ('screaming' === style) {
|
|
112
|
-
return words.map(upperASCII).join('_')
|
|
113
|
-
}
|
|
114
|
-
if ('kebab' === style) {
|
|
115
|
-
return words.map(lowerASCII).join('-')
|
|
116
|
-
}
|
|
117
|
-
const caps = words.map((w) => capitalise(w, acronyms))
|
|
118
|
-
if ('pascal' === style) {
|
|
119
|
-
return caps.join('')
|
|
120
|
-
}
|
|
121
|
-
// camel: the first word lower, and never an acronym.
|
|
122
|
-
return lowerASCII(words[0]) + caps.slice(1).join('')
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
export function ident(
|
|
126
|
-
name: string, role: string, ctx: LowerCtx, path: string, bare: boolean
|
|
127
|
-
): string {
|
|
128
|
-
const rules = ctx.profile.ident ?? {}
|
|
129
|
-
const style: string = rules.case?.[role] ?? 'as-is'
|
|
130
|
-
const acronyms: string[] = rules.acronyms ?? []
|
|
131
|
-
let out = caseName(name, style, acronyms)
|
|
132
|
-
if (bare && (rules.reserved ?? []).includes(out)) {
|
|
133
|
-
ctx.lossy.push({
|
|
134
|
-
unit: ctx.unit, path, tier: 1, construct: 'reserved',
|
|
135
|
-
reason: out + ' is reserved in ' + ctx.profile.lang + '; renamed ' + out + '_',
|
|
136
|
-
})
|
|
137
|
-
out += '_'
|
|
138
|
-
}
|
|
139
|
-
return out
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
// --- literals ----------------------------------------------------------
|
|
144
|
-
|
|
145
|
-
function hex4(n: number): string {
|
|
146
|
-
return '\\u' + n.toString(16).padStart(4, '0')
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// A string literal: the profile's quote, and one pass over the code
|
|
150
|
-
// points through its escape table. A control character the table does
|
|
151
|
-
// not name is a \u escape; so are the quote and the backslash when the
|
|
152
|
-
// table is silent about them; everything else, non-ASCII and astral
|
|
153
|
-
// included, is verbatim.
|
|
154
|
-
export function quote(s: string, profile: any): string {
|
|
155
|
-
const q: string = profile.str?.quote ?? '"'
|
|
156
|
-
const table: any = profile.str?.escape ?? {}
|
|
157
|
-
let out = q
|
|
158
|
-
for (const c of Array.from(s)) {
|
|
159
|
-
const cp = c.codePointAt(0) as number
|
|
160
|
-
const esc = table[String(cp)]
|
|
161
|
-
if ('string' === typeof esc) {
|
|
162
|
-
out += esc
|
|
163
|
-
}
|
|
164
|
-
else if (cp < 0x20 || 0x7f === cp || c === q || '\\' === c) {
|
|
165
|
-
out += hex4(cp)
|
|
166
|
-
}
|
|
167
|
-
else {
|
|
168
|
-
out += c
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
return out + q
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// A literal value as the target spells it: a string quoted, a number
|
|
175
|
-
// through exactJSON (no second number formatter), a boolean as is, and
|
|
176
|
-
// null as the family's null.
|
|
177
|
-
export function literal(v: any, ctx: LowerCtx): string {
|
|
178
|
-
if ('string' === typeof v) {
|
|
179
|
-
return quote(v, ctx.profile)
|
|
180
|
-
}
|
|
181
|
-
if (null === v) {
|
|
182
|
-
return 'go' === ctx.family ? 'nil' : 'null'
|
|
183
|
-
}
|
|
184
|
-
if ('boolean' === typeof v) {
|
|
185
|
-
return v ? 'true' : 'false'
|
|
186
|
-
}
|
|
187
|
-
return exactJSON(v)
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
// --- types -------------------------------------------------------------
|
|
192
|
-
|
|
193
|
-
type Expr = { text: string, prec: number }
|
|
194
|
-
|
|
195
|
-
function form(ctx: LowerCtx, name: string): any {
|
|
196
|
-
return ctx.profile.types?.[name] ?? { open: '', close: '', prec: 9, childPrec: 0 }
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
function under(inner: Expr, f: any): string {
|
|
200
|
-
return inner.prec < f.childPrec ? '(' + inner.text + ')' : inner.text
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
function loss(ctx: LowerCtx, path: string, construct: string, reason: string): void {
|
|
204
|
-
ctx.lossy.push({ unit: ctx.unit, path, tier: 1, construct, reason })
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// The primitive kind a literal set shares, for a family with no
|
|
208
|
-
// literal type: strings, integers, floats, booleans, null, or any.
|
|
209
|
-
function litPrim(of: any[]): string {
|
|
210
|
-
const kinds = of.map((v) => null === v ? 'null' : typeof v)
|
|
211
|
-
const first = kinds[0]
|
|
212
|
-
if (!kinds.every((k) => k === first)) {
|
|
213
|
-
return 'any'
|
|
214
|
-
}
|
|
215
|
-
if ('number' === first) {
|
|
216
|
-
return of.every((v) => Number.isInteger(v)) ? 'int' : 'float'
|
|
217
|
-
}
|
|
218
|
-
return 'string' === first ? 'string' : 'boolean' === first ? 'bool' : 'null'
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
function prim(ctx: LowerCtx, name: string): string {
|
|
222
|
-
return ctx.profile.types?.prim?.[name] ?? name
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// THE TYPE EXPRESSION, from the profile's type forms (D6): an atom has
|
|
226
|
-
// prec 9; a form is `open + inner + close`, its precedence its own, its
|
|
227
|
-
// inner parenthesised below childPrec. A `text` leaf is verbatim
|
|
228
|
-
// target syntax and tier 3, as a text declaration is.
|
|
229
|
-
export function typeExpr(t: any, ctx: LowerCtx, path: string): Expr {
|
|
230
|
-
const k: string = t.k
|
|
231
|
-
if ('prim' === k) {
|
|
232
|
-
return { text: prim(ctx, t.prim), prec: 9 }
|
|
233
|
-
}
|
|
234
|
-
if ('ref' === k) {
|
|
235
|
-
return { text: ident(t.name, 'record', ctx, path, false), prec: 9 }
|
|
236
|
-
}
|
|
237
|
-
if ('text' === k) {
|
|
238
|
-
ctx.lossy.push({
|
|
239
|
-
unit: ctx.unit, path, tier: 3, construct: 'text',
|
|
240
|
-
reason: 'verbatim ' + t.lang + ': the renderer checks nothing in it',
|
|
241
|
-
})
|
|
242
|
-
return { text: t.text, prec: 9 }
|
|
243
|
-
}
|
|
244
|
-
if ('list' === k) {
|
|
245
|
-
const f = form(ctx, 'list')
|
|
246
|
-
return { text: f.open + under(typeExpr(t.n, ctx, path + '.n'), f) + f.close, prec: f.prec }
|
|
247
|
-
}
|
|
248
|
-
if ('map' === k) {
|
|
249
|
-
const f = form(ctx, 'map')
|
|
250
|
-
const key = under(typeExpr(t.key, ctx, path + '.key'), f)
|
|
251
|
-
const of = under(typeExpr(t.n, ctx, path + '.n'), f)
|
|
252
|
-
const sep = 'go' === ctx.family ? ']' : ', '
|
|
253
|
-
return { text: f.open + key + sep + of + f.close, prec: f.prec }
|
|
254
|
-
}
|
|
255
|
-
if ('opt' === k) {
|
|
256
|
-
const f = form(ctx, 'opt')
|
|
257
|
-
return { text: f.open + under(typeExpr(t.n, ctx, path + '.n'), f) + f.close, prec: f.prec }
|
|
258
|
-
}
|
|
259
|
-
if ('union' === k) {
|
|
260
|
-
if ('go' === ctx.family) {
|
|
261
|
-
loss(ctx, path, 'union', 'go has no union type: any')
|
|
262
|
-
return { text: prim(ctx, 'any'), prec: 9 }
|
|
263
|
-
}
|
|
264
|
-
const f = form(ctx, 'union')
|
|
265
|
-
const members = t.n.map((m: any, i: number) =>
|
|
266
|
-
under(typeExpr(m, ctx, path + '.n.' + i), f))
|
|
267
|
-
return { text: f.open + members.join(' | ') + f.close, prec: f.prec }
|
|
268
|
-
}
|
|
269
|
-
// lit
|
|
270
|
-
if ('go' === ctx.family) {
|
|
271
|
-
const p = litPrim(t.n)
|
|
272
|
-
loss(ctx, path, 'lit', 'go has no literal type: ' + p)
|
|
273
|
-
return { text: prim(ctx, p), prec: 9 }
|
|
274
|
-
}
|
|
275
|
-
const f = form(ctx, 'lit')
|
|
276
|
-
const lits = t.n.map((v: any) => literal(v, ctx))
|
|
277
|
-
return { text: f.open + lits.join(' | ') + f.close, prec: f.prec }
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
// --- comments ----------------------------------------------------------
|
|
282
|
-
|
|
283
|
-
// A doc comment as pieces at a depth: the profile's doc form -- open,
|
|
284
|
-
// a prefix per line, close -- and the line form when a profile has no
|
|
285
|
-
// doc form. A deprecation is the last line, in the family's spelling.
|
|
286
|
-
function doc(d: any, at: number, ctx: LowerCtx): any[] {
|
|
287
|
-
if (null == d) {
|
|
288
|
-
return []
|
|
289
|
-
}
|
|
290
|
-
const forms = ctx.profile.comment ?? {}
|
|
291
|
-
const f = forms.doc ?? forms.line ?? { prefix: '// ' }
|
|
292
|
-
const prefix: string = f.prefix ?? ''
|
|
293
|
-
const lines: string[] = String(d.text).split('\n')
|
|
294
|
-
if (null != d.deprecated) {
|
|
295
|
-
const dep = d.deprecated
|
|
296
|
-
const note = [dep.msg, dep.use ? 'use ' + dep.use : '', dep.since ? 'since ' + dep.since : '']
|
|
297
|
-
.filter((s: string) => s).join('; ')
|
|
298
|
-
lines.push('go' === ctx.family ? 'Deprecated: ' + note : '@deprecated ' + note)
|
|
299
|
-
}
|
|
300
|
-
const out: any[] = []
|
|
301
|
-
if (null != f.open) {
|
|
302
|
-
out.push(ln(at, f.open))
|
|
303
|
-
}
|
|
304
|
-
for (const l of lines) {
|
|
305
|
-
out.push(ln(at, (prefix + l).replace(/[ \t]+$/, '')))
|
|
306
|
-
}
|
|
307
|
-
if (null != f.close) {
|
|
308
|
-
out.push(ln(at, f.close))
|
|
309
|
-
}
|
|
310
|
-
return out
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
function checks(list: any[] | undefined, path: string, ctx: LowerCtx): void {
|
|
314
|
-
(list ?? []).forEach((c: any, i: number) => {
|
|
315
|
-
loss(ctx, path + '.check.' + i, 'check',
|
|
316
|
-
'the check ' + c.c + ' is not enforced by the ' + ctx.profile.lang + ' type system')
|
|
317
|
-
})
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
// --- the unit header ---------------------------------------------------
|
|
322
|
-
|
|
323
|
-
// The path from one unit's directory to another unit, for a TypeScript
|
|
324
|
-
// import: `./other` beside, `../shared/x` across, the extension gone.
|
|
325
|
-
function relImport(from: string, to: string): string {
|
|
326
|
-
const a = from.split('/').slice(0, -1)
|
|
327
|
-
const b = to.split('/')
|
|
328
|
-
const file = (b.pop() as string).replace(/\.[^.]*$/, '')
|
|
329
|
-
let i = 0
|
|
330
|
-
while (i < a.length && i < b.length && a[i] === b[i]) {
|
|
331
|
-
i++
|
|
332
|
-
}
|
|
333
|
-
const up = a.slice(i).map(() => '..')
|
|
334
|
-
const parts = [...up, ...b.slice(i), file]
|
|
335
|
-
return (0 === up.length ? './' : '') + parts.join('/')
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
// Imports derived from `{k:"ref", unit}` nodes, in order of first
|
|
339
|
-
// appearance, grouped by unit -- TypeScript only: a Go reference names
|
|
340
|
-
// a type in the same package.
|
|
341
|
-
function derivedImports(unit: any): any[] {
|
|
342
|
-
const groups: any[] = []
|
|
343
|
-
const visit = (t: any): void => {
|
|
344
|
-
if (null == t || 'object' !== typeof t) {
|
|
345
|
-
return
|
|
346
|
-
}
|
|
347
|
-
if (Array.isArray(t)) {
|
|
348
|
-
t.forEach(visit)
|
|
349
|
-
return
|
|
350
|
-
}
|
|
351
|
-
if ('ref' === t.k && 'string' === typeof t.unit) {
|
|
352
|
-
let g = groups.find((x) => x.from === t.unit)
|
|
353
|
-
if (undefined === g) {
|
|
354
|
-
g = { from: t.unit, names: [] }
|
|
355
|
-
groups.push(g)
|
|
356
|
-
}
|
|
357
|
-
if (!g.names.includes(t.name)) {
|
|
358
|
-
g.names.push(t.name)
|
|
359
|
-
}
|
|
360
|
-
return
|
|
361
|
-
}
|
|
362
|
-
for (const key of ['type', 'n', 'key', 'returns', 'fields', 'params']) {
|
|
363
|
-
visit(t[key])
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
visit(unit.decls)
|
|
367
|
-
return groups
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
// The banner from `code.source`, the package clause, and the imports:
|
|
371
|
-
// what stands before the declarations, each part followed by a blank.
|
|
372
|
-
export function lowerHeader(unit: any, source: any, ctx: LowerCtx): any[] {
|
|
373
|
-
const parts: any[][] = []
|
|
374
|
-
const banner: string | undefined = ctx.profile.banner
|
|
375
|
-
if (null != banner && null != source?.path) {
|
|
376
|
-
const prefix: string = ctx.profile.comment?.line?.prefix ?? ''
|
|
377
|
-
parts.push([prefix + banner.split('{path}').join(source.path)
|
|
378
|
-
.split('{hash}').join(source.hash ?? '')])
|
|
379
|
-
}
|
|
380
|
-
if ('go' === ctx.family && 'string' === typeof unit.pkg) {
|
|
381
|
-
parts.push(['package ' + unit.pkg])
|
|
382
|
-
}
|
|
383
|
-
const imports: any[] = [...(unit.imports ?? [])]
|
|
384
|
-
if ('typescript' === ctx.family) {
|
|
385
|
-
for (const g of derivedImports(unit)) {
|
|
386
|
-
imports.push({
|
|
387
|
-
from: relImport(unit.path, g.from),
|
|
388
|
-
names: g.names.map((n: string) => ident(n, 'record', ctx, '', false)),
|
|
389
|
-
})
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
if (0 < imports.length) {
|
|
393
|
-
const lines: any[] = []
|
|
394
|
-
if ('go' === ctx.family) {
|
|
395
|
-
const spell = (im: any) =>
|
|
396
|
-
(null == im.alias ? '' : im.alias + ' ') + quote(im.from, ctx.profile)
|
|
397
|
-
if (1 === imports.length) {
|
|
398
|
-
lines.push('import ' + spell(imports[0]))
|
|
399
|
-
}
|
|
400
|
-
else {
|
|
401
|
-
lines.push('import (')
|
|
402
|
-
for (const im of imports) {
|
|
403
|
-
lines.push(ln(1, spell(im)))
|
|
404
|
-
}
|
|
405
|
-
lines.push(')')
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
else {
|
|
409
|
-
for (const im of imports) {
|
|
410
|
-
const from = quote(im.from, ctx.profile)
|
|
411
|
-
if (Array.isArray(im.names) && 0 < im.names.length) {
|
|
412
|
-
lines.push('import { ' + im.names.join(', ') + ' } from ' + from + ';')
|
|
413
|
-
}
|
|
414
|
-
else if (null != im.alias) {
|
|
415
|
-
lines.push('import * as ' + im.alias + ' from ' + from + ';')
|
|
416
|
-
}
|
|
417
|
-
else {
|
|
418
|
-
lines.push('import ' + from + ';')
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
parts.push(lines)
|
|
423
|
-
}
|
|
424
|
-
// The parts, a blank between each two; the fold puts the blank
|
|
425
|
-
// after the last when a declaration follows.
|
|
426
|
-
const out: any[] = []
|
|
427
|
-
parts.forEach((part, i) => {
|
|
428
|
-
if (0 < i) {
|
|
429
|
-
out.push(BLANK)
|
|
430
|
-
}
|
|
431
|
-
out.push(...part)
|
|
432
|
-
})
|
|
433
|
-
return out
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
// --- declarations ------------------------------------------------------
|
|
438
|
-
|
|
439
|
-
// A body fragment's pieces, one level deeper: a bare string is a line
|
|
440
|
-
// at depth 1, a line or a raw keeps its shape at `at + 1`, a blank is
|
|
441
|
-
// a blank.
|
|
442
|
-
function nest(pieces: any[]): any[] {
|
|
443
|
-
return pieces.map((p) => 'string' === typeof p ? ln(1, p)
|
|
444
|
-
: 'blank' === p.k ? p : { ...p, at: (p.at ?? 0) + 1 })
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
function params(list: any[], ctx: LowerCtx, path: string): string {
|
|
448
|
-
return list.map((p: any, i: number) => {
|
|
449
|
-
const ppath = path + '.params.' + i
|
|
450
|
-
const name = ident(p.name, 'param', ctx, ppath + '.name', true)
|
|
451
|
-
const type = typeExpr(p.type, ctx, ppath + '.type').text
|
|
452
|
-
if ('go' === ctx.family) {
|
|
453
|
-
if (undefined !== p.default) {
|
|
454
|
-
loss(ctx, ppath + '.default', 'default',
|
|
455
|
-
'go has no default parameter: the default of ' + p.name + ' is dropped')
|
|
456
|
-
}
|
|
457
|
-
return name + ' ' + type
|
|
458
|
-
}
|
|
459
|
-
return name + ': ' + type +
|
|
460
|
-
(undefined === p.default ? '' : ' = ' + literal(p.default, ctx))
|
|
461
|
-
}).join(', ')
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
// The lowering of one declaration to pieces. A `frag` and a `text`
|
|
465
|
-
// never arrive here: the fold takes those itself.
|
|
466
|
-
export function lowerDecl(decl: any, path: string, ctx: LowerCtx): any[] {
|
|
467
|
-
const go = 'go' === ctx.family
|
|
468
|
-
const out: any[] = []
|
|
469
|
-
const k: string = decl.k
|
|
470
|
-
|
|
471
|
-
if ('record' === k) {
|
|
472
|
-
const name = ident(decl.name, 'record', ctx, path + '.name', true)
|
|
473
|
-
out.push(...doc(decl.doc, 0, ctx))
|
|
474
|
-
out.push(go ? 'type ' + name + ' struct {' : 'export interface ' + name + ' {')
|
|
475
|
-
decl.fields.forEach((f: any, i: number) => {
|
|
476
|
-
const fpath = path + '.fields.' + i
|
|
477
|
-
out.push(...doc(f.doc, 1, ctx))
|
|
478
|
-
const fname = ident(f.name, 'field', ctx, fpath + '.name', false)
|
|
479
|
-
const type = typeExpr(f.type, ctx, fpath + '.type')
|
|
480
|
-
checks(f.check, fpath, ctx)
|
|
481
|
-
const opt = true === f.optional
|
|
482
|
-
if (go) {
|
|
483
|
-
const star = opt && 'opt' !== f.type.k ? '*' : ''
|
|
484
|
-
out.push(ln(1, fname + ' ' + star + type.text +
|
|
485
|
-
' `json:"' + f.name + (opt ? ',omitempty' : '') + '"`'))
|
|
486
|
-
}
|
|
487
|
-
else {
|
|
488
|
-
out.push(ln(1, fname + (opt ? '?' : '') + ': ' + type.text + ';'))
|
|
489
|
-
}
|
|
490
|
-
if (undefined !== f.default) {
|
|
491
|
-
loss(ctx, fpath + '.default', 'default', 'a field default is not expressed by a ' +
|
|
492
|
-
(go ? 'go struct' : 'typescript interface'))
|
|
493
|
-
}
|
|
494
|
-
})
|
|
495
|
-
if (true === decl.open) {
|
|
496
|
-
if (go) {
|
|
497
|
-
loss(ctx, path + '.open', 'open', 'go has no open struct: extra keys are dropped')
|
|
498
|
-
}
|
|
499
|
-
else {
|
|
500
|
-
out.push(ln(1, '[key: string]: unknown;'))
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
checks(decl.check, path, ctx)
|
|
504
|
-
out.push('}')
|
|
505
|
-
return out
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
if ('enum' === k) {
|
|
509
|
-
const name = ident(decl.name, 'enum', ctx, path + '.name', true)
|
|
510
|
-
out.push(...doc(decl.doc, 0, ctx))
|
|
511
|
-
const members: any[] = decl.members
|
|
512
|
-
if (go) {
|
|
513
|
-
const numeric = 0 < members.length &&
|
|
514
|
-
members.every((m: any) => 'number' === typeof m.value)
|
|
515
|
-
out.push('type ' + name + ' ' + prim(ctx, numeric ? 'int' : 'string'))
|
|
516
|
-
out.push(BLANK, 'const (')
|
|
517
|
-
members.forEach((m: any, i: number) => {
|
|
518
|
-
out.push(...doc(m.doc, 1, ctx))
|
|
519
|
-
const mname = name + ident(m.name, 'member', ctx, path + '.members.' + i + '.name', false)
|
|
520
|
-
const value = undefined === m.value ? quote(m.name, ctx.profile) : literal(m.value, ctx)
|
|
521
|
-
out.push(ln(1, mname + ' ' + name + ' = ' + value))
|
|
522
|
-
})
|
|
523
|
-
out.push(')')
|
|
524
|
-
}
|
|
525
|
-
else {
|
|
526
|
-
out.push('export enum ' + name + ' {')
|
|
527
|
-
members.forEach((m: any, i: number) => {
|
|
528
|
-
out.push(...doc(m.doc, 1, ctx))
|
|
529
|
-
const mname = ident(m.name, 'member', ctx, path + '.members.' + i + '.name', false)
|
|
530
|
-
out.push(ln(1, mname + (undefined === m.value ? '' : ' = ' + literal(m.value, ctx)) + ','))
|
|
531
|
-
})
|
|
532
|
-
out.push('}')
|
|
533
|
-
}
|
|
534
|
-
return out
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
if ('alias' === k) {
|
|
538
|
-
const name = ident(decl.name, 'alias', ctx, path + '.name', true)
|
|
539
|
-
out.push(...doc(decl.doc, 0, ctx))
|
|
540
|
-
const type = typeExpr(decl.type, ctx, path + '.type').text
|
|
541
|
-
checks(decl.check, path, ctx)
|
|
542
|
-
out.push(go ? 'type ' + name + ' ' + type : 'export type ' + name + ' = ' + type + ';')
|
|
543
|
-
return out
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
if ('const' === k) {
|
|
547
|
-
const name = ident(decl.name, 'const', ctx, path + '.name', true)
|
|
548
|
-
out.push(...doc(decl.doc, 0, ctx))
|
|
549
|
-
const type = null == decl.type ? undefined : typeExpr(decl.type, ctx, path + '.type').text
|
|
550
|
-
const value = literal(decl.value, ctx)
|
|
551
|
-
out.push(go
|
|
552
|
-
? 'const ' + name + (undefined === type ? '' : ' ' + type) + ' = ' + value
|
|
553
|
-
: 'export const ' + name + (undefined === type ? '' : ': ' + type) + ' = ' + value + ';')
|
|
554
|
-
return out
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
// func
|
|
558
|
-
const name = ident(decl.name, 'func', ctx, path + '.name', true)
|
|
559
|
-
out.push(...doc(decl.doc, 0, ctx))
|
|
560
|
-
const sig = params(decl.params, ctx, path)
|
|
561
|
-
const returns = null == decl.returns ? undefined : typeExpr(decl.returns, ctx, path + '.returns').text
|
|
562
|
-
const abstract = 'abstract' === decl.body.k
|
|
563
|
-
if (go) {
|
|
564
|
-
out.push('func ' + name + '(' + sig + ')' + (undefined === returns ? '' : ' ' + returns) + ' {')
|
|
565
|
-
if (abstract) {
|
|
566
|
-
loss(ctx, path + '.body', 'abstract', 'go has no abstract function: the body panics')
|
|
567
|
-
out.push(ln(1, 'panic(' + quote('abstract', ctx.profile) + ')'))
|
|
568
|
-
}
|
|
569
|
-
else {
|
|
570
|
-
out.push(...nest(decl.body.n))
|
|
571
|
-
}
|
|
572
|
-
out.push('}')
|
|
573
|
-
}
|
|
574
|
-
else {
|
|
575
|
-
const ret = ': ' + (returns ?? 'void')
|
|
576
|
-
if (abstract) {
|
|
577
|
-
out.push('export declare function ' + name + '(' + sig + ')' + ret + ';')
|
|
578
|
-
}
|
|
579
|
-
else {
|
|
580
|
-
out.push('export function ' + name + '(' + sig + ')' + ret + ' {')
|
|
581
|
-
out.push(...nest(decl.body.n))
|
|
582
|
-
out.push('}')
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
return out
|
|
586
|
-
}
|