1/// <reference types="tree-sitter-cli/dsl" />
2// Tree-sitter grammar for the Knot programming language.
4// Knot is layout-sensitive. An external scanner emits NEWLINE, INDENT, and
5// DEDENT tokens based on indentation. Newlines are NOT in extras — the scanner
6// handles all \n characters.
22module.exports = grammar({
25 // Spaces, tabs, CR, comments, and line continuations. NO raw newlines.
26 extras: ($) => [/[ \t\r]/, $.comment, $._line_cont],
28 word: ($) => $.lower_identifier,
30 externals: ($) => [$._newline, $._indent, $._dedent, $._line_cont],
33 [$.constraint, $.named_type],
34 // do-block: pattern vs expression (resolved by presence of `<-`)
35 [$.variable_pattern, $.variable_expression],
36 [$.record_pattern, $.record_expression],
37 [$.list_pattern, $.list_expression],
38 [$.literal_pattern, $._atom_expression],
39 [$.field_pattern, $.variable_expression],
40 // trait param vs type variable in trait declaration
41 [$._trait_param, $.type_variable],
42 // trait method with default body vs separate default body
43 [$.trait_method, $.trait_default_body],
47 // ── Module ──────────────────────────────────────────────────────
50 optional(seq($.module_declaration, $._newline)),
51 sep($._declaration, $._newline),
54 module_declaration: ($) =>
55 seq("module", field("name", $.upper_identifier)),
57 // ── Declarations ────────────────────────────────────────────────
65 _declaration_body: ($) =>
68 $.type_alias_declaration,
71 $.derived_declaration,
75 $.migrate_declaration,
77 $.route_composite_declaration,
81 // ── Import ──────────────────────────────────────────────────────
82 import_declaration: ($) =>
85 field("path", $.import_path),
86 optional(seq("(", sep1($.import_item, ","), ")")),
90 seq(".", "/", sep1($.lower_identifier, "/")),
93 choice($.lower_identifier, $.upper_identifier),
95 // ── Export ───────────────────────────────────────────────────────
96 export_declaration: ($) =>
97 seq("export", $._declaration_body),
99 // ── Data ────────────────────────────────────────────────────────
100 data_declaration: ($) =>
103 field("name", $.upper_identifier),
104 field("params", repeat($.lower_identifier)),
106 // Multi-line: indented constructors on separate lines
109 sep1($.constructor_def, $._newline),
110 optional(seq($._newline, $.deriving_clause)),
113 // Single-line: constructors on same line as `data`
115 repeat1($.constructor_def),
116 optional($.deriving_clause),
121 constructor_def: ($) =>
124 field("name", $.upper_identifier),
125 field("fields", $.record_type_body),
128 deriving_clause: ($) =>
129 seq("deriving", "(", sep1($.upper_identifier, ","), ")"),
131 // ── Type alias ──────────────────────────────────────────────────
132 type_alias_declaration: ($) =>
135 field("name", $.upper_identifier),
136 field("params", repeat($.lower_identifier)),
138 field("type", $._type),
141 // ── Source ───────────────────────────────────────────────────────
142 source_declaration: ($) =>
146 field("type", $._type),
149 // ── View ────────────────────────────────────────────────────────
150 view_declaration: ($) =>
151 seq($.source_ref, "=", field("body", $._block_body)),
153 // ── Derived ─────────────────────────────────────────────────────
154 derived_declaration: ($) =>
155 seq($.derived_ref, "=", field("body", $._block_body)),
157 // ── Fun ─────────────────────────────────────────────────────────
158 fun_declaration: ($) =>
160 field("name", $.lower_identifier),
163 optional(seq(":", field("type", $.type_scheme))),
165 field("body", $._block_body),
167 seq(":", field("type", $.type_scheme)),
171 // ── Trait ────────────────────────────────────────────────────────
172 trait_declaration: ($) =>
175 repeat($.supertrait),
176 field("name", $.upper_identifier),
177 field("params", repeat1($._trait_param)),
180 sep1($._trait_item, $._newline),
185 prec.dynamic(1, seq($.upper_identifier, repeat1($._type_atom), "=>")),
190 seq("(", $.lower_identifier, ":", $._type, ")"),
194 choice($.trait_method, $.trait_associated_type, $.trait_default_body),
199 field("name", $.lower_identifier),
201 seq(":", field("type", $.type_scheme)),
202 seq(repeat($._pattern_atom), "=", $._expression),
207 trait_associated_type: ($) =>
208 seq("type", $.upper_identifier, repeat($.lower_identifier)),
210 trait_default_body: ($) =>
213 repeat($._pattern_atom),
218 // ── Impl ────────────────────────────────────────────────────────
219 impl_declaration: ($) =>
222 repeat(seq($.constraint, "=>")),
223 field("trait", $.upper_identifier),
224 field("args", repeat1($._type_atom)),
227 sep1($._impl_item, $._newline),
232 choice($.impl_method, $.impl_associated_type),
236 field("name", $.lower_identifier),
237 field("params", repeat($._pattern_atom)),
239 field("body", $._block_body),
242 impl_associated_type: ($) =>
243 seq("type", $.upper_identifier, repeat($._type_atom), "=", $._type),
245 // ── Migrate ─────────────────────────────────────────────────────
246 migrate_declaration: ($) =>
249 field("relation", $.source_ref),
251 field("from", $._type),
253 field("to", $._type),
255 field("using", $._expression),
258 // ── Route ───────────────────────────────────────────────────────
259 route_declaration: ($) =>
262 field("name", $.upper_identifier),
265 sep1($._route_item, $._newline),
269 route_composite_declaration: ($) =>
272 field("name", $.upper_identifier),
274 sep1($.upper_identifier, "|"),
278 choice($.route_entry, $.route_path_group),
280 route_path_group: ($) =>
282 repeat1($._route_path_segment),
284 sep1($._route_item, $._newline),
291 field("method", $.http_method),
293 $._route_path_segment,
294 field("body", $.record_type_body),
295 seq("?", field("query", $.record_type_body)),
296 seq("headers", $.record_type_body),
299 field("response", $._type),
300 optional(seq("headers", $.record_type_body)),
302 field("constructor", $.upper_identifier),
306 _route_path_segment: ($) =>
308 seq("/", field("name", $.lower_identifier)),
309 seq("/", token.immediate("{"), field("name", $.lower_identifier), ":", field("type", $._type), "}"),
313 http_method: ($) => choice("GET", "POST", "PUT", "DELETE", "PATCH"),
315 // ── Subset constraints ──────────────────────────────────────────
316 subset_constraint: ($) =>
323 _constraint_path: ($) =>
325 seq($.source_ref, ".", $.lower_identifier),
329 // ── Types ───────────────────────────────────────────────────────
331 type_scheme: ($) => seq(repeat(seq($.constraint, "=>")), $._type),
333 constraint: ($) => seq($.upper_identifier, repeat1($._type_atom)),
335 _type: ($) => choice($.function_type, $.refined_type, $._type_app),
337 function_type: ($) =>
339 seq(field("param", $.refined_type), "->", field("result", $._type)),
344 seq(field("base", $._type_app), "where", field("predicate", $._expression)),
348 _type_app: ($) => choice($.type_application, $._type_atom),
350 type_application: ($) =>
353 seq(choice($.named_type, $.type_application), $._type_atom),
364 $.parenthesized_type,
367 named_type: ($) => $.upper_identifier,
368 type_variable: ($) => $.lower_identifier,
375 // Record fields: {name: Type, age: Int}
377 sep1($.type_field, ","),
378 optional(seq("|", $.lower_identifier)),
380 // Bare identifiers for effect sets: {console, network}
381 // Also covers: reads *rel, writes *rel
390 seq($.lower_identifier, $.source_ref),
394 record_type_body: ($) =>
395 seq("{", optional(sep1($.type_field, ",")), "}"),
398 seq(field("name", $.lower_identifier), ":", field("type", $._type)),
400 relation_type: ($) => seq("[", optional($._type), "]"),
405 sep1($.variant_constructor, "|"),
406 optional(seq("|", $.lower_identifier)),
410 variant_constructor: ($) =>
411 seq($.upper_identifier, optional($.record_type_body)),
413 type_hole: ($) => "_",
415 parenthesized_type: ($) => seq("(", $._type, ")"),
417 // ── Expressions ─────────────────────────────────────────────────
419 // Expression body that may span multiple indented lines.
420 // Same-line: just an expression. Multi-line: wrapped in INDENT/DEDENT.
424 seq($._indent, $._expression, $._dedent),
434 $.replace_set_expression,
439 $._binary_expression,
442 lambda_expression: ($) =>
446 repeat1($._pattern_atom),
448 field("body", $._expression),
452 if_expression: ($) =>
456 field("condition", $._expression),
458 field("then", $._expression),
460 field("else", $._expression),
464 case_expression: ($) =>
468 field("scrutinee", $._expression),
471 sep1($.case_arm, $._newline),
479 field("pattern", $._pattern),
481 field("body", $._expression),
485 do_expression: ($) =>
486 seq("do", $._indent, sep1($._statement, $._newline), $._dedent),
496 bind_statement: ($) =>
500 field("pattern", $._bind_pattern),
502 field("value", $._expression),
506 _bind_pattern: ($) =>
508 $.constructor_pattern,
513 $.parenthesized_pattern,
516 let_statement: ($) =>
519 field("pattern", $._pattern),
521 field("value", $._expression),
524 where_statement: ($) =>
525 prec.right(seq("where", field("condition", $._expression))),
527 set_expression: ($) =>
531 field("target", $.source_ref),
533 field("value", $._expression),
537 replace_set_expression: ($) =>
542 field("target", $.source_ref),
544 field("value", $._expression),
548 yield_expression: ($) =>
549 prec.right(seq("yield", field("value", $._expression))),
551 atomic_expression: ($) =>
552 prec.right(seq("atomic", field("body", $._expression))),
554 refine_expression: ($) =>
555 prec.right(seq("refine", field("value", $._expression))),
557 let_in_expression: ($) =>
559 seq("let", $._pattern, "=", $._expression, "in", $._expression),
562 // ── Binary / unary / application / postfix ──────────────────────
564 _binary_expression: ($) =>
569 $.equality_expression,
570 $.comparison_expression,
577 pipe_expression: ($) =>
580 seq($._binary_expression, "|>", $._binary_expression),
583 or_expression: ($) =>
586 seq($._binary_expression, "||", $._binary_expression),
589 and_expression: ($) =>
592 seq($._binary_expression, "&&", $._binary_expression),
595 equality_expression: ($) =>
598 seq($._binary_expression, choice("==", "!="), $._binary_expression),
601 comparison_expression: ($) =>
605 $._binary_expression,
606 choice("<", ">", "<=", ">="),
607 $._binary_expression,
611 concat_expression: ($) =>
614 seq($._binary_expression, "++", $._binary_expression),
617 add_expression: ($) =>
620 seq($._binary_expression, choice("+", "-"), $._binary_expression),
623 mul_expression: ($) =>
626 seq($._binary_expression, choice("*", "/"), $._binary_expression),
629 _unary_expression: ($) =>
631 $.negation_expression,
633 $._application_expression,
636 negation_expression: ($) =>
637 prec(PREC.UNARY, seq("-", $._application_expression)),
639 not_expression: ($) =>
640 prec(PREC.UNARY, seq("not", $._application_expression)),
642 _application_expression: ($) =>
643 choice($.application_expression, $._postfix_expression),
645 application_expression: ($) =>
648 seq($._application_expression, $._postfix_expression),
651 _postfix_expression: ($) =>
652 choice($.field_access_expression, $._atom_expression),
654 field_access_expression: ($) =>
657 seq($._postfix_expression, ".", $.lower_identifier),
660 _atom_expression: ($) =>
663 $.variable_expression,
664 $.constructor_expression,
669 $.parenthesized_expression,
672 variable_expression: ($) => $.lower_identifier,
673 constructor_expression: ($) => $.upper_identifier,
675 source_ref: ($) => token(seq("*", /[a-z_][a-zA-Z0-9_']*/)),
676 derived_ref: ($) => token(seq("&", /[a-z_][a-zA-Z0-9_']*/)),
678 record_expression: ($) =>
679 seq("{", optional(sep1($.record_field, ",")), "}"),
684 field("name", $.lower_identifier),
686 field("value", $._expression),
688 field("value", $._expression),
691 list_expression: ($) =>
692 seq("[", optional(sep1($._expression, ",")), "]"),
694 parenthesized_expression: ($) => seq("(", $._expression, ")"),
696 // ── Patterns ────────────────────────────────────────────────────
698 _pattern: ($) => choice($.constructor_pattern, $._pattern_atom),
700 constructor_pattern: ($) =>
704 field("constructor", $.upper_identifier),
705 field("payload", $._pattern_atom),
709 _pattern_atom: ($) =>
716 $.parenthesized_pattern,
719 variable_pattern: ($) => $.lower_identifier,
720 wildcard_pattern: ($) => "_",
721 literal_pattern: ($) => $._literal,
723 record_pattern: ($) =>
724 seq("{", optional(sep1($.field_pattern, ",")), "}"),
726 field_pattern: ($) =>
729 field("name", $.lower_identifier),
731 field("pattern", $._pattern),
736 list_pattern: ($) => seq("[", optional(sep1($._pattern, ",")), "]"),
738 parenthesized_pattern: ($) => seq("(", $._pattern, ")"),
740 // ── Literals ────────────────────────────────────────────────────
751 integer_literal: ($) => /[0-9][0-9_]*/,
753 float_literal: ($) => /[0-9][0-9_]*\.[0-9][0-9_]*/,
755 string_literal: ($) =>
756 seq('"', repeat(choice($.escape_sequence, $.string_content)), '"'),
758 boolean_literal: ($) => choice("true", "false"),
760 bytes_literal: ($) =>
761 seq('b"', repeat(choice($.escape_sequence, $.string_content)), '"'),
763 escape_sequence: ($) => token.immediate(prec(1, /\\([\\\"ntr]|x[0-9a-fA-F]{2})/)),
765 string_content: ($) => token.immediate(prec(0, /[^"\\]+/)),
767 // ── Identifiers ─────────────────────────────────────────────────
769 lower_identifier: ($) => /[a-z_][a-zA-Z0-9_']*/,
771 upper_identifier: ($) => /[A-Z][a-zA-Z0-9_']*/,
773 // ── Misc ────────────────────────────────────────────────────────
775 comment: ($) => token(seq("--", /.*/)),
779function sep1(rule, separator) {
780 return seq(rule, repeat(seq(separator, rule)));
783function sep(rule, separator) {
784 return optional(sep1(rule, separator));
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list