Skip to content

Evaluate string interpolation at parse time#11562

Merged
kubouch merged 6 commits intonushell:mainfrom
ysthakur:const-str-interpolate
Jan 22, 2024
Merged

Evaluate string interpolation at parse time#11562
kubouch merged 6 commits intonushell:mainfrom
ysthakur:const-str-interpolate

Conversation

@ysthakur
Copy link
Copy Markdown
Member

@ysthakur ysthakur commented Jan 18, 2024

Closes #11561

Description

This PR will allow string interpolation at parse time.

Since the actual config hasn't been loaded at parse time, this uses the get_config() method on StateWorkingSet. So file sizes and datetimes (I think those are the only things whose string representations depend on the config) may be formatted differently from how users have configured things, which may come as a surprise to some. It does seem unlikely that anyone would be formatting file sizes or date times at parse time. Still, something to think about if/before this PR merged.

Also, I changed the ModuleNotFound error to include the name of the module.

User-Facing Changes

Users will be able to do stuff like:

const x = [1 2 3]
const y = $"foo($x)" // foo[1, 2, 3]

The main use case is use-ing and source-ing files at parse time:

const file = "foo.nu"
use $"($file)"

If the module isn't found, you'll see an error like this:

Error: nu::parser::module_not_found

  × Module not found.
   ╭─[entry #3:1:1]
 1 │  use $"($file)"
   ·      ─────┬────
   ·           ╰── module foo.nu not found
   ╰────
  help: module files and their paths must be available before your script is run as parsing occurs before anything is evaluated

Tests + Formatting

After Submitting

Although there's user-facing changes, there's probably no need to change the docs since people probably already expect string interpolation to work at parse time.

Edit: @kubouch pointed out that we'd need to document the fact that stuff like file sizes and datetimes won't get formatted according to user's runtime configs, so I'll make a PR to nushell.github.io after this one

@fdncred
Copy link
Copy Markdown
Contributor

fdncred commented Jan 18, 2024

so, any string interpolation is a const now? that sounds cool. people frequently ask for things like this.

source $'($nu.default-config-dir)/some-file.nu'
use $'($some_constrcuted_dir)/a/path/to/file.nu'

@ysthakur
Copy link
Copy Markdown
Member Author

@fdncred Yeah, you'll be able to do that now. I realized after your comment that if you use a non-existent module, the error message doesn't show the module name, so I added that to the error message

@ysthakur ysthakur marked this pull request as ready for review January 18, 2024 19:42
@texastoland
Copy link
Copy Markdown
Contributor

You're so fast 🔥

@fdncred
Copy link
Copy Markdown
Contributor

fdncred commented Jan 18, 2024

Since @kubouch worked so much on the const code, I'd like to get his take on this before landing.

@hustcer
Copy link
Copy Markdown
Contributor

hustcer commented Jan 18, 2024

Bravo

@WindSoilder
Copy link
Copy Markdown
Contributor

Not really sure, if I put a sub command into string interpolation:

echo "hello" o> /tmp/hello.txt
$"aaa (cat /tmp/hello.txt)"

Can it be handled at parse time?

@texastoland
Copy link
Copy Markdown
Contributor

texastoland commented Jan 18, 2024

Not really sure, if I put a sub command into string interpolation:

@WindSoilder I'm not sure which operations are recognized as constant but in your specific example the value isn't constant at parse time (must be written first). This PR makes only recognizes string interpolation as similar to use or source expressions with +.

@kubouch
Copy link
Copy Markdown
Contributor

kubouch commented Jan 19, 2024

@WindSoilder It can't be parse time because it tries to call an external command which is not supported at parse time.

@kubouch
Copy link
Copy Markdown
Contributor

kubouch commented Jan 19, 2024

@ysthakur I think we should limit the string interpolation to only those cases where we wouldn't need $env.config for formatting. I'm not sure how feasible that is, though, it might require some surgery to separate the string formatting that requires and doesn't require the config. The idea of constants is that they do not support environment variables (except PWD which is unavoidable), so if we were purists, we wouldn't allow config-defined formatting. Not sure if it's that big of a deal, though. We might just as well land this and take a note of it for next time when we get to refactor some formatting code.

The second issue is querying the config. To get config at runtime you need to pass the Stack, and we have a canonical way of doing it:

pub fn get_config(engine_state: &EngineState, stack: &Stack) -> Config {
(unfortunately it clones the config, that's suboptimal). The key is to use Stack as well to query the config, otherwise any local updates to $env.config won't be visible. We had a bunch of bugs related to this where engine_state.get_config() was used instead of the linked function and things like $env.config.xxx = yyy; command-that-uses-xxx-config wouldn't work. There are bugs like that probably still lurking around.

I guess you could create get_config(state: Self::State<'_>, mut_state &Self::MutState). The whole config is ripe for a thorough refactor…

@ysthakur
Copy link
Copy Markdown
Member Author

@kubouch Hmm, it'd be a bit tough to limit string interpolation to only non-filesize and non-datetime values. Perhaps into_string() could take a Option containing a config, and use defaults if no config is given?

Also, thanks for the tip on getting the config at runtime, I didn't know that existed. Cloning the config is unfortunate. I'll convert this back to a draft for now since it seems like it's going to take a bit of thinking

@ysthakur ysthakur marked this pull request as draft January 20, 2024 00:55
@fdncred
Copy link
Copy Markdown
Contributor

fdncred commented Jan 20, 2024

There are so many value-to-string conversions now. It would be nice to consolidate at some point, not necessarily for this PR.

@kubouch
Copy link
Copy Markdown
Contributor

kubouch commented Jan 20, 2024

I wouldn't block landing it because of the string conversions, we'd just need to document it. It would likely require messing with the string conversion code, and that would be better for another PR. The main thing is the Stack.

I'm thinking that instead of cloning, the get_config() could return an equivalent of

enum MaybeOwned {
    Owned(Config)
    Borrowed(&Config)
}

@ysthakur
Copy link
Copy Markdown
Member Author

ysthakur commented Jan 20, 2024

Do you want me to make get_config return a MaybeOwned/Cow right in this PR or leave it for later? (I assume this one another one)

@ysthakur ysthakur marked this pull request as ready for review January 20, 2024 22:44
@kubouch
Copy link
Copy Markdown
Contributor

kubouch commented Jan 21, 2024

You can do it in this PR for the Eval::get_config() that you added. But refactoring the one in nu_engine::env should be in a separate PR.

@kubouch kubouch added the deprecated:pr-language (deprecated: this label is too vague) This PR makes some language changes label Jan 22, 2024
@kubouch
Copy link
Copy Markdown
Contributor

kubouch commented Jan 22, 2024

OK, looks good, let's land it!

@kubouch kubouch merged commit 90d65bb into nushell:main Jan 22, 2024
@ysthakur ysthakur deleted the const-str-interpolate branch January 22, 2024 07:16
@fdncred
Copy link
Copy Markdown
Contributor

fdncred commented Jan 22, 2024

Great work @ysthakur! I'm loving this. Now we just have to rewrite our documentation that says these are not possible.
image

And this one too

const f = "foo.nu"
source $f

@hustcer hustcer added this to the v0.90.0 milestone Jan 22, 2024
@kubouch kubouch added the notes:mention Include the release notes summary in the "Hall of Fame" section label Jan 22, 2024
dmatos2012 pushed a commit to dmatos2012/nushell that referenced this pull request Feb 20, 2024
<!--
if this PR closes one or more issues, you can automatically link the PR
with
them by using one of the [*linking
keywords*](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword),
e.g.
- this PR should close #xxxx
- fixes #xxxx

you can also mention related issues, PRs or discussions!
-->

Closes nushell#11561

# Description
<!--
Thank you for improving Nushell. Please, check our [contributing
guide](../CONTRIBUTING.md) and talk to the core team before making major
changes.

Description of your pull request goes here. **Provide examples and/or
screenshots** if your changes affect the user experience.
-->

This PR will allow string interpolation at parse time.

Since the actual config hasn't been loaded at parse time, this uses the
`get_config()` method on `StateWorkingSet`. So file sizes and datetimes
(I think those are the only things whose string representations depend
on the config) may be formatted differently from how users have
configured things, which may come as a surprise to some. It does seem
unlikely that anyone would be formatting file sizes or date times at
parse time. Still, something to think about if/before this PR merged.

Also, I changed the `ModuleNotFound` error to include the name of the
module.

# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->

Users will be able to do stuff like:

```nu
const x = [1 2 3]
const y = $"foo($x)" // foo[1, 2, 3]
```

The main use case is `use`-ing and `source`-ing files at parse time:

```nu
const file = "foo.nu"
use $"($file)"
```

If the module isn't found, you'll see an error like this:
```
Error: nu::parser::module_not_found

  × Module not found.
   ╭─[entry nushell#3:1:1]
 1 │  use $"($file)"
   ·      ─────┬────
   ·           ╰── module foo.nu not found
   ╰────
  help: module files and their paths must be available before your script is run as parsing occurs before anything is evaluated
```

# Tests + Formatting
<!--
Don't forget to add tests that cover your changes.

Make sure you've run and fixed any issues with these commands:

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used` to
check that you're using the standard code style
- `cargo test --workspace` to check that all tests pass (on Windows make
sure to [enable developer
mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
- `cargo run -- -c "use std testing; testing run-tests --path
crates/nu-std"` to run the tests for the standard library

> **Note**
> from `nushell` you can also use the `toolkit` as follows
> ```bash
> use toolkit.nu # or use an `env_change` hook to activate it
automatically
> toolkit check pr
> ```
-->

# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->

Although there's user-facing changes, there's probably no need to change
the docs since people probably already expect string interpolation to
work at parse time.

Edit: @kubouch pointed out that we'd need to document the fact that
stuff like file sizes and datetimes won't get formatted according to
user's runtime configs, so I'll make a PR to nushell.github.io after
this one
rgwood pushed a commit that referenced this pull request Mar 2, 2024
<!--
if this PR closes one or more issues, you can automatically link the PR
with
them by using one of the [*linking
keywords*](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword),
e.g.
- this PR should close #xxxx
- fixes #xxxx

you can also mention related issues, PRs or discussions!
-->

# Description
<!--
Thank you for improving Nushell. Please, check our [contributing
guide](../CONTRIBUTING.md) and talk to the core team before making major
changes.

Description of your pull request goes here. **Provide examples and/or
screenshots** if your changes affect the user experience.
-->

Currently, in the test for interpolating strings at parse-time, the
formatted string includes `(X years ago)` (from formatting a date) (test
came from #11562). I didn't
realize when I was writing it that it would have to be updated every
year. This PR uses regex to check the output instead.

# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->

# Tests + Formatting
<!--
Don't forget to add tests that cover your changes.

Make sure you've run and fixed any issues with these commands:

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used` to
check that you're using the standard code style
- `cargo test --workspace` to check that all tests pass (on Windows make
sure to [enable developer
mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
- `cargo run -- -c "use std testing; testing run-tests --path
crates/nu-std"` to run the tests for the standard library

> **Note**
> from `nushell` you can also use the `toolkit` as follows
> ```bash
> use toolkit.nu # or use an `env_change` hook to activate it
automatically
> toolkit check pr
> ```
-->

# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

deprecated:pr-language (deprecated: this label is too vague) This PR makes some language changes notes:mention Include the release notes summary in the "Hall of Fame" section

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Interpolated string is not a parse time constant

6 participants