crush icon indicating copy to clipboard operation
crush copied to clipboard

Commands that contain the '-' char are not parsed correctly.

Open tom-a-wagner opened this issue 5 years ago • 8 comments

Example:

crush> nix
*works*

crush> nix-env
Error: Unknown variable nix

and

crush> nix "--help"
*works*

crush> nix-env "--help"
Error: Stray Arguments

tom-a-wagner avatar Apr 04 '20 09:04 tom-a-wagner

That's expected, since - is used for subtraction is Crush. The real question is what one should do about this. Force the user to quote commands with the '-' characters? Change the subtraction operator? Something else?

liljencrantz avatar Apr 07 '20 16:04 liljencrantz

BTW, the first proposed workaround mentioned above works, so as of today, the way to run a command containing a - is to quote the command name using single quotes, like so:

crush> 'nix-env'

liljencrantz avatar Apr 07 '20 16:04 liljencrantz

I don't think changing the subtraction operator is a good idea, as you would have to pick something that is not allowed in filenames, which is only the NUL char and '/' on Ext4.

I see a few solutions here.

  1. Keep the current behaviour and force quotes. That would probably be the easiest solution, but feels very clunky from a usablility perspective and could confuse users coming from other shells.
  2. Force whitespace around the '-' when doing subtractions. Nicer for the casual user, but making the behaviour of your script dependent on whitespace is one of the things I really dislike about other shells.
  3. Try subtraction first, and if one of the two variables is not defined, try to run it as a command instead (or the other way around, see if a program with that name exists). This would probably complicate parsing, as parsing then depends on external factors (defined variables or programs in $PATH), and a scripts behaviour could unexpectedly change depending on those factors.
  4. Only do calculation in blocks like $((2 - 1)) in bash. This, like 2., also makes syntax worse and less intuitive to use.

None of those are really great IMO, but maybe this leads to some better ideas.

Anyway, I appreciate the effort to make this shell!

Edit: Both 2. and 4. would not only solve this, but also #7 (allow * as glob character) and allow using '/' for division.

tom-a-wagner avatar Apr 08 '20 11:04 tom-a-wagner

Another solution would be to add add, sub, mul and div builtins and remove the four operators. Calculating a number would then be add 1 2, similar to how functions like mod or max work in Haskell. While the syntax is slightly worse than keeping the operators. I don't think this has any other big downsides.

tom-a-wagner avatar Apr 08 '20 12:04 tom-a-wagner

If you look at the git history of crush, you will see that crush used to use add 1 2 to add two numbers together. I felt that the language at that point was quite clunky and hard to read, which is why I tried this new version. So far, I like the tradeoff of the new solution better.

The idea of becoming extremely whitespace sensitive by differentiating between a-b and a - b is in itself highly unappealing, but there are definitely big advantages. Like you say, it will solve all current issues with operators, and instead create a fleet of different new problems.

liljencrantz avatar Apr 08 '20 15:04 liljencrantz

In CSS they solved the problem by declaring that subtraction operator must have spaces on both side. Otherwise it’s a part of a token. So nix-env is one token, nix -env are two tokens and nix - env is subtraction.

alexeyten avatar Aug 10 '20 06:08 alexeyten

Mandating whitespace around - doesn't completely solve the problem either. Many standard *nix utilities (cat, tar, etc.) use - as an argument to designate stdin/stdout. Forcing users to jump through hoops to "un-math" an argument that's documented in hundreds of man pages seems like a huge step backwards in shell usability.

Instead, I think math operations should be encapsulated in a special context, perhaps using the bash-like (( ... )). This should also address #7, adhere to the Principle of Least Surprise, and might even simplify crush's parsing logic.

gromgit avatar Aug 12 '20 09:08 gromgit

I'm a lurker here, working on a different pipe-objects-instead-of-strings shell, but I do have some perspective on this issue. Trying to preserve expected syntax on the command line, while trying to ALSO cram in familiar syntax from other languages is somewhere between very difficult and impossible. My shell is bash-like (commands, arguments, short and long options, pipes), but it exposes Python expressions (only, not arbitary Python code) on the command line in places where some logic or computation is required. The rule I use is simple: Python expressions go inside top-level parens. So there is never any confusion about the meaning of *, -, or other tokens that have meaning in both languages.

Parsing is quite simple. When I see a top-level '(', I just scan for a matching ')' (paying attention to quotes and escapes on the way), and then everything inside the parens is turned into a function which the shell command can then invoke.

geophile avatar Aug 12 '20 14:08 geophile