- Add tree-sitter grammar for syntax highlighting - Add Neovim plugin with syntax, LSP integration, and commands - Add code formatter (lux fmt) with check mode - Add CLI commands: fmt, check, test, watch, init - Add project initialization with lux.toml template Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
600 lines
12 KiB
JavaScript
600 lines
12 KiB
JavaScript
// Tree-sitter grammar for the Lux programming language
|
|
module.exports = grammar({
|
|
name: 'lux',
|
|
|
|
extras: $ => [
|
|
/\s/,
|
|
$.line_comment,
|
|
$.doc_comment,
|
|
],
|
|
|
|
conflicts: $ => [
|
|
[$.primary_expression, $.pattern],
|
|
[$.type_expression, $.primary_expression],
|
|
],
|
|
|
|
word: $ => $.identifier,
|
|
|
|
rules: {
|
|
source_file: $ => repeat($._declaration),
|
|
|
|
_declaration: $ => choice(
|
|
$.function_declaration,
|
|
$.let_declaration,
|
|
$.type_declaration,
|
|
$.effect_declaration,
|
|
$.handler_declaration,
|
|
$.trait_declaration,
|
|
$.impl_declaration,
|
|
$.import_declaration,
|
|
$.export_declaration,
|
|
),
|
|
|
|
// Comments
|
|
line_comment: $ => token(seq('//', /.*/)),
|
|
doc_comment: $ => token(seq('///', /.*/)),
|
|
|
|
// Function declaration
|
|
function_declaration: $ => seq(
|
|
'fn',
|
|
field('name', $.identifier),
|
|
optional($.type_parameters),
|
|
$.parameter_list,
|
|
':',
|
|
field('return_type', $.type_expression),
|
|
optional($.effect_clause),
|
|
optional($.property_clause),
|
|
'=',
|
|
field('body', $._expression),
|
|
),
|
|
|
|
parameter_list: $ => seq(
|
|
'(',
|
|
optional(seq(
|
|
$.parameter,
|
|
repeat(seq(',', $.parameter)),
|
|
optional(','),
|
|
)),
|
|
')',
|
|
),
|
|
|
|
parameter: $ => seq(
|
|
field('name', $.identifier),
|
|
':',
|
|
field('type', $.type_expression),
|
|
),
|
|
|
|
type_parameters: $ => seq(
|
|
'<',
|
|
$.identifier,
|
|
repeat(seq(',', $.identifier)),
|
|
optional(','),
|
|
'>',
|
|
),
|
|
|
|
effect_clause: $ => seq(
|
|
'with',
|
|
'{',
|
|
optional(seq(
|
|
$.identifier,
|
|
repeat(seq(',', $.identifier)),
|
|
optional(','),
|
|
)),
|
|
'}',
|
|
),
|
|
|
|
property_clause: $ => seq(
|
|
'is',
|
|
$.identifier,
|
|
repeat(seq(',', $.identifier)),
|
|
),
|
|
|
|
// Let declaration
|
|
let_declaration: $ => seq(
|
|
'let',
|
|
field('name', $.identifier),
|
|
optional(seq(':', field('type', $.type_expression))),
|
|
'=',
|
|
field('value', $._expression),
|
|
),
|
|
|
|
// Type declaration
|
|
type_declaration: $ => seq(
|
|
'type',
|
|
field('name', $.identifier),
|
|
optional($.type_parameters),
|
|
'=',
|
|
field('definition', $.type_definition),
|
|
),
|
|
|
|
type_definition: $ => choice(
|
|
$.enum_definition,
|
|
$.record_type,
|
|
$.type_expression,
|
|
),
|
|
|
|
enum_definition: $ => seq(
|
|
repeat1(seq('|', $.variant)),
|
|
),
|
|
|
|
variant: $ => seq(
|
|
field('name', $.identifier),
|
|
optional(choice(
|
|
$.tuple_fields,
|
|
$.record_fields,
|
|
)),
|
|
),
|
|
|
|
tuple_fields: $ => seq(
|
|
'(',
|
|
$.type_expression,
|
|
repeat(seq(',', $.type_expression)),
|
|
optional(','),
|
|
')',
|
|
),
|
|
|
|
record_fields: $ => seq(
|
|
'{',
|
|
$.record_field,
|
|
repeat(seq(',', $.record_field)),
|
|
optional(','),
|
|
'}',
|
|
),
|
|
|
|
record_field: $ => seq(
|
|
field('name', $.identifier),
|
|
':',
|
|
field('type', $.type_expression),
|
|
),
|
|
|
|
// Effect declaration
|
|
effect_declaration: $ => seq(
|
|
'effect',
|
|
field('name', $.identifier),
|
|
optional($.type_parameters),
|
|
'{',
|
|
repeat($.effect_operation),
|
|
'}',
|
|
),
|
|
|
|
effect_operation: $ => seq(
|
|
'fn',
|
|
field('name', $.identifier),
|
|
$.parameter_list,
|
|
':',
|
|
field('return_type', $.type_expression),
|
|
),
|
|
|
|
// Handler declaration
|
|
handler_declaration: $ => seq(
|
|
'handler',
|
|
field('name', $.identifier),
|
|
':',
|
|
field('effect', $.identifier),
|
|
'{',
|
|
repeat($.handler_operation),
|
|
'}',
|
|
),
|
|
|
|
handler_operation: $ => seq(
|
|
'fn',
|
|
field('name', $.identifier),
|
|
'(',
|
|
optional(seq(
|
|
$.identifier,
|
|
repeat(seq(',', $.identifier)),
|
|
optional(','),
|
|
)),
|
|
')',
|
|
'=',
|
|
field('body', $._expression),
|
|
),
|
|
|
|
// Trait declaration
|
|
trait_declaration: $ => seq(
|
|
'trait',
|
|
field('name', $.identifier),
|
|
optional($.type_parameters),
|
|
'{',
|
|
repeat($.trait_method),
|
|
'}',
|
|
),
|
|
|
|
trait_method: $ => seq(
|
|
'fn',
|
|
field('name', $.identifier),
|
|
$.parameter_list,
|
|
':',
|
|
field('return_type', $.type_expression),
|
|
),
|
|
|
|
// Impl declaration
|
|
impl_declaration: $ => seq(
|
|
'impl',
|
|
field('trait', $.identifier),
|
|
'for',
|
|
field('type', $.type_expression),
|
|
'{',
|
|
repeat($.function_declaration),
|
|
'}',
|
|
),
|
|
|
|
// Import/Export
|
|
import_declaration: $ => seq(
|
|
'import',
|
|
$.import_path,
|
|
optional($.import_clause),
|
|
),
|
|
|
|
import_path: $ => seq(
|
|
$.identifier,
|
|
repeat(seq('.', $.identifier)),
|
|
),
|
|
|
|
import_clause: $ => seq(
|
|
'{',
|
|
$.identifier,
|
|
repeat(seq(',', $.identifier)),
|
|
optional(','),
|
|
'}',
|
|
),
|
|
|
|
export_declaration: $ => seq(
|
|
'export',
|
|
'{',
|
|
$.identifier,
|
|
repeat(seq(',', $.identifier)),
|
|
optional(','),
|
|
'}',
|
|
),
|
|
|
|
// Type expressions
|
|
type_expression: $ => choice(
|
|
$.identifier,
|
|
$.generic_type,
|
|
$.function_type,
|
|
$.tuple_type,
|
|
$.record_type,
|
|
seq('(', $.type_expression, ')'),
|
|
),
|
|
|
|
generic_type: $ => seq(
|
|
$.identifier,
|
|
'<',
|
|
$.type_expression,
|
|
repeat(seq(',', $.type_expression)),
|
|
optional(','),
|
|
'>',
|
|
),
|
|
|
|
function_type: $ => seq(
|
|
'fn',
|
|
'(',
|
|
optional(seq(
|
|
$.type_expression,
|
|
repeat(seq(',', $.type_expression)),
|
|
optional(','),
|
|
)),
|
|
')',
|
|
':',
|
|
$.type_expression,
|
|
optional($.effect_clause),
|
|
),
|
|
|
|
tuple_type: $ => seq(
|
|
'(',
|
|
$.type_expression,
|
|
',',
|
|
$.type_expression,
|
|
repeat(seq(',', $.type_expression)),
|
|
optional(','),
|
|
')',
|
|
),
|
|
|
|
record_type: $ => seq(
|
|
'{',
|
|
optional(seq(
|
|
$.record_field,
|
|
repeat(seq(',', $.record_field)),
|
|
optional(','),
|
|
)),
|
|
'}',
|
|
),
|
|
|
|
// Expressions
|
|
_expression: $ => choice(
|
|
$.primary_expression,
|
|
$.binary_expression,
|
|
$.unary_expression,
|
|
$.call_expression,
|
|
$.member_expression,
|
|
$.index_expression,
|
|
$.if_expression,
|
|
$.match_expression,
|
|
$.block_expression,
|
|
$.lambda_expression,
|
|
$.run_expression,
|
|
$.resume_expression,
|
|
$.pipe_expression,
|
|
),
|
|
|
|
primary_expression: $ => choice(
|
|
$.identifier,
|
|
$.number,
|
|
$.string,
|
|
$.char,
|
|
$.boolean,
|
|
$.unit,
|
|
$.list_expression,
|
|
$.tuple_expression,
|
|
$.record_expression,
|
|
seq('(', $._expression, ')'),
|
|
),
|
|
|
|
// Literals
|
|
number: $ => choice(
|
|
$.integer,
|
|
$.float,
|
|
),
|
|
|
|
integer: $ => /\d+/,
|
|
|
|
float: $ => /\d+\.\d+/,
|
|
|
|
string: $ => choice(
|
|
$.simple_string,
|
|
$.interpolated_string,
|
|
),
|
|
|
|
simple_string: $ => seq(
|
|
'"',
|
|
repeat(choice(
|
|
$.string_content,
|
|
$.escape_sequence,
|
|
)),
|
|
'"',
|
|
),
|
|
|
|
interpolated_string: $ => seq(
|
|
'"',
|
|
repeat(choice(
|
|
$.string_content,
|
|
$.escape_sequence,
|
|
$.interpolation,
|
|
)),
|
|
'"',
|
|
),
|
|
|
|
string_content: $ => /[^"\\{]+/,
|
|
|
|
escape_sequence: $ => /\\[nrt\\'"0{}\}]/,
|
|
|
|
interpolation: $ => seq(
|
|
'{',
|
|
$._expression,
|
|
'}',
|
|
),
|
|
|
|
char: $ => seq(
|
|
"'",
|
|
choice(
|
|
/[^'\\]/,
|
|
$.escape_sequence,
|
|
),
|
|
"'",
|
|
),
|
|
|
|
boolean: $ => choice('true', 'false'),
|
|
|
|
unit: $ => seq('(', ')'),
|
|
|
|
list_expression: $ => seq(
|
|
'[',
|
|
optional(seq(
|
|
$._expression,
|
|
repeat(seq(',', $._expression)),
|
|
optional(','),
|
|
)),
|
|
']',
|
|
),
|
|
|
|
tuple_expression: $ => seq(
|
|
'(',
|
|
$._expression,
|
|
',',
|
|
$._expression,
|
|
repeat(seq(',', $._expression)),
|
|
optional(','),
|
|
')',
|
|
),
|
|
|
|
record_expression: $ => seq(
|
|
'{',
|
|
optional(seq(
|
|
$.field_assignment,
|
|
repeat(seq(',', $.field_assignment)),
|
|
optional(','),
|
|
)),
|
|
'}',
|
|
),
|
|
|
|
field_assignment: $ => seq(
|
|
field('name', $.identifier),
|
|
':',
|
|
field('value', $._expression),
|
|
),
|
|
|
|
// Binary expressions
|
|
binary_expression: $ => choice(
|
|
prec.left(1, seq($._expression, '||', $._expression)),
|
|
prec.left(2, seq($._expression, '&&', $._expression)),
|
|
prec.left(3, seq($._expression, choice('==', '!='), $._expression)),
|
|
prec.left(4, seq($._expression, choice('<', '>', '<=', '>='), $._expression)),
|
|
prec.left(5, seq($._expression, choice('+', '-'), $._expression)),
|
|
prec.left(6, seq($._expression, choice('*', '/', '%'), $._expression)),
|
|
),
|
|
|
|
unary_expression: $ => prec(7, choice(
|
|
seq('-', $._expression),
|
|
seq('!', $._expression),
|
|
)),
|
|
|
|
// Pipe expression
|
|
pipe_expression: $ => prec.left(0, seq(
|
|
$._expression,
|
|
'|>',
|
|
$._expression,
|
|
)),
|
|
|
|
// Call expression
|
|
call_expression: $ => prec(8, seq(
|
|
field('function', $._expression),
|
|
'(',
|
|
optional(seq(
|
|
$._expression,
|
|
repeat(seq(',', $._expression)),
|
|
optional(','),
|
|
)),
|
|
')',
|
|
)),
|
|
|
|
// Member expression
|
|
member_expression: $ => prec(9, seq(
|
|
field('object', $._expression),
|
|
'.',
|
|
field('member', $.identifier),
|
|
)),
|
|
|
|
// Index expression
|
|
index_expression: $ => prec(8, seq(
|
|
field('object', $._expression),
|
|
'[',
|
|
field('index', $._expression),
|
|
']',
|
|
)),
|
|
|
|
// If expression
|
|
if_expression: $ => prec.right(seq(
|
|
'if',
|
|
field('condition', $._expression),
|
|
'then',
|
|
field('then', $._expression),
|
|
'else',
|
|
field('else', $._expression),
|
|
)),
|
|
|
|
// Match expression
|
|
match_expression: $ => seq(
|
|
'match',
|
|
field('value', $._expression),
|
|
'{',
|
|
repeat1($.match_arm),
|
|
'}',
|
|
),
|
|
|
|
match_arm: $ => seq(
|
|
field('pattern', $.pattern),
|
|
'=>',
|
|
field('body', $._expression),
|
|
optional(','),
|
|
),
|
|
|
|
pattern: $ => choice(
|
|
$.identifier,
|
|
$.number,
|
|
$.string,
|
|
$.boolean,
|
|
'_',
|
|
$.constructor_pattern,
|
|
$.tuple_pattern,
|
|
$.record_pattern,
|
|
),
|
|
|
|
constructor_pattern: $ => seq(
|
|
field('name', $.identifier),
|
|
optional(seq(
|
|
'(',
|
|
$.pattern,
|
|
repeat(seq(',', $.pattern)),
|
|
optional(','),
|
|
')',
|
|
)),
|
|
),
|
|
|
|
tuple_pattern: $ => seq(
|
|
'(',
|
|
$.pattern,
|
|
',',
|
|
$.pattern,
|
|
repeat(seq(',', $.pattern)),
|
|
optional(','),
|
|
')',
|
|
),
|
|
|
|
record_pattern: $ => seq(
|
|
'{',
|
|
$.field_pattern,
|
|
repeat(seq(',', $.field_pattern)),
|
|
optional(','),
|
|
'}',
|
|
),
|
|
|
|
field_pattern: $ => seq(
|
|
field('name', $.identifier),
|
|
optional(seq(':', field('pattern', $.pattern))),
|
|
),
|
|
|
|
// Block expression
|
|
block_expression: $ => seq(
|
|
'{',
|
|
repeat($.statement),
|
|
optional($._expression),
|
|
'}',
|
|
),
|
|
|
|
statement: $ => seq(
|
|
$.let_declaration,
|
|
),
|
|
|
|
// Lambda expression
|
|
lambda_expression: $ => seq(
|
|
'fn',
|
|
$.parameter_list,
|
|
optional(seq(':', $.type_expression)),
|
|
'=>',
|
|
$._expression,
|
|
),
|
|
|
|
// Run expression
|
|
run_expression: $ => seq(
|
|
'run',
|
|
field('expression', $._expression),
|
|
'with',
|
|
'{',
|
|
optional(seq(
|
|
$.handler_binding,
|
|
repeat(seq(',', $.handler_binding)),
|
|
optional(','),
|
|
)),
|
|
'}',
|
|
),
|
|
|
|
handler_binding: $ => seq(
|
|
field('effect', $.identifier),
|
|
'=',
|
|
field('handler', $.identifier),
|
|
),
|
|
|
|
// Resume expression
|
|
resume_expression: $ => seq(
|
|
'resume',
|
|
'(',
|
|
$._expression,
|
|
')',
|
|
),
|
|
|
|
// Identifier
|
|
identifier: $ => /[a-zA-Z_][a-zA-Z0-9_]*/,
|
|
}
|
|
});
|