rework grammar to be friendly to injections

want to later support the case of multiple nix-shell directives in injection
This commit is contained in:
2023-07-05 00:01:38 +00:00
parent d5adafbc03
commit c9d79b0734
5 changed files with 114 additions and 149 deletions

View File

@@ -2,136 +2,84 @@
empty nix-shell empty nix-shell
================== ==================
#!/usr/bin/env nix-shell
#!nix-shell #!nix-shell
--- ---
(source_file (source_file
(first_line) (nix_shell_directive))
(next_line
(annotation_line)))
================== ==================
whitespace in nix-shell nix-shell no interpreter
================== ==================
#!/usr/bin/env nix-shell #!nix-shell -p gnused
#! nix-shell
--- ---
(source_file (source_file
(first_line) (nix_shell_directive))
(next_line
(annotation_line)))
================== ==================
minimal nix-shell invocation with interpreter minimal nix-shell invocation with interpreter
================== ==================
#!/usr/bin/env nix-shell
#!nix-shell -i bash #!nix-shell -i bash
--- ---
(source_file (source_file
(first_line) (nix_shell_directive
(next_line (interpreter)))
(annotation_line
(nix_shell_args ==================
(interpreter whitespace in nix-shell
(sh_arg)))))) ==================
#! nix-shell -i bash
---
(source_file
(nix_shell_directive
(interpreter)))
================== ==================
non-interpreter argument non-interpreter argument
================== ==================
#!/usr/bin/env nix-shell
#!nix-shell -i bash -p gnused #!nix-shell -i bash -p gnused
--- ---
(source_file (source_file
(first_line) (nix_shell_directive
(next_line (interpreter)))
(annotation_line
(nix_shell_args
(interpreter
(sh_arg))
(sh_arg)
(sh_arg)))))
================== ==================
non-interpreter flags non-interpreter flags
================== ==================
#!/usr/bin/env nix-shell
#!nix-shell -i bash -p gnused awk #!nix-shell -i bash -p gnused awk
--- ---
(source_file (source_file
(first_line) (nix_shell_directive
(next_line (interpreter)))
(annotation_line
(nix_shell_args
(interpreter
(sh_arg))
(sh_arg)
(sh_arg)
(sh_arg)))))
================== ==================
quoted arguments quoted arguments
================== ==================
#!/usr/bin/env nix-shell
#!nix-shell -i bash arg\ one 'arg"Two' "'argThree" #!nix-shell -i bash arg\ one 'arg"Two' "'argThree"
--- ---
(source_file (source_file
(first_line) (nix_shell_directive
(next_line (interpreter)))
(annotation_line
(nix_shell_args
(interpreter
(sh_arg))
(sh_arg)
(sh_arg)
(sh_arg)))))
================== ==================
order-independent order-independent
================== ==================
#!/usr/bin/env nix-shell
#!nix-shell -p gnused -i bash #!nix-shell -p gnused -i bash
--- ---
(source_file (source_file
(first_line) (nix_shell_directive
(next_line (interpreter)))
(annotation_line
(nix_shell_args
(sh_arg)
(sh_arg)
(interpreter
(sh_arg))))))
==================
multiple annotation lines
==================
#!/usr/bin/env nix-shell
#!nix-shell -p gnused
#!nix-shell -i bash
---
(source_file
(first_line)
(next_line
(annotation_line
(nix_shell_args
(sh_arg)
(sh_arg))))
(next_line
(annotation_line
(nix_shell_args
(interpreter
(sh_arg))))))

View File

@@ -1,3 +1,11 @@
==================
empty
==================
---
(source_file)
================== ==================
nix-shell with a bash payload nix-shell with a bash payload
================== ==================
@@ -8,13 +16,8 @@ echo "hello, world!"
--- ---
(source_file (source_file
(first_line) (nix_shell_directive
(next_line (interpreter)))
(annotation_line
(nix_shell_args
(interpreter
(sh_arg)))))
(next_line))
================== ==================
nix-shell with interspersed payload nix-shell with interspersed payload
@@ -28,12 +31,39 @@ echo everybody
--- ---
(source_file (source_file
(first_line) (nix_shell_directive
(next_line) (interpreter)))
(next_line)
(next_line ==================
(annotation_line nix-shell with multiple directives
(nix_shell_args ==================
(interpreter
(sh_arg))))) #!/usr/bin/env nix-shell
(next_line)) echo hello
#!nix-shell -p gnused
#!nix-shell -i bash
echo again
#!nix-shell
echo everybody
---
(source_file
(nix_shell_directive)
(nix_shell_directive
(interpreter))
(nix_shell_directive))
==================
nix-shell with conflicting interpreters (last wins, but good luck with that)
==================
#!/usr/bin/env nix-shell
#!nix-shell -i bash
#!nix-shell -i python3
---
(source_file
(nix_shell_directive
(interpreter))
(nix_shell_directive
(interpreter)))

