From 9e176674a5b08d7af63bc78db21ce3351e9b64a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C5=BD=C3=A1dn=C3=ADk?= Date: Sun, 26 Sep 2021 13:25:52 +0300 Subject: [PATCH] Start parsing 'use'; Add Use command --- crates/nu-command/src/default_context.rs | 4 +- crates/nu-command/src/lib.rs | 2 + crates/nu-command/src/use_.rs | 29 +++++++++ crates/nu-parser/src/errors.rs | 4 ++ crates/nu-parser/src/parser.rs | 60 +++++++++++++++++++ crates/nu-protocol/src/engine/engine_state.rs | 19 ++++++ 6 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 crates/nu-command/src/use_.rs diff --git a/crates/nu-command/src/default_context.rs b/crates/nu-command/src/default_context.rs index 43f68d27e4..4c793baa4c 100644 --- a/crates/nu-command/src/default_context.rs +++ b/crates/nu-command/src/default_context.rs @@ -7,7 +7,7 @@ use nu_protocol::{ use crate::{ where_::Where, Alias, Benchmark, BuildString, Def, Do, Each, External, For, Git, GitCheckout, - If, Length, Let, LetEnv, Lines, ListGitBranches, Ls, Module, Table, + If, Length, Let, LetEnv, Lines, ListGitBranches, Ls, Module, Table, Use, }; pub fn create_default_context() -> Rc> { @@ -48,6 +48,8 @@ pub fn create_default_context() -> Rc> { working_set.add_decl(Box::new(Module)); + working_set.add_decl(Box::new(Use)); + working_set.add_decl(Box::new(Table)); working_set.add_decl(Box::new(External)); diff --git a/crates/nu-command/src/lib.rs b/crates/nu-command/src/lib.rs index 86142f32b0..a96a41ec3b 100644 --- a/crates/nu-command/src/lib.rs +++ b/crates/nu-command/src/lib.rs @@ -18,6 +18,7 @@ mod ls; mod module; mod run_external; mod table; +mod use_; mod where_; pub use alias::Alias; @@ -40,3 +41,4 @@ pub use ls::Ls; pub use module::Module; pub use run_external::External; pub use table::Table; +pub use use_::Use; diff --git a/crates/nu-command/src/use_.rs b/crates/nu-command/src/use_.rs new file mode 100644 index 0000000000..e317adbffb --- /dev/null +++ b/crates/nu-command/src/use_.rs @@ -0,0 +1,29 @@ +use nu_protocol::ast::Call; +use nu_protocol::engine::{Command, EvaluationContext}; +use nu_protocol::{Signature, SyntaxShape, Value}; + +pub struct Use; + +impl Command for Use { + fn name(&self) -> &str { + "use" + } + + fn usage(&self) -> &str { + "Use definitions from a module" + } + + fn signature(&self) -> nu_protocol::Signature { + Signature::build("use") + .required("module_name", SyntaxShape::String, "module name") + } + + fn run( + &self, + _context: &EvaluationContext, + call: &Call, + _input: Value, + ) -> Result { + Ok(Value::Nothing { span: call.head }) + } +} diff --git a/crates/nu-parser/src/errors.rs b/crates/nu-parser/src/errors.rs index 6d400f67b4..52f3983c1f 100644 --- a/crates/nu-parser/src/errors.rs +++ b/crates/nu-parser/src/errors.rs @@ -65,6 +65,10 @@ pub enum ParseError { #[diagnostic(code(nu::parser::variable_not_found), url(docsrs))] VariableNotFound(#[label = "variable not found"] Span), + #[error("Module not found.")] + #[diagnostic(code(nu::parser::module_not_found), url(docsrs))] + ModuleNotFound(#[label = "module not found"] Span), + #[error("Unknown command.")] #[diagnostic( code(nu::parser::unknown_command), diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs index 5fbebc4ebd..822fd2f403 100644 --- a/crates/nu-parser/src/parser.rs +++ b/crates/nu-parser/src/parser.rs @@ -2806,6 +2806,65 @@ pub fn parse_module( } } +pub fn parse_use( + working_set: &mut StateWorkingSet, + spans: &[Span], +) -> (Statement, Option) { + let mut error = None; + 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 { + let (module_name_expr, err) = parse_string(working_set, spans[1]); + error = error.or(err); + + let module_name = module_name_expr + .as_string() + .expect("internal error: module name is not a string"); + + let module_name_bytes = module_name.as_bytes().to_vec(); + + let block = if let Some(block_id) = working_set.find_module(&module_name_bytes) { + working_set.get_block(block_id) + } else { + return ( + garbage_statement(spans), + Some(ParseError::ModuleNotFound(spans[1])), + ); + }; + + let use_decl_id = working_set + .find_decl(b"use") + .expect("internal error: missing use command"); + + let call = Box::new(Call { + head: spans[0], + decl_id: use_decl_id, + positional: vec![module_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: use ".into(), + span(spans), + )), + ) + } +} + pub fn parse_let( working_set: &mut StateWorkingSet, spans: &[Span], @@ -2865,6 +2924,7 @@ pub fn parse_statement( b"let" => parse_let(working_set, spans), b"alias" => parse_alias(working_set, spans), b"module" => parse_module(working_set, spans), + b"use" => parse_use(working_set, spans), _ => { let (expr, err) = parse_expression(working_set, spans); (Statement::Pipeline(Pipeline::from_vec(vec![expr])), err) diff --git a/crates/nu-protocol/src/engine/engine_state.rs b/crates/nu-protocol/src/engine/engine_state.rs index 7108225312..bd51f47647 100644 --- a/crates/nu-protocol/src/engine/engine_state.rs +++ b/crates/nu-protocol/src/engine/engine_state.rs @@ -78,6 +78,9 @@ impl EngineState { for item in first.aliases.into_iter() { last.aliases.insert(item.0, item.1); } + for item in first.modules.into_iter() { + last.modules.insert(item.0, item.1); + } } } @@ -394,6 +397,22 @@ impl<'a> StateWorkingSet<'a> { None } + pub fn find_module(&self, name: &[u8]) -> Option { + for scope in self.delta.scope.iter().rev() { + if let Some(block_id) = scope.modules.get(name) { + return Some(*block_id); + } + } + + for scope in self.permanent_state.scope.iter().rev() { + if let Some(block_id) = scope.modules.get(name) { + return Some(*block_id); + } + } + + None + } + // pub fn update_decl(&mut self, decl_id: usize, block: Option) { // let decl = self.get_decl_mut(decl_id); // decl.body = block;