2023-02-24 15:54:42 +00:00
|
|
|
use nu_protocol::engine::{EngineState, StateWorkingSet};
|
|
|
|
|
|
|
|
use crate::*;
|
|
|
|
|
|
|
|
pub fn create_default_context() -> EngineState {
|
|
|
|
let mut engine_state = EngineState::new();
|
|
|
|
|
|
|
|
let delta = {
|
|
|
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
|
|
|
|
|
|
|
macro_rules! bind_command {
|
|
|
|
( $( $command:expr ),* $(,)? ) => {
|
|
|
|
$( working_set.add_decl(Box::new($command)); )*
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// Core
|
|
|
|
bind_command! {
|
|
|
|
Alias,
|
|
|
|
Break,
|
2023-03-24 09:50:23 +00:00
|
|
|
Collect,
|
2023-02-24 15:54:42 +00:00
|
|
|
Const,
|
|
|
|
Continue,
|
|
|
|
Def,
|
|
|
|
DefEnv,
|
|
|
|
Describe,
|
|
|
|
Do,
|
|
|
|
Echo,
|
|
|
|
ErrorMake,
|
|
|
|
ExportAlias,
|
|
|
|
ExportCommand,
|
Module: support defining const and use const variables inside of function (#9773)
<!--
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.
-->
Relative: #8248
After this pr, user can define const variable inside a module.
![image](https://github.com/nushell/nushell/assets/22256154/e3e03e56-c4b5-4144-a944-d1b20bec1cbd)
And user can export const variables, the following screenshot shows how
it works (it follows
https://github.com/nushell/nushell/issues/8248#issuecomment-1637442612):
![image](https://github.com/nushell/nushell/assets/22256154/b2c14760-3f27-41cc-af77-af70a4367f2a)
## About the change
1. To make module support const, we need to change `parse_module_block`
to support `const` keyword.
2. To suport export `const`, we need to make module tracking variables,
so we add `variables` attribute to `Module`
3. During eval, the const variable may not exists in `stack`, because we
don't eval `const` when we define a module, so we need to find variables
which are already registered in `engine_state`
## One more thing to note about the const value.
Consider the following code
```
module foo { const b = 3; export def bar [] { $b } }
use foo bar
const b = 4;
bar
```
The result will be 3 (which is defined in module) rather than 4. I think
it's expected behavior.
It's something like [dynamic
binding](https://www.gnu.org/software/emacs/manual/html_node/elisp/Dynamic-Binding-Tips.html)
vs [lexical
binding](https://www.gnu.org/software/emacs/manual/html_node/elisp/Lexical-Binding.html)
in lisp like language, and lexical binding should be right behavior
which generates more predicable result, and it doesn't introduce really
subtle bugs in nushell code.
What if user want dynamic-binding?(For example: the example code returns
`4`)
There is no way to do this, user should consider passing the value as
argument to custom command rather than const.
## TODO
- [X] adding tests for the feature.
- [X] support export const out of module to use.
# 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 -A
clippy::needless_collect -A clippy::result_large_err` to check that
you're using the standard code style
- `cargo test --workspace` to check that all tests pass
- `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.
-->
2023-07-31 23:09:52 +00:00
|
|
|
ExportConst,
|
2023-02-24 15:54:42 +00:00
|
|
|
ExportDef,
|
|
|
|
ExportDefEnv,
|
|
|
|
ExportExtern,
|
|
|
|
ExportUse,
|
2023-05-06 18:39:54 +00:00
|
|
|
ExportModule,
|
2023-02-24 15:54:42 +00:00
|
|
|
Extern,
|
2023-07-14 21:51:28 +00:00
|
|
|
ExternWrapped,
|
2023-02-24 15:54:42 +00:00
|
|
|
For,
|
|
|
|
Hide,
|
|
|
|
HideEnv,
|
|
|
|
If,
|
|
|
|
Ignore,
|
|
|
|
Overlay,
|
|
|
|
OverlayUse,
|
|
|
|
OverlayList,
|
|
|
|
OverlayNew,
|
|
|
|
OverlayHide,
|
Feature: Userland LazyRecords (#8332)
# Description
Despite the innocent-looking title, this PR involves quite a few backend
changes as the existing LazyRecord trait was not at all friendly towards
the idea of these values being generated on the fly from Nu code.
In particular, here are a few changes involved:
- The LazyRecord trait now involves a lifetime `'a`, and this lifetime
is used in the return value of `get_column_names`. This means it no
longer returns `'static str`s (but implementations still can return
these). This is more stringent on the consumption side.
- The LazyRecord trait now must be able to clone itself via a new
`clone_value` method (as requiring `Clone` is not object safe). This
pattern is borrowed from `Value::CustomValue`.
- LazyRecord no longer requires being serde serializable and
deserializable.
These, in hand, allow for the following:
- LazyRecord can now clone itself, which means that they don't have to
be collected into a Record when being cloned.
- This is especially useful in Stack, which is cloned on each repl line
and in a few other cases. This would mean that _every_ LazyRecord
instance stored in a variable would be collected in its entirety and
cloned, which can be catastrophic for performance. See: `let nulol =
$nu`.
- LazyRecord's columns don't have to be static, they can have the same
lifetime of the struct itself, so different instances of the same
LazyRecord type can have different columns and values (like the new
`NuLazyRecord`)
- Serialization and deserialization are no longer meaningless, they are
simply less.
I would consider this PR very "drafty", but everything works. It
probably requires some cleanup and testing, though, but I'd like some
eyes and pointers first.
# User-Facing Changes
New command. New restrictions are largely internal. Maybe there are some
plugins affected?
Example of new command's usage:
```
lazy make --columns [a b c] --get-value { |name| print $"getting ($name)"; $name | str upcase }
```
You can also trivially implement something like `lazy make record` to
take a record of closures and turn it into a getter-like lazy struct:
```
def "lazy make record" [
record: record
] {
let columns = ($record | columns)
lazy make --columns $columns --get-value { |col| do ($record | get $col) }
}
```
Open to bikeshedding. `lazy make` is similar to `error make` which is
also in the core commands. I didn't like `make lazy` since it sounded
like some transformation was going on.
# Tour for reviewers
Take a look at LazyMake's examples. They have `None` as the results, as
such they aren't _really_ correct and aren't being tested at all. I
didn't do this because creating the Value::LazyRecord is a little tricky
and didn't want to risk messing it up, especially as the necessary
variables aren't available when creating the examples (like stack and
engine state).
Also take a look at NuLazyRecord's get_value implementation, or in
general. It uses an Arc<Mutex<_>> for the stack, which must be accessed
mutably for eval_block but get_value only provides us with a `&self`.
This is a sad state of affairs, but I don't know if there's a better
way.
On the same code path, we also have pipeline handling, and any pipeline
that isn't a Pipeline::Value will return Value::nothing. I believe
returning a Value::Error is probably better, or maybe some other
handling. Couldn't decide on which ShellError to settle with for that
branch.
The "unfortunate casualty" in the columns.rs file. I'm not sure just how
bad that is, though, I simply had to fight a little with the borrow
checker.
A few leftover comments like derives, comments about the now
non-existing serde requirements, and impls. I'll definitely get around
to those eventually but they're in atm
Should NuLazyRecord implement caching? I'm leaning heavily towards
**yes**, this was one of the main reasons not to use a record of
closures (besides convenience), but maybe it could be opt-out. I'd
wonder about its implementation too, but a simple way would be to move a
HashMap into the mutex state and keep cached values there.
2023-05-17 23:35:22 +00:00
|
|
|
LazyMake,
|
2023-02-24 15:54:42 +00:00
|
|
|
Let,
|
|
|
|
Loop,
|
2023-03-24 01:52:01 +00:00
|
|
|
Match,
|
2023-02-24 15:54:42 +00:00
|
|
|
Module,
|
|
|
|
Mut,
|
|
|
|
Return,
|
2023-06-20 21:33:01 +00:00
|
|
|
Scope,
|
|
|
|
ScopeAliases,
|
|
|
|
ScopeCommands,
|
|
|
|
ScopeEngineStats,
|
|
|
|
ScopeModules,
|
|
|
|
ScopeVariables,
|
2023-02-24 15:54:42 +00:00
|
|
|
Try,
|
|
|
|
Use,
|
|
|
|
Version,
|
|
|
|
While,
|
|
|
|
};
|
|
|
|
|
|
|
|
//#[cfg(feature = "plugin")]
|
|
|
|
bind_command!(Register);
|
|
|
|
|
|
|
|
working_set.render()
|
|
|
|
};
|
|
|
|
|
|
|
|
if let Err(err) = engine_state.merge_delta(delta) {
|
|
|
|
eprintln!("Error creating default context: {err:?}");
|
|
|
|
}
|
|
|
|
|
|
|
|
engine_state
|
|
|
|
}
|