aontu 0.63.0 → 0.64.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 +6 -6
- 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 +2 -2
- package/dist/cli.js +147 -335
- package/dist/cli.js.map +1 -1
- package/dist/helpdoc.js +1 -1
- package/dist/helpdoc.js.map +1 -1
- package/dist/hints.js +3 -0
- package/dist/hints.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-tool.js +16 -1
- package/dist/mod-tool.js.map +1 -1
- package/dist/profile.d.ts +9 -0
- package/dist/profile.js +28 -0
- package/dist/profile.js.map +1 -0
- 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/tasks.md +9 -7
- package/src/aontu.ts +3 -8
- package/src/aontumodel.ts +6 -12
- package/src/casing.ts +95 -0
- package/src/cli.ts +162 -359
- package/src/helpdoc.ts +1 -1
- package/src/hints.ts +3 -0
- package/src/mcp.ts +0 -39
- package/src/mod-tool.ts +18 -1
- package/src/profile.ts +42 -0
- 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/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/render.ts +0 -580
package/src/mcp.ts
CHANGED
|
@@ -25,7 +25,6 @@ import { jsonSchema } from './jsonschema'
|
|
|
25
25
|
import { relationCheck } from './relation'
|
|
26
26
|
import { reachCheck } from './reach'
|
|
27
27
|
import { view } from './view'
|
|
28
|
-
import { render } from './render'
|
|
29
28
|
import { patch } from './patch'
|
|
30
29
|
|
|
31
30
|
|
|
@@ -600,44 +599,6 @@ const TOOLS: ToolDef[] = [
|
|
|
600
599
|
at: null == a.at ? undefined : str(a.at), path: paths.source,
|
|
601
600
|
}),
|
|
602
601
|
},
|
|
603
|
-
{
|
|
604
|
-
name: 'render',
|
|
605
|
-
description:
|
|
606
|
-
'Render a document that evaluates to an aontu:code instance: ' +
|
|
607
|
-
'evaluate it, vet the value at `at` against the bundled ' +
|
|
608
|
-
'vocabulary, and fold code.units into bytes. Returns verdict ' +
|
|
609
|
-
'(ok | lossy | error), the units -- each a path, a language and ' +
|
|
610
|
-
'its text -- and a `lossy` list of what the renderer could not ' +
|
|
611
|
-
'check (a fragment says nothing about the target\'s syntax; a ' +
|
|
612
|
-
'text escape or a raw block is verbatim). The tool never writes: ' +
|
|
613
|
-
'the caller receives the units as text and places them itself.',
|
|
614
|
-
properties: {
|
|
615
|
-
source: { type: 'string', description: 'The document' },
|
|
616
|
-
at: {
|
|
617
|
-
type: 'string',
|
|
618
|
-
description: 'Render the value at this path of the document ($.a.b)',
|
|
619
|
-
},
|
|
620
|
-
unit: {
|
|
621
|
-
type: 'string',
|
|
622
|
-
description: 'Render only the unit with this path',
|
|
623
|
-
},
|
|
624
|
-
strict: {
|
|
625
|
-
type: 'boolean',
|
|
626
|
-
description: 'Refuse the opaque escapes (a text declaration, a raw block)',
|
|
627
|
-
},
|
|
628
|
-
},
|
|
629
|
-
required: ['source'],
|
|
630
|
-
docs: ['source'],
|
|
631
|
-
refuse: (_a, finding) =>
|
|
632
|
-
({ verdict: 'error', units: [], lossy: [], errors: [finding] }),
|
|
633
|
-
run: (a, _trust, paths) =>
|
|
634
|
-
render(str(a.source), {
|
|
635
|
-
at: null == a.at ? undefined : str(a.at),
|
|
636
|
-
unit: null == a.unit ? undefined : str(a.unit),
|
|
637
|
-
strict: true === a.strict,
|
|
638
|
-
path: paths.source,
|
|
639
|
-
}),
|
|
640
|
-
},
|
|
641
602
|
]
|
|
642
603
|
|
|
643
604
|
|
package/src/mod-tool.ts
CHANGED
|
@@ -502,7 +502,24 @@ export function modManifest(
|
|
|
502
502
|
}
|
|
503
503
|
|
|
504
504
|
const newSrc = readFileSync(main, 'utf8')
|
|
505
|
-
const
|
|
505
|
+
const got = options.eval(newSrc, main)
|
|
506
|
+
|
|
507
|
+
// Nothing to pin: `tidy` refuses the same way.
|
|
508
|
+
if (!got.ok) {
|
|
509
|
+
return {
|
|
510
|
+
verdict: 'error',
|
|
511
|
+
mod,
|
|
512
|
+
version: self.version,
|
|
513
|
+
canon: '',
|
|
514
|
+
config: MODULE_CONFIG_MEDIA_TYPE,
|
|
515
|
+
files: [],
|
|
516
|
+
annotations: {},
|
|
517
|
+
missing: [self.main],
|
|
518
|
+
findings: [],
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
const canon = got.hash
|
|
506
523
|
|
|
507
524
|
const report: ModManifestReport = {
|
|
508
525
|
verdict: 'ok',
|
package/src/profile.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/* Copyright (c) 2026 Richard Rodger, MIT License */
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
import { Aontu } from './aontu'
|
|
5
|
+
import { vet, failureFinding } from './vet'
|
|
6
|
+
import { hcanon } from './hcanon'
|
|
7
|
+
import { includeOpts } from './utility'
|
|
8
|
+
|
|
9
|
+
import type { IncludeOptions } from './utility'
|
|
10
|
+
import type { VetFinding } from './vet'
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
export type ProfileOptions = IncludeOptions & {
|
|
14
|
+
// Where the document CAME FROM, so a relative `@"file"` load inside
|
|
15
|
+
// it resolves from its own directory.
|
|
16
|
+
path?: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
const PROFILE_VOCABULARY = '@"aontu:profile"'
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
// A language declared as data, vetted, or the findings that refuse it.
|
|
24
|
+
export function loadProfile(src: string, options?: ProfileOptions):
|
|
25
|
+
{ profile?: any, errors?: VetFinding[] } {
|
|
26
|
+
const opts = options ?? {}
|
|
27
|
+
const aontu = new Aontu(includeOpts(opts))
|
|
28
|
+
const actx = aontu.ctx({ collect: true })
|
|
29
|
+
const root: any = aontu.unify(src, { path: opts.path, collect: true }, actx)
|
|
30
|
+
if (0 < actx.err.length || true === root?.isNil) {
|
|
31
|
+
return { errors: [failureFinding(actx, opts.path, root)] }
|
|
32
|
+
}
|
|
33
|
+
const report = vet(PROFILE_VOCABULARY, hcanon(root))
|
|
34
|
+
if ('valid' !== report.verdict) {
|
|
35
|
+
return { errors: report.findings }
|
|
36
|
+
}
|
|
37
|
+
// The meet: the vocabulary requires `lang`, so a value the vet
|
|
38
|
+
// admitted has a `Lang`.
|
|
39
|
+
const instance = new Aontu().generate(
|
|
40
|
+
PROFILE_VOCABULARY + '\naontu: Lang: ' + hcanon(root.peg.aontu.peg.Lang))
|
|
41
|
+
return { profile: instance.aontu.Lang }
|
|
42
|
+
}
|
package/src/trace.ts
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/* Copyright (c) 2026 Richard Rodger, MIT License */
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
import type { Val } from './type'
|
|
5
|
+
|
|
6
|
+
import { Aontu } from './aontu'
|
|
7
|
+
import { cmpCodePoint } from './keyorder'
|
|
8
|
+
import { includeOpts } from './utility'
|
|
9
|
+
import { anchorAt, failureFinding } from './vet'
|
|
10
|
+
import { makeNilErr } from './err'
|
|
11
|
+
|
|
12
|
+
import type { IncludeOptions } from './utility'
|
|
13
|
+
import type { VetFinding } from './vet'
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
export type TraceOptions = IncludeOptions & {
|
|
17
|
+
path?: string
|
|
18
|
+
at?: string
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
export type TraceReport = {
|
|
23
|
+
verdict: 'ok' | 'error'
|
|
24
|
+
trace: TraceEntry[]
|
|
25
|
+
errors?: VetFinding[]
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
// WHAT A TRACE ENTRY CARRIES. The file it reached, the rule set that
|
|
30
|
+
// wrote it, and the model node the dispatch matched.
|
|
31
|
+
export type TraceEntry = {
|
|
32
|
+
at: string
|
|
33
|
+
file: string
|
|
34
|
+
node: string
|
|
35
|
+
rule: string
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
function walkVals(root: any, fn: (v: any, path: string[]) => void): void {
|
|
40
|
+
const walk = (v: any, path: string[]): void => {
|
|
41
|
+
fn(v, path)
|
|
42
|
+
if (true === v.isList && null != v.peg) {
|
|
43
|
+
for (let i = 0; i < v.peg.length; i++) {
|
|
44
|
+
walk(v.peg[i], [...path, String(i)])
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
else if (true === v.isMap && null != v.peg) {
|
|
48
|
+
for (const k of Object.keys(v.peg).sort(cmpCodePoint)) {
|
|
49
|
+
if (!v.aliasKeys?.includes(k)) {
|
|
50
|
+
walk(v.peg[k], [...path, k])
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
walk(root, [])
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
function addr(path: string[]): string {
|
|
60
|
+
return '$' + path.map((seg) => '.' + seg).join('')
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
function scalar(v: any): string | undefined {
|
|
65
|
+
return (true === v?.isScalar && 'string' === typeof v.peg) ? v.peg : undefined
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
function fileName(v: any): string | undefined {
|
|
70
|
+
if (true !== v?.isMap) {
|
|
71
|
+
return undefined
|
|
72
|
+
}
|
|
73
|
+
return 'File' === scalar(v.peg?.cmp) ? scalar(v.peg?.props?.peg?.name) :
|
|
74
|
+
undefined
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
// THE INNERMOST FILE WINS: the longest matching prefix, not the first.
|
|
79
|
+
function enclosing(files: { at: string, name: string }[],
|
|
80
|
+
path: string): string | undefined {
|
|
81
|
+
let best: string | undefined
|
|
82
|
+
let long = -1
|
|
83
|
+
for (const f of files) {
|
|
84
|
+
if ((path === f.at || path.startsWith(f.at + '.')) && f.at.length > long) {
|
|
85
|
+
best = f.name
|
|
86
|
+
long = f.at.length
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return best
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
// Every piece a dispatch stamped, attributed to the file it reached.
|
|
94
|
+
// One pass: `walkVals` is depth-first in document order, so a file is
|
|
95
|
+
// always seen before the marks beneath it.
|
|
96
|
+
function traceTree(root: Val): TraceEntry[] {
|
|
97
|
+
const files: { at: string, name: string }[] = []
|
|
98
|
+
const marks: { at: string, mark: any }[] = []
|
|
99
|
+
walkVals(root, (v: any, path: string[]) => {
|
|
100
|
+
const name = fileName(v)
|
|
101
|
+
if (undefined !== name) {
|
|
102
|
+
files.push({ at: addr(path), name })
|
|
103
|
+
}
|
|
104
|
+
if (null != v.emitted) {
|
|
105
|
+
marks.push({ at: addr(path), mark: v.emitted })
|
|
106
|
+
}
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
const out: TraceEntry[] = []
|
|
110
|
+
for (const m of marks) {
|
|
111
|
+
const file = enclosing(files, m.at)
|
|
112
|
+
if (undefined === file) {
|
|
113
|
+
continue
|
|
114
|
+
}
|
|
115
|
+
out.push({ at: m.at, file, node: m.mark.node, rule: m.mark.rule })
|
|
116
|
+
}
|
|
117
|
+
return out
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
// THE MARKS ARE OPT-IN. `emit` stamps a piece only when the context
|
|
122
|
+
// carries a `reads` set, so the verb asks for one the way `render
|
|
123
|
+
// --trace` did.
|
|
124
|
+
function traceRun(src: string, options?: TraceOptions): TraceReport {
|
|
125
|
+
const opts = options ?? {}
|
|
126
|
+
const aontu = new Aontu(includeOpts(opts))
|
|
127
|
+
const actx = aontu.ctx({ collect: true, reads: new Set<string>() })
|
|
128
|
+
const root: any = aontu.unify(
|
|
129
|
+
src, { path: opts.path, collect: true }, actx)
|
|
130
|
+
if (0 < actx.err.length || true === root?.isNil) {
|
|
131
|
+
return {
|
|
132
|
+
verdict: 'error', trace: [],
|
|
133
|
+
errors: [failureFinding(actx, opts.path, root)],
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
let node: any = root
|
|
138
|
+
const at = opts.at ?? '$.out'
|
|
139
|
+
const found: any = anchorAt(root, at)
|
|
140
|
+
if (null == found) {
|
|
141
|
+
const nil: any = makeNilErr(actx, 'no_path', root, undefined, 'at')
|
|
142
|
+
actx.err.push(nil)
|
|
143
|
+
return {
|
|
144
|
+
verdict: 'error', trace: [],
|
|
145
|
+
errors: [failureFinding(actx, opts.path, root)],
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
node = found
|
|
149
|
+
|
|
150
|
+
return { verdict: 'ok', trace: traceTree(node) }
|
|
151
|
+
} /* node:coverage ignore next 6 */
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
export {
|
|
155
|
+
traceTree,
|
|
156
|
+
traceRun,
|
|
157
|
+
}
|
package/src/unify.ts
CHANGED
|
@@ -67,6 +67,7 @@ const unite = (ctx: AontuContext, a: any, b: any, whence: string) => {
|
|
|
67
67
|
&& !a.isConjunct && !a.isDisjunct
|
|
68
68
|
&& !a.isRef && !a.isPref && !a.isFunc && !a.isExpect
|
|
69
69
|
&& !a.isTop && !b.isTop
|
|
70
|
+
&& !a.isRefer
|
|
70
71
|
&& !a.isRel && !a.isGraphAtom && !a.isRecurse) {
|
|
71
72
|
// The deprecation record survives the fast path too (G3):
|
|
72
73
|
// `deprecate(5) & 5` short-circuits here.
|
|
@@ -242,12 +243,6 @@ function riders(a: any, b: any, to: any): void {
|
|
|
242
243
|
to.origin = org
|
|
243
244
|
}
|
|
244
245
|
}
|
|
245
|
-
if (null == to.emitted) {
|
|
246
|
-
const emt = av.emitted ?? bv.emitted
|
|
247
|
-
if (null != emt) {
|
|
248
|
-
to.emitted = emt
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
246
|
}
|
|
252
247
|
|
|
253
248
|
|
package/src/val/CmpFuncVal.ts
CHANGED
|
@@ -25,60 +25,78 @@ type CmpDef = {
|
|
|
25
25
|
children: string[]
|
|
26
26
|
text?: string
|
|
27
27
|
// Whether that prop is required. `project`'s folder is the one that
|
|
28
|
-
// is not: Jostraca defaults it to
|
|
29
|
-
// stricter than the component it drives.
|
|
28
|
+
// is not: Jostraca defaults it to `.`.
|
|
30
29
|
req: boolean
|
|
30
|
+
// Whether that prop is a SPAN of target text rather than a name. A
|
|
31
|
+
// blank line is a span; a folder called "" is a mistake.
|
|
32
|
+
span?: boolean
|
|
31
33
|
// A prop that must be present and must be a list.
|
|
32
34
|
bag?: string
|
|
35
|
+
// Hand-kept: no jostraca dependency to derive it from.
|
|
36
|
+
props: string[]
|
|
33
37
|
}
|
|
34
38
|
|
|
39
|
+
|
|
40
|
+
const SPAN_PROPS =
|
|
41
|
+
['arg', 'src', 'name', 'indent', 'extra', 'replace', 'raw']
|
|
42
|
+
|
|
35
43
|
const CMP_DEF: Record<string, CmpDef> = {
|
|
36
44
|
// The output root. Its `folder` is refused an absolute path or a
|
|
37
45
|
// `..` segment on the Jostraca side, where the tree is data.
|
|
38
46
|
project: {
|
|
39
47
|
cmp: 'Project', text: 'folder', req: false,
|
|
40
48
|
children: ['project', 'folder', 'file', 'copyfiles'],
|
|
49
|
+
props: ['name', 'folder'],
|
|
41
50
|
},
|
|
42
51
|
folder: {
|
|
43
52
|
cmp: 'Folder', text: 'name', req: true,
|
|
44
53
|
children: ['folder', 'file', 'copyfiles'],
|
|
54
|
+
props: ['name'],
|
|
45
55
|
},
|
|
46
56
|
file: {
|
|
47
57
|
cmp: 'File', text: 'name', req: true,
|
|
48
58
|
children: ['content', 'line', 'fragment', 'inject', 'listitems', 'copyfiles'],
|
|
59
|
+
props: ['name', 'exclude', 'mode'],
|
|
49
60
|
},
|
|
50
61
|
content: {
|
|
51
|
-
cmp: 'Content', text: 'src', req: true,
|
|
62
|
+
cmp: 'Content', text: 'src', req: true, span: true,
|
|
52
63
|
children: [],
|
|
64
|
+
props: SPAN_PROPS,
|
|
53
65
|
},
|
|
54
66
|
// A span with a newline added, which is the whole difference.
|
|
55
67
|
line: {
|
|
56
|
-
cmp: 'Line', text: 'src', req: true,
|
|
68
|
+
cmp: 'Line', text: 'src', req: true, span: true,
|
|
57
69
|
children: [],
|
|
70
|
+
props: SPAN_PROPS,
|
|
58
71
|
},
|
|
59
72
|
// A file read from disk with its `<[SLOT]>` markers filled.
|
|
60
73
|
fragment: {
|
|
61
74
|
cmp: 'Fragment', text: 'from', req: true,
|
|
62
75
|
children: ['slot', 'content', 'line', 'listitems'],
|
|
76
|
+
props: ['from', 'indent', 'replace', 'eject'],
|
|
63
77
|
},
|
|
64
78
|
slot: {
|
|
65
79
|
cmp: 'Slot', text: 'name', req: true,
|
|
66
80
|
children: ['content', 'line', 'fragment', 'listitems'],
|
|
81
|
+
props: ['name'],
|
|
67
82
|
},
|
|
68
83
|
// A body written between markers in a file that already exists.
|
|
69
84
|
inject: {
|
|
70
85
|
cmp: 'Inject', text: 'name', req: true,
|
|
71
86
|
children: ['content', 'line', 'listitems'],
|
|
87
|
+
props: ['name', 'markers', 'exclude'],
|
|
72
88
|
},
|
|
73
89
|
// `Copy` under a name aontu has free: `copy` is taken by the builtin
|
|
74
90
|
// that copies a VALUE, and a file copy is a different verb.
|
|
75
91
|
copyfiles: {
|
|
76
92
|
cmp: 'CopyFiles', text: 'from', req: true,
|
|
77
93
|
children: [],
|
|
94
|
+
props: ['from', 'to', 'replace', 'exclude'],
|
|
78
95
|
},
|
|
79
96
|
listitems: {
|
|
80
97
|
cmp: 'ListItems', req: true, bag: 'item',
|
|
81
98
|
children: ['content', 'line', 'fragment'],
|
|
99
|
+
props: ['item', 'line', 'indent'],
|
|
82
100
|
},
|
|
83
101
|
}
|
|
84
102
|
|
|
@@ -100,6 +118,29 @@ function nodeCmp(v: any): string | undefined {
|
|
|
100
118
|
}
|
|
101
119
|
|
|
102
120
|
|
|
121
|
+
function cmpNode(cmp: string, props: Val, children: Val,
|
|
122
|
+
ctx: AontuContext): MapVal {
|
|
123
|
+
const node = new MapVal({ peg: { cmp: new StringVal({ peg: cmp }, ctx), props, children } }, ctx)
|
|
124
|
+
node.closed = true
|
|
125
|
+
return node
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
// A bare string child is a LINE: `Content` writes no newline, and the
|
|
130
|
+
// emit mark rides across or `aontu trace` loses the line's rule.
|
|
131
|
+
function lineNode(from: any, src: string, ctx: AontuContext): MapVal {
|
|
132
|
+
const node = cmpNode(
|
|
133
|
+
CMP_DEF.line.cmp,
|
|
134
|
+
new MapVal({ peg: { src: new StringVal({ peg: src }, ctx) } }, ctx),
|
|
135
|
+
new ListVal({ peg: [] }, ctx),
|
|
136
|
+
ctx)
|
|
137
|
+
if (null != from?.emitted) {
|
|
138
|
+
; (node as any).emitted = from.emitted
|
|
139
|
+
}
|
|
140
|
+
return node
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
|
|
103
144
|
function propText(props: any, key: string): string | undefined {
|
|
104
145
|
const v: any = props?.peg?.[key]
|
|
105
146
|
return (true === v?.isScalar && 'string' === typeof v.peg) ? v.peg : undefined
|
|
@@ -109,9 +150,7 @@ function propText(props: any, key: string): string | undefined {
|
|
|
109
150
|
class CmpFuncVal extends FuncBaseVal {
|
|
110
151
|
isCmpFunc = true
|
|
111
152
|
|
|
112
|
-
// The
|
|
113
|
-
// also the `cmp` key of the node and the Jostraca component the
|
|
114
|
-
// bridge looks up.
|
|
153
|
+
// The function's own name, which is also the node's `cmp` key.
|
|
115
154
|
cmp: string
|
|
116
155
|
|
|
117
156
|
constructor(
|
|
@@ -151,13 +190,21 @@ class CmpFuncVal extends FuncBaseVal {
|
|
|
151
190
|
return makeNilErr(ctx, 'invalid-arg', this, spec, 'spec')
|
|
152
191
|
}
|
|
153
192
|
|
|
193
|
+
// An unknown prop is a silently dropped `indent` or `mode`.
|
|
194
|
+
for (const key of Object.keys((props as any).peg)) {
|
|
195
|
+
if (!def.props.includes(key)) {
|
|
196
|
+
return makeNilErr(ctx, 'invalid-arg', this, props, key)
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
154
200
|
if (undefined !== def.text) {
|
|
155
201
|
const text = propText(props, def.text)
|
|
156
|
-
if (def.req &&
|
|
202
|
+
if (def.req &&
|
|
203
|
+
(undefined === text || ('' === text && true !== def.span))) {
|
|
157
204
|
return makeNilErr(ctx, 'invalid-arg', this, props, def.text)
|
|
158
205
|
}
|
|
159
206
|
if (!def.req && undefined !== (props as any).peg?.[def.text] &&
|
|
160
|
-
undefined === text) {
|
|
207
|
+
(undefined === text || ('' === text && true !== def.span))) {
|
|
161
208
|
return makeNilErr(ctx, 'invalid-arg', this, props, def.text)
|
|
162
209
|
}
|
|
163
210
|
}
|
|
@@ -188,6 +235,15 @@ class CmpFuncVal extends FuncBaseVal {
|
|
|
188
235
|
}
|
|
189
236
|
continue
|
|
190
237
|
}
|
|
238
|
+
const text = (true === (kid as any)?.isScalar &&
|
|
239
|
+
'string' === typeof (kid as any).peg) ? (kid as any).peg : undefined
|
|
240
|
+
if (undefined !== text) {
|
|
241
|
+
if (!def.children.includes('line')) {
|
|
242
|
+
return kid
|
|
243
|
+
}
|
|
244
|
+
flat.push(lineNode(kid, text, ctx))
|
|
245
|
+
continue
|
|
246
|
+
}
|
|
191
247
|
const kcmp = nodeCmp(kid)
|
|
192
248
|
if (undefined === kcmp || !def.children.includes(kcmp)) {
|
|
193
249
|
return kid
|
|
@@ -206,17 +262,7 @@ class CmpFuncVal extends FuncBaseVal {
|
|
|
206
262
|
return makeNilErr(ctx, 'invalid-arg', this, kids, 'children')
|
|
207
263
|
}
|
|
208
264
|
|
|
209
|
-
|
|
210
|
-
peg: {
|
|
211
|
-
cmp: new StringVal({ peg: def.cmp }, ctx),
|
|
212
|
-
props,
|
|
213
|
-
children,
|
|
214
|
-
}
|
|
215
|
-
}, ctx)
|
|
216
|
-
|
|
217
|
-
node.closed = true
|
|
218
|
-
|
|
219
|
-
return this.place(node)
|
|
265
|
+
return this.place(cmpNode(def.cmp, props, children, ctx))
|
|
220
266
|
}
|
|
221
267
|
|
|
222
268
|
} /* node:coverage ignore next 3 */
|
package/src/val/NomFuncVal.ts
CHANGED
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
caseName,
|
|
18
18
|
lowerASCII,
|
|
19
19
|
capitalise,
|
|
20
|
-
} from '../
|
|
20
|
+
} from '../casing'
|
|
21
21
|
|
|
22
22
|
import { MapVal } from './MapVal'
|
|
23
23
|
import { StringVal } from './StringVal'
|
|
@@ -36,10 +36,8 @@ const NOM_STYLES = [
|
|
|
36
36
|
'upper', // USER_ID
|
|
37
37
|
]
|
|
38
38
|
|
|
39
|
-
// nom's style name -> the
|
|
40
|
-
// `text` are nom's spellings
|
|
41
|
-
// SCREAMING_SNAKE and that name is pinned cross-port, so the mapping
|
|
42
|
-
// lives here rather than in the shared vocabulary.
|
|
39
|
+
// nom's style name -> the case style `caseName` serves. `upper` and
|
|
40
|
+
// `text` are nom's spellings.
|
|
43
41
|
const CASENAME_STYLES: Record<string, string> = {
|
|
44
42
|
camel: 'camel',
|
|
45
43
|
kebab: 'kebab',
|
package/src/val/ReferFuncVal.ts
CHANGED
|
@@ -181,6 +181,10 @@ class ReferVal extends FeatureVal {
|
|
|
181
181
|
return makeNilErr(ctx, this.addrcode, this, peer, 'refer')
|
|
182
182
|
}
|
|
183
183
|
|
|
184
|
+
if (true === p.isFunc) {
|
|
185
|
+
return peer.unify(this, ctx)
|
|
186
|
+
}
|
|
187
|
+
|
|
184
188
|
return this.with(ctx, {
|
|
185
189
|
held: null == this.held ? peer : unite(ctx, this.held, peer, 'refer-held'),
|
|
186
190
|
}, this)
|
package/dist/lower.d.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import type { RenderLoss } from './render';
|
|
2
|
-
export type LowerCtx = {
|
|
3
|
-
profile: any;
|
|
4
|
-
family: string;
|
|
5
|
-
unit: string;
|
|
6
|
-
lossy: RenderLoss[];
|
|
7
|
-
};
|
|
8
|
-
export declare function splitWords(name: string): string[];
|
|
9
|
-
export declare function lowerASCII(s: string): string;
|
|
10
|
-
export declare function upperASCII(s: string): string;
|
|
11
|
-
export declare function capitalise(word: string, acronyms: string[]): string;
|
|
12
|
-
export declare function caseName(name: string, style: string, acronyms: string[]): string;
|
|
13
|
-
export declare function ident(name: string, role: string, ctx: LowerCtx, path: string, bare: boolean): string;
|
|
14
|
-
export declare function quote(s: string, profile: any): string;
|
|
15
|
-
export declare function literal(v: any, ctx: LowerCtx): string;
|
|
16
|
-
type Expr = {
|
|
17
|
-
text: string;
|
|
18
|
-
prec: number;
|
|
19
|
-
};
|
|
20
|
-
export declare function typeExpr(t: any, ctx: LowerCtx, path: string): Expr;
|
|
21
|
-
export declare function lowerHeader(unit: any, source: any, ctx: LowerCtx): any[];
|
|
22
|
-
export declare function lowerDecl(decl: any, path: string, ctx: LowerCtx): any[];
|
|
23
|
-
export {};
|