wryun / es-shell

es: a shell with higher-order functions
http://wryun.github.io/es-shell/
Other
307 stars 25 forks source link

`cmd < <{subcmd}` produces syntax error #56

Open jpco opened 1 year ago

jpco commented 1 year ago

This seems like syntax that should be valid, right? Minimal (useless) example:

; cat < <{echo *}
syntax error

Found while thinking about alternatives to https://github.com/wryun/es-shell/pull/55.

memreflect commented 1 year ago

cat < <{echo *} and echo * > >{cat} should both be (mostly) behaviorally equivalent to %pipe {echo *} 1 0 {cat}, if this worked, and i can't see a good reason to implement this when the net outcome is the same as with a regular pipe.

Another non-sh shell, fish, currently does not have a %writeto equivalent, but it does have %readfrom in the form of cmd (subcmd | psub). The fish equivalent of cmd < <{subcmd}, i.e. cmd < (subcmd | psub), just happens to work because of how psub works; it isn't intended to be used in that way.

jpco commented 1 year ago

The difference between cat < <{echo *}, echo * > >{cat}, and echo * | cat come down to which command runs in the local process (for this example, pretend cat is a builtin).

As es works today, echo * | cat runs both commands in subshells (this is what #55 would change if merged). The %readfrom and %writeto based examples, however, run their respective "outer" command in the local shell, which allows more side effects.

You can see this in action by manually "parsing" the syntax down to hook functions:

; a = b
; {a = c; echo foo bar} | rev
rab oof
; echo $a
b
; # {a = c; echo foo bar} > >{rev}
; %writeto _devfd1 {rev} {%create 1 <={%one $_devfd1} {a = c; echo foo bar}}
rab oof
; echo $a
c

I find the whole "use process substitution to work around pipes being executed in a subprocess" thing in bash to be obnoxious, but at least there's some syntax at all for it.

memreflect commented 1 year ago

I can see your reasoning, but %writeto var {cmd > $var} works fine already:

; # Simple example: equivalent of non-working
  #  {a = c; echo foo bar} > >{rev}
; a = b
; %writeto _devfdz {rev} {{a = c; echo foo bar} > $_devfdz}
rab oof
; echo $a
c

And %readfrom var input {cmd < $var} works as well, if you need Bash's lastpipe behavior as proposed in #55:

; # Adapted from PR #55: equivalent of non-working
  #   {for-each @ l{ accum = $accum $l }} < <{cat input}
; fn for-each lambda {
    let (line = ())
      while {!~ <={line = <=%read} ()} {
        $lambda $line
      }
  }
; cat input
one two
three
four
; %readfrom _devfdz {cat input} {{for-each @ l{ accum = $accum $l }} < $_devfdz}
; for (line = $accum)
    echo $line
one two
three
four

I guess my point is that the benefit does not seem worth the trouble involved in modifying the yacc grammar to be able to obtain

%open 0 <={%one
    !NODE:nredir($_devfdX {%readfrom _devfdX input PLACEHOLDER})
} {cmd}

and subsequently rewrite it to

%readfrom _devfdX input {
    %open 0 <={%one $_devfdX} cmd
}

The same would also need to be done for %writeto _devfdX output with %create 1 as well to get cmd > >{output} working of course.

I certainly agree that the syntax and general behavior of the shell is currently too limited to allow for the desired behavior, but i imagine that's one of the best reasons why the functions are available in es: they allow you to do things that otherwise aren't possible. With es, you're not at the mercy of the shell's syntax and behaviors.