Add environment variable support for modules (#331)

* Add 'expor env' dummy command

* (WIP) Abstract away module exportables as Overlay

* Switch to Overlays for use/hide

Works for decls only right now.

* Fix passing import patterns of hide to eval

* Simplify use/hide of decls

* Add ImportPattern as Expr; Add use env eval

Still no parsing of "export env" so I can't test it yet.

* Refactor export parsing; Add InternalError

* Add env var export and activation; Misc changes

Now it is possible to `use` env var that was exported from a module.

This commit also adds some new errors and other small changes.

* Add env var hiding

* Fix eval not recognizing hidden decls

Without this change, calling `hide foo`, the evaluator does not know
whether a custom command named "foo" was hidden during parsing,
therefore, it is not possible to reliably throw an error about the "foo"
name not found.

* Add use/hide/export env var tests; Cleanup; Notes

* Ignore hide env related tests for now

* Fix main branch merge mess

* Fixed multi-word export def

* Fix hiding tests on Windows

* Remove env var hiding for now
This commit is contained in:
Jakub Žádník
2021-11-16 01:16:06 +02:00
committed by GitHub
parent ab22619f4a
commit 5459d30a24
22 changed files with 1050 additions and 261 deletions

View File

@ -395,7 +395,7 @@ fn better_block_types() -> TestResult {
}
#[test]
fn module_imports_1() -> TestResult {
fn module_def_imports_1() -> TestResult {
run_test(
r#"module foo { export def a [] { 1 }; def b [] { 2 } }; use foo; foo a"#,
"1",
@ -403,7 +403,7 @@ fn module_imports_1() -> TestResult {
}
#[test]
fn module_imports_2() -> TestResult {
fn module_def_imports_2() -> TestResult {
run_test(
r#"module foo { export def a [] { 1 }; def b [] { 2 } }; use foo a; a"#,
"1",
@ -411,7 +411,7 @@ fn module_imports_2() -> TestResult {
}
#[test]
fn module_imports_3() -> TestResult {
fn module_def_imports_3() -> TestResult {
run_test(
r#"module foo { export def a [] { 1 }; export def b [] { 2 } }; use foo *; b"#,
"2",
@ -419,7 +419,7 @@ fn module_imports_3() -> TestResult {
}
#[test]
fn module_imports_4() -> TestResult {
fn module_def_imports_4() -> TestResult {
fail_test(
r#"module foo { export def a [] { 1 }; export def b [] { 2 } }; use foo c"#,
"not find import",
@ -427,35 +427,101 @@ fn module_imports_4() -> TestResult {
}
#[test]
fn module_imports_5() -> TestResult {
fn module_def_imports_5() -> TestResult {
run_test(
r#"module foo { export def a [] { 1 }; def b [] { 2 }; export def c [] { 3 } }; use foo [a, c]; c"#,
r#"module foo { export def a [] { 1 }; def b [] { '2' }; export def c [] { '3' } }; use foo [a, c]; c"#,
"3",
)
}
#[test]
fn module_import_uses_internal_command() -> TestResult {
fn module_env_imports_1() -> TestResult {
run_test(
r#"module foo { export env a { '1' } }; use foo; $nu.env.'foo a'"#,
"1",
)
}
#[test]
fn module_env_imports_2() -> TestResult {
run_test(
r#"module foo { export env a { '1' } }; use foo a; $nu.env.a"#,
"1",
)
}
#[test]
fn module_env_imports_3() -> TestResult {
run_test(
r#"module foo { export env a { '1' }; export env b { '2' } }; use foo *; $nu.env.b"#,
"2",
)
}
#[test]
fn module_env_imports_4() -> TestResult {
fail_test(
r#"module foo { export env a { '1' }; export env b { '2' } }; use foo c"#,
"not find import",
)
}
#[test]
fn module_env_imports_5() -> TestResult {
run_test(
r#"module foo { export env a { '1' }; export env b { '2' }; export env c { '3' } }; use foo [a, c]; $nu.env.c"#,
"3",
)
}
#[test]
fn module_def_import_uses_internal_command() -> TestResult {
run_test(
r#"module foo { def b [] { 2 }; export def a [] { b } }; use foo; foo a"#,
"2",
)
}
#[test]
fn module_env_import_uses_internal_command() -> TestResult {
run_test(
r#"module foo { def b [] { "2" }; export env a { b } }; use foo; $nu.env.'foo a'"#,
"2",
)
}
// TODO: Test the use/hide tests also as separate lines in REPL (i.e., with merging the delta in between)
#[test]
fn hides_def() -> TestResult {
fail_test(r#"def foo [] { "foo" }; hide foo; foo"#, not_found_msg())
}
#[ignore]
fn hides_env() -> TestResult {
fail_test(
r#"let-env foo = "foo"; hide foo; $nu.env.foo"#,
"did you mean",
)
}
#[test]
fn hides_def_then_redefines() -> TestResult {
// this one should fail because of predecl -- cannot have more defs with the same name in a
// block
fail_test(
r#"def foo [] { "foo" }; hide foo; def foo [] { "bar" }; foo"#,
"defined more than once",
)
}
#[ignore]
fn hides_env_then_redefines() -> TestResult {
run_test(
r#"let-env foo = "foo"; hide foo; let-env foo = "bar"; $nu.env.foo"#,
"bar",
)
}
#[test]
fn hides_def_in_scope_1() -> TestResult {
fail_test(
@ -488,40 +554,96 @@ fn hides_def_in_scope_4() -> TestResult {
)
}
#[test]
fn hide_twice_not_allowed() -> TestResult {
#[ignore]
fn hides_env_in_scope_1() -> TestResult {
fail_test(
r#"def foo [] { "foo" }; hide foo; hide foo"#,
"unknown command",
r#"let-env foo = "foo"; do { hide foo; $nu.env.foo }"#,
"did you mean",
)
}
#[ignore]
fn hides_env_in_scope_2() -> TestResult {
// TODO: Revisit this -- 'hide foo' should restore the env, not hide it completely
run_test(
r#"let-env foo = "foo"; do { let-env foo = "bar"; hide foo; $nu.env.foo }"#,
"foo",
)
}
#[ignore]
fn hides_env_in_scope_3() -> TestResult {
fail_test(
r#"let-env foo = "foo"; do { hide foo; let-env foo = "bar"; hide foo; $nu.env.foo }"#,
"did you mean",
)
}
#[ignore]
fn hides_env_in_scope_4() -> TestResult {
// TODO: Revisit this -- 'hide foo' should restore the env, not hide it completely
fail_test(
r#"let-env foo = "foo"; do { let-env foo = "bar"; hide foo; hide foo; $nu.env.foo }"#,
"did you mean",
)
}
#[test]
fn hides_import_1() -> TestResult {
fn hide_def_twice_not_allowed() -> TestResult {
fail_test(r#"def foo [] { "foo" }; hide foo; hide foo"#, "not found")
}
#[ignore]
fn hide_env_twice_not_allowed() -> TestResult {
fail_test(r#"let-env foo = "foo"; hide foo; hide foo"#, "did not find")
}
#[ignore]
fn hides_def_runs_env() -> TestResult {
// TODO: We need some precedence system to handle this. Currently, 'hide foo' hides both the
// def and env var.
run_test(
r#"let-env foo = "bar"; def foo [] { "foo" }; hide foo; $nu.env.foo"#,
"bar",
)
}
#[ignore]
fn hides_def_and_env() -> TestResult {
// TODO: We need some precedence system to handle this. Currently, 'hide foo' hides both the
// def and env var.
fail_test(
r#"module spam { export def foo [] { "foo" } }; use spam; hide spam foo; foo"#,
r#"let-env foo = "bar"; def foo [] { "foo" }; hide foo; hide foo; $nu.env.foo"#,
not_found_msg(),
)
}
#[test]
fn hides_import_2() -> TestResult {
fn hides_def_import_1() -> TestResult {
fail_test(
r#"module spam { export def foo [] { "foo" } }; use spam; hide spam *; foo"#,
r#"module spam { export def foo [] { "foo" } }; use spam; hide spam foo; spam foo"#,
not_found_msg(),
)
}
#[test]
fn hides_import_3() -> TestResult {
fn hides_def_import_2() -> TestResult {
fail_test(
r#"module spam { export def foo [] { "foo" } }; use spam; hide spam [foo]; foo"#,
r#"module spam { export def foo [] { "foo" } }; use spam; hide spam *; spam foo"#,
not_found_msg(),
)
}
#[test]
fn hides_import_4() -> TestResult {
fn hides_def_import_3() -> TestResult {
fail_test(
r#"module spam { export def foo [] { "foo" } }; use spam; hide spam [foo]; spam foo"#,
not_found_msg(),
)
}
#[test]
fn hides_def_import_4() -> TestResult {
fail_test(
r#"module spam { export def foo [] { "foo" } }; use spam foo; hide foo; foo"#,
not_found_msg(),
@ -529,7 +651,7 @@ fn hides_import_4() -> TestResult {
}
#[test]
fn hides_import_5() -> TestResult {
fn hides_def_import_5() -> TestResult {
fail_test(
r#"module spam { export def foo [] { "foo" } }; use spam *; hide foo; foo"#,
not_found_msg(),
@ -537,13 +659,61 @@ fn hides_import_5() -> TestResult {
}
#[test]
fn hides_import_6() -> TestResult {
fn hides_def_import_6() -> TestResult {
fail_test(
r#"module spam { export def foo [] { "foo" } }; use spam; hide spam; foo"#,
r#"module spam { export def foo [] { "foo" } }; use spam; hide spam; spam foo"#,
not_found_msg(),
)
}
#[ignore]
fn hides_env_import_1() -> TestResult {
fail_test(
r#"module spam { export env foo { "foo" } }; use spam; hide spam foo; $nu.env.'spam foo'"#,
"did you mean",
)
}
#[ignore]
fn hides_env_import_2() -> TestResult {
fail_test(
r#"module spam { export env foo { "foo" } }; use spam; hide spam *; $nu.env.'spam foo'"#,
"did you mean",
)
}
#[ignore]
fn hides_env_import_3() -> TestResult {
fail_test(
r#"module spam { export env foo { "foo" }; } use spam; hide spam [foo]; $nu.env.'spam foo'"#,
"did you mean",
)
}
#[ignore]
fn hides_env_import_4() -> TestResult {
fail_test(
r#"module spam { export env foo { "foo" } }; use spam foo; hide foo; $nu.env.foo"#,
"did you mean",
)
}
#[ignore]
fn hides_env_import_5() -> TestResult {
fail_test(
r#"module spam { export env foo { "foo" } }; use spam *; hide foo; $nu.env.foo"#,
"did you mean",
)
}
#[ignore]
fn hides_env_import_6() -> TestResult {
fail_test(
r#"module spam { export env foo { "foo" } }; use spam; hide spam; $nu.env.'spam foo'"#,
"did you mean",
)
}
#[test]
fn def_twice_should_fail() -> TestResult {
fail_test(
@ -553,13 +723,21 @@ fn def_twice_should_fail() -> TestResult {
}
#[test]
fn use_import_after_hide() -> TestResult {
fn use_def_import_after_hide() -> TestResult {
run_test(
r#"module spam { export def foo [] { "foo" } }; use spam foo; hide foo; use spam foo; foo"#,
"foo",
)
}
#[ignore]
fn use_env_import_after_hide() -> TestResult {
run_test(
r#"module spam { export env foo { "foo" } }; use spam foo; hide foo; use spam foo; $nu.env.foo"#,
"foo",
)
}
#[test]
fn hide_shadowed_decl() -> TestResult {
run_test(
@ -568,6 +746,15 @@ fn hide_shadowed_decl() -> TestResult {
)
}
#[ignore]
fn hide_shadowed_env() -> TestResult {
// TODO: waiting for a fix
run_test(
r#"module spam { export env foo { "bar" } }; let-env foo = "foo"; do { use spam foo; hide foo; $nu.env.foo }"#,
"foo",
)
}
#[test]
fn hides_all_decls_within_scope() -> TestResult {
fail_test(
@ -576,6 +763,14 @@ fn hides_all_decls_within_scope() -> TestResult {
)
}
#[ignore]
fn hides_all_envs_within_scope() -> TestResult {
fail_test(
r#"module spam { export env foo { "bar" } }; let-env foo = "foo"; use spam foo; hide foo; $nu.env.foo"#,
"did you mean",
)
}
#[test]
fn from_json_1() -> TestResult {
run_test(r#"('{"name": "Fred"}' | from json).name"#, "Fred")