oils-for-unix / oils

Oils is our upgrade path from bash to a better language and runtime. It's also for Python and JavaScript users who avoid shell!
http://www.oilshell.org/
Other
2.82k stars 154 forks source link

evalExpr() should accept a string (as well as value.Expr), evalShExpr too #1780

Open bar-g opened 8 months ago

bar-g commented 8 months ago

I read ${!indirect} is deprecated in https://www.oilshell.org/release/latest/doc/ysh-tour.html but could not find any idea how variable indirection is done properly in ysh.

  1. for variable substitutions? instead of echo ${!a}, i.e. how to use variable named like some "$composable$string"

  2. for assignments setvar $main$sub = "value" ?

bar-g commented 8 months ago

Maybe something with &places ?

bar-g commented 8 months ago
  1. in shell:
    main='a'
    sub='b'
    local $main$sub='value'
    echo ab=$ab  # ab=value
andychu commented 8 months ago

I think the main one would be evalExpr(), and that's now implemented

So you can do evalExpr('my' ++ 'var') and it gives the value of looking up that variable name

I can add a couple words there

andychu commented 8 months ago

Hmm actually I realized evalExpr() is for YSH expressions, but really we would need something for these Bourne shell expressions

${!indirect} It's actually similar to what test -v does, there is dynamic parsing of a[0] and so forth

I guess I can can change it to a TODO in the doc ... Hopefully some user of ${!indirect} will see that

bar-g commented 8 months ago

Well, I'd like to avoid that implicit parsing in ${...}, also therefore looking for a safe way of indirect variable access (for 1. read and 2. write) in ysh.

Can't seem to make evalExpr work for reading vars, though.

ysh ysh-0.19.0$ ab=foo

ysh ysh-0.19.0$ echo $[evalExpr('a' ++ 'b')]

  echo $[evalExpr('a' ++ 'b')]
                      ^~
[ interactive ]:99: fatal: Arg 1 should be a Expr, got Str
andychu commented 8 months ago

Ah good point, yeah you need a value.Expr quotation type which is ^[...]

But that means "a" ++ "b" doesn't really work

OK we should change it to also accept a string. Just like now eval 'echo cmd' accepts a string but also a value.Command quotation type

Melkor333 commented 8 months ago

And maybe make sure to have some examples for

var x = 5
var s1 = "echo x = $x"
var s2 = ^"echo x = $x" # or ^["echo x = $x"]
setvar x = 10

echo $[evalExpr(s1)]
-> x = 5
echo $[evalExpr(s2)]
-> echo x = 10              # i hope that's correct? or is it "x = 10"?
bar-g commented 8 months ago

Hm, with me originally looking for a safe way for variable (name) indirection, I'm not sure if using the full eval fuctionality is desired for that.

IIUC ${!...} was once meant as a safer replacement instead of using eval, but apparently failed in doing so in bash due to the extensive parsing that includes execution. So ${!...} is not advisable to use, even though safer (only) in osh and ysh due to eval_unsafe_arith being disabled by default.

So, taking a step back, it's probably advisable to only use the provided structured ysh data types instead (list/dict). And consequently, for the ${!indirect} mention in the ysh-tour doc, maybe it could or should simply point to replace it with, e.g.:

var d2 = {[key ++ '_z']: 'ZZZ'}  # Computed key name
echo $[d2.alice_z]   # => ZZZ    # Reminder: expression sub
bar-g commented 8 months ago

Hm, but a remaining question from me would be how to use a "computed key" for data output, e.g. in an expression sub.

Is this correct?: (but not implemented, yet)

osh-0.19.0$ echo $[d->get("$a$b")]

  echo $[d->get("$a$b")]
            ^~~
[ interactive ]:45: fatal: Method 'get' does not exist on type Dict
andychu commented 8 months ago

You can use d["$a$b"] , which will raise an exception like Python if it doesn't exist

I think d => get("$a$b") is not implemented yet, which will return null like Python, should do that

We made a change recently where -> is for mutating methods and => is for transforming methods, though it's not completely enforced yet

bar-g commented 8 months ago

Thanks! Looking forward if the most basic set of dict access is actually possible already. But it's a bit unfortunate, that I wasn't able to get these from the current docs though.

See, I don't fit to this slogan: YSH is for Python and JavaScript users who avoid shell!

So maybe something like this could also be considered?: YSH is also for users of ordinary shells which want to work efficiently also with other data types than lines of words, and without having to continuously drag around a bag of pitfall workarounds for every task.

In this vein, I'd like to suggest, re-adding the mention of bash's ${!indirect} to the ysh-tour doc again. For example, with something like this:

- Most of what's in `${}` has *more readable and safer* alternatives.  For example
   - `"${s#/tmp}"` could be `s => removePrefix('/tmp')` (TODO).
   - `"${!indirect}"` could be `$[mydict["$mykey"]]` (shorter: $[mydict[mykey]]), or mydict=>get(mykey) (returns null insted of error).
andychu commented 4 months ago

Bumping this bug, it came up again - https://oilshell.zulipchat.com/#narrow/stream/417617-help-wanted/topic/evalExpr.28.29.20should.20accept.20a.20string.20too