mirror of
https://github.com/nushell/nushell.git
synced 2024-12-17 20:51:57 +01:00
Merge pull request #74 from kubouch/module-export
Modules: export & hide
This commit is contained in:
commit
1d7ab28a0f
35
crates/nu-command/src/core_commands/export_def.rs
Normal file
35
crates/nu-command/src/core_commands/export_def.rs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
use nu_protocol::ast::Call;
|
||||||
|
use nu_protocol::engine::{Command, EvaluationContext};
|
||||||
|
use nu_protocol::{Signature, SyntaxShape, Value};
|
||||||
|
|
||||||
|
pub struct ExportDef;
|
||||||
|
|
||||||
|
impl Command for ExportDef {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"export def"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Define a custom command and export it from a module"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
|
Signature::build("export def")
|
||||||
|
.required("target", SyntaxShape::String, "definition name")
|
||||||
|
.required("params", SyntaxShape::Signature, "parameters")
|
||||||
|
.required(
|
||||||
|
"block",
|
||||||
|
SyntaxShape::Block(Some(vec![])),
|
||||||
|
"body of the definition",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
_context: &EvaluationContext,
|
||||||
|
call: &Call,
|
||||||
|
_input: Value,
|
||||||
|
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
||||||
|
Ok(Value::Nothing { span: call.head })
|
||||||
|
}
|
||||||
|
}
|
28
crates/nu-command/src/core_commands/hide.rs
Normal file
28
crates/nu-command/src/core_commands/hide.rs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
use nu_protocol::ast::Call;
|
||||||
|
use nu_protocol::engine::{Command, EvaluationContext};
|
||||||
|
use nu_protocol::{Signature, SyntaxShape, Value};
|
||||||
|
|
||||||
|
pub struct Hide;
|
||||||
|
|
||||||
|
impl Command for Hide {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"hide"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Hide definitions in the current scope"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
|
Signature::build("hide").required("pattern", SyntaxShape::String, "import pattern")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
_context: &EvaluationContext,
|
||||||
|
call: &Call,
|
||||||
|
_input: Value,
|
||||||
|
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
||||||
|
Ok(Value::Nothing { span: call.head })
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,9 @@
|
|||||||
mod alias;
|
mod alias;
|
||||||
mod def;
|
mod def;
|
||||||
mod do_;
|
mod do_;
|
||||||
|
mod export_def;
|
||||||
mod help;
|
mod help;
|
||||||
|
mod hide;
|
||||||
mod if_;
|
mod if_;
|
||||||
mod let_;
|
mod let_;
|
||||||
mod module;
|
mod module;
|
||||||
@ -10,7 +12,9 @@ mod use_;
|
|||||||
pub use alias::Alias;
|
pub use alias::Alias;
|
||||||
pub use def::Def;
|
pub use def::Def;
|
||||||
pub use do_::Do;
|
pub use do_::Do;
|
||||||
|
pub use export_def::ExportDef;
|
||||||
pub use help::Help;
|
pub use help::Help;
|
||||||
|
pub use hide::Hide;
|
||||||
pub use if_::If;
|
pub use if_::If;
|
||||||
pub use let_::Let;
|
pub use let_::Let;
|
||||||
pub use module::Module;
|
pub use module::Module;
|
||||||
|
@ -14,7 +14,7 @@ impl Command for Use {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn signature(&self) -> nu_protocol::Signature {
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
Signature::build("use").required("module_name", SyntaxShape::String, "module name")
|
Signature::build("use").required("pattern", SyntaxShape::String, "import pattern")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
|
@ -19,12 +19,14 @@ pub fn create_default_context() -> Rc<RefCell<EngineState>> {
|
|||||||
working_set.add_decl(Box::new(Def));
|
working_set.add_decl(Box::new(Def));
|
||||||
working_set.add_decl(Box::new(Do));
|
working_set.add_decl(Box::new(Do));
|
||||||
working_set.add_decl(Box::new(Each));
|
working_set.add_decl(Box::new(Each));
|
||||||
|
working_set.add_decl(Box::new(ExportDef));
|
||||||
working_set.add_decl(Box::new(External));
|
working_set.add_decl(Box::new(External));
|
||||||
working_set.add_decl(Box::new(For));
|
working_set.add_decl(Box::new(For));
|
||||||
working_set.add_decl(Box::new(From));
|
working_set.add_decl(Box::new(From));
|
||||||
working_set.add_decl(Box::new(FromJson));
|
working_set.add_decl(Box::new(FromJson));
|
||||||
working_set.add_decl(Box::new(Get));
|
working_set.add_decl(Box::new(Get));
|
||||||
working_set.add_decl(Box::new(Help));
|
working_set.add_decl(Box::new(Help));
|
||||||
|
working_set.add_decl(Box::new(Hide));
|
||||||
working_set.add_decl(Box::new(If));
|
working_set.add_decl(Box::new(If));
|
||||||
working_set.add_decl(Box::new(Length));
|
working_set.add_decl(Box::new(Length));
|
||||||
working_set.add_decl(Box::new(Let));
|
working_set.add_decl(Box::new(Let));
|
||||||
|
@ -57,6 +57,14 @@ pub enum ParseError {
|
|||||||
#[diagnostic(code(nu::parser::expected_keyword), url(docsrs))]
|
#[diagnostic(code(nu::parser::expected_keyword), url(docsrs))]
|
||||||
ExpectedKeyword(String, #[label("expected {0}")] Span),
|
ExpectedKeyword(String, #[label("expected {0}")] Span),
|
||||||
|
|
||||||
|
#[error("Unexpected keyword.")]
|
||||||
|
#[diagnostic(
|
||||||
|
code(nu::parser::unexpected_keyword),
|
||||||
|
url(docsrs),
|
||||||
|
help("'export' keyword is allowed only in a module.")
|
||||||
|
)]
|
||||||
|
UnexpectedKeyword(String, #[label("unexpected {0}")] Span),
|
||||||
|
|
||||||
#[error("Multiple rest params.")]
|
#[error("Multiple rest params.")]
|
||||||
#[diagnostic(code(nu::parser::multiple_rest_params), url(docsrs))]
|
#[diagnostic(code(nu::parser::multiple_rest_params), url(docsrs))]
|
||||||
MultipleRestParams(#[label = "multiple rest params"] Span),
|
MultipleRestParams(#[label = "multiple rest params"] Span),
|
||||||
@ -69,6 +77,10 @@ pub enum ParseError {
|
|||||||
#[diagnostic(code(nu::parser::module_not_found), url(docsrs))]
|
#[diagnostic(code(nu::parser::module_not_found), url(docsrs))]
|
||||||
ModuleNotFound(#[label = "module not found"] Span),
|
ModuleNotFound(#[label = "module not found"] Span),
|
||||||
|
|
||||||
|
#[error("Duplicate command definition within a block.")]
|
||||||
|
#[diagnostic(code(nu::parser::duplicate_command_def), url(docsrs))]
|
||||||
|
DuplicateCommandDef(#[label = "defined more than once"] Span),
|
||||||
|
|
||||||
#[error("Unknown command.")]
|
#[error("Unknown command.")]
|
||||||
#[diagnostic(
|
#[diagnostic(
|
||||||
code(nu::parser::unknown_command),
|
code(nu::parser::unknown_command),
|
||||||
|
@ -13,9 +13,16 @@ use crate::{
|
|||||||
ParseError,
|
ParseError,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn parse_def_predecl(working_set: &mut StateWorkingSet, spans: &[Span]) {
|
pub fn parse_def_predecl(working_set: &mut StateWorkingSet, spans: &[Span]) -> Option<ParseError> {
|
||||||
let name = working_set.get_span_contents(spans[0]);
|
let name = working_set.get_span_contents(spans[0]);
|
||||||
|
|
||||||
|
// handle "export def" same as "def"
|
||||||
|
let (name, spans) = if name == b"export" && spans.len() >= 2 {
|
||||||
|
(working_set.get_span_contents(spans[1]), &spans[1..])
|
||||||
|
} else {
|
||||||
|
(name, spans)
|
||||||
|
};
|
||||||
|
|
||||||
if name == b"def" && spans.len() >= 4 {
|
if name == b"def" && spans.len() >= 4 {
|
||||||
let (name_expr, ..) = parse_string(working_set, spans[1]);
|
let (name_expr, ..) = parse_string(working_set, spans[1]);
|
||||||
let name = name_expr.as_string();
|
let name = name_expr.as_string();
|
||||||
@ -35,9 +42,13 @@ pub fn parse_def_predecl(working_set: &mut StateWorkingSet, spans: &[Span]) {
|
|||||||
signature.name = name;
|
signature.name = name;
|
||||||
let decl = signature.predeclare();
|
let decl = signature.predeclare();
|
||||||
|
|
||||||
working_set.add_decl(decl);
|
if working_set.add_predecl(decl).is_some() {
|
||||||
|
return Some(ParseError::DuplicateCommandDef(spans[1]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_def(
|
pub fn parse_def(
|
||||||
@ -88,17 +99,22 @@ pub fn parse_def(
|
|||||||
call.positional.push(block);
|
call.positional.push(block);
|
||||||
|
|
||||||
if let (Some(name), Some(mut signature), Some(block_id)) =
|
if let (Some(name), Some(mut signature), Some(block_id)) =
|
||||||
(name, signature, block_id)
|
(&name, signature, block_id)
|
||||||
{
|
{
|
||||||
let decl_id = working_set
|
if let Some(decl_id) = working_set.find_decl(name.as_bytes()) {
|
||||||
.find_decl(name.as_bytes())
|
let declaration = working_set.get_decl_mut(decl_id);
|
||||||
.expect("internal error: predeclaration failed to add definition");
|
|
||||||
|
|
||||||
let declaration = working_set.get_decl_mut(decl_id);
|
signature.name = name.clone();
|
||||||
|
|
||||||
signature.name = name;
|
*declaration = signature.into_block_command(block_id);
|
||||||
|
} else {
|
||||||
*declaration = signature.into_block_command(block_id);
|
error = error.or_else(|| {
|
||||||
|
Some(ParseError::UnknownState(
|
||||||
|
"Could not define hidden command".into(),
|
||||||
|
spans[1],
|
||||||
|
))
|
||||||
|
});
|
||||||
|
};
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let err_span = Span {
|
let err_span = Span {
|
||||||
@ -111,6 +127,19 @@ pub fn parse_def(
|
|||||||
}
|
}
|
||||||
working_set.exit_scope();
|
working_set.exit_scope();
|
||||||
|
|
||||||
|
if let Some(name) = name {
|
||||||
|
// It's OK if it returns None: The decl was already merged in previous parse
|
||||||
|
// pass.
|
||||||
|
working_set.merge_predecl(name.as_bytes());
|
||||||
|
} else {
|
||||||
|
error = error.or_else(|| {
|
||||||
|
Some(ParseError::UnknownState(
|
||||||
|
"Could not get string from string expression".into(),
|
||||||
|
*name_span,
|
||||||
|
))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
call
|
call
|
||||||
} else {
|
} else {
|
||||||
let err_span = Span {
|
let err_span = Span {
|
||||||
@ -218,6 +247,71 @@ pub fn parse_alias(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn parse_export(
|
||||||
|
working_set: &mut StateWorkingSet,
|
||||||
|
spans: &[Span],
|
||||||
|
) -> (Statement, Option<ParseError>) {
|
||||||
|
let bytes = working_set.get_span_contents(spans[0]);
|
||||||
|
|
||||||
|
if bytes == b"export" && spans.len() >= 3 {
|
||||||
|
let export_name = working_set.get_span_contents(spans[1]);
|
||||||
|
|
||||||
|
match export_name {
|
||||||
|
b"def" => {
|
||||||
|
let (stmt, err) = parse_def(working_set, &spans[1..]);
|
||||||
|
|
||||||
|
let export_def_decl_id = working_set
|
||||||
|
.find_decl(b"export def")
|
||||||
|
.expect("internal error: missing 'export def' command");
|
||||||
|
|
||||||
|
// Trying to warp the 'def' call into the 'export def' in a very clumsy way
|
||||||
|
let stmt = if let Statement::Pipeline(ref pipe) = stmt {
|
||||||
|
if !pipe.expressions.is_empty() {
|
||||||
|
if let Expr::Call(ref call) = pipe.expressions[0].expr {
|
||||||
|
let mut call = call.clone();
|
||||||
|
|
||||||
|
call.head = span(&spans[0..=1]);
|
||||||
|
call.decl_id = export_def_decl_id;
|
||||||
|
|
||||||
|
Statement::Pipeline(Pipeline::from_vec(vec![Expression {
|
||||||
|
expr: Expr::Call(call),
|
||||||
|
span: span(spans),
|
||||||
|
ty: Type::Unknown,
|
||||||
|
custom_completion: None,
|
||||||
|
}]))
|
||||||
|
} else {
|
||||||
|
stmt
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stmt
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stmt
|
||||||
|
};
|
||||||
|
|
||||||
|
(stmt, err)
|
||||||
|
}
|
||||||
|
_ => (
|
||||||
|
garbage_statement(spans),
|
||||||
|
Some(ParseError::Expected(
|
||||||
|
// TODO: Fill in more as they come
|
||||||
|
"def keyword".into(),
|
||||||
|
spans[1],
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
garbage_statement(spans),
|
||||||
|
Some(ParseError::UnknownState(
|
||||||
|
// TODO: fill in more as they come
|
||||||
|
"Expected structure: export def [] {}".into(),
|
||||||
|
span(spans),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse_module(
|
pub fn parse_module(
|
||||||
working_set: &mut StateWorkingSet,
|
working_set: &mut StateWorkingSet,
|
||||||
spans: &[Span],
|
spans: &[Span],
|
||||||
@ -307,15 +401,21 @@ pub fn parse_module(
|
|||||||
b"def" => {
|
b"def" => {
|
||||||
let (stmt, err) = parse_def(working_set, &pipeline.commands[0].parts);
|
let (stmt, err) = parse_def(working_set, &pipeline.commands[0].parts);
|
||||||
|
|
||||||
|
(stmt, err)
|
||||||
|
}
|
||||||
|
b"export" => {
|
||||||
|
let (stmt, err) =
|
||||||
|
parse_export(working_set, &pipeline.commands[0].parts);
|
||||||
|
|
||||||
if err.is_none() {
|
if err.is_none() {
|
||||||
let decl_name =
|
let decl_name =
|
||||||
working_set.get_span_contents(pipeline.commands[0].parts[1]);
|
// parts[2] is safe since it's checked in parse_def already
|
||||||
|
working_set.get_span_contents(pipeline.commands[0].parts[2]);
|
||||||
|
|
||||||
let decl_id = working_set
|
let decl_id = working_set
|
||||||
.find_decl(decl_name)
|
.find_decl(decl_name)
|
||||||
.expect("internal error: failed to find added declaration");
|
.expect("internal error: failed to find added declaration");
|
||||||
|
|
||||||
// TODO: Later, we want to put this behind 'export'
|
|
||||||
exports.push((decl_name.into(), decl_id));
|
exports.push((decl_name.into(), decl_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -324,7 +424,8 @@ pub fn parse_module(
|
|||||||
_ => (
|
_ => (
|
||||||
garbage_statement(&pipeline.commands[0].parts),
|
garbage_statement(&pipeline.commands[0].parts),
|
||||||
Some(ParseError::Expected(
|
Some(ParseError::Expected(
|
||||||
"def".into(),
|
// TODO: Fill in more as they com
|
||||||
|
"def or export keyword".into(),
|
||||||
pipeline.commands[0].parts[0],
|
pipeline.commands[0].parts[0],
|
||||||
)),
|
)),
|
||||||
),
|
),
|
||||||
@ -393,8 +494,6 @@ pub fn parse_use(
|
|||||||
let mut error = None;
|
let mut error = None;
|
||||||
let bytes = working_set.get_span_contents(spans[0]);
|
let bytes = working_set.get_span_contents(spans[0]);
|
||||||
|
|
||||||
// TODO: Currently, this directly imports the module's definitions into the current scope.
|
|
||||||
// Later, we want to put them behind the module's name and add selective importing
|
|
||||||
if bytes == b"use" && spans.len() >= 2 {
|
if bytes == b"use" && spans.len() >= 2 {
|
||||||
let (module_name_expr, err) = parse_string(working_set, spans[1]);
|
let (module_name_expr, err) = parse_string(working_set, spans[1]);
|
||||||
error = error.or(err);
|
error = error.or(err);
|
||||||
@ -403,8 +502,6 @@ pub fn parse_use(
|
|||||||
error = error.or(err);
|
error = error.or(err);
|
||||||
|
|
||||||
let exports = if let Some(block_id) = working_set.find_module(&import_pattern.head) {
|
let exports = if let Some(block_id) = working_set.find_module(&import_pattern.head) {
|
||||||
// TODO: Since we don't use the Block at all, we might just as well create a separate
|
|
||||||
// Module that holds only the exports, without having Blocks in the way.
|
|
||||||
working_set.get_block(block_id).exports.clone()
|
working_set.get_block(block_id).exports.clone()
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
@ -492,6 +589,57 @@ pub fn parse_use(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn parse_hide(
|
||||||
|
working_set: &mut StateWorkingSet,
|
||||||
|
spans: &[Span],
|
||||||
|
) -> (Statement, Option<ParseError>) {
|
||||||
|
let mut error = None;
|
||||||
|
let bytes = working_set.get_span_contents(spans[0]);
|
||||||
|
|
||||||
|
if bytes == b"hide" && spans.len() >= 2 {
|
||||||
|
let (name_expr, err) = parse_string(working_set, spans[1]);
|
||||||
|
error = error.or(err);
|
||||||
|
|
||||||
|
let name_bytes: Vec<u8> = working_set.get_span_contents(spans[1]).into();
|
||||||
|
|
||||||
|
// TODO: Do the import pattern stuff for bulk-hiding
|
||||||
|
|
||||||
|
if working_set.hide_decl(&name_bytes).is_none() {
|
||||||
|
error = error.or_else(|| Some(ParseError::UnknownCommand(spans[1])));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the Hide command call
|
||||||
|
let hide_decl_id = working_set
|
||||||
|
.find_decl(b"hide")
|
||||||
|
.expect("internal error: missing hide command");
|
||||||
|
|
||||||
|
let call = Box::new(Call {
|
||||||
|
head: spans[0],
|
||||||
|
decl_id: hide_decl_id,
|
||||||
|
positional: vec![name_expr],
|
||||||
|
named: vec![],
|
||||||
|
});
|
||||||
|
|
||||||
|
(
|
||||||
|
Statement::Pipeline(Pipeline::from_vec(vec![Expression {
|
||||||
|
expr: Expr::Call(call),
|
||||||
|
span: span(spans),
|
||||||
|
ty: Type::Unknown,
|
||||||
|
custom_completion: None,
|
||||||
|
}])),
|
||||||
|
error,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
garbage_statement(spans),
|
||||||
|
Some(ParseError::UnknownState(
|
||||||
|
"Expected structure: hide <name>".into(),
|
||||||
|
span(spans),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse_let(
|
pub fn parse_let(
|
||||||
working_set: &mut StateWorkingSet,
|
working_set: &mut StateWorkingSet,
|
||||||
spans: &[Span],
|
spans: &[Span],
|
||||||
|
@ -14,7 +14,7 @@ use nu_protocol::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::parse_keywords::{
|
use crate::parse_keywords::{
|
||||||
parse_alias, parse_def, parse_def_predecl, parse_let, parse_module, parse_use,
|
parse_alias, parse_def, parse_def_predecl, parse_hide, parse_let, parse_module, parse_use,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -2619,6 +2619,11 @@ pub fn parse_statement(
|
|||||||
b"alias" => parse_alias(working_set, spans),
|
b"alias" => parse_alias(working_set, spans),
|
||||||
b"module" => parse_module(working_set, spans),
|
b"module" => parse_module(working_set, spans),
|
||||||
b"use" => parse_use(working_set, spans),
|
b"use" => parse_use(working_set, spans),
|
||||||
|
b"export" => (
|
||||||
|
garbage_statement(spans),
|
||||||
|
Some(ParseError::UnexpectedKeyword("export".into(), spans[0])),
|
||||||
|
),
|
||||||
|
b"hide" => parse_hide(working_set, spans),
|
||||||
_ => {
|
_ => {
|
||||||
let (expr, err) = parse_expression(working_set, spans);
|
let (expr, err) = parse_expression(working_set, spans);
|
||||||
(Statement::Pipeline(Pipeline::from_vec(vec![expr])), err)
|
(Statement::Pipeline(Pipeline::from_vec(vec![expr])), err)
|
||||||
@ -2635,16 +2640,18 @@ pub fn parse_block(
|
|||||||
working_set.enter_scope();
|
working_set.enter_scope();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut error = None;
|
||||||
|
|
||||||
// Pre-declare any definition so that definitions
|
// Pre-declare any definition so that definitions
|
||||||
// that share the same block can see each other
|
// that share the same block can see each other
|
||||||
for pipeline in &lite_block.block {
|
for pipeline in &lite_block.block {
|
||||||
if pipeline.commands.len() == 1 {
|
if pipeline.commands.len() == 1 {
|
||||||
parse_def_predecl(working_set, &pipeline.commands[0].parts);
|
if let Some(err) = parse_def_predecl(working_set, &pipeline.commands[0].parts) {
|
||||||
|
error = error.or(Some(err));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut error = None;
|
|
||||||
|
|
||||||
let block: Block = lite_block
|
let block: Block = lite_block
|
||||||
.block
|
.block
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
use super::Command;
|
use super::Command;
|
||||||
use crate::{ast::Block, BlockId, DeclId, Signature, Span, Type, VarId};
|
use crate::{ast::Block, BlockId, DeclId, Signature, Span, Type, VarId};
|
||||||
use core::panic;
|
use core::panic;
|
||||||
use std::{collections::HashMap, slice::Iter};
|
use std::{
|
||||||
|
collections::{HashMap, HashSet},
|
||||||
|
slice::Iter,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct EngineState {
|
pub struct EngineState {
|
||||||
files: Vec<(String, usize, usize)>,
|
files: Vec<(String, usize, usize)>,
|
||||||
@ -18,6 +21,7 @@ pub struct ScopeFrame {
|
|||||||
decls: HashMap<Vec<u8>, DeclId>,
|
decls: HashMap<Vec<u8>, DeclId>,
|
||||||
aliases: HashMap<Vec<u8>, Vec<Span>>,
|
aliases: HashMap<Vec<u8>, Vec<Span>>,
|
||||||
modules: HashMap<Vec<u8>, BlockId>,
|
modules: HashMap<Vec<u8>, BlockId>,
|
||||||
|
hiding: HashSet<DeclId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScopeFrame {
|
impl ScopeFrame {
|
||||||
@ -27,6 +31,7 @@ impl ScopeFrame {
|
|||||||
decls: HashMap::new(),
|
decls: HashMap::new(),
|
||||||
aliases: HashMap::new(),
|
aliases: HashMap::new(),
|
||||||
modules: HashMap::new(),
|
modules: HashMap::new(),
|
||||||
|
hiding: HashSet::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,6 +86,9 @@ impl EngineState {
|
|||||||
for item in first.modules.into_iter() {
|
for item in first.modules.into_iter() {
|
||||||
last.modules.insert(item.0, item.1);
|
last.modules.insert(item.0, item.1);
|
||||||
}
|
}
|
||||||
|
for item in first.hiding.into_iter() {
|
||||||
|
last.hiding.insert(item);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,9 +132,15 @@ impl EngineState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_decl(&self, name: &[u8]) -> Option<DeclId> {
|
pub fn find_decl(&self, name: &[u8]) -> Option<DeclId> {
|
||||||
|
let mut hiding: HashSet<DeclId> = HashSet::new();
|
||||||
|
|
||||||
for scope in self.scope.iter().rev() {
|
for scope in self.scope.iter().rev() {
|
||||||
|
hiding.extend(&scope.hiding);
|
||||||
|
|
||||||
if let Some(decl_id) = scope.decls.get(name) {
|
if let Some(decl_id) = scope.decls.get(name) {
|
||||||
return Some(*decl_id);
|
if !hiding.contains(decl_id) {
|
||||||
|
return Some(*decl_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,9 +252,10 @@ pub struct StateWorkingSet<'a> {
|
|||||||
pub struct StateDelta {
|
pub struct StateDelta {
|
||||||
files: Vec<(String, usize, usize)>,
|
files: Vec<(String, usize, usize)>,
|
||||||
pub(crate) file_contents: Vec<u8>,
|
pub(crate) file_contents: Vec<u8>,
|
||||||
vars: Vec<Type>, // indexed by VarId
|
vars: Vec<Type>, // indexed by VarId
|
||||||
decls: Vec<Box<dyn Command>>, // indexed by DeclId
|
decls: Vec<Box<dyn Command>>, // indexed by DeclId
|
||||||
blocks: Vec<Block>, // indexed by BlockId
|
blocks: Vec<Block>, // indexed by BlockId
|
||||||
|
predecls: HashMap<Vec<u8>, DeclId>, // this should get erased after every def call
|
||||||
pub scope: Vec<ScopeFrame>,
|
pub scope: Vec<ScopeFrame>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,6 +289,7 @@ impl<'a> StateWorkingSet<'a> {
|
|||||||
file_contents: vec![],
|
file_contents: vec![],
|
||||||
vars: vec![],
|
vars: vec![],
|
||||||
decls: vec![],
|
decls: vec![],
|
||||||
|
predecls: HashMap::new(),
|
||||||
blocks: vec![],
|
blocks: vec![],
|
||||||
scope: vec![ScopeFrame::new()],
|
scope: vec![ScopeFrame::new()],
|
||||||
},
|
},
|
||||||
@ -304,11 +320,71 @@ impl<'a> StateWorkingSet<'a> {
|
|||||||
.scope
|
.scope
|
||||||
.last_mut()
|
.last_mut()
|
||||||
.expect("internal error: missing required scope frame");
|
.expect("internal error: missing required scope frame");
|
||||||
|
|
||||||
scope_frame.decls.insert(name, decl_id);
|
scope_frame.decls.insert(name, decl_id);
|
||||||
|
|
||||||
decl_id
|
decl_id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_predecl(&mut self, decl: Box<dyn Command>) -> Option<DeclId> {
|
||||||
|
let name = decl.name().as_bytes().to_vec();
|
||||||
|
|
||||||
|
self.delta.decls.push(decl);
|
||||||
|
let decl_id = self.num_decls() - 1;
|
||||||
|
|
||||||
|
self.delta.predecls.insert(name, decl_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn merge_predecl(&mut self, name: &[u8]) -> Option<DeclId> {
|
||||||
|
if let Some(decl_id) = self.delta.predecls.remove(name) {
|
||||||
|
let scope_frame = self
|
||||||
|
.delta
|
||||||
|
.scope
|
||||||
|
.last_mut()
|
||||||
|
.expect("internal error: missing required scope frame");
|
||||||
|
|
||||||
|
scope_frame.decls.insert(name.into(), decl_id);
|
||||||
|
|
||||||
|
return Some(decl_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hide_decl(&mut self, name: &[u8]) -> Option<DeclId> {
|
||||||
|
let mut hiding: HashSet<DeclId> = HashSet::new();
|
||||||
|
|
||||||
|
// Since we can mutate scope frames in delta, remove the id directly
|
||||||
|
for scope in self.delta.scope.iter_mut().rev() {
|
||||||
|
hiding.extend(&scope.hiding);
|
||||||
|
|
||||||
|
if let Some(decl_id) = scope.decls.remove(name) {
|
||||||
|
return Some(decl_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We cannot mutate the permanent state => store the information in the current scope frame
|
||||||
|
let last_scope_frame = self
|
||||||
|
.delta
|
||||||
|
.scope
|
||||||
|
.last_mut()
|
||||||
|
.expect("internal error: missing required scope frame");
|
||||||
|
|
||||||
|
for scope in self.permanent_state.scope.iter().rev() {
|
||||||
|
hiding.extend(&scope.hiding);
|
||||||
|
|
||||||
|
if let Some(decl_id) = scope.decls.get(name) {
|
||||||
|
if !hiding.contains(decl_id) {
|
||||||
|
// Do not hide already hidden decl
|
||||||
|
last_scope_frame.hiding.insert(*decl_id);
|
||||||
|
return Some(*decl_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
pub fn add_block(&mut self, block: Block) -> BlockId {
|
pub fn add_block(&mut self, block: Block) -> BlockId {
|
||||||
self.delta.blocks.push(block);
|
self.delta.blocks.push(block);
|
||||||
|
|
||||||
@ -416,15 +492,27 @@ impl<'a> StateWorkingSet<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_decl(&self, name: &[u8]) -> Option<DeclId> {
|
pub fn find_decl(&self, name: &[u8]) -> Option<DeclId> {
|
||||||
|
let mut hiding: HashSet<DeclId> = HashSet::new();
|
||||||
|
|
||||||
|
if let Some(decl_id) = self.delta.predecls.get(name) {
|
||||||
|
return Some(*decl_id);
|
||||||
|
}
|
||||||
|
|
||||||
for scope in self.delta.scope.iter().rev() {
|
for scope in self.delta.scope.iter().rev() {
|
||||||
|
hiding.extend(&scope.hiding);
|
||||||
|
|
||||||
if let Some(decl_id) = scope.decls.get(name) {
|
if let Some(decl_id) = scope.decls.get(name) {
|
||||||
return Some(*decl_id);
|
return Some(*decl_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for scope in self.permanent_state.scope.iter().rev() {
|
for scope in self.permanent_state.scope.iter().rev() {
|
||||||
|
hiding.extend(&scope.hiding);
|
||||||
|
|
||||||
if let Some(decl_id) = scope.decls.get(name) {
|
if let Some(decl_id) = scope.decls.get(name) {
|
||||||
return Some(*decl_id);
|
if !hiding.contains(decl_id) {
|
||||||
|
return Some(*decl_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
76
src/tests.rs
76
src/tests.rs
@ -346,7 +346,7 @@ fn better_block_types() -> TestResult {
|
|||||||
#[test]
|
#[test]
|
||||||
fn module_imports_1() -> TestResult {
|
fn module_imports_1() -> TestResult {
|
||||||
run_test(
|
run_test(
|
||||||
r#"module foo { def a [] { 1 }; def b [] { 2 } }; use foo; foo.a"#,
|
r#"module foo { export def a [] { 1 }; def b [] { 2 } }; use foo; foo.a"#,
|
||||||
"1",
|
"1",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -354,7 +354,7 @@ fn module_imports_1() -> TestResult {
|
|||||||
#[test]
|
#[test]
|
||||||
fn module_imports_2() -> TestResult {
|
fn module_imports_2() -> TestResult {
|
||||||
run_test(
|
run_test(
|
||||||
r#"module foo { def a [] { 1 }; def b [] { 2 } }; use foo.a; a"#,
|
r#"module foo { export def a [] { 1 }; def b [] { 2 } }; use foo.a; a"#,
|
||||||
"1",
|
"1",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -362,7 +362,7 @@ fn module_imports_2() -> TestResult {
|
|||||||
#[test]
|
#[test]
|
||||||
fn module_imports_3() -> TestResult {
|
fn module_imports_3() -> TestResult {
|
||||||
run_test(
|
run_test(
|
||||||
r#"module foo { def a [] { 1 }; def b [] { 2 } }; use foo.*; b"#,
|
r#"module foo { export def a [] { 1 }; export def b [] { 2 } }; use foo.*; b"#,
|
||||||
"2",
|
"2",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -370,7 +370,7 @@ fn module_imports_3() -> TestResult {
|
|||||||
#[test]
|
#[test]
|
||||||
fn module_imports_4() -> TestResult {
|
fn module_imports_4() -> TestResult {
|
||||||
fail_test(
|
fail_test(
|
||||||
r#"module foo { def a [] { 1 }; def b [] { 2 } }; use foo.c"#,
|
r#"module foo { export def a [] { 1 }; export def b [] { 2 } }; use foo.c"#,
|
||||||
"not find import",
|
"not find import",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -378,11 +378,77 @@ fn module_imports_4() -> TestResult {
|
|||||||
#[test]
|
#[test]
|
||||||
fn module_imports_5() -> TestResult {
|
fn module_imports_5() -> TestResult {
|
||||||
run_test(
|
run_test(
|
||||||
r#"module foo { def a [] { 1 }; def b [] { 2 }; 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",
|
"3",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn module_import_uses_internal_command() -> TestResult {
|
||||||
|
run_test(
|
||||||
|
r#"module foo { def b [] { 2 }; export def a [] { b } }; use foo; foo.a"#,
|
||||||
|
"2",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hides_def() -> TestResult {
|
||||||
|
fail_test(r#"def foo [] { "foo" }; hide foo; foo"#, "not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hides_def_then_redefines() -> TestResult {
|
||||||
|
fail_test(
|
||||||
|
r#"def foo [] { "foo" }; hide foo; def foo [] { "bar" }; foo"#,
|
||||||
|
"defined more than once",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hides_def_in_scope_1() -> TestResult {
|
||||||
|
fail_test(r#"def foo [] { "foo" }; do { hide foo; foo }"#, "not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hides_def_in_scope_2() -> TestResult {
|
||||||
|
run_test(
|
||||||
|
r#"def foo [] { "foo" }; do { def foo [] { "bar" }; hide foo; foo }"#,
|
||||||
|
"foo",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hides_def_in_scope_3() -> TestResult {
|
||||||
|
fail_test(
|
||||||
|
r#"def foo [] { "foo" }; do { hide foo; def foo [] { "bar" }; hide foo; foo }"#,
|
||||||
|
"not found",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hides_def_in_scope_4() -> TestResult {
|
||||||
|
fail_test(
|
||||||
|
r#"def foo [] { "foo" }; do { def foo [] { "bar" }; hide foo; hide foo; foo }"#,
|
||||||
|
"not found",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hide_twice_not_allowed() -> TestResult {
|
||||||
|
fail_test(
|
||||||
|
r#"def foo [] { "foo" }; hide foo; hide foo"#,
|
||||||
|
"unknown command",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn def_twice_should_fail() -> TestResult {
|
||||||
|
fail_test(
|
||||||
|
r#"def foo [] { "foo" }; def foo [] { "bar" }"#,
|
||||||
|
"defined more than once",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn from_json_1() -> TestResult {
|
fn from_json_1() -> TestResult {
|
||||||
run_test(r#"('{"name": "Fred"}' | from json).name"#, "Fred")
|
run_test(r#"('{"name": "Fred"}' | from json).name"#, "Fred")
|
||||||
|
Loading…
Reference in New Issue
Block a user