aontu 0.45.1 → 0.48.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.
Files changed (68) hide show
  1. package/dist/aontu.js +5 -0
  2. package/dist/aontu.js.map +1 -1
  3. package/dist/cli.d.ts +9 -0
  4. package/dist/cli.js +203 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/err.js +1 -1
  7. package/dist/err.js.map +1 -1
  8. package/dist/hints.js +1 -0
  9. package/dist/hints.js.map +1 -1
  10. package/dist/lang.d.ts +1 -1
  11. package/dist/lang.js +45 -18
  12. package/dist/lang.js.map +1 -1
  13. package/dist/lsp-server.d.ts +14 -0
  14. package/dist/lsp-server.js +87 -0
  15. package/dist/lsp-server.js.map +1 -0
  16. package/dist/lsp.d.ts +71 -0
  17. package/dist/lsp.js +375 -0
  18. package/dist/lsp.js.map +1 -0
  19. package/dist/tsconfig.tsbuildinfo +1 -1
  20. package/dist/type.d.ts +1 -1
  21. package/dist/unify.js +31 -4
  22. package/dist/unify.js.map +1 -1
  23. package/dist/utility.js +9 -2
  24. package/dist/utility.js.map +1 -1
  25. package/dist/val/BagVal.js +20 -2
  26. package/dist/val/BagVal.js.map +1 -1
  27. package/dist/val/DisjunctVal.js +25 -16
  28. package/dist/val/DisjunctVal.js.map +1 -1
  29. package/dist/val/IntegerVal.js +1 -0
  30. package/dist/val/IntegerVal.js.map +1 -1
  31. package/dist/val/LowerFuncVal.d.ts +1 -1
  32. package/dist/val/LowerFuncVal.js +1 -1
  33. package/dist/val/LowerFuncVal.js.map +1 -1
  34. package/dist/val/MapVal.js +127 -16
  35. package/dist/val/MapVal.js.map +1 -1
  36. package/dist/val/NumberVal.js +7 -2
  37. package/dist/val/NumberVal.js.map +1 -1
  38. package/dist/val/OpBaseVal.d.ts +1 -1
  39. package/dist/val/OpBaseVal.js +1 -1
  40. package/dist/val/OpBaseVal.js.map +1 -1
  41. package/dist/val/ScalarVal.js +3 -1
  42. package/dist/val/ScalarVal.js.map +1 -1
  43. package/dist/val/Val.d.ts +2 -0
  44. package/dist/val/Val.js +16 -1
  45. package/dist/val/Val.js.map +1 -1
  46. package/dist/val/VarVal.js +5 -3
  47. package/dist/val/VarVal.js.map +1 -1
  48. package/package.json +25 -13
  49. package/src/aontu.ts +5 -0
  50. package/src/cli.ts +193 -0
  51. package/src/err.ts +1 -1
  52. package/src/hints.ts +2 -0
  53. package/src/lang.ts +46 -18
  54. package/src/lsp-server.ts +109 -0
  55. package/src/lsp.ts +486 -0
  56. package/src/type.ts +1 -1
  57. package/src/unify.ts +33 -4
  58. package/src/utility.ts +11 -2
  59. package/src/val/BagVal.ts +22 -2
  60. package/src/val/DisjunctVal.ts +27 -18
  61. package/src/val/IntegerVal.ts +1 -0
  62. package/src/val/LowerFuncVal.ts +1 -1
  63. package/src/val/MapVal.ts +138 -23
  64. package/src/val/NumberVal.ts +7 -2
  65. package/src/val/OpBaseVal.ts +1 -1
  66. package/src/val/ScalarVal.ts +3 -1
  67. package/src/val/Val.ts +18 -1
  68. package/src/val/VarVal.ts +6 -4
package/src/lang.ts CHANGED
@@ -5,42 +5,43 @@
5
5
 
6
6
  import {
7
7
  Jsonic,
8
+ Tabnas,
8
9
  Plugin,
9
10
  Rule,
10
11
  RuleSpec,
11
12
  Context as JsonicContext,
12
13
  JsonicError,
13
- } from 'jsonic'
14
+ } from '@tabnas/jsonic'
14
15
 
15
16
 
16
- import { Debug } from 'jsonic/debug'
17
+ import { Debug } from '@tabnas/debug'
17
18
 
18
19
  import {
19
20
  MultiSource
20
- } from '@jsonic/multisource'
21
+ } from '@tabnas/multisource'
21
22
 
22
- // TODO: @jsonic/multisource should support virtual fs
23
+ // TODO: @tabnas/multisource should support virtual fs
23
24
 
24
25
  import {
25
26
  makeFileResolver
26
- } from '@jsonic/multisource/resolver/file'
27
+ } from '@tabnas/multisource/resolver/file'
27
28
 
28
29
  import {
29
30
  makePkgResolver
30
- } from '@jsonic/multisource/resolver/pkg'
31
+ } from '@tabnas/multisource/resolver/pkg'
31
32
 