View File

@@ -5,8 +5,7 @@ nix-shell shebang without any interpreter option
#!/usr/bin/env nix-shell #!/usr/bin/env nix-shell
--- ---
(source_file (source_file)
(first_line))
================== ==================
nix-shell shebang whitespace nix-shell shebang whitespace
@@ -15,8 +14,7 @@ nix-shell shebang whitespace
#! /usr/bin/env nix-shell #! /usr/bin/env nix-shell
--- ---
(source_file (source_file)
(first_line))
================== ==================
nix-shell shebang tab nix-shell shebang tab
@@ -25,8 +23,7 @@ nix-shell shebang tab
#! /usr/bin/env nix-shell #! /usr/bin/env nix-shell
--- ---
(source_file (source_file)
(first_line))
================== ==================
nix-shell shebang tabs and spaces nix-shell shebang tabs and spaces
@@ -35,28 +32,7 @@ nix-shell shebang tabs and spaces
#! /usr/bin/env nix-shell #! /usr/bin/env nix-shell
--- ---
(source_file (source_file)
(first_line))
==================
nix-shell shebang no env
==================
#!nix-shell
---
(source_file
(first_line))
==================
nix-shell shebang whitespace no env
==================
#! nix-shell
---
(source_file
(first_line))
================== ==================
nix-shell shebang via relative env nix-shell shebang via relative env
@@ -65,8 +41,7 @@ nix-shell shebang via relative env
#!env nix-shell #!env nix-shell
--- ---
(source_file (source_file)
(first_line))
================== ==================
nix-shell shebang via /bin/env nix-shell shebang via /bin/env
@@ -75,5 +50,14 @@ nix-shell shebang via /bin/env
#!/bin/env nix-shell #!/bin/env nix-shell
--- ---
(source_file (source_file)
(first_line))
==================
nix-shell multiple shebangs
==================
#!/usr/bin/env nix-shell
#!/usr/bin/env nix-shell
---
(source_file)

View File

@@ -2,26 +2,29 @@ module.exports = grammar({
name: 'nix_shell', name: 'nix_shell',
rules: { rules: {
// TODO: allow trailing whitespace on any line source_file: $ => seq(
source_file: $ => seq($.first_line, repeat($.next_line)), optional($._first_line),
repeat($._next_line),
first_line: $ => seq($._shebang_open, $._opt_ws, optional(seq($._env, $._ws)), $._nix_shell), ),
next_line: $ => seq($._newline, choice( _first_line: $ => choice(
$.annotation_line, $.nix_shell_directive,
repeat(/./), repeat1(/./),
)), ),
annotation_line: $ => seq($._shebang_open, $._opt_ws, $._nix_shell, optional($.nix_shell_args)), _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`. // 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. // 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. // the full scope cannot be covered, but the minimal scope here gets 99% of use cases.
nix_shell_args: $ => repeat1(seq($._ws, $._nix_shell_arg)),
_nix_shell_arg: $ => choice( _nix_shell_arg: $ => choice(
seq('-i', $._opt_ws, $.interpreter), seq('-i', $._opt_ws, $.interpreter),
$.sh_arg, $._sh_arg,
), ),
interpreter: $ => $.sh_arg, interpreter: $ => $._sh_arg,
sh_arg: $ => choice( _sh_arg: $ => choice(
$._sh_lit, $._sh_lit,
$._sh_quote1, $._sh_quote1,
$._sh_quote2, $._sh_quote2,
@@ -47,20 +50,12 @@ module.exports = grammar({
'"', '"',
), ),
_shebang_open: $ => '#!',
// TODO: env accepts flags like `-v` or `--unset=NAME` or `VAR=VALUE` before `nix-shell`,
_env: $ => choice(
'/usr/bin/env',
'/bin/env',
'env',
),
_nix_shell: $ => 'nix-shell',
// N.B.: this accepts more than it needs to: // N.B.: this accepts more than it needs to:
// - shebang parser allows tab characters
// - #!nix-shell directives do not support tabs // - #!nix-shell directives do not support tabs
// - wherever #!nix-shell directives allow whitespace, it's a *single* space -- never multiple // - 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]+/, _ws: $ => /[ \t]+/,
_opt_ws: $ => /[ \t]*/, _opt_ws: $ => /[ \t]*/,
_newline: $ => '\n',
} }
}); });

8
queries/injections.scm Normal file
View File

@@ -0,0 +1,8 @@
; if we find an interpreter, mark the *entire file* as injected content.
; nix-shell doesn't process the source file before sending it to the interpreter,
; so nix-shell directives like `#!nix-shell -i bash` are both a directive to nix-shell
; and actual language content handled by the specified interpreter.
((source_file
(nix_shell_directive
(interpreter) @injection.language)) @injection.content
(#set! injection.include-children))