aontu 0.50.1 → 0.52.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/bin/aontu-lsp.js +6 -0
- package/bin/aontu.js +7 -0
- package/dist/aontu.d.ts +4 -2
- package/dist/aontu.js +21 -8
- package/dist/aontu.js.map +1 -1
- package/dist/cli.d.ts +0 -1
- package/dist/cli.js +43 -53
- package/dist/cli.js.map +1 -1
- package/dist/ctx.js +1 -1
- package/dist/ctx.js.map +1 -1
- package/dist/err.js +3 -2
- package/dist/err.js.map +1 -1
- package/dist/exactjson.d.ts +37 -0
- package/dist/exactjson.js +228 -0
- package/dist/exactjson.js.map +1 -0
- package/dist/hints.d.ts +3 -1
- package/dist/hints.js +187 -5
- package/dist/hints.js.map +1 -1
- package/dist/keyorder.d.ts +9 -0
- package/dist/keyorder.js +65 -0
- package/dist/keyorder.js.map +1 -0
- package/dist/lang.js +263 -30
- package/dist/lang.js.map +1 -1
- package/dist/lsp-server.d.ts +2 -2
- package/dist/lsp-server.js +10 -10
- package/dist/lsp-server.js.map +1 -1
- package/dist/lsp.js +39 -21
- package/dist/lsp.js.map +1 -1
- package/dist/site.js +1 -1
- package/dist/site.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/unify.js +48 -1
- package/dist/unify.js.map +1 -1
- package/dist/utility.js +3 -8
- package/dist/utility.js.map +1 -1
- package/dist/val/BagVal.js +9 -6
- package/dist/val/BagVal.js.map +1 -1
- package/dist/val/BigDecimalVal.d.ts +13 -0
- package/dist/val/BigDecimalVal.js +80 -0
- package/dist/val/BigDecimalVal.js.map +1 -0
- package/dist/val/BigIntegerVal.d.ts +11 -0
- package/dist/val/BigIntegerVal.js +75 -0
- package/dist/val/BigIntegerVal.js.map +1 -0
- package/dist/val/BooleanVal.js +1 -1
- package/dist/val/BooleanVal.js.map +1 -1
- package/dist/val/CloseFuncVal.js +1 -1
- package/dist/val/CloseFuncVal.js.map +1 -1
- package/dist/val/ConjunctVal.js +1 -1
- package/dist/val/ConjunctVal.js.map +1 -1
- package/dist/val/ConstraintVal.d.ts +56 -0
- package/dist/val/ConstraintVal.js +407 -0
- package/dist/val/ConstraintVal.js.map +1 -0
- package/dist/val/CopyFuncVal.js +1 -1
- package/dist/val/CopyFuncVal.js.map +1 -1
- package/dist/val/Decimal.d.ts +32 -0
- package/dist/val/Decimal.js +318 -0
- package/dist/val/Decimal.js.map +1 -0
- package/dist/val/DisjunctVal.js +1 -29
- package/dist/val/DisjunctVal.js.map +1 -1
- package/dist/val/ExpectVal.js +1 -1
- package/dist/val/ExpectVal.js.map +1 -1
- package/dist/val/FeatureVal.js +1 -1
- package/dist/val/FeatureVal.js.map +1 -1
- package/dist/val/FuncBaseVal.js +1 -1
- package/dist/val/FuncBaseVal.js.map +1 -1
- package/dist/val/HideFuncVal.js +1 -1
- package/dist/val/HideFuncVal.js.map +1 -1
- package/dist/val/IntegerVal.d.ts +2 -0
- package/dist/val/IntegerVal.js +77 -5
- package/dist/val/IntegerVal.js.map +1 -1
- package/dist/val/JunctionVal.js +1 -1
- package/dist/val/JunctionVal.js.map +1 -1
- package/dist/val/KeyFuncVal.d.ts +1 -1
- package/dist/val/KeyFuncVal.js +34 -4
- package/dist/val/KeyFuncVal.js.map +1 -1
- package/dist/val/ListVal.js +7 -7
- package/dist/val/ListVal.js.map +1 -1
- package/dist/val/LowerFuncVal.js +21 -4
- package/dist/val/LowerFuncVal.js.map +1 -1
- package/dist/val/MapVal.js +17 -13
- package/dist/val/MapVal.js.map +1 -1
- package/dist/val/MoveFuncVal.js +26 -9
- package/dist/val/MoveFuncVal.js.map +1 -1
- package/dist/val/NilVal.d.ts +1 -0
- package/dist/val/NilVal.js +10 -3
- package/dist/val/NilVal.js.map +1 -1
- package/dist/val/NullVal.js +1 -1
- package/dist/val/NullVal.js.map +1 -1
- package/dist/val/NumberVal.d.ts +1 -0
- package/dist/val/NumberVal.js +25 -4
- package/dist/val/NumberVal.js.map +1 -1
- package/dist/val/OpBaseVal.js +10 -8
- package/dist/val/OpBaseVal.js.map +1 -1
- package/dist/val/OpenFuncVal.js +1 -1
- package/dist/val/OpenFuncVal.js.map +1 -1
- package/dist/val/PathFuncVal.js +10 -2
- package/dist/val/PathFuncVal.js.map +1 -1
- package/dist/val/PlusOpVal.d.ts +3 -1
- package/dist/val/PlusOpVal.js +140 -29
- package/dist/val/PlusOpVal.js.map +1 -1
- package/dist/val/PrefFuncVal.js +1 -1
- package/dist/val/PrefFuncVal.js.map +1 -1
- package/dist/val/PrefVal.d.ts +2 -0
- package/dist/val/PrefVal.js +44 -7
- package/dist/val/PrefVal.js.map +1 -1
- package/dist/val/RefVal.d.ts +2 -0
- package/dist/val/RefVal.js +135 -11
- package/dist/val/RefVal.js.map +1 -1
- package/dist/val/ScalarKindVal.d.ts +12 -2
- package/dist/val/ScalarKindVal.js +108 -13
- package/dist/val/ScalarKindVal.js.map +1 -1
- package/dist/val/ScalarVal.d.ts +1 -0
- package/dist/val/ScalarVal.js +18 -3
- package/dist/val/ScalarVal.js.map +1 -1
- package/dist/val/StringVal.js +1 -1
- package/dist/val/StringVal.js.map +1 -1
- package/dist/val/SuperFuncVal.d.ts +1 -1
- package/dist/val/SuperFuncVal.js +15 -2
- package/dist/val/SuperFuncVal.js.map +1 -1
- package/dist/val/TopVal.js +1 -1
- package/dist/val/TopVal.js.map +1 -1
- package/dist/val/TypeFuncVal.js +1 -1
- package/dist/val/TypeFuncVal.js.map +1 -1
- package/dist/val/UpperFuncVal.js +21 -4
- package/dist/val/UpperFuncVal.js.map +1 -1
- package/dist/val/Val.d.ts +1 -1
- package/dist/val/Val.js +11 -21
- package/dist/val/Val.js.map +1 -1
- package/dist/val/VarVal.js +8 -2
- package/dist/val/VarVal.js.map +1 -1
- package/dist/val/numcmp.d.ts +14 -0
- package/dist/val/numcmp.js +134 -0
- package/dist/val/numcmp.js.map +1 -0
- package/dist/val/numkind.d.ts +6 -0
- package/dist/val/numkind.js +224 -0
- package/dist/val/numkind.js.map +1 -0
- package/dist/val/valutil.d.ts +1 -0
- package/dist/val/valutil.js +35 -0
- package/dist/val/valutil.js.map +1 -1
- package/package.json +7 -5
- package/src/aontu.ts +26 -9
- package/src/cli.ts +48 -21
- package/src/ctx.ts +1 -1
- package/src/err.ts +3 -2
- package/src/exactjson.ts +254 -0
- package/src/hints.ts +213 -4
- package/src/keyorder.ts +73 -0
- package/src/lang.ts +303 -31
- package/src/lsp-server.ts +17 -15
- package/src/lsp.ts +37 -14
- package/src/site.ts +1 -1
- package/src/type.ts +1 -1
- package/src/unify.ts +52 -1
- package/src/utility.ts +3 -9
- package/src/val/BagVal.ts +9 -6
- package/src/val/BigDecimalVal.ts +117 -0
- package/src/val/BigIntegerVal.ts +110 -0
- package/src/val/BooleanVal.ts +1 -1
- package/src/val/CloseFuncVal.ts +1 -1
- package/src/val/ConjunctVal.ts +1 -1
- package/src/val/ConstraintVal.ts +561 -0
- package/src/val/CopyFuncVal.ts +1 -1
- package/src/val/Decimal.ts +385 -0
- package/src/val/DisjunctVal.ts +1 -37
- package/src/val/ExpectVal.ts +1 -1
- package/src/val/FeatureVal.ts +1 -1
- package/src/val/FuncBaseVal.ts +1 -1
- package/src/val/HideFuncVal.ts +1 -1
- package/src/val/IntegerVal.ts +81 -5
- package/src/val/JunctionVal.ts +1 -1
- package/src/val/KeyFuncVal.ts +36 -4
- package/src/val/ListVal.ts +3 -5
- package/src/val/LowerFuncVal.ts +22 -5
- package/src/val/MapVal.ts +13 -9
- package/src/val/MoveFuncVal.ts +27 -10
- package/src/val/NilVal.ts +13 -3
- package/src/val/NullVal.ts +1 -1
- package/src/val/NumberVal.ts +27 -4
- package/src/val/OpBaseVal.ts +10 -10
- package/src/val/OpenFuncVal.ts +1 -1
- package/src/val/PathFuncVal.ts +10 -2
- package/src/val/PlusOpVal.ts +168 -32
- package/src/val/PrefFuncVal.ts +1 -1
- package/src/val/PrefVal.ts +70 -8
- package/src/val/RefVal.ts +143 -11
- package/src/val/ScalarKindVal.ts +123 -12
- package/src/val/ScalarVal.ts +20 -3
- package/src/val/StringVal.ts +1 -1
- package/src/val/SuperFuncVal.ts +15 -2
- package/src/val/TopVal.ts +1 -1
- package/src/val/TypeFuncVal.ts +1 -1
- package/src/val/UpperFuncVal.ts +22 -5
- package/src/val/Val.ts +13 -8
- package/src/val/VarVal.ts +8 -2
- package/src/val/numcmp.ts +189 -0
- package/src/val/numkind.ts +256 -0
- package/src/val/valutil.ts +36 -0
package/src/lang.ts
CHANGED
|
@@ -63,9 +63,18 @@ import {
|
|
|
63
63
|
|
|
64
64
|
|
|
65
65
|
|
|
66
|
-
import {
|
|
66
|
+
import {
|
|
67
|
+
ScalarKindVal,
|
|
68
|
+
BigDecimal,
|
|
69
|
+
BigInteger,
|
|
70
|
+
Float,
|
|
71
|
+
Integer,
|
|
72
|
+
} from './val/ScalarKindVal'
|
|
67
73
|
|
|
68
74
|
|
|
75
|
+
import { BigDecimalVal } from './val/BigDecimalVal'
|
|
76
|
+
import { BigIntegerVal } from './val/BigIntegerVal'
|
|
77
|
+
import { BIG_LITERAL_RE, readBigLiteral } from './val/Decimal'
|
|
69
78
|
import { BooleanVal } from './val/BooleanVal'
|
|
70
79
|
import { ConjunctVal } from './val/ConjunctVal'
|
|
71
80
|
import { DisjunctVal } from './val/DisjunctVal'
|
|
@@ -75,6 +84,7 @@ import { MapVal } from './val/MapVal'
|
|
|
75
84
|
import { NilVal } from './val/NilVal'
|
|
76
85
|
import { NullVal } from './val/NullVal'
|
|
77
86
|
import { NumberVal } from './val/NumberVal'
|
|
87
|
+
import { isIntegerKind, isLossyIntegerLiteral } from './val/numkind'
|
|
78
88
|
import { PrefVal } from './val/PrefVal'
|
|
79
89
|
import { RefVal } from './val/RefVal'
|
|
80
90
|
import { StringVal } from './val/StringVal'
|
|
@@ -92,10 +102,58 @@ import { PrefFuncVal } from './val/PrefFuncVal'
|
|
|
92
102
|
import { CloseFuncVal } from './val/CloseFuncVal'
|
|
93
103
|
import { OpenFuncVal } from './val/OpenFuncVal'
|
|
94
104
|
import { SuperFuncVal } from './val/SuperFuncVal'
|
|
105
|
+
import {
|
|
106
|
+
MinConstraintVal,
|
|
107
|
+
MaxConstraintVal,
|
|
108
|
+
AboveConstraintVal,
|
|
109
|
+
BelowConstraintVal,
|
|
110
|
+
NeqConstraintVal,
|
|
111
|
+
} from './val/ConstraintVal'
|
|
95
112
|
|
|
96
113
|
|
|
97
114
|
const asPlugin = (p: unknown): Plugin => p as Plugin
|
|
98
115
|
|
|
116
|
+
|
|
117
|
+
// Build the Val for a matched `0d` literal (see the `0d` value matcher
|
|
118
|
+
// below). Leaf by source: digits only is a biginteger, a `.` or an
|
|
119
|
+
// exponent makes it a bigdecimal.
|
|
120
|
+
//
|
|
121
|
+
// A literal over the D6 exactness budget becomes a LOCATED ERROR here
|
|
122
|
+
// and not a rounded or expanded value: `0d1e1000000000` has a one-digit
|
|
123
|
+
// coefficient, so only the scale bound catches it, and it is caught at
|
|
124
|
+
// parse -- before plain-form rendering would try to materialise a
|
|
125
|
+
// gigabyte of zeros.
|
|
126
|
+
// The source text of a negated exact literal. `-` is a prefix OPERATOR,
|
|
127
|
+
// not part of the literal, so the text has to be rebuilt here to keep
|
|
128
|
+
// `src` meaning "how this value is spelled" (see bigVal).
|
|
129
|
+
function negsrc(src: string): string {
|
|
130
|
+
// src is never empty here: it is a BIG_LITERAL_RE match (at minimum
|
|
131
|
+
// the two chars `0d`) or an already-negated spelling. Computed exact
|
|
132
|
+
// values do carry src '', but they are built at unify time, long
|
|
133
|
+
// after this parse-time opmap.
|
|
134
|
+
return src.startsWith('-') ? src.slice(1) : '-' + src
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
function bigVal(res: RegExpExecArray): Val {
|
|
139
|
+
const lit = readBigLiteral(res)
|
|
140
|
+
// `src` is the literal's own text, and it is not decoration: a path
|
|
141
|
+
// segment is spelled text, so `$.a.0d1` must address the key `0d1` --
|
|
142
|
+
// the same key `a:{0d1:7}` creates -- rather than the number 1. See
|
|
143
|
+
// RefVal.append. Without it the segment was empty and the reference
|
|
144
|
+
// silently resolved to its own container.
|
|
145
|
+
const src = res[0]
|
|
146
|
+
return 'biginteger' === lit.leaf ? new BigIntegerVal({ peg: lit.int, src }) :
|
|
147
|
+
'bigdecimal' === lit.leaf ? new BigDecimalVal({ peg: lit.dec, src }) :
|
|
148
|
+
new NilVal({ why: lit.code })
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
// Char codes of the literal's fixed opening, for the guard below.
|
|
153
|
+
const CC_0 = 48
|
|
154
|
+
const CC_d = 100
|
|
155
|
+
const CC_D = 68
|
|
156
|
+
|
|
99
157
|
let AontuJsonic: Plugin = function AontuLang(jsonic: Jsonic) {
|
|
100
158
|
|
|
101
159
|
jsonic.use(asPlugin(Path))
|
|
@@ -104,6 +162,12 @@ let AontuJsonic: Plugin = function AontuLang(jsonic: Jsonic) {
|
|
|
104
162
|
// docs/reference-language.md; go/lang.go sets the same). Clear the
|
|
105
163
|
// underlying jsonic comment markers entirely, then define # directly,
|
|
106
164
|
// so the comment set is hash-only regardless of those defaults.
|
|
165
|
+
let dotRef = (r: Rule, ctx: JsonicContext, terms: any, prefix: boolean) => {
|
|
166
|
+
terms = dropUnfilled(terms)
|
|
167
|
+
if (0 === terms.length) return incompleteNil(r, ctx)
|
|
168
|
+
return addsite(new RefVal({ peg: terms, prefix }), r, ctx)
|
|
169
|
+
}
|
|
170
|
+
|
|
107
171
|
jsonic.options({ comment: { def: null } })
|
|
108
172
|
jsonic.options({
|
|
109
173
|
comment: {
|
|
@@ -114,6 +178,104 @@ let AontuJsonic: Plugin = function AontuLang(jsonic: Jsonic) {
|
|
|
114
178
|
},
|
|
115
179
|
})
|
|
116
180
|
|
|
181
|
+
// Digit separators are legal only as a SINGLE separator BETWEEN
|
|
182
|
+
// digits (the rule test/spec/engine-parity.tsv records as the engine's
|
|
183
|
+
// adjudication; pinned by the sep-* rows in
|
|
184
|
+
// test/spec/number-model.tsv). The engine's number matcher
|
|
185
|
+
// enforces most of that already — `1_`, `_1`, `1_.5`, `1._5`, `1e_2`,
|
|
186
|
+
// `1e2_` all fall through to text — but two gaps remain: a REPEATED
|
|
187
|
+
// separator (`1__0` lexed as 10) and a separator at the edge of a
|
|
188
|
+
// base-prefixed digit run (`0x_ff`, `0xff_` lexed as 255). Both
|
|
189
|
+
// silently accept a typo as a different number, so aontu declines the
|
|
190
|
+
// whole run instead and it lexes as text ("1__0"), exactly as `1_`
|
|
191
|
+
// already does.
|
|
192
|
+
//
|
|
193
|
+
// `number.exclude` is tested against the matched number source and,
|
|
194
|
+
// when it matches, makes the matcher decline the entire span. Kept in
|
|
195
|
+
// lock-step with sepInvalid in go/lang.go, which is wired to the
|
|
196
|
+
// equivalent `Number.Exclude` hook (and to the Check hook, which
|
|
197
|
+
// constructs big base-prefixed tokens itself and so bypasses Exclude).
|
|
198
|
+
jsonic.options({
|
|
199
|
+
number: {
|
|
200
|
+
// `__` repeated separator; `0x_`/`0o_`/`0b_` separator opening a
|
|
201
|
+
// base-prefixed run; `_` closing any run. The prefix letter is
|
|
202
|
+
// matched case-insensitively so the rule does not depend on which
|
|
203
|
+
// prefix spellings the engine accepts.
|
|
204
|
+
exclude: /__|^[-+]?0[xXoObB]_|_$/,
|
|
205
|
+
},
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
// D3 -- the `0d` literal, the only route to the exact leaves
|
|
209
|
+
// (biginteger and bigdecimal). See BIG_LITERAL_RE for the grammar and
|
|
210
|
+
// the leaf-by-source rule.
|
|
211
|
+
//
|
|
212
|
+
// The literal is claimed by the TEXT MATCHER'S CHECK HOOK (the sibling
|
|
213
|
+
// of the `Number.Check` hook the Go port already uses for big
|
|
214
|
+
// base-prefixed literals), not by a `value.def` entry, because a `0d`
|
|
215
|
+
// run may contain a `.` and the two claim source differently:
|
|
216
|
+
//
|
|
217
|
+
// - A value def -- even a consuming one, matched against the full
|
|
218
|
+
// forward source -- is applied INSIDE the text matcher, AFTER its
|
|
219
|
+
// ender regexp has already carved the run at the `.`. The def
|
|
220
|
+
// claims `0d1.5` whole, but the matcher then still emits the
|
|
221
|
+
// ender's `.` as a fixed token, so `x:0d1.5` lexed as the
|
|
222
|
+
// bigdecimal FOLLOWED BY a dangling member-access dot (a path
|
|
223
|
+
// cycle). Verified, not theorised.
|
|
224
|
+
// - The check hook runs BEFORE that ender regexp and returns the
|
|
225
|
+
// token outright, so the run is claimed whole and nothing else is
|
|
226
|
+
// emitted.
|
|
227
|
+
//
|
|
228
|
+
// A `match.value` matcher (which runs ahead of every other matcher)
|
|
229
|
+
// also claims it correctly, but it is a candidate at EVERY lex
|
|
230
|
+
// position and materializes the forward source there: ~8% on a
|
|
231
|
+
// text-heavy document, for a syntax almost none of them use. The check
|
|
232
|
+
// hook only runs where the text matcher already runs, and measures at
|
|
233
|
+
// parity with not having it.
|
|
234
|
+
//
|
|
235
|
+
// The number matcher never sees these runs at all: it declines `0d…`
|
|
236
|
+
// outright, since `d` is not an ender.
|
|
237
|
+
jsonic.options({
|
|
238
|
+
text: {
|
|
239
|
+
check: (lex: any) => {
|
|
240
|
+
// Guard first, on char codes: this hook runs at every text
|
|
241
|
+
// position, and the common case (any run that cannot be a `0d`
|
|
242
|
+
// literal) must cost two char reads and no allocation.
|
|
243
|
+
const pnt = lex.pnt
|
|
244
|
+
const src = lex.src
|
|
245
|
+
if (CC_0 !== src.charCodeAt(pnt.sI)) {
|
|
246
|
+
return undefined
|
|
247
|
+
}
|
|
248
|
+
const c1 = src.charCodeAt(pnt.sI + 1)
|
|
249
|
+
if (CC_d !== c1 && CC_D !== c1) {
|
|
250
|
+
return undefined
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// BIG_LITERAL_RE is `^`-anchored and read against the forward
|
|
254
|
+
// source (memoized per position by refwd), which is what lets
|
|
255
|
+
// it claim the `.` of `0d1.5`.
|
|
256
|
+
const res = BIG_LITERAL_RE.exec(lex.refwd())
|
|
257
|
+
if (null == res) {
|
|
258
|
+
return undefined
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const msrc = res[0]
|
|
262
|
+
// The token value is a FUNCTION so Val construction happens at
|
|
263
|
+
// parse time, where the rule and context needed for the site
|
|
264
|
+
// exist (jsonic calls a #VL token's function value with them).
|
|
265
|
+
// A `0d` literal never spans a line, so only the source and
|
|
266
|
+
// column positions advance.
|
|
267
|
+
const tkn = lex.token(
|
|
268
|
+
'#VL',
|
|
269
|
+
(r: Rule, ctx: JsonicContext) => addsite(bigVal(res), r, ctx),
|
|
270
|
+
msrc,
|
|
271
|
+
pnt)
|
|
272
|
+
pnt.sI += msrc.length
|
|
273
|
+
pnt.cI += msrc.length
|
|
274
|
+
return { done: true, token: tkn }
|
|
275
|
+
},
|
|
276
|
+
},
|
|
277
|
+
})
|
|
278
|
+
|
|
117
279
|
// TODO: refactor Val constructor
|
|
118
280
|
// let addsite = (v: Val, p: string[]) => (v.path = [...(p || [])], v)
|
|
119
281
|
let addsite = (v: Val, r: Rule, ctx: JsonicContext) => {
|
|
@@ -121,7 +283,8 @@ let AontuJsonic: Plugin = function AontuLang(jsonic: Jsonic) {
|
|
|
121
283
|
v.site.row = null == r.o0 ? -1 : r.o0.rI
|
|
122
284
|
v.site.col = null == r.o0 ? -1 : r.o0.cI
|
|
123
285
|
v.site.url = ctx.meta.multisource ? ctx.meta.multisource.path : ''
|
|
124
|
-
|
|
286
|
+
// A keyed rule always carries a path array; a keyless one has none.
|
|
287
|
+
v.path = r.k ? [...r.k.path] : []
|
|
125
288
|
|
|
126
289
|
return v
|
|
127
290
|
}
|
|
@@ -161,6 +324,9 @@ help isolate the syntax error.`,
|
|
|
161
324
|
val: (r: Rule, ctx: JsonicContext) =>
|
|
162
325
|
addsite(new ScalarKindVal({ peg: String }), r, ctx)
|
|
163
326
|
},
|
|
327
|
+
// `number` is a pure supertype: it matches a concrete value of
|
|
328
|
+
// any numeric leaf and never tags one itself. `integer` and
|
|
329
|
+
// `float` are the leaves (see ScalarKindVal for the lattice).
|
|
164
330
|
'number': {
|
|
165
331
|
val: (r: Rule, ctx: JsonicContext) =>
|
|
166
332
|
addsite(new ScalarKindVal({ peg: Number }), r, ctx)
|
|
@@ -169,6 +335,21 @@ help isolate the syntax error.`,
|
|
|
169
335
|
val: (r: Rule, ctx: JsonicContext) =>
|
|
170
336
|
addsite(new ScalarKindVal({ peg: Integer }), r, ctx)
|
|
171
337
|
},
|
|
338
|
+
'float': {
|
|
339
|
+
val: (r: Rule, ctx: JsonicContext) =>
|
|
340
|
+
addsite(new ScalarKindVal({ peg: Float }), r, ctx)
|
|
341
|
+
},
|
|
342
|
+
// The two exact leaves. Their keywords are the marker class
|
|
343
|
+
// names lowercased, which is also how ScalarKindVal canons them.
|
|
344
|
+
'biginteger': {
|
|
345
|
+
val: (r: Rule, ctx: JsonicContext) =>
|
|
346
|
+
addsite(new ScalarKindVal({ peg: BigInteger }), r, ctx)
|
|
347
|
+
},
|
|
348
|
+
'bigdecimal': {
|
|
349
|
+
val: (r: Rule, ctx: JsonicContext) =>
|
|
350
|
+
addsite(new ScalarKindVal({ peg: BigDecimal }), r, ctx)
|
|
351
|
+
},
|
|
352
|
+
|
|
172
353
|
'boolean': {
|
|
173
354
|
val: (r: Rule, ctx: JsonicContext) =>
|
|
174
355
|
addsite(new ScalarKindVal({ peg: Boolean }), r, ctx)
|
|
@@ -229,6 +410,16 @@ help isolate the syntax error.`,
|
|
|
229
410
|
close: CloseFuncVal,
|
|
230
411
|
open: OpenFuncVal,
|
|
231
412
|
super: SuperFuncVal,
|
|
413
|
+
|
|
414
|
+
// The constraint algebra's Band A atoms (G1 phase 1;
|
|
415
|
+
// docs/reference-language.md, "The constraint algebra"): bounds
|
|
416
|
+
// and exclusion enter through the function registry — the
|
|
417
|
+
// established extension point — with zero grammar change.
|
|
418
|
+
min: MinConstraintVal,
|
|
419
|
+
max: MaxConstraintVal,
|
|
420
|
+
above: AboveConstraintVal,
|
|
421
|
+
below: BelowConstraintVal,
|
|
422
|
+
neq: NeqConstraintVal,
|
|
232
423
|
}
|
|
233
424
|
|
|
234
425
|
|
|
@@ -249,17 +440,14 @@ help isolate the syntax error.`,
|
|
|
249
440
|
'disjunct-infix': (r: Rule, ctx: JsonicContext, _op: Op, terms: any) =>
|
|
250
441
|
addsite(new DisjunctVal({ peg: dropUnfilled(terms) }), r, ctx),
|
|
251
442
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
443
|
+
// `.a` (prefix) and `a.b` (infix) build the same reference; only the
|
|
444
|
+
// prefix flag differs, and both need the same missing-operand guard,
|
|
445
|
+
// so they share one builder.
|
|
446
|
+
'dot-prefix': (r: Rule, ctx: JsonicContext, _op: Op, terms: any) =>
|
|
447
|
+
dotRef(r, ctx, terms, true),
|
|
257
448
|
|
|
258
|
-
'dot-infix': (r: Rule, ctx: JsonicContext, _op: Op, terms: any) =>
|
|
259
|
-
|
|
260
|
-
if (0 === terms.length) return incompleteNil(r, ctx)
|
|
261
|
-
return addsite(new RefVal({ peg: terms }), r, ctx)
|
|
262
|
-
},
|
|
449
|
+
'dot-infix': (r: Rule, ctx: JsonicContext, _op: Op, terms: any) =>
|
|
450
|
+
dotRef(r, ctx, terms, false),
|
|
263
451
|
|
|
264
452
|
'star-prefix': (r: Rule, ctx: JsonicContext, _op: Op, terms: any) => {
|
|
265
453
|
if (null == terms[0]) return incompleteNil(r, ctx)
|
|
@@ -284,6 +472,18 @@ help isolate the syntax error.`,
|
|
|
284
472
|
'negative-prefix': (r: Rule, ctx: JsonicContext, _op: Op, terms: any) => {
|
|
285
473
|
let val = terms[0]
|
|
286
474
|
if (null == val) return incompleteNil(r, ctx)
|
|
475
|
+
// The exact leaves negate exactly and never change kind. R2/D5
|
|
476
|
+
// holds here too: bigint has a single zero, and Decimal's
|
|
477
|
+
// normalising constructor sends every zero to the same form, so
|
|
478
|
+
// `-0d0` is `0d0` and `-0d0.0` is `0d0.0`.
|
|
479
|
+
if (val instanceof BigIntegerVal) {
|
|
480
|
+
return addsite(new BigIntegerVal(
|
|
481
|
+
{ peg: -val.peg, src: negsrc(val.src) }), r, ctx)
|
|
482
|
+
}
|
|
483
|
+
if (val instanceof BigDecimalVal) {
|
|
484
|
+
return addsite(new BigDecimalVal(
|
|
485
|
+
{ peg: val.peg.negate(), src: negsrc(val.src) }), r, ctx)
|
|
486
|
+
}
|
|
287
487
|
// Negating a non-numeric operand (`k-x` splits into k, -x) is an
|
|
288
488
|
// error nil, not NaN (mirrors negate() in go/lang.go).
|
|
289
489
|
if (!(val instanceof IntegerVal) && !(val instanceof NumberVal)) {
|
|
@@ -296,7 +496,11 @@ help isolate the syntax error.`,
|
|
|
296
496
|
let peg = -1 * val.peg
|
|
297
497
|
// Normalize -0 to 0 (keeps the AST and canon free of negative zero).
|
|
298
498
|
if (0 === peg) peg = 0
|
|
299
|
-
|
|
499
|
+
// Negation never narrows the kind: a number stays a number. An
|
|
500
|
+
// integer stays an integer unless the negation leaves the int64
|
|
501
|
+
// range (only -(-2^63), which no literal can express), in which
|
|
502
|
+
// case it widens to a number rather than failing.
|
|
503
|
+
const out = val instanceof IntegerVal && isIntegerKind(peg)
|
|
300
504
|
? new IntegerVal({ peg })
|
|
301
505
|
: new NumberVal({ peg })
|
|
302
506
|
return addsite(out, r, ctx)
|
|
@@ -347,6 +551,28 @@ help isolate the syntax error.`,
|
|
|
347
551
|
right: 21_000_000,
|
|
348
552
|
},
|
|
349
553
|
|
|
554
|
+
// Re-base the unary prefixes for the same reason. Every aontu
|
|
555
|
+
// operator sits far above the @tabnas/expr defaults, so the
|
|
556
|
+
// default prefix binding power of 4_000_000 left unary `-`/`+`
|
|
557
|
+
// LOOSER than every infix operator: `-1 & integer` parsed as
|
|
558
|
+
// `-(1 & integer)`, and negative-prefix (below) then rejected
|
|
559
|
+
// the composite operand as a `negative` error nil (likewise
|
|
560
|
+
// `-2+3`, `-1|2`). Unary minus must bind tighter than `+`, `&`
|
|
561
|
+
// and `|` — but still looser than `.` (dot-infix left is
|
|
562
|
+
// 25_000_000), so `-0xFF.5` stays `-(0xFF.5)` and `$`.
|
|
563
|
+
// Kept in lock-step with the op table in go/lang.go.
|
|
564
|
+
'negative': {
|
|
565
|
+
src: '-',
|
|
566
|
+
prefix: true,
|
|
567
|
+
right: 22_000_000,
|
|
568
|
+
},
|
|
569
|
+
|
|
570
|
+
'positive': {
|
|
571
|
+
src: '+',
|
|
572
|
+
prefix: true,
|
|
573
|
+
right: 22_000_000,
|
|
574
|
+
},
|
|
575
|
+
|
|
350
576
|
'dollar-prefix': {
|
|
351
577
|
src: '$',
|
|
352
578
|
prefix: true,
|
|
@@ -489,8 +715,28 @@ help isolate the syntax error.`,
|
|
|
489
715
|
if (!Number.isFinite(r.node)) {
|
|
490
716
|
valnode = addsite(new NilVal({ why: 'not_number' }), r, ctx)
|
|
491
717
|
}
|
|
492
|
-
//
|
|
493
|
-
|
|
718
|
+
// D7 -- A LOSSY INTEGER LITERAL IS REFUSED, NOT ROUNDED. The
|
|
719
|
+
// token above is already a double, so a literal the double
|
|
720
|
+
// cannot hold exactly (2^53+1, 0x7fffffffffffffff,
|
|
721
|
+
// 0xffffffffffffffff) has ALREADY become a different number by
|
|
722
|
+
// the time it gets here. Storing it would mean the document
|
|
723
|
+
// silently means something other than what it says, so the
|
|
724
|
+
// literal becomes a located error whose hint names the escape:
|
|
725
|
+
// write it `0d…` and get the exact value.
|
|
726
|
+
//
|
|
727
|
+
// The rule is EXACTNESS, not magnitude -- 10^20 and 2^124 are
|
|
728
|
+
// both far outside the int64 window and both land exactly on a
|
|
729
|
+
// binary64, so both stay values (see isLossyIntegerLiteral).
|
|
730
|
+
else if (isLossyIntegerLiteral(r.node, r.o0.src)) {
|
|
731
|
+
const nil = new NilVal({ why: 'lossy_integer_literal' })
|
|
732
|
+
nil.details = { src: r.o0.src }
|
|
733
|
+
valnode = addsite(nil, r, ctx)
|
|
734
|
+
}
|
|
735
|
+
// A literal is integer kind only if its source has no '.', its
|
|
736
|
+
// value is integral, and it fits the int64 range: `1.0` is a
|
|
737
|
+
// number, and so are 1e21 and 100000000000000000000 (see
|
|
738
|
+
// isIntegerKind).
|
|
739
|
+
else if (isIntegerKind(r.node, r.o0.src)) {
|
|
494
740
|
valnode = addsite(new IntegerVal({ peg: r.node, src: r.o0.src }), r, ctx)
|
|
495
741
|
}
|
|
496
742
|
else {
|
|
@@ -588,18 +834,10 @@ help isolate the syntax error.`,
|
|
|
588
834
|
}
|
|
589
835
|
}
|
|
590
836
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
// TODO: needs addpath?
|
|
596
|
-
let aopv = new ListVal({ peg: aop })
|
|
597
|
-
aopv.optionalKeys = optionalKeys
|
|
598
|
-
|
|
599
|
-
r.node =
|
|
600
|
-
addsite(new ConjunctVal({ peg: [aopv, ...ao.___merge] }), r, ctx)
|
|
601
|
-
}
|
|
602
|
-
else {
|
|
837
|
+
// No ___merge arm here: the deferred map.merge that writes it
|
|
838
|
+
// only ever fires for a `pair` rule, whose parent is always a
|
|
839
|
+
// `map` rule with a plain-object node — never a list.
|
|
840
|
+
{
|
|
603
841
|
r.node = addsite(new ListVal({ peg: ao }), r, ctx)
|
|
604
842
|
r.node.optionalKeys = optionalKeys
|
|
605
843
|
}
|
|
@@ -779,7 +1017,10 @@ function makeModelResolver(options: any) {
|
|
|
779
1017
|
jsonic: Tabnas
|
|
780
1018
|
) {
|
|
781
1019
|
|
|
782
|
-
|
|
1020
|
+
// The aontu val rule's ac has already wrapped every raw string node
|
|
1021
|
+
// as a StringVal, so spec is a Val here (or a raw object from a
|
|
1022
|
+
// .json/.js include, whose peg is undefined -> not found).
|
|
1023
|
+
let path = spec?.peg
|
|
783
1024
|
|
|
784
1025
|
// A bare `@` with no path (`a:@`) has nothing to resolve; report
|
|
785
1026
|
// not-found instead of crashing in the underlying resolvers.
|
|
@@ -821,6 +1062,34 @@ function makeModelResolver(options: any) {
|
|
|
821
1062
|
// the aontu val rule conversions (mirrors asVal in go/lang.go; like
|
|
822
1063
|
// there, source text is unavailable, so an integral number is an
|
|
823
1064
|
// integer).
|
|
1065
|
+
// The targeted parse hint for CUE-trained authors and models: `>` and
|
|
1066
|
+
// `<` are not Aontu operators (the op-chars reservation stands), and an
|
|
1067
|
+
// agent that emits `number > 0` should be redirected to the bound
|
|
1068
|
+
// atoms, not left with a bare "unexpected character". Appended to a
|
|
1069
|
+
// parse error's message when the source carries an unquoted `<` or
|
|
1070
|
+
// `>`; the Go twin is opCharHint in go/lang.go, byte-identical text.
|
|
1071
|
+
function opCharHint(src: string): string {
|
|
1072
|
+
let q = ''
|
|
1073
|
+
for (let i = 0; i < src.length; i++) {
|
|
1074
|
+
const c = src[i]
|
|
1075
|
+
if ('' !== q) {
|
|
1076
|
+
if (c === q && '\\' !== src[i - 1]) {
|
|
1077
|
+
q = ''
|
|
1078
|
+
}
|
|
1079
|
+
continue
|
|
1080
|
+
}
|
|
1081
|
+
if ('"' === c || '\'' === c || '`' === c) {
|
|
1082
|
+
q = c
|
|
1083
|
+
}
|
|
1084
|
+
else if ('<' === c || '>' === c) {
|
|
1085
|
+
return '\nThe > and < characters are not Aontu operators: write the ' +
|
|
1086
|
+
'bound functions min(x), max(x), above(x), below(x) instead.'
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
return ''
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
|
|
824
1093
|
function rawToVal(n: any): Val {
|
|
825
1094
|
if (null == n) {
|
|
826
1095
|
return new NullVal({ peg: null })
|
|
@@ -836,7 +1105,10 @@ function rawToVal(n: any): Val {
|
|
|
836
1105
|
return new StringVal({ peg: n })
|
|
837
1106
|
}
|
|
838
1107
|
if ('number' === t) {
|
|
839
|
-
|
|
1108
|
+
// No source text here, so the "no '.'" condition is vacuous and the
|
|
1109
|
+
// integral + int64-range conditions decide (same helper as the val
|
|
1110
|
+
// rule, so the two paths cannot drift).
|
|
1111
|
+
return isIntegerKind(n) ?
|
|
840
1112
|
new IntegerVal({ peg: n }) : new NumberVal({ peg: n })
|
|
841
1113
|
}
|
|
842
1114
|
if ('boolean' === t) {
|
|
@@ -940,7 +1212,7 @@ class Lang {
|
|
|
940
1212
|
why: 'parse',
|
|
941
1213
|
err: new NilVal({
|
|
942
1214
|
why: 'syntax',
|
|
943
|
-
msg: e.message,
|
|
1215
|
+
msg: e.message + opCharHint(src),
|
|
944
1216
|
err: e,
|
|
945
1217
|
})
|
|
946
1218
|
})
|
|
@@ -952,7 +1224,7 @@ class Lang {
|
|
|
952
1224
|
|
|
953
1225
|
return val
|
|
954
1226
|
}
|
|
955
|
-
}
|
|
1227
|
+
} /* node:coverage ignore next 6 */
|
|
956
1228
|
|
|
957
1229
|
export {
|
|
958
1230
|
Lang,
|
package/src/lsp-server.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
1
|
/* Copyright (c) 2025 Richard Rodger, MIT License */
|
|
3
2
|
|
|
4
3
|
// Aontu Language Server (stdio).
|
|
@@ -85,25 +84,28 @@ class FrameCodec {
|
|
|
85
84
|
}
|
|
86
85
|
|
|
87
86
|
|
|
88
|
-
|
|
87
|
+
// The streams and exit are injectable (defaulting to real stdio) so the
|
|
88
|
+
// full wiring is unit-testable — the same shape as the Go server's
|
|
89
|
+
// serve(in, out, logw).
|
|
90
|
+
function main(
|
|
91
|
+
stdin: NodeJS.ReadableStream = process.stdin,
|
|
92
|
+
write: (chunk: Buffer) => void = (chunk) => void process.stdout.write(chunk),
|
|
93
|
+
exit: (code: number) => void = (code) => process.exit(code),
|
|
94
|
+
): FrameCodec {
|
|
89
95
|
const handler = new LspHandler()
|
|
90
|
-
const codec = new FrameCodec(
|
|
91
|
-
handler,
|
|
92
|
-
(chunk) => process.stdout.write(chunk),
|
|
93
|
-
(code) => process.exit(code),
|
|
94
|
-
)
|
|
95
|
-
|
|
96
|
-
process.stdin.on('data', (chunk: Buffer) => codec.push(chunk))
|
|
97
|
-
process.stdin.on('end', () => codec.end())
|
|
98
|
-
}
|
|
96
|
+
const codec = new FrameCodec(handler, write, exit)
|
|
99
97
|
|
|
98
|
+
stdin.on('data', (chunk: Buffer) => codec.push(chunk))
|
|
99
|
+
stdin.on('end', () => codec.end())
|
|
100
|
+
return codec
|
|
101
|
+
} /* node:coverage ignore next 11 */
|
|
100
102
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
103
|
+
|
|
104
|
+
// No require.main guard here: bin/aontu-lsp.js is the executable entry
|
|
105
|
+
// and calls main() itself, so this module stays import-only.
|
|
105
106
|
|
|
106
107
|
|
|
107
108
|
export {
|
|
108
109
|
FrameCodec,
|
|
110
|
+
main,
|
|
109
111
|
}
|
package/src/lsp.ts
CHANGED
|
@@ -78,8 +78,9 @@ function computeDiagnostics(
|
|
|
78
78
|
const aontu = new Aontu()
|
|
79
79
|
|
|
80
80
|
let root: any
|
|
81
|
+
let ac: any
|
|
81
82
|
try {
|
|
82
|
-
|
|
83
|
+
ac = aontu.ctx({ collect: true })
|
|
83
84
|
if (opts?.vars) {
|
|
84
85
|
Object.assign(ac.vars, opts.vars)
|
|
85
86
|
}
|
|
@@ -92,7 +93,21 @@ function computeDiagnostics(
|
|
|
92
93
|
}
|
|
93
94
|
|
|
94
95
|
const nils: any[] = []
|
|
95
|
-
|
|
96
|
+
const seen = new Set()
|
|
97
|
+
walkNils(root, nils, seen)
|
|
98
|
+
|
|
99
|
+
// Errors recorded on the context but not present in the tree — e.g. a
|
|
100
|
+
// budget_passes exhaustion nil, which is about the whole evaluation
|
|
101
|
+
// rather than any node — would otherwise be invisible here, and the
|
|
102
|
+
// trust contract forbids silent truncation (docs/trust.md clause 2).
|
|
103
|
+
// Tree nils are already on ctx.err too, so dedup by identity; the
|
|
104
|
+
// transient disjunct-trial sentinel never surfaces.
|
|
105
|
+
for (const e of ac.err) {
|
|
106
|
+
if (e?.isNil && '|:trial-nil' !== e.why && !seen.has(e)) {
|
|
107
|
+
seen.add(e)
|
|
108
|
+
nils.push(e)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
96
111
|
|
|
97
112
|
return nils.map(nilToDiagnostic)
|
|
98
113
|
}
|
|
@@ -277,8 +292,8 @@ function collectHoverCandidates(
|
|
|
277
292
|
if (seen.has(v)) return
|
|
278
293
|
seen.add(v)
|
|
279
294
|
|
|
280
|
-
const row = v.site
|
|
281
|
-
const col = v.site
|
|
295
|
+
const row = v.site.row
|
|
296
|
+
const col = v.site.col
|
|
282
297
|
let canon = ''
|
|
283
298
|
try { canon = v.canon } catch { canon = '' }
|
|
284
299
|
// Hover targets concrete values (scalars, kinds, refs, …), not
|
|
@@ -305,8 +320,9 @@ function collectHoverCandidates(
|
|
|
305
320
|
|
|
306
321
|
|
|
307
322
|
function hoverMarkdown(val: any): string {
|
|
308
|
-
|
|
309
|
-
|
|
323
|
+
// No try needed: a Val only becomes a hover candidate after
|
|
324
|
+
// collectHoverCandidates read this same getter successfully.
|
|
325
|
+
const canon = val.canon
|
|
310
326
|
return '```aontu\n' + canon + '\n```\n\n' + '*' + valKind(val) + '*'
|
|
311
327
|
}
|
|
312
328
|
|
|
@@ -315,11 +331,14 @@ function hoverMarkdown(val: any): string {
|
|
|
315
331
|
function valKind(val: any): string {
|
|
316
332
|
if (val.isNil) return 'error'
|
|
317
333
|
if (val.isScalarKind) return 'type'
|
|
318
|
-
if (val.
|
|
319
|
-
if (val.isList) return 'list'
|
|
334
|
+
if (val.isConstraint) return 'constraint'
|
|
320
335
|
if (val.isRef) return 'reference'
|
|
321
336
|
if (val.isInteger) return 'integer'
|
|
322
|
-
|
|
337
|
+
// NumberVal is the binary64 leaf, whose kind keyword is `float`;
|
|
338
|
+
// `number` is the supertype and never labels a concrete value.
|
|
339
|
+
if (val.isNumber) return 'float'
|
|
340
|
+
if (val.isBigInteger) return 'biginteger'
|
|
341
|
+
if (val.isBigDecimal) return 'bigdecimal'
|
|
323
342
|
if (val.isString) return 'string'
|
|
324
343
|
if (val.isBoolean) return 'boolean'
|
|
325
344
|
if (val.isScalar) return 'scalar'
|
|
@@ -339,15 +358,19 @@ type CompletionItem = {
|
|
|
339
358
|
const COMPLETION_FUNCTION = 3
|
|
340
359
|
const COMPLETION_KEYWORD = 14
|
|
341
360
|
|
|
342
|
-
// The
|
|
361
|
+
// The seventeen built-in functions. Kept in sync with the engine by
|
|
343
362
|
// `lsp.test.ts`, which asserts each is recognised and no others are.
|
|
344
363
|
const BUILTIN_FUNCS = [
|
|
345
|
-
'close', 'copy', 'hide', 'key', 'lower', '
|
|
346
|
-
'open', 'path', 'pref', 'super', 'type', 'upper',
|
|
364
|
+
'above', 'below', 'close', 'copy', 'hide', 'key', 'lower', 'max',
|
|
365
|
+
'min', 'move', 'neq', 'open', 'path', 'pref', 'super', 'type', 'upper',
|
|
347
366
|
]
|
|
348
367
|
|
|
349
368
|
// Scalar-kind and literal keywords.
|
|
350
|
-
|
|
369
|
+
// `number` is the numeric supertype; `integer`, `float`, `biginteger`
|
|
370
|
+
// and `bigdecimal` are its leaves. New leaves join this list as they land.
|
|
371
|
+
const KIND_KEYWORDS = [
|
|
372
|
+
'string', 'number', 'integer', 'float', 'biginteger', 'bigdecimal', 'boolean',
|
|
373
|
+
]
|
|
351
374
|
const LITERAL_KEYWORDS = ['true', 'false', 'null', 'top']
|
|
352
375
|
|
|
353
376
|
|
|
@@ -456,7 +479,7 @@ class LspHandler {
|
|
|
456
479
|
private publish(uri: string): OutMessage {
|
|
457
480
|
return publishDiagnosticsMsg(uri, computeDiagnostics(this.docs.get(uri) ?? ''))
|
|
458
481
|
}
|
|
459
|
-
}
|
|
482
|
+
} /* node:coverage ignore next 28 */
|
|
460
483
|
|
|
461
484
|
|
|
462
485
|
export {
|
package/src/site.ts
CHANGED