32
33
  import {
33
34
  makeMemResolver
34
- } from '@jsonic/multisource/resolver/mem'
35
+ } from '@tabnas/multisource/resolver/mem'
35
36
 
36
37
  import {
37
38
  Expr,
38
39
  Op,
39
- } from '@jsonic/expr'
40
+ } from '@tabnas/expr'
40
41
 
41
42
  import {
42
43
  Path
43
- } from '@jsonic/path'
44
+ } from '@tabnas/path'
44
45
 
45
46
  import type {
46
47
  Val,
@@ -93,9 +94,11 @@ import { OpenFuncVal } from './val/OpenFuncVal'
93
94
  import { SuperFuncVal } from './val/SuperFuncVal'
94
95
 
95
96
 
97
+ const asPlugin = (p: unknown): Plugin => p as Plugin
98
+
96
99
  let AontuJsonic: Plugin = function AontuLang(jsonic: Jsonic) {
97
100
 
98
- jsonic.use(Path)
101
+ jsonic.use(asPlugin(Path))
99
102
 
100
103
  // TODO: refactor Val constructor
101
104
  // let addsite = (v: Val, p: string[]) => (v.path = [...(p || [])], v)
@@ -250,6 +253,8 @@ help isolate the syntax error.`,
250
253
  'negative-prefix': (r: Rule, ctx: JsonicContext, _op: Op, terms: any) => {
251
254
  let val = terms[0]
252
255
  val.peg = -1 * val.peg
256
+ // Normalize -0 to 0 (keeps the AST and canon free of negative zero).
257
+ if (0 === val.peg) val.peg = 0
253
258
  return addsite(val, r, ctx)
254
259
  },
255
260
 
@@ -277,7 +282,7 @@ help isolate the syntax error.`,
277
282
 
278
283
 
279
284
  jsonic
280
- .use(Expr, {
285
+ .use(asPlugin(Expr), {
281
286
  op: {
282
287
  // disjunct < conjunct: c & b | a -> (c & b) | a
283
288
  'conjunct': {
@@ -374,13 +379,23 @@ help isolate the syntax error.`,
374
379
 
375
380
  rs
376
381
  .open([
377
- { s: [CJ, CL], p: 'map', b: 2, n: { pk: 1 }, g: 'spread' },
382
+ {
383
+ s: [CJ, CL], p: 'map', b: 2, n: { pk: 1 },
384
+ // @tabnas seeds a descended rule's node from its parent; without
385
+ // a fresh node here the nested spread map (`a:&:{x:1}`) would
386
+ // share the parent map's node object and self-reference.
387
+ a: (r: Rule) => { r.node = {} },
388
+ g: 'spread'
389
+ },
378
390
 
379
391
  {
380
392
  s: [OPTKEY, QM],
381
393
  c: (r) => 0 == r.d,
382
394
  p: 'map',
383
395
  b: 2,
396
+ // Fresh node (see spread alt above): the optional dive descends
397
+ // to a map and must not share the parent's node object.
398
+ a: (r: Rule) => { r.node = {} },
384
399
  g: 'pair,jsonic,top,aontu-optional',
385
400
  },
386
401
 
@@ -389,12 +404,13 @@ help isolate the syntax error.`,
389
404
  p: 'map',
390
405
  b: 2,
391
406
  n: { pk: 1 },
407
+ a: (r: Rule) => { r.node = {} },
392
408
  g: 'pair,jsonic,top,dive,aontu-optional',
393
409
  },
394
410
 
395
411
  ])
396
412
 
397
- .bc((r: Rule, ctx: JsonicContext) => {
413
+ .ac((r: Rule, ctx: JsonicContext) => {
398
414
 
399
415
  let valnode: Val = r.node
400
416
  let valtype = typeof valnode
@@ -555,7 +571,7 @@ help isolate the syntax error.`,
555
571
  }
556
572
  ])
557
573
 
558
- // NOTE: manually adjust path - @jsonic/path ignores as not pair:true
574
+ // NOTE: manually adjust path - @tabnas/path ignores as not pair:true
559
575
  .ao((r) => {
560
576
  if (0 < r.d && r.u.spread) {
561
577
  r.child.k.path = [...r.k.path, '&']
@@ -640,6 +656,13 @@ help isolate the syntax error.`,
640
656
 
641
657
 
642
658
 
659
+ // SECURITY: the default resolver reads any file/package the process can
660
+ // reach — @"path" follows relative paths (`@"../../etc/passwd"`) and
661
+ // symlinks with no containment check, and @"pkg" can require() arbitrary
662
+ // installed modules. This is intentional for the CLI, but it means a
663
+ // `.aon` source can read referenced files; the LSP uses this same
664
+ // resolver, so treat opening an untrusted source as running it. Pass a
665
+ // confined `options.resolver` to restrict reads in less-trusted contexts.
643
666
  function makeModelResolver(options: any) {
644
667
  const useRequire = options.require || require
645
668
 
@@ -662,7 +685,7 @@ function makeModelResolver(options: any) {
662
685
  popts: any,
663
686
  rule: Rule,
664
687
  ctx: JsonicContext,
665
- jsonic: Jsonic
688
+ jsonic: Tabnas
666
689
  ) {
667
690
 
668
691
  let path = 'string' === typeof spec ? spec : spec?.peg
@@ -711,20 +734,25 @@ class Lang {
711
734
  this.jsonic = Jsonic.make()
712
735
 
713
736
  if (this.opts.debug) {
714
- this.jsonic.use(Debug, {
737
+ this.jsonic.use(asPlugin(Debug), {
715
738
  trace: this.opts.trace
716
739
  })
717
740
  }
718
741
 
719
742
  this.jsonic
720
- .use(AontuJsonic)
721
- .use(MultiSource, {
743
+ .use(asPlugin(MultiSource), {
722
744
  resolver: options?.resolver || modelResolver,
745
+ // `.aon` is the preferred Aontu source extension; `.aontu` also
746
+ // works. `.jsonic` is retired (no longer auto-resolved); the
747
+ // default `['jsonic','jsc','json','js']` is overridden here.
748
+ // (Upstream option name is the misspelled `implictExt`.)
749
+ implictExt: ['aon', 'aontu'],
723
750
  processor: {
724
751
  aontu: 'jsonic',
725
752
  aon: 'jsonic',
726
753
  }
727
754
  })
755
+ .use(AontuJsonic)
728
756
  }
729
757
 
730
758
 
@@ -0,0 +1,109 @@
1
+ #!/usr/bin/env node
2
+ /* Copyright (c) 2025 Richard Rodger, MIT License */
3
+
4
+ // Aontu Language Server (stdio).
5
+ //
6
+ // aontu-lsp
7
+ //
8
+ // Speaks LSP over stdio (JSON-RPC with Content-Length framing) and
9
+ // publishes unification diagnostics as `.aontu` files are edited. This
10
+ // binary is intentionally thin: all protocol logic lives in the reusable
11
+ // library ./lsp (LspHandler + computeDiagnostics). See docs/lsp.md for
12
+ // editor configuration.
13
+
14
+ import { LspHandler, Message, OutMessage } from './lsp'
15
+
16
+
17
+ // A byte-level LSP framing codec: feed it incoming chunks, give it a
18
+ // handler, and it decodes Content-Length frames, dispatches them, and
19
+ // writes framed replies. Kept transport-injectable (write/onExit) so it
20
+ // can be unit-tested without real stdio.
21
+ class FrameCodec {
22
+ private buffer = Buffer.alloc(0)
23
+
24
+ constructor(
25
+ private handler: LspHandler,
26
+ private write: (chunk: Buffer) => void,
27
+ private onExit: (code: number) => void,
28
+ ) { }
29
+
30
+ // Feed a chunk of incoming bytes; processes any complete frames.
31
+ push(chunk: Buffer) {
32
+ this.buffer = Buffer.concat([this.buffer, chunk])
33
+ this.drain()
34
+ }
35
+
36
+ // Called when the input stream ends.
37
+ end() {
38
+ this.onExit(this.handler.exitCode)
39
+ }
40
+
41
+ private drain() {
42
+ for (; ;) {
43
+ const headerEnd = this.buffer.indexOf('\r\n\r\n')
44
+ if (headerEnd < 0) return
45
+
46
+ const header = this.buffer.subarray(0, headerEnd).toString('ascii')
47
+ const match = /Content-Length:\s*(\d+)/i.exec(header)
48
+ if (null == match) {
49
+ // Malformed header block: skip past it and continue.
50
+ this.buffer = this.buffer.subarray(headerEnd + 4)
51
+ continue
52
+ }
53
+
54
+ const length = parseInt(match[1], 10)
55
+ const bodyStart = headerEnd + 4
56
+ if (this.buffer.length < bodyStart + length) return // need more bytes
57
+
58
+ const body = this.buffer.subarray(bodyStart, bodyStart + length).toString('utf8')
59
+ this.buffer = this.buffer.subarray(bodyStart + length)
60
+
61
+ let msg: Message
62
+ try {
63
+ msg = JSON.parse(body)
64
+ }
65
+ catch {
66
+ continue // ignore unparseable frame
67
+ }
68
+
69
+ for (const out of this.handler.handle(msg)) {
70
+ this.send(out)
71
+ }
72
+
73
+ if (this.handler.shouldExit) {
74
+ this.onExit(this.handler.exitCode)
75
+ return
76
+ }
77
+ }
78
+ }
79
+
80
+ private send(out: OutMessage) {
81
+ const body = Buffer.from(JSON.stringify(out), 'utf8')
82
+ this.write(Buffer.from('Content-Length: ' + body.length + '\r\n\r\n', 'ascii'))
83
+ this.write(body)
84
+ }
85
+ }
86
+
87
+
88
+ function main() {
89
+ 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
+ }
99
+
100
+
101
+ // Only auto-run when invoked as a program, not when imported by tests.
102
+ if (require.main === module) {
103
+ main()
104
+ }
105
+
106
+
107
+ export {
108
+ FrameCodec,
109
+ }