mvdan / sh

A shell parser, formatter, and interpreter with bash support; includes shfmt
https://pkg.go.dev/mvdan.cc/sh/v3
BSD 3-Clause "New" or "Revised" License
6.99k stars 333 forks source link

Feature Req: Directive to make parser skip over lines. #960

Closed axgkl closed 1 year ago

axgkl commented 1 year ago

Hi, first of all thanks for your work!

in #680 you write:

The only possible change I can see here is a way to make the parser skip over lines which have syntax errors,

and I wanted to further motivate the idea:

I have a script which can be sourced OR run. When sourced it just does a few simple things which work in zsh OR bash, so I'd like to have it work in both shells.

Problem: To find the script location when sourced I need an assignment which violates bash (but works in zsh):

scriptpath="${BASH_SOURCE[0]:-${(%):-%x}}" # this script. works in bash AND zsh

And that makes shfmt deny formatting the whole file.

Edit: Yes $0 would work outside of functions in zsh and that would be compatible with bash syntax, still the above one has some advantages, I assume there are other such crazies.

-> An ignore directive would, imho, have some use cases.

mvdan commented 1 year ago

You seem to be taking advantage of the fact that bash does lazy parsing. The part after :- is only actually parsed and evaluated when it's needed, not upfront, so Bash doesn't see the error as long as BASH_SOURCE is set properly. As you have found, our parser isn't lazy, as it attempts to parse the full source statically. https://github.com/mvdan/sh#caveats has some other examples where the static parsing being strict can show up.

On one hand, if a shell script parses and works in Bash, I generally want it to parse without errors with our parser. On the other hand, I have to draw a line in the sand somewhere. Bash's parser is fairly lazy and forgiving, and our parser wouldn't be very useful if it did the same :)

I wonder if https://github.com/mvdan/sh/issues/120 would be a solution for you. If you use a script which mixes bash and zsh, and presumably zsh's syntax has more features than bash's, then perhaps you should parse and format in a future zsh mode rather than bash. I imagine zsh is not actually a superset of bash syntax-wise, but since you're writing a script that is somewhere between the two, I imagine parsing in a zsh mode would be good enough.

axgkl commented 1 year ago

Hi, yes, I use the lazy eval feat of bash in this, admittedly exotic use case: A script which provides a few functions when sourced by zsh OR bash, but which is also runnable - then for bash only.

And yes, a zsh mode would prbly(!) parse the whole script but I guess there are bash constructs which zsh does NOT support. Not sure, I use zsh only for the REPL, like most of us.

Reading #120, I see you really want to support zsh. My gut feeling tells me, that in the process you'll prbly want to support an "ignore this/next line" directive anyways ;-)

mvdan commented 1 year ago

Let's close this in favor of #120. I may be proven wrong about not needing a "skip this line" directive, but I really want to exhaust all options. As a solution, it would feel like a very hacky workaround.

axgkl commented 1 year ago

Ok, happy coding - for zsh you'll need endurance.


For my specific problem I think the way to go is not set a script (with 95% bash code but sourceable also in zsh) to zsh syntax, looking at this (good) list of differences here: https://hyperpolyglot.org/unix-shells - there will be incompatibilites.

The way to go is to find out the dir of the script with code which is bash compatible but works in zsh as well (i.e. the $0, not the % syntax) - and then source a zsh specific helper script when zsh is it, and zsh specific syntax is required:

if [ -n "$ZSH_VERSION" ]; then . "$here/zsh_funcs"; fi

It's an additional file but one death we have to die, mixing 2 languages in one file is also not exactly nice.

Thanks again!