tree-sitter-bynk 0.141.0 → 0.163.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/grammar.js CHANGED
@@ -59,6 +59,12 @@ module.exports = grammar({
59
59
  // closing `)` is followed — or not — by `=>`.
60
60
  [$.lambda_param, $._primary],
61
61
  [$._primary, $.lambda_expr],
62
+ // v0.145: a match-arm `if`-guard is an `_expression` immediately followed by
63
+ // the arm's `=>`. When the guard opens with `(`, one token can't tell a unit
64
+ // literal `()` (a guard, with `=>` opening the arm) from a zero-param lambda
65
+ // `() =>` (the guard itself). GLR keeps both alive; only the unit parse
66
+ // completes the arm.
67
+ [$.lambda_expr, $.unit_literal],
62
68
  // v0.117: after `expect Cap.op called`, one token of lookahead can't tell a
63
69
  // following `<n> times` count from the end of the observation — GLR keeps
64
70
  // both parses alive until the next token decides.
@@ -220,7 +226,7 @@ module.exports = grammar({
220
226
  ),
221
227
 
222
228
  _test_body_item: ($) =>
223
- choice($.uses_decl, $.consumes_decl, $.provides_clause, $.case, $.property_decl),
229
+ choice($.uses_decl, $.consumes_decl, $.stub_clause, $.case, $.property_decl),
224
230
 
225
231
  // -- Headers / clauses --
226
232
 
@@ -278,6 +284,9 @@ module.exports = grammar({
278
284
  seq(
279
285
  "type",
280
286
  field("name", $.identifier),
287
+ // v0.157 (ADR 0183): optional `[A, B]` type parameters — only a record
288
+ // body may be generic (enforced by the checker, not the grammar).
289
+ optional(seq("[", sep1(field("type_param", $.identifier), ","), "]")),
281
290
  "=",
282
291
  field("body", $._type_body),
283
292
  ),
@@ -326,7 +335,28 @@ module.exports = grammar({
326
335
  optional(seq("=", field("init", $._expression))),
327
336
  ),
328
337
 
329
- sum_type: ($) => prec.right(repeat1($.sum_variant)),
338
+ // v0.154 (ADR 0178): a sum body may carry a trailing `embeds E as V, …`
339
+ // clause declaring error embeddings the `?` operator auto-converts.
340
+ // `embeds`/`as` are contextual — matched only in this position.
341
+ sum_type: ($) =>
342
+ prec.right(
343
+ seq(
344
+ repeat1($.sum_variant),
345
+ optional(
346
+ seq(
347
+ "embeds",
348
+ sep1(
349
+ seq(
350
+ field("source", $._type_ref),
351
+ "as",
352
+ field("variant", $.constant_name),
353
+ ),
354
+ ",",
355
+ ),
356
+ ),
357
+ ),
358
+ ),
359
+ ),
330
360
  sum_variant: ($) =>
331
361
  seq(
332
362
  "|",
@@ -356,7 +386,10 @@ module.exports = grammar({
356
386
  "}",
357
387
  ),
358
388
 
359
- refinement: ($) => sep1($._refinement_pred, "and"),
389
+ // #548: refinement predicates join with `&&` — the one conjunction spelling,
390
+ // shared with contracts/`expect` (was the `and` keyword before the
391
+ // keyword-hygiene batch). The catalogue stays conjunction-only (no `||`/`!`).
392
+ refinement: ($) => sep1($._refinement_pred, "&&"),
360
393
  _refinement_pred: ($) =>
361
394
  choice(
362
395
  $.pred_call,
@@ -388,9 +421,21 @@ module.exports = grammar({
388
421
  $.unit_type,
389
422
  $.validation_error_type,
390
423
  $.generic_type_ref,
424
+ $.applied_type_ref,
391
425
  $.identifier,
392
426
  ),
393
427
 
428
+ // v0.157 (ADR 0183): `Name[Arg, …]` — an application of a user-declared
429
+ // generic type. Distinct from `generic_type_ref` (the fixed built-in
430
+ // generics); the head is a user identifier.
431
+ applied_type_ref: ($) =>
432
+ seq(
433
+ field("name", $.identifier),
434
+ "[",
435
+ sep1(field("arg", $._type_ref), ","),
436
+ "]",
437
+ ),
438
+
394
439
  // v0.20a: a function type — `A -> B`, `(A, B) -> C`, `() -> B`,
395
440
  // right-associative (prec.right). A parenthesised parameter list is only
396
441
  // a function type when followed by `->`; bare `()` stays the unit type.
@@ -404,6 +449,7 @@ module.exports = grammar({
404
449
  $.unit_type,
405
450
  $.validation_error_type,
406
451
  $.generic_type_ref,
452
+ $.applied_type_ref,
407
453
  $.identifier,
408
454
  seq("(", sep1($._type_ref, ","), optional(","), ")"),
409
455
  ),
@@ -540,6 +586,10 @@ module.exports = grammar({
540
586
  "service",
541
587
  field("name", $.identifier),
542
588
  optional(field("protocol", $.service_protocol)),
589
+ // v0.155: optional service-level `by`/`given` defaults on the header —
590
+ // the ambient contract every handler inherits unless it declares its own.
591
+ optional(field("default_by", $.by_clause)),
592
+ optional(field("default_given", $.given_clause)),
543
593
  "{",
544
594
  // v0.131 (ADR 0158): the optional `cors { }` policy, in header position
545
595
  // before the handlers (a `from http` concern; the checker restricts it).
@@ -547,6 +597,9 @@ module.exports = grammar({
547
597
  // v0.141 (ADR 0164): the optional `security { }` policy, likewise a
548
598
  // header-position `from http` concern restricted by the checker.
549
599
  optional(field("security", $.security_policy)),
600
+ // v0.142 (ADR 0166): the optional `limits { }` policy, likewise a
601
+ // header-position `from http` concern restricted by the checker.
602
+ optional(field("limits", $.limits_policy)),
550
603
  repeat($.handler),
551
604
  "}",
552
605
  ),
@@ -574,9 +627,23 @@ module.exports = grammar({
574
627
  ),
575
628
  security_field: ($) =>
576
629
  seq(field("name", $.identifier), ":", field("value", $._expression)),
630
+ // v0.142: `limits { <name>: <value>, … }`. `limits` is a contextual
631
+ // keyword (like `cors`/`security`); field names are ordinary identifiers
632
+ // matched against the closed set by the checker.
633
+ limits_policy: ($) =>
634
+ seq(
635
+ "limits",
636
+ "{",
637
+ repeat(seq($.limits_field, optional(","))),
638
+ "}",
639
+ ),
640
+ limits_field: ($) =>
641
+ seq(field("name", $.identifier), ":", field("value", $._expression)),
577
642
  // v0.44: `from <protocol>` on the service header.
578
- // v0.103: `from WebSocket(in: I, out: O)` binds the inbound/outbound frame
579
- // types. `WebSocket`, `in`, and `out` are contextual (resolved by `word`).
643
+ // v0.103: `from websocket(in: I, out: O)` binds the inbound/outbound frame
644
+ // types. `websocket`, `in`, and `out` are contextual (resolved by `word`).
645
+ // #548: `websocket` lowercased to match the other protocol sources (was
646
+ // `WebSocket`).
580
647
  service_protocol: ($) =>
581
648
  seq(
582
649
  "from",
@@ -585,7 +652,7 @@ module.exports = grammar({
585
652
  "cron",
586
653
  seq("queue", "(", field("name", $.string_literal), ")"),
587
654
  seq(
588
- "WebSocket",
655
+ "websocket",
589
656
  "(",
590
657
  "in",
591
658
  ":",
@@ -688,13 +755,13 @@ module.exports = grammar({
688
755
  "on",
689
756
  "call",
690
757
  optional(field("method", $.identifier)),
691
- optional(field("by", $.by_clause)),
692
758
  "(",
693
759
  optional(sep1($.param, ",")),
694
760
  optional(","),
695
761
  ")",
696
762
  "->",
697
763
  field("return_type", $._type_ref),
764
+ optional(field("by", $.by_clause)),
698
765
  optional(field("given", $.given_clause)),
699
766
  field("body", $.block),
700
767
  ),
@@ -707,13 +774,13 @@ module.exports = grammar({
707
774
  "(",
708
775
  field("path", $.string_literal),
709
776
  ")",
710
- optional(field("by", $.by_clause)),
711
777
  "(",
712
778
  optional(sep1($.param, ",")),
713
779
  optional(","),
714
780
  ")",
715
781
  "->",
716
782
  field("return_type", $._type_ref),
783
+ optional(field("by", $.by_clause)),
717
784
  optional(field("given", $.given_clause)),
718
785
  field("body", $.block),
719
786
  ),
@@ -726,13 +793,13 @@ module.exports = grammar({
726
793
  "(",
727
794
  field("schedule", $.string_literal),
728
795
  ")",
729
- optional(field("by", $.by_clause)),
730
796
  "(",
731
797
  optional(sep1($.param, ",")),
732
798
  optional(","),
733
799
  ")",
734
800
  "->",
735
801
  field("return_type", $._type_ref),
802
+ optional(field("by", $.by_clause)),
736
803
  optional(field("given", $.given_clause)),
737
804
  field("body", $.block),
738
805
  ),
@@ -742,30 +809,30 @@ module.exports = grammar({
742
809
  seq(
743
810
  "on",
744
811
  "message",
745
- optional(field("by", $.by_clause)),
746
812
  "(",
747
813
  optional(sep1($.param, ",")),
748
814
  optional(","),
749
815
  ")",
750
816
  "->",
751
817
  field("return_type", $._type_ref),
818
+ optional(field("by", $.by_clause)),
752
819
  optional(field("given", $.given_clause)),
753
820
  field("body", $.block),
754
821
  ),
755
822
  // v0.103/v0.106: `on open` / `on close` lifecycle handlers of a
756
- // `from WebSocket` service. `open`/`close` are contextual (resolved by
823
+ // `from websocket` service. `open`/`close` are contextual (resolved by
757
824
  // `word`). `on message` (the inbound-frame handler) reuses queue_handler.
758
825
  ws_open_handler: ($) =>
759
826
  seq(
760
827
  "on",
761
828
  "open",
762
- optional(field("by", $.by_clause)),
763
829
  "(",
764
830
  optional(sep1($.param, ",")),
765
831
  optional(","),
766
832
  ")",
767
833
  "->",
768
834
  field("return_type", $._type_ref),
835
+ optional(field("by", $.by_clause)),
769
836
  optional(field("given", $.given_clause)),
770
837
  field("body", $.block),
771
838
  ),
@@ -773,13 +840,13 @@ module.exports = grammar({
773
840
  seq(
774
841
  "on",
775
842
  "close",
776
- optional(field("by", $.by_clause)),
777
843
  "(",
778
844
  optional(sep1($.param, ",")),
779
845
  optional(","),
780
846
  ")",
781
847
  "->",
782
848
  field("return_type", $._type_ref),
849
+ optional(field("by", $.by_clause)),
783
850
  optional(field("given", $.given_clause)),
784
851
  field("body", $.block),
785
852
  ),
@@ -789,9 +856,18 @@ module.exports = grammar({
789
856
  seq("given", sep1(field("capability", $.qualified_name), ",")),
790
857
 
791
858
  // v0.45: an actor declaration — a boundary contract. Normal form
792
- // `actor Name { auth = Scheme (, identity = Type)? }`; the reserved
793
- // refinement form `actor Admin = Base where <predicate>` is parsed (and
794
- // rejected by the checker in Foundations).
859
+ // `actor Name { auth = Scheme (, identity = Type)? }`. The refinement form
860
+ // `actor Admin = Base where <predicate>` narrows a base actor by an
861
+ // authorisation claim (`actor Admin = User where hasClaim("admin")`).
862
+ //
863
+ // #548: the predicate is a full `_expression`, matching what the compiler
864
+ // parses (a general expression). It is *not* the type-refinement catalogue
865
+ // (`Matches`/`InRange`/…, rule `refinement`) — that mis-modelled `hasClaim`
866
+ // as an ERROR. As with function contracts, the grammar parses the broad
867
+ // expression and a static-semantics rule restricts it to the closed
868
+ // actor-claim catalogue (`hasClaim`/`claimEquals` composed with `&&`/`||`/`!`;
869
+ // `bynk.actor.refinement_predicate_unsupported`). See spec §5 / grammar
870
+ // reference for the three `where` predicate tiers.
795
871
  actor_decl: ($) =>
796
872
  seq(
797
873
  "actor",
@@ -814,13 +890,14 @@ module.exports = grammar({
814
890
  "=",
815
891
  field("base", $.identifier),
816
892
  "where",
817
- field("predicate", $.refinement),
893
+ field("predicate", $._expression),
818
894
  ),
819
895
  ),
820
896
  ),
821
- // The closed, compiler-known scheme set. `None`/`Internal` are admitted in
822
- // Foundations; `Bearer`/`Signature` are reserved-and-rejected.
823
- scheme: () => choice("None", "Internal", "Bearer", "Signature"),
897
+ // The closed, compiler-known scheme set. `None`/`Internal` are zero-crypto;
898
+ // `Bearer` (JWT/HS256), `Signature` (HMAC over the body), and `Oidc`
899
+ // (JWKS/RS256+ES256) are compiler-generated verifiers.
900
+ scheme: () => choice("None", "Internal", "Bearer", "Signature", "Oidc"),
824
901
  // v0.51: the keyed-args scheme config — `(key = value, …)`. Values are
825
902
  // string or integer literals; the checker validates which keys each scheme
826
903
  // admits (Bearer: secret; Signature: secret/header/timestamp/tolerance).
@@ -854,16 +931,15 @@ module.exports = grammar({
854
931
  // -- v0.7: test bodies --
855
932
 
856
933
  // v0.118 (testing track slice 6): a test-scope stub for one capability
857
- // operation — `provides Cap.op(<arg pattern>, …) <rhs>`. Distinguished from
858
- // the production provider declaration (`provides Cap = Impl …`) by the
859
- // `.op(` shape, and only admitted inside a suite/case body. An arg pattern
860
- // is `_` (any) or a value expression; the right-hand side is
861
- // `returns <expr>`, `returns each [ <outcome>, … ]` (a scripted sequence,
862
- // where an outcome is `fails` or an expr), or `fails`. `returns`/`each`/
863
- // `fails` are contextual words.
864
- provides_clause: ($) =>
934
+ // operation — `stub Cap.op(<arg pattern>, …) <rhs>`. Only admitted inside a
935
+ // suite/case body. An arg pattern is `_` (any) or a value expression; the
936
+ // right-hand side is `returns <expr>`, `returns each [ <outcome>, … ]` (a
937
+ // scripted sequence, where an outcome is `fails` or an expr), or `fails`.
938
+ // `returns`/`each`/`fails` are contextual words. Keyword `stub` since the
939
+ // keyword-hygiene batch (#548); formerly a pun on `provides`.
940
+ stub_clause: ($) =>
865
941
  seq(
866
- "provides",
942
+ "stub",
867
943
  field("cap", $.identifier),
868
944
  ".",
869
945
  field("op", $.identifier),
@@ -893,7 +969,7 @@ module.exports = grammar({
893
969
  ),
894
970
  ),
895
971
  // v0.118: an optional `as <tier>` classifier plus a body of leading
896
- // `provides` stubs, then the ordinary statements and tail. The tier words
972
+ // `stub` clauses, then the ordinary statements and tail. The tier words
897
973
  // (`unit`/`integration`/`system`) are contextual, as on `suite_decl`.
898
974
  case: ($) =>
899
975
  seq(
@@ -901,7 +977,7 @@ module.exports = grammar({
901
977
  field("name", $.string_literal),
902
978
  optional(seq("as", field("tier", choice("unit", "integration", "system")))),
903
979
  "{",
904
- repeat($.provides_clause),
980
+ repeat($.stub_clause),
905
981
  repeat($._statement),
906
982
  optional(field("tail", $._expression)),
907
983
  "}",
@@ -948,6 +1024,7 @@ module.exports = grammar({
948
1024
  $.let_stmt,
949
1025
  $.effect_let_stmt,
950
1026
  $.effect_send_stmt,
1027
+ $.do_stmt,
951
1028
  $.assign_stmt,
952
1029
  prec(1, $.expect_expr),
953
1030
  ),
@@ -970,6 +1047,9 @@ module.exports = grammar({
970
1047
  ),
971
1048
  // v0.79: `~> expr` — an asynchronous fire-and-forget send (no binder).
972
1049
  effect_send_stmt: ($) => seq("~>", field("value", $._expression)),
1050
+ // v0.146 (ADR 0170): `do expr` — perform a unit effect as a statement
1051
+ // (the binder-free `let _ <- expr` for an `Effect[()]`).
1052
+ do_stmt: ($) => seq("do", field("value", $._expression)),
973
1053
  // v0.81 (storage track): `name := expr` — a `Cell` store write.
974
1054
  assign_stmt: ($) =>
975
1055
  seq(field("target", $.identifier), ":=", field("value", $._expression)),
@@ -1043,13 +1123,14 @@ module.exports = grammar({
1043
1123
  ),
1044
1124
  ),
1045
1125
 
1126
+ // v0.146 (ADR 0170): `else` is optional. A missing `else` defaults to a
1127
+ // unit branch (legal only for a unit then-branch, enforced by the checker).
1046
1128
  if_expr: ($) =>
1047
1129
  seq(
1048
1130
  "if",
1049
1131
  field("cond", $._expression),
1050
1132
  field("then", $.block),
1051
- "else",
1052
- field("else", choice($.if_expr, $.block)),
1133
+ optional(seq("else", field("else", choice($.if_expr, $.block)))),
1053
1134
  ),
1054
1135
 
1055
1136
  match_expr: ($) =>
@@ -1066,6 +1147,10 @@ module.exports = grammar({
1066
1147
  prec.right(
1067
1148
  seq(
1068
1149
  field("pattern", $._pattern),
1150
+ // v0.145 (ADR 0169): an optional trailing `if <Bool-expr>` guard,
1151
+ // between the pattern and `=>`, gating the arm over its bindings. The
1152
+ // `if` reuses the existing keyword token (already highlighted).
1153
+ optional(seq("if", field("guard", $._expression))),
1069
1154
  "=>",
1070
1155
  // `_expression` already includes `block` via `_primary`, so a
1071
1156
  // `{ … }` arm body is covered without a separate alternative.
@@ -1105,14 +1190,19 @@ module.exports = grammar({
1105
1190
  ),
1106
1191
  ),
1107
1192
  ),
1193
+ // v0.145 (ADR 0169, decision B): a variant payload matches its field against
1194
+ // a full sub-`_pattern` (one recursion point), so `Some(Ok(x))` /
1195
+ // `Err(PollClosed)` parse as nested `variant_pattern` nodes rather than flat
1196
+ // bindings. A bare lowercase identifier is a payload-less `variant_pattern`
1197
+ // (a binding, recovered in the highlight query by capitalisation); `_` is a
1198
+ // `wildcard_pattern`; an uppercase-led one discriminates a nested variant.
1108
1199
  _pattern_binding: ($) =>
1109
1200
  choice(
1110
1201
  $.named_binding,
1111
- $.positional_binding,
1202
+ $._pattern,
1112
1203
  ),
1113
1204
  named_binding: ($) =>
1114
- seq(field("field", $.identifier), ":", field("name", choice($.identifier, "_"))),
1115
- positional_binding: ($) => choice($.identifier, "_"),
1205
+ seq(field("field", $.identifier), ":", field("name", $._pattern)),
1116
1206
 
1117
1207
  is_expr: ($) =>
1118
1208
  prec.left(
@@ -1312,12 +1402,12 @@ module.exports = grammar({
1312
1402
  // overshadowing `identifier`; we use a regex.
1313
1403
  constant_name: () => /[A-Z][A-Za-z0-9_]*/,
1314
1404
 
1315
- number_literal: () => /[0-9]+/,
1405
+ number_literal: () => /[0-9]+(_[0-9]+)*/,
1316
1406
  // v0.21: a float literal — fraction with a digit on both sides of the
1317
1407
  // `.`, an exponent, or both. Longest-match keeps `1.toFloat()` lexing
1318
1408
  // as a method call on an integer literal.
1319
1409
  float_literal: () =>
1320
- /[0-9]+\.[0-9]+([eE][+-]?[0-9]+)?|[0-9]+[eE][+-]?[0-9]+/,
1410
+ /[0-9]+(_[0-9]+)*\.[0-9]+(_[0-9]+)*([eE][+-]?[0-9]+(_[0-9]+)*)?|[0-9]+(_[0-9]+)*[eE][+-]?[0-9]+(_[0-9]+)*/,
1321
1411
  string_literal: ($) =>
1322
1412
  seq(
1323
1413
  '"',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tree-sitter-bynk",
3
- "version": "0.141.0",
3
+ "version": "0.163.0",
4
4
  "description": "Tree-sitter grammar for the Bynk DSL (v0–v0.16).",
5
5
  "main": "bindings/node",
6
6
  "types": "bindings/node",
@@ -12,6 +12,7 @@
12
12
  "match"
13
13
  "is"
14
14
  "expect"
15
+ "do"
15
16
  "on"
16
17
  "given"
17
18
  "call"
@@ -19,6 +20,7 @@
19
20
  "http"
20
21
  "cron"
21
22
  "queue"
23
+ "websocket"
22
24
  "schedule"
23
25
  "message"
24
26
  ] @keyword
@@ -36,11 +38,13 @@
36
38
  "fn"
37
39
  "capability"
38
40
  "provides"
41
+ "stub"
39
42
  "service"
40
43
  "agent"
41
44
  "store"
42
45
  "cors"
43
46
  "security"
47
+ "limits"
44
48
  "exports"
45
49
  "key"
46
50
  "suite"
@@ -59,11 +63,11 @@
59
63
 
60
64
  [
61
65
  "where"
62
- "and"
63
66
  "implies"
64
67
  "enum"
65
68
  "for"
66
69
  "all"
70
+ "embeds"
67
71
  ] @keyword.operator
68
72
 
69
73
  ; v0.117: the observation sugar words — contextual (these tokens exist only
@@ -79,7 +83,7 @@
79
83
  ] @keyword.operator
80
84
 
81
85
  ; v0.118: the contextual test-tier words on a `suite`/`case` header (after `as`)
82
- ; and the `provides` stub right-hand-side words. Like the observation words,
86
+ ; and the `stub` clause right-hand-side words. Like the observation words,
83
87
  ; these tokens exist only in their clauses, so a global match never repaints an
84
88
  ; ordinary identifier of the same spelling.
85
89
  [
@@ -108,6 +112,10 @@
108
112
  (type_decl name: (identifier) @type)
109
113
  (opaque_type) @type
110
114
  (generic_type_ref arg: (identifier) @type)
115
+ ; v0.157 (ADR 0183): a user generic-type application — the head name and each
116
+ ; argument are types.
117
+ (applied_type_ref name: (identifier) @type)
118
+ (applied_type_ref arg: (identifier) @type)
111
119
  (record_field type: (identifier) @type)
112
120
  (variant_payload_field type: (identifier) @type)
113
121
  (param type: (identifier) @type)
@@ -150,7 +158,6 @@
150
158
  (field_init name: (identifier) @field)
151
159
  (field_init shorthand: (identifier) @field)
152
160
  (named_binding field: (identifier) @field)
153
- (positional_binding (identifier) @variable)
154
161
 
155
162
  ; -- Variants & constants --
156
163
 
@@ -160,6 +167,12 @@
160
167
  ; pattern variant as a constant.
161
168
  ((variant_pattern variant: (identifier) @constant)
162
169
  (#match? @constant "^[A-Z]"))
170
+ ; v0.145 (ADR 0169): a lowercase-led pattern variant is a payload *binding* (the
171
+ ; universal capitalisation convention) — a bare `n` or the leaf of a nested
172
+ ; pattern like `Some(n)`. Highlight it as a variable, mirroring the retired
173
+ ; `positional_binding` capture now that the payload position is a full pattern.
174
+ ((variant_pattern variant: (identifier) @variable)
175
+ (#match? @variable "^[a-z_]"))
163
176
  (boolean_literal) @constant.builtin
164
177
 
165
178
  ; -- Literals --