Files
tree-sitter-nix-shell/grammar.js
Colin c9d79b0734 rework grammar to be friendly to injections
want to later support the case of multiple nix-shell directives in injection
2023-07-05 00:01:38 +00:00

62 lines
1.7 KiB
JavaScript

module.exports = grammar({
name: 'nix_shell',
rules: {
source_file: $ => seq(
optional($._first_line),
repeat($._next_line),
),
_first_line: $ => choice(
$.nix_shell_directive,
repeat1(/./),
),
_next_line: $ => seq('\n', $._first_line),
nix_shell_directive: $ => seq(
/#![ \t]*nix-shell/,
repeat(seq($._ws, optional($._nix_shell_arg))),
),
// nix acts as if it actually just `exec`'s whichever line contains `#! nix-shell` in the style of `sh`'s `exec`.
// so arguments can be quoted, invoke subshells, interpolate environment variables, etc.
// the full scope cannot be covered, but the minimal scope here gets 99% of use cases.
_nix_shell_arg: $ => choice(
seq('-i', $._opt_ws, $.interpreter),
$._sh_arg,
),
interpreter: $ => $._sh_arg,
_sh_arg: $ => choice(
$._sh_lit,
$._sh_quote1,
$._sh_quote2,
),
_sh_lit: $ => repeat1(choice(
/[^ \\]/,
seq('\\', /./),
)),
_sh_quote1: $ => seq(
'\'',
repeat(choice(
/[^'\\]/,
seq('\\', /./),
)),
'\'',
),
_sh_quote2: $ => seq(
'"',
repeat(choice(
/[^"\\]/,
seq('\\', /./),
)),
'"',
),
// N.B.: this accepts more than it needs to:
// - #!nix-shell directives do not support tabs
// - some places where whitespace is accepted (e.g. `#! nix-shell`), only a *single* space is accepted.
// it's not clear how much of this is intentional, v.s. oversight in matching broader shell parsing,
// so err on overaccepting in case future nix-shell supports these.
_ws: $ => /[ \t]+/,
_opt_ws: $ => /[ \t]*/,
}
});