Julia: parse command interpolations at parse time

Created on 19 May 2013  路  11Comments  路  Source: JuliaLang/julia

It would be nice to have a way to "double interpolate" a variable with respects to nested possible interpolation contexts to make it easier to write quoted code blocks that wants to interpolate a variable inside a string. e.g., so the below code echos "hi". Not sure what the best way to go about that is.

macro example()
 f="hi"
 quote
  run(`echo $$f`)
 end
end
help wanted parser

Most helpful comment

This is an interesting approach particularly because it separates the detailed shell parsing rules from the rules for interpolating values. I suppose this means the parser changes will be much more minimal and completely consistent with the rules for string interpolation which is welcome.

I think this would be an excellent thing, provided it's sound! But I can think of at least one case where interpolation into normal strings vs interpolation into cmd strings behaves quite differently:

julia> `hi '$a'`
`hi '$a'`

julia> "hi '$a'"
ERROR: UndefVarError: a not defined
Stacktrace:
 [1] top-level scope at REPL[5]:1

Looking at the source of shell_parse this seems to be the only case. So in principle this one difference could be dealt with in the parser. It's a bit of an unfortunate difference though.

All 11 comments

the following already works without ambiguity, and doesn't require changing string parsing based on context:

macro example()
 f="hi"
 quote
  run($(`echo $f`))
 end
end

Is there an example where this isn't quite so straightforward?

What about something like

macro example()
 f="a"
 esc(quote
  g="b"
  run(`echo $f $g`)
 end)
end

You could do instead

macro example()
 f="a"
 esc(quote
  g="b"
  h=$(f)
  run(`echo $h $g`)
 end)
end

but it's a little weird to be forced to create a temporary variable, since I'm not sure there is any other situation in Julia in which replacing a reference to a variable with its known value will change the semantics of the program. While here, changing $h to $(f) in the echo line changes the meaning of the program.

(edit: fix the variable in the second example)

That's tricky, but you don't exactly see it until you try to expand the macro. In the first example, f only exists at macro expansion time and g only exists at run time. Assigning the value of f to h seems to be necessary to convert the value into a variable. For strings, it is easy to use string(...) instead of interpolation to avoid the distinction.

julia> macro example()                             
               f="a"
               quote
                g="b"
                run(Cmd(ByteString["echo",$(f),g]))
               end                                       
              end

julia> @example
a b

julia> macroexpand(quote @example end)
quote  # none, line 1:
    begin  # none, line 4:
        g#4 = "b" # line 5:
        run(Cmd(ByteString["echo","a",g#4]))
    end
end

(edit: I forgot Cmd() is the analog to string(), so I fixed the example)

$($f) would work if we moved command expression expansion to the parse stage like we did for string interpolation.

The variable-to-value substitution you mention would be:

  h=$(f)
  run(`echo $h $g`)

to

  run(`echo $($f) $g`)

You can't drop the extra $. This doesn't work now, but would with the change I described.

this would be a new feature and thus not appropriate for the v0.4.x bugfix series. i'm going to tentatively move this to v0.5, please comment if you think that is not appropriate.

Since we're not doing https://github.com/JuliaLang/julia/issues/12139 we can do this.

From triage: can live without this for 1.0.

Removing from milestone, noting that this may change post 1.0, which won't affect normal code but could affect macros that deal with commands.

Would changing the parsing of `a$(b)c` from (macrocall (core @cmd) "a$(b)c") to (macrocall (core @cmd) (string "a" b "c")) be too breaking for a 1.x release? That would be a slightly different approach than #13199. I think this would definitely make for some more intuitive behavior and would also, for example, make #37007 redundant.

This is an interesting approach particularly because it separates the detailed shell parsing rules from the rules for interpolating values. I suppose this means the parser changes will be much more minimal and completely consistent with the rules for string interpolation which is welcome.

I think this would be an excellent thing, provided it's sound! But I can think of at least one case where interpolation into normal strings vs interpolation into cmd strings behaves quite differently:

julia> `hi '$a'`
`hi '$a'`

julia> "hi '$a'"
ERROR: UndefVarError: a not defined
Stacktrace:
 [1] top-level scope at REPL[5]:1

Looking at the source of shell_parse this seems to be the only case. So in principle this one difference could be dealt with in the parser. It's a bit of an unfortunate difference though.

That is quite intentional as it mirrors what the shell does. You often use single quotes in a shell command when you need to write some code that uses $ and not worry about escaping it. I think that deviating from the shell here would be a major reduction in usability. Of we're not going to cleave very closely to how the shell works, then it would be better to just use an array API for spawning commands.

A more general observation here is that the current arrangement is strictly more flexible: the macros get to decide exactly how and if they do interpolation. Yes, that makes implementing interpolation harder, but that's the trade off. Given that implementing macros like this is generally considered an advanced activity, that feels like the right trade off to me. The main downside is that user string expressions can't allow nested quotation like normal strings can; I haven't found that limitation to be especially bothersome鈥攊f you're putting quotes in the things you're interpolating into quotes, I think you're better off doing it in separate expressions anyway.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

dpsanders picture dpsanders  路  3Comments

tkoolen picture tkoolen  路  3Comments

wilburtownsend picture wilburtownsend  路  3Comments

StefanKarpinski picture StefanKarpinski  路  3Comments

omus picture omus  路  3Comments