aontu 0.62.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 +7 -7
- package/dist/agentsmd.js +0 -27
- package/dist/agentsmd.js.map +1 -1
- package/dist/alias.js.map +1 -1
- package/dist/allow.js +0 -92
- package/dist/allow.js.map +1 -1
- package/dist/aontu.d.ts +3 -3
- package/dist/aontu.js +4 -84
- package/dist/aontu.js.map +1 -1
- package/dist/aontumodel.d.ts +4 -0
- package/dist/aontumodel.js +27 -0
- package/dist/aontumodel.js.map +1 -0
- 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 +228 -935
- package/dist/cli.js.map +1 -1
- package/dist/ctx.js +0 -48
- package/dist/ctx.js.map +1 -1
- package/dist/diff.js +0 -32
- package/dist/diff.js.map +1 -1
- package/dist/err.js +0 -40
- package/dist/err.js.map +1 -1
- package/dist/escape.js +0 -45
- package/dist/escape.js.map +1 -1
- package/dist/exactjson.d.ts +0 -35
- package/dist/exactjson.js +0 -131
- package/dist/exactjson.js.map +1 -1
- package/dist/format.js +13 -203
- package/dist/format.js.map +1 -1
- package/dist/grammar.d.ts +9 -0
- package/dist/grammar.js +54 -0
- package/dist/grammar.js.map +1 -0
- package/dist/graph.js +0 -26
- package/dist/graph.js.map +1 -1
- package/dist/hcanon.js +0 -82
- package/dist/hcanon.js.map +1 -1
- package/dist/helpdoc.js +2 -2
- package/dist/helpdoc.js.map +1 -1
- package/dist/hints.d.ts +0 -6
- package/dist/hints.js +57 -55
- package/dist/hints.js.map +1 -1
- package/dist/jsonschema.js +0 -114
- package/dist/jsonschema.js.map +1 -1
- package/dist/keyorder.d.ts +0 -7
- package/dist/keyorder.js +0 -41
- package/dist/keyorder.js.map +1 -1
- package/dist/lang.js +17 -915
- package/dist/lang.js.map +1 -1
- package/dist/lsp-server.js +0 -16
- package/dist/lsp-server.js.map +1 -1
- package/dist/lsp.d.ts +1 -1
- package/dist/lsp.js +12 -159
- package/dist/lsp.js.map +1 -1
- package/dist/mcp-server.js +0 -26
- package/dist/mcp-server.js.map +1 -1
- package/dist/mcp.js +0 -149
- package/dist/mcp.js.map +1 -1
- package/dist/mod-tool.js +16 -131
- package/dist/mod-tool.js.map +1 -1
- package/dist/mod.js +0 -162
- package/dist/mod.js.map +1 -1
- package/dist/patch.js +0 -217
- package/dist/patch.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/provenance.js +0 -140
- package/dist/provenance.js.map +1 -1
- package/dist/query.js +0 -75
- package/dist/query.js.map +1 -1
- package/dist/reach.js +0 -43
- package/dist/reach.js.map +1 -1
- package/dist/relation.js +0 -61
- package/dist/relation.js.map +1 -1
- package/dist/report-sarif.d.ts +0 -11
- package/dist/report-sarif.js +0 -28
- package/dist/report-sarif.js.map +1 -1
- package/dist/sig.js +0 -35
- package/dist/sig.js.map +1 -1
- package/dist/sigdecl.js +1 -1
- package/dist/sigdecl.js.map +1 -1
- package/dist/siggate.js +0 -4
- package/dist/siggate.js.map +1 -1
- package/dist/site.js +3 -29
- package/dist/site.js.map +1 -1
- package/dist/subsume.d.ts +0 -10
- package/dist/subsume.js +0 -137
- package/dist/subsume.js.map +1 -1
- package/dist/template.d.ts +2 -1
- package/dist/template.js +58 -138
- package/dist/template.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/trim.js +0 -41
- package/dist/trim.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/type.js.map +1 -1
- package/dist/unify.js +13 -248
- package/dist/unify.js.map +1 -1
- package/dist/utility.js +0 -22
- package/dist/utility.js.map +1 -1
- package/dist/val/AbnfFuncVal.d.ts +18 -0
- package/dist/val/AbnfFuncVal.js +132 -0
- package/dist/val/AbnfFuncVal.js.map +1 -0
- package/dist/val/AbsentVal.d.ts +11 -0
- package/dist/val/AbsentVal.js +30 -0
- package/dist/val/AbsentVal.js.map +1 -0
- package/dist/val/AggFuncVal.d.ts +10 -1
- package/dist/val/AggFuncVal.js +104 -116
- package/dist/val/AggFuncVal.js.map +1 -1
- package/dist/val/ArithFuncVal.js +0 -12
- package/dist/val/ArithFuncVal.js.map +1 -1
- package/dist/val/BagVal.js +1 -78
- package/dist/val/BagVal.js.map +1 -1
- package/dist/val/BigDecimalVal.js +0 -16
- package/dist/val/BigDecimalVal.js.map +1 -1
- package/dist/val/BigIntegerVal.js +0 -16
- package/dist/val/BigIntegerVal.js.map +1 -1
- package/dist/val/CloseFuncVal.js +0 -9
- package/dist/val/CloseFuncVal.js.map +1 -1
- package/dist/val/CmpFuncVal.d.ts +2 -0
- package/dist/val/CmpFuncVal.js +49 -72
- package/dist/val/CmpFuncVal.js.map +1 -1
- package/dist/val/ConjunctVal.js +0 -29
- package/dist/val/ConjunctVal.js.map +1 -1
- package/dist/val/ConstraintVal.js +0 -500
- package/dist/val/ConstraintVal.js.map +1 -1
- package/dist/val/ContainerKindVal.js +0 -2
- package/dist/val/ContainerKindVal.js.map +1 -1
- package/dist/val/CopyFuncVal.js +0 -3
- package/dist/val/CopyFuncVal.js.map +1 -1
- package/dist/val/Decimal.js +0 -179
- package/dist/val/Decimal.js.map +1 -1
- package/dist/val/DeprecateFuncVal.js.map +1 -1
- package/dist/val/DisjunctVal.js +0 -152
- package/dist/val/DisjunctVal.js.map +1 -1
- package/dist/val/EachFuncVal.js +0 -3
- package/dist/val/EachFuncVal.js.map +1 -1
- package/dist/val/EmitFuncVal.d.ts +1 -1
- package/dist/val/EmitFuncVal.js +6 -119
- package/dist/val/EmitFuncVal.js.map +1 -1
- package/dist/val/ExpectVal.js +0 -62
- package/dist/val/ExpectVal.js.map +1 -1
- package/dist/val/FilterFuncVal.js +0 -25
- package/dist/val/FilterFuncVal.js.map +1 -1
- package/dist/val/FuncBaseVal.d.ts +1 -0
- package/dist/val/FuncBaseVal.js +7 -127
- package/dist/val/FuncBaseVal.js.map +1 -1
- package/dist/val/GraphAtomVal.js +0 -15
- package/dist/val/GraphAtomVal.js.map +1 -1
- package/dist/val/HideFuncVal.js +0 -13
- package/dist/val/HideFuncVal.js.map +1 -1
- package/dist/val/IntegerVal.js +0 -61
- package/dist/val/IntegerVal.js.map +1 -1
- package/dist/val/JunctionVal.js +0 -20
- package/dist/val/JunctionVal.js.map +1 -1
- package/dist/val/KeyFuncVal.js +0 -46
- package/dist/val/KeyFuncVal.js.map +1 -1
- package/dist/val/ListVal.js +0 -57
- package/dist/val/ListVal.js.map +1 -1
- package/dist/val/LowerFuncVal.js +0 -11
- package/dist/val/LowerFuncVal.js.map +1 -1
- package/dist/val/MapVal.js +0 -151
- package/dist/val/MapVal.js.map +1 -1
- package/dist/val/MatchFuncVal.js +0 -27
- package/dist/val/MatchFuncVal.js.map +1 -1
- package/dist/val/{FormFuncVal.d.ts → MaybeFuncVal.d.ts} +5 -5
- package/dist/val/MaybeFuncVal.js +50 -0
- package/dist/val/MaybeFuncVal.js.map +1 -0
- package/dist/val/MoveFuncVal.js +0 -18
- package/dist/val/MoveFuncVal.js.map +1 -1
- package/dist/val/NilVal.js +0 -60
- package/dist/val/NilVal.js.map +1 -1
- package/dist/val/NomFuncVal.js +18 -54
- package/dist/val/NomFuncVal.js.map +1 -1
- package/dist/val/NumberVal.js +0 -15
- package/dist/val/NumberVal.js.map +1 -1
- package/dist/val/OpBaseVal.d.ts +1 -0
- package/dist/val/OpBaseVal.js +3 -15
- package/dist/val/OpBaseVal.js.map +1 -1
- package/dist/val/PackFuncVal.js +0 -34
- package/dist/val/PackFuncVal.js.map +1 -1
- package/dist/val/PathFuncVal.js +0 -6
- package/dist/val/PathFuncVal.js.map +1 -1
- package/dist/val/PathVal.js +0 -41
- package/dist/val/PathVal.js.map +1 -1
- package/dist/val/PlaceVal.js +0 -25
- package/dist/val/PlaceVal.js.map +1 -1
- package/dist/val/PlusOpVal.d.ts +1 -7
- package/dist/val/PlusOpVal.js +13 -74
- package/dist/val/PlusOpVal.js.map +1 -1
- package/dist/val/PrefFuncVal.js +0 -1
- package/dist/val/PrefFuncVal.js.map +1 -1
- package/dist/val/PrefVal.js +0 -167
- package/dist/val/PrefVal.js.map +1 -1
- package/dist/val/RecurseVal.js +0 -55
- package/dist/val/RecurseVal.js.map +1 -1
- package/dist/val/RefVal.js +0 -282
- package/dist/val/RefVal.js.map +1 -1
- package/dist/val/ReferFuncVal.js +3 -232
- package/dist/val/ReferFuncVal.js.map +1 -1
- package/dist/val/ScalarKindVal.js +0 -49
- package/dist/val/ScalarKindVal.js.map +1 -1
- package/dist/val/ScalarVal.js +0 -11
- package/dist/val/ScalarVal.js.map +1 -1
- package/dist/val/StrFuncVal.js +0 -18
- package/dist/val/StrFuncVal.js.map +1 -1
- package/dist/val/SuperFuncVal.js +0 -32
- package/dist/val/SuperFuncVal.js.map +1 -1
- package/dist/val/TopVal.js +0 -1
- package/dist/val/TopVal.js.map +1 -1
- package/dist/val/TranslateFuncVal.js +1 -3
- package/dist/val/TranslateFuncVal.js.map +1 -1
- package/dist/val/UpperFuncVal.js +0 -11
- package/dist/val/UpperFuncVal.js.map +1 -1
- package/dist/val/Val.d.ts +1 -0
- package/dist/val/Val.js +2 -133
- package/dist/val/Val.js.map +1 -1
- package/dist/val/VarVal.js +0 -12
- package/dist/val/VarVal.js.map +1 -1
- package/dist/val/arith.js +0 -37
- package/dist/val/arith.js.map +1 -1
- package/dist/val/caserange.js +0 -61
- package/dist/val/caserange.js.map +1 -1
- package/dist/val/members.js +0 -6
- package/dist/val/members.js.map +1 -1
- package/dist/val/numcmp.js +0 -11
- package/dist/val/numcmp.js.map +1 -1
- package/dist/val/numkind.js +0 -145
- package/dist/val/numkind.js.map +1 -1
- package/dist/val/valutil.js +0 -16
- package/dist/val/valutil.js.map +1 -1
- package/dist/vet.js +0 -461
- package/dist/vet.js.map +1 -1
- package/dist/view.js +0 -414
- package/dist/view.js.map +1 -1
- package/dist/walk.js +0 -41
- package/dist/walk.js.map +1 -1
- package/grammar/aontu.abnf +8 -6
- package/grammar/aontu.gbnf +4 -4
- package/grammar/aontu.lark +4 -4
- package/grammar/aontu.tmLanguage.json +1 -1
- package/package.json +4 -2
- package/skill/tasks.md +9 -7
- package/src/agentsmd.ts +0 -35
- package/src/alias.ts +0 -39
- package/src/allow.ts +1 -96
- package/src/aontu.ts +4 -116
- package/src/aontumodel.ts +26 -0
- package/src/casing.ts +95 -0
- package/src/cli.ts +258 -1024
- package/src/ctx.ts +0 -103
- package/src/diff.ts +0 -40
- package/src/err.ts +0 -40
- package/src/escape.ts +0 -46
- package/src/exactjson.ts +0 -131
- package/src/format.ts +14 -257
- package/src/grammar.ts +72 -0
- package/src/graph.ts +0 -61
- package/src/hcanon.ts +0 -82
- package/src/helpdoc.ts +2 -2
- package/src/hints.ts +69 -57
- package/src/jsonschema.ts +0 -123
- package/src/keyorder.ts +0 -42
- package/src/lang.ts +19 -931
- package/src/lsp-server.ts +0 -16
- package/src/lsp.ts +12 -180
- package/src/mcp-server.ts +0 -31
- package/src/mcp.ts +0 -169
- package/src/mod-tool.ts +18 -159
- package/src/mod.ts +0 -178
- package/src/patch.ts +0 -232
- package/src/profile.ts +42 -0
- package/src/provenance.ts +0 -183
- package/src/query.ts +0 -84
- package/src/reach.ts +0 -53
- package/src/relation.ts +0 -84
- package/src/report-sarif.ts +0 -48
- package/src/sig.ts +0 -35
- package/src/sigdecl.ts +1 -1
- package/src/siggate.ts +0 -30
- package/src/site.ts +3 -29
- package/src/subsume.ts +1 -161
- package/src/template.ts +69 -140
- package/src/trace.ts +157 -0
- package/src/trim.ts +0 -53
- package/src/type.ts +2 -45
- package/src/unify.ts +14 -257
- package/src/utility.ts +0 -31
- package/src/val/AbnfFuncVal.ts +181 -0
- package/src/val/AbsentVal.ts +54 -0
- package/src/val/AggFuncVal.ts +152 -188
- package/src/val/ArithFuncVal.ts +0 -20
- package/src/val/BagVal.ts +1 -78
- package/src/val/BigDecimalVal.ts +0 -16
- package/src/val/BigIntegerVal.ts +0 -16
- package/src/val/CloseFuncVal.ts +0 -9
- package/src/val/CmpFuncVal.ts +68 -184
- package/src/val/ConjunctVal.ts +0 -33
- package/src/val/ConstraintVal.ts +2 -537
- package/src/val/ContainerKindVal.ts +0 -18
- package/src/val/CopyFuncVal.ts +0 -5
- package/src/val/Decimal.ts +1 -185
- package/src/val/DeprecateFuncVal.ts +0 -10
- package/src/val/DisjunctVal.ts +0 -157
- package/src/val/EachFuncVal.ts +0 -40
- package/src/val/EmitFuncVal.ts +8 -208
- package/src/val/ExpectVal.ts +0 -62
- package/src/val/FilterFuncVal.ts +0 -55
- package/src/val/FuncBaseVal.ts +9 -130
- package/src/val/GraphAtomVal.ts +0 -42
- package/src/val/HideFuncVal.ts +0 -15
- package/src/val/IntegerVal.ts +0 -61
- package/src/val/JunctionVal.ts +0 -20
- package/src/val/KeyFuncVal.ts +0 -48
- package/src/val/ListVal.ts +0 -59
- package/src/val/LowerFuncVal.ts +0 -12
- package/src/val/MapVal.ts +0 -151
- package/src/val/MatchFuncVal.ts +0 -59
- package/src/val/MaybeFuncVal.ts +86 -0
- package/src/val/MoveFuncVal.ts +0 -20
- package/src/val/NilVal.ts +0 -60
- package/src/val/NomFuncVal.ts +10 -99
- package/src/val/NumberVal.ts +0 -16
- package/src/val/OpBaseVal.ts +4 -17
- package/src/val/PackFuncVal.ts +0 -63
- package/src/val/PathFuncVal.ts +0 -32
- package/src/val/PathVal.ts +0 -66
- package/src/val/PlaceVal.ts +0 -45
- package/src/val/PlusOpVal.ts +18 -75
- package/src/val/PrefFuncVal.ts +0 -1
- package/src/val/PrefVal.ts +0 -179
- package/src/val/RecurseVal.ts +0 -81
- package/src/val/RefVal.ts +1 -285
- package/src/val/ReferFuncVal.ts +4 -255
- package/src/val/ScalarKindVal.ts +0 -50
- package/src/val/ScalarVal.ts +0 -12
- package/src/val/StrFuncVal.ts +0 -44
- package/src/val/SuperFuncVal.ts +0 -42
- package/src/val/TopVal.ts +0 -1
- package/src/val/TranslateFuncVal.ts +1 -51
- package/src/val/UpperFuncVal.ts +0 -12
- package/src/val/Val.ts +3 -192
- package/src/val/VarVal.ts +0 -15
- package/src/val/arith.ts +0 -92
- package/src/val/caserange.ts +0 -62
- package/src/val/members.ts +0 -23
- package/src/val/numcmp.ts +1 -27
- package/src/val/numkind.ts +0 -149
- package/src/val/valutil.ts +0 -16
- package/src/vet.ts +1 -582
- package/src/view.ts +0 -507
- package/src/walk.ts +0 -41
- package/dist/lower.d.ts +0 -23
- package/dist/lower.js +0 -578
- package/dist/lower.js.map +0 -1
- package/dist/render.d.ts +0 -53
- package/dist/render.js +0 -547
- package/dist/render.js.map +0 -1
- package/dist/std.d.ts +0 -3
- package/dist/std.js +0 -672
- package/dist/std.js.map +0 -1
- package/dist/val/FormFuncVal.js +0 -55
- package/dist/val/FormFuncVal.js.map +0 -1
- package/dist/val/NamerFuncVal.d.ts +0 -12
- package/dist/val/NamerFuncVal.js +0 -176
- package/dist/val/NamerFuncVal.js.map +0 -1
- package/src/lower.ts +0 -636
- package/src/render.ts +0 -732
- package/src/std.ts +0 -683
package/src/format.ts
CHANGED
|
@@ -1,26 +1,5 @@
|
|
|
1
1
|
/* Copyright (c) 2026 Richard Rodger, MIT License */
|
|
2
2
|
|
|
3
|
-
// THE SOURCE FORMATTER (docs/design/FMT.0.md): `aontu fmt`, in the
|
|
4
|
-
// tradition of gofmt. One agreed form for Aontu source, so that layout
|
|
5
|
-
// is never argued about and a diff shows only what changed.
|
|
6
|
-
//
|
|
7
|
-
// It reads the token stream the parser reads -- the lex subscriber the
|
|
8
|
-
// parser stack exposes -- so it sees what the value tree throws away:
|
|
9
|
-
// comments, blank lines, the quote a string used, the spelling of a
|
|
10
|
-
// number. From that stream it builds a layout tree, decides the shape
|
|
11
|
-
// of every container by the rules of the note's §3, and emits. Before
|
|
12
|
-
// returning it re-parses what it wrote and compares the two parse
|
|
13
|
-
// trees: a formatter that cannot prove its output is the same document
|
|
14
|
-
// refuses rather than return it.
|
|
15
|
-
//
|
|
16
|
-
// Two tiers. The syntactic (P1): whitespace, commas, quotes, bare
|
|
17
|
-
// keys, chains and pair elements, none of which changes the parse
|
|
18
|
-
// tree. The lawful (P2), over it: repeat the prefix, and merge what
|
|
19
|
-
// repeats -- rewrites that rest on the meet, each checked by the meet
|
|
20
|
-
// in isolation and kept only where the engine agrees.
|
|
21
|
-
//
|
|
22
|
-
// The Go twin is go/format.go, function for function; the shared
|
|
23
|
-
// behaviour is test/spec/fmt.tsv, executed by both spec runners.
|
|
24
3
|
|
|
25
4
|
import { Aontu } from './aontu'
|
|
26
5
|
import { failureFinding } from './vet'
|
|
@@ -29,16 +8,8 @@ import type { Resolver } from './type'
|
|
|
29
8
|
import { desugarTemplate, resugarTemplate, templateOutputs } from './template'
|
|
30
9
|
|
|
31
10
|
|
|
32
|
-
// The packing budget (§3.1). It decides which of two legal spellings
|
|
33
|
-
// to use, one line or several, and nothing else: the formatter never
|
|
34
|
-
// breaks a line, so a value wider than this stays as wide as it is.
|
|
35
11
|
const BUDGET = 80
|
|
36
12
|
|
|
37
|
-
// THE DEPTH BUDGET. The layout is recursive, as the tree it reads is,
|
|
38
|
-
// and the canonical port's stack is finite: past the evaluation budget
|
|
39
|
-
// of 1000 levels -- the depth at which unification itself refuses --
|
|
40
|
-
// the formatter stops reading and refuses, so a pathological document
|
|
41
|
-
// is a finding rather than a crash.
|
|
42
13
|
const MAX_DEPTH = 1000
|
|
43
14
|
|
|
44
15
|
export type FormatOptions = {
|
|
@@ -47,11 +18,6 @@ export type FormatOptions = {
|
|
|
47
18
|
// Report the style findings of §4 -- key case, repeated shapes --
|
|
48
19
|
// beside the text. The formatter never acts on them.
|
|
49
20
|
lint?: boolean
|
|
50
|
-
// THE SOURCE IS A GENERATOR, and this is its marker
|
|
51
|
-
// (docs/design/TEMPLATE.0.md; FMT.0.md §3.14). The file is desugared,
|
|
52
|
-
// formatted and resugared, so `text` is a template again -- the aontu
|
|
53
|
-
// in the agreed form, indented after the marker, and every line of
|
|
54
|
-
// output exactly where it was.
|
|
55
21
|
template?: string
|
|
56
22
|
}
|
|
57
23
|
|
|
@@ -82,18 +48,10 @@ export type FormatReport =
|
|
|
82
48
|
|
|
83
49
|
type Tok = { name: string, src: string, val: any, sI: number }
|
|
84
50
|
|
|
85
|
-
// EVERY INCLUDE RESOLVES TO NOTHING. The formatter reads the file it is
|
|
86
|
-
// given and no other (§3.13), so `@"..."` is answered from memory with
|
|
87
|
-
// an empty source: the directive parses, the include is a token like
|
|
88
|
-
// any other, and no capability is needed because no file is read.
|
|
89
51
|
const stubResolver: Resolver = ((spec: any) => ({
|
|
90
52
|
...spec, kind: 'aon', full: '__fmt__.aon', src: '', found: true, search: [],
|
|
91
53
|
})) as any
|
|
92
54
|
|
|
93
|
-
// ONE ENGINE, ONE SUBSCRIBER. The parser's subscriber list is
|
|
94
|
-
// append-only, so the subscription is made once and writes to
|
|
95
|
-
// whichever sink the current parse installed; the sink is cleared
|
|
96
|
-
// before the parse returns, so the check's re-parse collects nothing.
|
|
97
55
|
let ENGINE: Aontu | undefined
|
|
98
56
|
let SINK: Tok[] | undefined
|
|
99
57
|
|
|
@@ -102,10 +60,6 @@ function engine(): Aontu {
|
|
|
102
60
|
ENGINE = new Aontu({ resolver: stubResolver })
|
|
103
61
|
ENGINE.lang.jsonic.sub({
|
|
104
62
|
lex: (tkn: any) => {
|
|
105
|
-
// Spaces carry nothing the layout needs, and the end token
|
|
106
|
-
// arrives once per nested parse -- the stub's empty includes
|
|
107
|
-
// among them -- so both are dropped here rather than skipped
|
|
108
|
-
// everywhere below.
|
|
109
63
|
if (undefined !== SINK && '#SP' !== tkn.name && '#ZZ' !== tkn.name) {
|
|
110
64
|
SINK.push({ name: tkn.name, src: tkn.src, val: tkn.val, sI: tkn.sI })
|
|
111
65
|
}
|
|
@@ -138,21 +92,14 @@ function parseDoc(src: string, path: string | undefined, sink: Tok[] | undefined
|
|
|
138
92
|
}
|
|
139
93
|
|
|
140
94
|
|
|
141
|
-
// ---------------------------------------------------------------------
|
|
142
|
-
// The layout tree
|
|
143
|
-
|
|
144
95
|
// One node shape for the whole tree, so the Go twin is one struct:
|
|
145
96
|
// the kind says which fields are meaningful.
|
|
146
97
|
type Node = {
|
|
147
98
|
t: 'pair' | 'spread' | 'include' | 'comment' | 'blank' | 'map' | 'list'
|
|
148
99
|
| 'atom' | 'call' | 'paren' | 'expr' | 'op' | 'prefix' | 'note'
|
|
149
100
|
|
|
150
|
-
// atom, include, comment, note, op, prefix: the text as written,
|
|
151
|
-
// normalised where §3.9 says (quotes), and nothing else.
|
|
152
101
|
text?: string
|
|
153
102
|
|
|
154
|
-
// pair: the key as it will be written, the optional marker, and the
|
|
155
|
-
// value; spread: the value.
|
|
156
103
|
key?: string
|
|
157
104
|
opt?: boolean
|
|
158
105
|
// pair: written with `=`, the alias declaration operator, rather than
|
|
@@ -175,7 +122,6 @@ type Node = {
|
|
|
175
122
|
// comment inside the expression), in source order.
|
|
176
123
|
items?: Node[]
|
|
177
124
|
|
|
178
|
-
// op: the author broke the line at this operator (§3.11).
|
|
179
125
|
brk?: boolean
|
|
180
126
|
|
|
181
127
|
// A comment on the last line of this entry.
|
|
@@ -273,10 +219,6 @@ class Reader {
|
|
|
273
219
|
('#QM' === this.name(1) && '#CL' === this.name(2)))
|
|
274
220
|
}
|
|
275
221
|
|
|
276
|
-
// The entries of a container up to its closer, or of the document up
|
|
277
|
-
// to its end. Comments attach by the rules of §3.7: on the line of
|
|
278
|
-
// the entry that precedes them, or of the opener, they trail it;
|
|
279
|
-
// alone on a line they stand as entries and precede what follows.
|
|
280
222
|
body(close: string, opened: boolean): { body: Node[], open?: string } {
|
|
281
223
|
const body: Node[] = []
|
|
282
224
|
let open: string | undefined
|
|
@@ -584,11 +526,6 @@ function unwrap(root: Node[]): Node[] {
|
|
|
584
526
|
// ---------------------------------------------------------------------
|
|
585
527
|
// The layout
|
|
586
528
|
|
|
587
|
-
// D1: a one-pair map in value position is written as a chain, and a
|
|
588
|
-
// one-pair map as a list element as a pair element. A map whose only
|
|
589
|
-
// entry is a spread keeps its braces (X-7), and one holding a comment
|
|
590
|
-
// keeps them too, because the comment needs the lines. A trailing
|
|
591
|
-
// comment on the map's line joins the pair's own.
|
|
592
529
|
function chain(node: Node): Node {
|
|
593
530
|
if ('map' !== node.t || undefined !== node.open || 1 !== node.body!.length ||
|
|
594
531
|
'pair' !== node.body![0].t) {
|
|
@@ -605,19 +542,17 @@ function width(s: string): number {
|
|
|
605
542
|
return Array.from(s).length
|
|
606
543
|
}
|
|
607
544
|
|
|
608
|
-
function pairHead(node: Node, tight: boolean): string {
|
|
545
|
+
function pairHead(node: Node, tight: boolean, value?: string): string {
|
|
609
546
|
// An alias declaration is `%name = value` at every width: the `=` is
|
|
610
547
|
// an operator, and operators are spaced (§3.2).
|
|
611
548
|
if (node.alias) {
|
|
612
549
|
return node.key! + ' = '
|
|
613
550
|
}
|
|
614
|
-
|
|
551
|
+
const pad = !tight ||
|
|
552
|
+
(undefined !== value && '' !== value && OPENS.includes(value[0]))
|
|
553
|
+
return node.key! + (node.opt ? '?' : '') + (pad ? ': ' : ':')
|
|
615
554
|
}
|
|
616
555
|
|
|
617
|
-
// The one-line spelling of a node, or undefined where it has none: a
|
|
618
|
-
// comment, a blank line, a break the author kept, a string that spans
|
|
619
|
-
// lines. `tight` is the inline form of a pair, `a:1`, used inside a
|
|
620
|
-
// container; a statement's pair is `a: 1`.
|
|
621
556
|
function inline(node: Node, tight: boolean): string | undefined {
|
|
622
557
|
if (undefined !== node.trail || node.held) {
|
|
623
558
|
return undefined
|
|
@@ -628,7 +563,7 @@ function inline(node: Node, tight: boolean): string | undefined {
|
|
|
628
563
|
return node.text!.includes('\n') ? undefined : node.text
|
|
629
564
|
case 'pair': {
|
|
630
565
|
const v = inline(chain(node.value!), tight)
|
|
631
|
-
return undefined === v ? undefined : pairHead(node, tight) + v
|
|
566
|
+
return undefined === v ? undefined : pairHead(node, tight, v) + v
|
|
632
567
|
}
|
|
633
568
|
case 'spread': {
|
|
634
569
|
// `{ &: integer }`, padded inside braces too: the marker reads as
|
|
@@ -685,12 +620,17 @@ function inlineSeq(items: Node[]): string | undefined {
|
|
|
685
620
|
return out
|
|
686
621
|
}
|
|
687
622
|
|
|
623
|
+
// A value that OPENS keeps the space after the colon (§3.2); `|` is
|
|
624
|
+
// tight within a line and spaced where it leads one (§3.11, §7.7).
|
|
625
|
+
const OPENS = '*{[('
|
|
626
|
+
const TIGHT_OP = '|'
|
|
627
|
+
|
|
688
628
|
function sepOf(node: Node): string {
|
|
689
629
|
return node.sep ? ', ' : ' '
|
|
690
630
|
}
|
|
691
631
|
|
|
692
|
-
// Binary operators spaced, prefixes tight (§3.11). An operand
|
|
693
|
-
// never directly after an operand: the reader ends a value there.
|
|
632
|
+
// Binary operators spaced, prefixes and `|` tight (§3.11). An operand
|
|
633
|
+
// is never directly after an operand: the reader ends a value there.
|
|
694
634
|
function inlineExpr(items: Node[]): string | undefined {
|
|
695
635
|
let out = ''
|
|
696
636
|
for (const it of items) {
|
|
@@ -698,7 +638,7 @@ function inlineExpr(items: Node[]): string | undefined {
|
|
|
698
638
|
return undefined
|
|
699
639
|
}
|
|
700
640
|
if ('op' === it.t) {
|
|
701
|
-
out += ' ' + it.text + ' '
|
|
641
|
+
out += TIGHT_OP === it.text ? it.text : ' ' + it.text + ' '
|
|
702
642
|
continue
|
|
703
643
|
}
|
|
704
644
|
if ('prefix' === it.t) {
|
|
@@ -755,9 +695,6 @@ class Writer {
|
|
|
755
695
|
return this.lines.slice(mark).concat([this.line]).map(rtrim).join('\n') + '\n'
|
|
756
696
|
}
|
|
757
697
|
|
|
758
|
-
// A blank line above the line at an index: the gap of §3.8, opened
|
|
759
|
-
// once the statement below it turns out to be a tree. Never at the
|
|
760
|
-
// top of the page, and never a second time.
|
|
761
698
|
gap(at: number): void {
|
|
762
699
|
if (0 < at && '' !== this.lines[at - 1]) {
|
|
763
700
|
this.lines.splice(at, 0, '')
|
|
@@ -790,21 +727,11 @@ function rtrim(s: string): string {
|
|
|
790
727
|
}
|
|
791
728
|
|
|
792
729
|
|
|
793
|
-
// The entries of a body, one per line at the indentation, with the
|
|
794
|
-
// blank lines the author kept between them (§3.8) -- never at the
|
|
795
|
-
// start or the end. In STATEMENT position (`stmt`: the root, and the
|
|
796
|
-
// body of a plain map that is itself the value of a statement) a pair
|
|
797
|
-
// is laid out by §3.4, which may repeat its key; anywhere else -- a
|
|
798
|
-
// list, an operand, an argument -- by §3.5 alone.
|
|
799
730
|
function emitBody(
|
|
800
731
|
w: Writer, body: Node[], indent: number, stmt: Stmt | undefined, root?: boolean
|
|
801
732
|
): void {
|
|
802
733
|
let pending = false
|
|
803
734
|
let count = 0
|
|
804
|
-
// Where the run being written begins: a statement, with the comments
|
|
805
|
-
// standing directly above it, so that the gap below opens ABOVE the
|
|
806
|
-
// comments rather than between them and what they describe. A blank
|
|
807
|
-
// line ends a run -- comments across a gap belong to what is above.
|
|
808
735
|
let head = 0
|
|
809
736
|
let noted = false
|
|
810
737
|
for (const node of body) {
|
|
@@ -836,14 +763,6 @@ function emitBody(
|
|
|
836
763
|
w.text(' ' + e.trail)
|
|
837
764
|
}
|
|
838
765
|
}
|
|
839
|
-
// A TOP-LEVEL STATEMENT WRITTEN AS A TREE STANDS APART (§3.8). A
|
|
840
|
-
// document states several things -- a service, then its entities,
|
|
841
|
-
// then its errors -- and where one of them is a tree rather than a
|
|
842
|
-
// line, the eye finds it by the space around it. What counts as a
|
|
843
|
-
// tree is measured rather than guessed: the statement took more
|
|
844
|
-
// than one line to write. So `a: 1` beside `b: 2` is left alone,
|
|
845
|
-
// and this rule cannot fire below the root, where a blank line is
|
|
846
|
-
// the author's (§3.8) and nothing else.
|
|
847
766
|
if (root && from < w.mark()) {
|
|
848
767
|
w.gap(head)
|
|
849
768
|
pending = true
|
|
@@ -892,18 +811,6 @@ function emitValue(w: Writer, node: Node, indent: number): void {
|
|
|
892
811
|
}
|
|
893
812
|
}
|
|
894
813
|
|
|
895
|
-
// A call, or a parenthesis, that has no one-line form or is too wide
|
|
896
|
-
// for the budget. Three shapes. Arguments that are all FLAT -- none
|
|
897
|
-
// holds a container -- stay on the one line however wide it is: a
|
|
898
|
-
// scalar is no narrower on a line of its own, and the formatter never
|
|
899
|
-
// breaks a line. The last argument HUGS the parentheses, `hide({` ...
|
|
900
|
-
// `})`, `close($.E & {` ... `})`, when it is a container, or an
|
|
901
|
-
// expression the author did not break that ends in one, and the
|
|
902
|
-
// arguments before it fit on the opener's line: the container decides
|
|
903
|
-
// its own lines. Otherwise the parenthesis opens a block: one argument
|
|
904
|
-
// per line one level in, the closer alone at the opener's level. A
|
|
905
|
-
// call whose last argument hugs is hugged in turn, `type(close({` ...
|
|
906
|
-
// `}))`: the schema idiom.
|
|
907
814
|
function emitCall(w: Writer, node: Node, indent: number): void {
|
|
908
815
|
const items = 'call' === node.t ? node.args! : node.inner!
|
|
909
816
|
const open = ('call' === node.t ? node.name! : '') + '('
|
|
@@ -1005,21 +912,8 @@ function emitBlock(
|
|
|
1005
912
|
w.text(close)
|
|
1006
913
|
}
|
|
1007
914
|
|
|
1008
|
-
// An expression that has no one-line form, or one too wide for the
|
|
1009
|
-
// budget: the author's breaks are kept, each at its operator, which
|
|
1010
|
-
// leads its continuation line (§3.11). The continuation is one level
|
|
1011
|
-
// in when the expression follows a key on its line, and level with
|
|
1012
|
-
// the first operand when the expression has the line to itself -- an
|
|
1013
|
-
// argument of a block call, say -- so a disjunction of alternatives
|
|
1014
|
-
// reads as the list it is. A container operand that does not fit from
|
|
1015
|
-
// where it stands is a block whose closer lines up with the line that
|
|
1016
|
-
// opened it.
|
|
1017
915
|
function emitExpr(w: Writer, items: Node[], indent: number): void {
|
|
1018
916
|
const cont = w.fresh() ? indent : indent + 2
|
|
1019
|
-
// Whether the last item was an operand: a comment after one is a
|
|
1020
|
-
// space away, and after an operator or the colon it is not. An
|
|
1021
|
-
// operand is never directly after an operand (the reader ends a
|
|
1022
|
-
// value there), so operands need no such check.
|
|
1023
917
|
let operand = false
|
|
1024
918
|
let cur = indent
|
|
1025
919
|
for (const it of items) {
|
|
@@ -1032,7 +926,7 @@ function emitExpr(w: Writer, items: Node[], indent: number): void {
|
|
|
1032
926
|
w.text(it.text + ' ')
|
|
1033
927
|
}
|
|
1034
928
|
else {
|
|
1035
|
-
w.text(' ' + it.text + ' ')
|
|
929
|
+
w.text(TIGHT_OP === it.text ? it.text! : ' ' + it.text + ' ')
|
|
1036
930
|
}
|
|
1037
931
|
operand = false
|
|
1038
932
|
continue
|
|
@@ -1058,25 +952,6 @@ function emitExpr(w: Writer, items: Node[], indent: number): void {
|
|
|
1058
952
|
}
|
|
1059
953
|
|
|
1060
954
|
|
|
1061
|
-
// ---------------------------------------------------------------------
|
|
1062
|
-
// The lawful tier (§3.4): repeat the prefix, and merge what repeats.
|
|
1063
|
-
//
|
|
1064
|
-
// Both rewrites rest on the meet. `s: a: 1` / `s: b: 2` is one document
|
|
1065
|
-
// with `s: { a:1 b:2 }`, because a key written twice is a meet and the
|
|
1066
|
-
// meet of two maps with disjoint keys is their union. So they apply
|
|
1067
|
-
// only to a PLAIN map in STATEMENT position -- an entry of the root, or
|
|
1068
|
-
// of a map that is itself the plain value of such an entry -- and never
|
|
1069
|
-
// to a map that is an operand, an argument or a list element, where
|
|
1070
|
-
// splitting it would change the document (`close({a:1})` /
|
|
1071
|
-
// `close({b:2})` does not evaluate at all). And every statement the
|
|
1072
|
-
// tier rewrites is checked by unification, locally (§7.3): the spelling
|
|
1073
|
-
// before and the spelling after must come to the same meet, or the
|
|
1074
|
-
// statement keeps the spelling before. The check is the engine's
|
|
1075
|
-
// agreement, not the formatter's self-check -- the engine's own repros
|
|
1076
|
-
// hold maps whose two spellings it evaluates differently -- so failing
|
|
1077
|
-
// it is no refusal.
|
|
1078
|
-
|
|
1079
|
-
// The check of one rewrite: the spelling before and the spelling after.
|
|
1080
955
|
type Meet = (before: string, after: string) => boolean
|
|
1081
956
|
|
|
1082
957
|
// Statement position: the check, and whether the statement being laid
|
|
@@ -1084,10 +959,6 @@ type Meet = (before: string, after: string) => boolean
|
|
|
1084
959
|
// Undefined anywhere else -- a list, an operand, an argument.
|
|
1085
960
|
type Stmt = { meet: Meet, covered: boolean }
|
|
1086
961
|
|
|
1087
|
-
// The entries of a plain map value: a braced map, or a chain, which is
|
|
1088
|
-
// a one-entry map. A map with a comment on its opener keeps its braces
|
|
1089
|
-
// (§3.7), so it is not plain here; nor is a map holding an include,
|
|
1090
|
-
// which the local check cannot follow.
|
|
1091
962
|
function plainEntries(v: Node): Node[] | undefined {
|
|
1092
963
|
if ('pair' === v.t) {
|
|
1093
964
|
return [v]
|
|
@@ -1115,17 +986,6 @@ function members(p: Node): Node[] | undefined {
|
|
|
1115
986
|
return entries.slice(0, -1).concat([{ ...last, trail }])
|
|
1116
987
|
}
|
|
1117
988
|
|
|
1118
|
-
// Adjacent statements naming one key, whose values are plain maps, are
|
|
1119
|
-
// one map: their entries in order, with the comments and blank lines
|
|
1120
|
-
// between the statements travelling with the statement they preceded.
|
|
1121
|
-
// Only ADJACENT statements merge -- a `server:` line, something else,
|
|
1122
|
-
// then another `server:` line stays as it is, because merging them
|
|
1123
|
-
// would move a statement, and the formatter never reorders (§3.13).
|
|
1124
|
-
// Nor do two statements merge into a map with two spreads: the engine
|
|
1125
|
-
// keeps those as a conjunction, which is not the meet of the two maps.
|
|
1126
|
-
// The tree is not changed: a merged statement is a new node that keeps
|
|
1127
|
-
// the statements it replaces as its `orig`, its spelling before, and a
|
|
1128
|
-
// statement merged somewhere below is copied the same way.
|
|
1129
989
|
function mergeRuns(body: Node[]): Node[] {
|
|
1130
990
|
const out: Node[] = []
|
|
1131
991
|
let i = 0
|
|
@@ -1190,15 +1050,6 @@ function mergeDeep(p: Node): Node {
|
|
|
1190
1050
|
return { ...p, value: 'pair' === v.t ? body[0] : { ...v, body }, orig: [p] }
|
|
1191
1051
|
}
|
|
1192
1052
|
|
|
1193
|
-
// Whether a value is a RECORD: a braced map of several entries, every
|
|
1194
|
-
// one of them a VALUE rather than another map. A field, an error, a
|
|
1195
|
-
// rule row -- something whose keys are what it IS, as against a level
|
|
1196
|
-
// of the tree, whose keys are a way through to something else.
|
|
1197
|
-
//
|
|
1198
|
-
// A CHAIN IS NOT ONE, whatever it holds: a one-entry map is D1's, and
|
|
1199
|
-
// D1 writes it as a chain at every width. Nor is a map holding a
|
|
1200
|
-
// spread, which says something about the map's MEMBERS and which D1's
|
|
1201
|
-
// exception already gives a spelling of its own inside a repeat.
|
|
1202
1053
|
function record(v: Node, entries: Node[]): boolean {
|
|
1203
1054
|
if ('map' !== v.t) {
|
|
1204
1055
|
return false
|
|
@@ -1219,24 +1070,6 @@ function record(v: Node, entries: Node[]): boolean {
|
|
|
1219
1070
|
return 1 < pairs
|
|
1220
1071
|
}
|
|
1221
1072
|
|
|
1222
|
-
// The lines of a map repeated under a prefix (§3.4, rule 2): every
|
|
1223
|
-
// entry written with the prefix in front of it as one line, or --
|
|
1224
|
-
// where an entry's value is a map that does not fit -- descended into
|
|
1225
|
-
// under the longer prefix. Comments and blank lines are kept where
|
|
1226
|
-
// they stood. Undefined where an entry cannot be one line: a list that
|
|
1227
|
-
// does not fit, a value that spans lines, a comment closing the map
|
|
1228
|
-
// (which a repeat could not keep in the map) -- and where the map holds
|
|
1229
|
-
// two spreads, which repeated would be two maps, and a different meet.
|
|
1230
|
-
//
|
|
1231
|
-
// A DESCENT ENDS AT A RECORD (§3.4, D2's amendment). The prefix reaches
|
|
1232
|
-
// through maps that hold maps, because those keys are a path and a line
|
|
1233
|
-
// carrying the whole path says where it is. It stops at a map that
|
|
1234
|
-
// holds only values: there the keys are the thing's own fields, the
|
|
1235
|
-
// prefix in front of each of them is the same prefix again, and the
|
|
1236
|
-
// map is written as a braced BLOCK under the prefix instead --
|
|
1237
|
-
// `entity: planet: field: id: {` and its seven facts indented once.
|
|
1238
|
-
// The statement's own map is not an entry of anything and is
|
|
1239
|
-
// unaffected, so a flat `service: host: …` is still one repeat.
|
|
1240
1073
|
type Line = {
|
|
1241
1074
|
t: 'text' | 'comment' | 'blank' | 'block'
|
|
1242
1075
|
text?: string
|
|
@@ -1284,12 +1117,6 @@ function repeatLines(entries: Node[], prefix: string, indent: number): Line[] |
|
|
|
1284
1117
|
if (undefined === lines) {
|
|
1285
1118
|
return undefined
|
|
1286
1119
|
}
|
|
1287
|
-
// THE DESCENT COULD GO ON, AND WHAT IT REACHES IS A RECORD: it
|
|
1288
|
-
// stops, and the record is a block under the prefix instead. The
|
|
1289
|
-
// deeper repeat is asked for first and thrown away deliberately --
|
|
1290
|
-
// the block REPLACES a descent that would have worked, and never
|
|
1291
|
-
// rescues one that would not, so a map this rule cannot reach two
|
|
1292
|
-
// ways round is laid out exactly as it was before the amendment.
|
|
1293
1120
|
if (record(e.value!, sub)) {
|
|
1294
1121
|
out.push({ t: 'block', text: head, node: e.value!, trail })
|
|
1295
1122
|
continue
|
|
@@ -1315,18 +1142,6 @@ function fits(indent: number, text: string): boolean {
|
|
|
1315
1142
|
return indent + width(text) <= BUDGET
|
|
1316
1143
|
}
|
|
1317
1144
|
|
|
1318
|
-
// A pair in statement position, by §3.4. `prefix` is what stands
|
|
1319
|
-
// before it on its line: the heads of the chain it hangs from, not yet
|
|
1320
|
-
// written. Its value is laid out by §3.5 unless it is a plain map, and
|
|
1321
|
-
// then in this order: a chain, when the map holds exactly one pair
|
|
1322
|
-
// (D1); one line, when that fits the budget; the key repeated over the
|
|
1323
|
-
// entries, when every entry can be one line that way; a braced block
|
|
1324
|
-
// otherwise, whose entries are statements in turn. Whether the
|
|
1325
|
-
// statement was rewritten by this tier -- merged, or repeated -- is
|
|
1326
|
-
// returned, and the outermost such statement is checked: its spelling
|
|
1327
|
-
// on the page against what the syntactic tier writes for the
|
|
1328
|
-
// statements it came from, at the same indentation, which is what
|
|
1329
|
-
// stays on the page when the check fails.
|
|
1330
1145
|
function emitStatement(w: Writer, p: Node, indent: number, stmt: Stmt, prefix: string): boolean {
|
|
1331
1146
|
const mark = w.mark()
|
|
1332
1147
|
let rewritten = undefined !== p.orig
|
|
@@ -1407,12 +1222,6 @@ function emit(root: Node[], meet: Meet | undefined): string {
|
|
|
1407
1222
|
}
|
|
1408
1223
|
|
|
1409
1224
|
|
|
1410
|
-
// ---------------------------------------------------------------------
|
|
1411
|
-
// The lint (§4): what the formatter points at and never touches. Two
|
|
1412
|
-
// rules, both advice: the formatter never renames a key (§4.1) and
|
|
1413
|
-
// never introduces an alias (§4.2), and a rule with a mechanical fix
|
|
1414
|
-
// that keeps the document would belong to §3 instead (§4.3).
|
|
1415
|
-
|
|
1416
1225
|
// The shape width at which a repeat is worth an alias (§4.2): below
|
|
1417
1226
|
// it, `{ a:1 }` twice is the shorter spelling. Measured over the use
|
|
1418
1227
|
// cases when the lint landed (§7.10).
|
|
@@ -1464,11 +1273,6 @@ function lineCol(text: string, at: number): { line: number, col: number } {
|
|
|
1464
1273
|
return { line: before.split('\n').length, col: at - before.lastIndexOf('\n') }
|
|
1465
1274
|
}
|
|
1466
1275
|
|
|
1467
|
-
// D4 (§4.1): keys are lower-case words, or CamelCase when a key is
|
|
1468
|
-
// several. A bare key holding `_`, or beginning with two capitals, is
|
|
1469
|
-
// reported with the spelling that would follow the form; a quoted key
|
|
1470
|
-
// is a deliberate spelling and a key of underscores alone names
|
|
1471
|
-
// nothing the rule can respell.
|
|
1472
1276
|
function keyCase(node: Node, text: string, out: LintFinding[]): void {
|
|
1473
1277
|
if ('pair' === node.t && BARE.test(node.key!) && /[A-Za-z]/.test(node.key!)) {
|
|
1474
1278
|
const why = node.key!.includes('_') ? 'holds an underscore'
|
|
@@ -1485,9 +1289,6 @@ function keyCase(node: Node, text: string, out: LintFinding[]): void {
|
|
|
1485
1289
|
}
|
|
1486
1290
|
}
|
|
1487
1291
|
|
|
1488
|
-
// The key as lower-case words or CamelCase: `credit_cents` is
|
|
1489
|
-
// `creditCents`, `HTTP_PORT` is `httpPort`, `HTTPServer` is
|
|
1490
|
-
// `httpServer`, `ID` is `id`.
|
|
1491
1292
|
function camel(key: string): string {
|
|
1492
1293
|
const words = key.split('_').filter((w) => '' !== w)
|
|
1493
1294
|
.map((w) => /^[A-Z]+$/.test(w) ? w.toLowerCase() : w)
|
|
@@ -1496,12 +1297,6 @@ function camel(key: string): string {
|
|
|
1496
1297
|
words.slice(1).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join('')
|
|
1497
1298
|
}
|
|
1498
1299
|
|
|
1499
|
-
// D3 (§4.2): a shape written twice can drift, and an alias names it
|
|
1500
|
-
// once. Every map or list whose shape recurs in the file, and whose
|
|
1501
|
-
// shape is REPEAT_MIN_WIDTH or wider, is reported once, at its first
|
|
1502
|
-
// site, with the count and the other sites; the naming is the
|
|
1503
|
-
// author's. A repeat inside a repeat is the outer one's: the walk does
|
|
1504
|
-
// not descend into a shape it reports.
|
|
1505
1300
|
function repeats(nodes: Node[], text: string, out: LintFinding[]): void {
|
|
1506
1301
|
const counts = new Map<string, number>()
|
|
1507
1302
|
const tally = (node: Node): void => {
|
|
@@ -1537,9 +1332,6 @@ function repeats(nodes: Node[], text: string, out: LintFinding[]): void {
|
|
|
1537
1332
|
}
|
|
1538
1333
|
}
|
|
1539
1334
|
|
|
1540
|
-
// A node's shape: its spelling with the layout, the comments and, for
|
|
1541
|
-
// a map, the order of its entries taken out, so that two spellings of
|
|
1542
|
-
// one value are one shape, as they are one canon.
|
|
1543
1335
|
function shape(node: Node): string {
|
|
1544
1336
|
switch (node.t) {
|
|
1545
1337
|
case 'map':
|
|
@@ -1581,15 +1373,6 @@ function sameDocument(root: any, after: string): boolean {
|
|
|
1581
1373
|
return undefined === p.errors && root.canon === p.root.canon
|
|
1582
1374
|
}
|
|
1583
1375
|
|
|
1584
|
-
// The check of a lawful rewrite: the spelling before and the spelling
|
|
1585
|
-
// after, evaluated in isolation, come to the same canon, the same
|
|
1586
|
-
// kinds of failure, and the same outcome of generation (§7.3). Local,
|
|
1587
|
-
// so it needs no include and no capability, and it applies whether or
|
|
1588
|
-
// not the document as a whole evaluates. The kinds, not the count: how
|
|
1589
|
-
// often one unresolved reference is reported depends on the order the
|
|
1590
|
-
// meet took. Generation too, because the engine generates from more
|
|
1591
|
-
// than the canon: a meet of maps with a nil member has refused a key
|
|
1592
|
-
// the same map written once generates.
|
|
1593
1376
|
function sameByMeet(before: string, after: string): boolean {
|
|
1594
1377
|
return meetOf(before) === meetOf(after)
|
|
1595
1378
|
}
|
|
@@ -1636,12 +1419,6 @@ function checkFinding(path: string | undefined, expected: string, actual: string
|
|
|
1636
1419
|
}
|
|
1637
1420
|
}
|
|
1638
1421
|
|
|
1639
|
-
// THE TARGET'S OWN LINES ARE HELD ON LINES OF THEIR OWN (§3.14). The
|
|
1640
|
-
// desugaring is line for line, so a line of output is known by the
|
|
1641
|
-
// offset it begins at, and the node beginning there -- the quoted
|
|
1642
|
-
// string the desugaring wrote -- is marked. From there on it has no
|
|
1643
|
-
// one-line form, so every container holding it opens, and no two lines
|
|
1644
|
-
// of the generated file are ever packed onto one.
|
|
1645
1422
|
function holdOutput(nodes: Node[], at: Set<number>): void {
|
|
1646
1423
|
for (const node of nodes) {
|
|
1647
1424
|
if (undefined !== node.at && at.has(node.at)) {
|
|
@@ -1673,11 +1450,6 @@ function outputAt(doc: string, flags: boolean[]): Set<number> {
|
|
|
1673
1450
|
}
|
|
1674
1451
|
|
|
1675
1452
|
|
|
1676
|
-
// A finding's column in the TEMPLATE rather than in the document it
|
|
1677
|
-
// carries (§3.14): the marker and its one space stand before the aontu
|
|
1678
|
-
// on every line the resugaring writes. Every finding is on such a
|
|
1679
|
-
// line -- the two rules point at a key or at a container, and a line of
|
|
1680
|
-
// output is a bare string, which is neither.
|
|
1681
1453
|
function shiftFindings(findings: LintFinding[], mark: string | undefined): LintFinding[] {
|
|
1682
1454
|
return undefined === mark ? findings :
|
|
1683
1455
|
findings.map((f) => ({ ...f, col: f.col + mark.length + 1 }))
|
|
@@ -1689,9 +1461,6 @@ function shiftFindings(findings: LintFinding[], mark: string | undefined): LintF
|
|
|
1689
1461
|
// what `--check` and `--list` report.
|
|
1690
1462
|
export function format(src: string, opts?: FormatOptions, hooks?: FormatHooks): FormatReport {
|
|
1691
1463
|
const text = lf(src)
|
|
1692
|
-
// A GENERATOR IS FORMATTED AS THE DOCUMENT IT CARRIES (§3.14): the
|
|
1693
|
-
// template surface's two transforms stand either side of the
|
|
1694
|
-
// formatter, and between them is what happens to any other document.
|
|
1695
1464
|
const mark = opts?.template
|
|
1696
1465
|
const doc = undefined === mark ? text : desugarTemplate(text, mark)
|
|
1697
1466
|
const toks: Tok[] = []
|
|
@@ -1726,9 +1495,6 @@ export function format(src: string, opts?: FormatOptions, hooks?: FormatHooks):
|
|
|
1726
1495
|
}
|
|
1727
1496
|
|
|
1728
1497
|
|
|
1729
|
-
// ---------------------------------------------------------------------
|
|
1730
|
-
// The unified diff of `--diff`
|
|
1731
|
-
|
|
1732
1498
|
// A patience diff: lines unique to both sides, in order, are the
|
|
1733
1499
|
// anchors, and the gaps between them recurse. Not always the shortest
|
|
1734
1500
|
// edit script, but linear in space, and the same script from both
|
|
@@ -1736,11 +1502,6 @@ export function format(src: string, opts?: FormatOptions, hooks?: FormatHooks):
|
|
|
1736
1502
|
|
|
1737
1503
|
type Edit = { op: ' ' | '-' | '+', text: string }
|
|
1738
1504
|
|
|
1739
|
-
// The lines of a text, with a marker on the last when the text does
|
|
1740
|
-
// not end in a newline: such a line never equals its
|
|
1741
|
-
// newline-terminated twin, which is how the diff reports the
|
|
1742
|
-
// difference, and the marker is rendered as diff renders it. NUL,
|
|
1743
|
-
// which no source line ends in.
|
|
1744
1505
|
const NO_NEWLINE = String.fromCharCode(0)
|
|
1745
1506
|
|
|
1746
1507
|
function textLines(text: string): string[] {
|
|
@@ -1846,8 +1607,6 @@ function patience(
|
|
|
1846
1607
|
}
|
|
1847
1608
|
}
|
|
1848
1609
|
|
|
1849
|
-
// The diff in unified format, three lines of context, the file named
|
|
1850
|
-
// on both sides. Empty when the texts are the same.
|
|
1851
1610
|
export function unifiedDiff(name: string, before: string, after: string): string {
|
|
1852
1611
|
const a = textLines(before)
|
|
1853
1612
|
const b = textLines(after)
|
|
@@ -1879,8 +1638,6 @@ export function unifiedDiff(name: string, before: string, after: string): string
|
|
|
1879
1638
|
for (const [s, e] of hunks) {
|
|
1880
1639
|
const from = Math.max(s - 3, 0)
|
|
1881
1640
|
const to = Math.min(e + 4, edits.length)
|
|
1882
|
-
// Everything between two hunks is context -- a change would have
|
|
1883
|
-
// opened a hunk -- so both sides advance together.
|
|
1884
1641
|
for (; next < from; next++) {
|
|
1885
1642
|
ai++
|
|
1886
1643
|
bi++
|
package/src/grammar.ts
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/* Copyright (c) 2026 Richard Rodger, MIT License */
|
|
2
|
+
|
|
3
|
+
// The grammar seam, ADR-033. Twin of go/grammar.go.
|
|
4
|
+
|
|
5
|
+
import { Tabnas } from '@tabnas/parser'
|
|
6
|
+
import { abnf } from '@tabnas/abnf'
|
|
7
|
+
|
|
8
|
+
import { AontuContext } from './ctx'
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
// A constant, not a trust knob: this bounds a third party's grammar.
|
|
12
|
+
const PARSE_STEP_MAX = 100000
|
|
13
|
+
|
|
14
|
+
const PARSE_CHECK_EVERY = 100
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
const cache = new Map<string, [any, string | undefined]>()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
const steps = { n: 0 }
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
function compileGrammar(src: string): [any, string | undefined] {
|
|
24
|
+
const hit = cache.get(src)
|
|
25
|
+
if (undefined !== hit) {
|
|
26
|
+
return hit
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let out: [any, string | undefined]
|
|
30
|
+
try {
|
|
31
|
+
// Lexing off, or the engine reads `1 . 2 . 3` as `1.2.3`. The hook
|
|
32
|
+
// is installed here because the engine takes it at construction.
|
|
33
|
+
const tn = new Tabnas({
|
|
34
|
+
space: { lex: false },
|
|
35
|
+
parse: {
|
|
36
|
+
budget: {
|
|
37
|
+
checkEveryN: PARSE_CHECK_EVERY,
|
|
38
|
+
onCheck: () => (steps.n += PARSE_CHECK_EVERY) <= PARSE_STEP_MAX,
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
tn.use(abnf)
|
|
43
|
+
tn.abnf(src)
|
|
44
|
+
out = [tn, undefined]
|
|
45
|
+
}
|
|
46
|
+
catch (e: any) {
|
|
47
|
+
out = [undefined, String(e?.message ?? e).split('\n')[0]]
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
cache.set(src, out)
|
|
51
|
+
return out
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
function parseWith(grammar: any, text: string, _ctx: AontuContext):
|
|
56
|
+
[any, string | undefined] {
|
|
57
|
+
steps.n = 0
|
|
58
|
+
try {
|
|
59
|
+
return [grammar.parse(text), undefined]
|
|
60
|
+
}
|
|
61
|
+
catch (e: any) {
|
|
62
|
+
return [undefined, String(e?.message ?? e).split('\n')[0]]
|
|
63
|
+
}
|
|
64
|
+
} /* node:coverage ignore next 8 */
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
export {
|
|
68
|
+
compileGrammar,
|
|
69
|
+
parseWith,
|
|
70
|
+
PARSE_STEP_MAX,
|
|
71
|
+
PARSE_CHECK_EVERY,
|
|
72
|
+
}
|