diff --git a/crates/nu-engine/src/eval.rs b/crates/nu-engine/src/eval.rs index 29ba6fc3e..2cfe44b37 100644 --- a/crates/nu-engine/src/eval.rs +++ b/crates/nu-engine/src/eval.rs @@ -156,10 +156,10 @@ pub fn eval_expression( Expr::Var(var_id) => context .get_var(*var_id) .map_err(move |_| ShellError::VariableNotFoundAtRuntime(expr.span)), - Expr::FullCellPath(column_path) => { - let value = eval_expression(context, &column_path.head)?; + Expr::FullCellPath(cell_path) => { + let value = eval_expression(context, &cell_path.head)?; - value.follow_cell_path(&column_path.tail) + value.follow_cell_path(&cell_path.tail) } Expr::RowCondition(_, expr) => eval_expression(context, expr), Expr::Call(call) => eval_call(context, call, Value::nothing()), diff --git a/crates/nu-parser/src/errors.rs b/crates/nu-parser/src/errors.rs index 52f3983c1..58123aac0 100644 --- a/crates/nu-parser/src/errors.rs +++ b/crates/nu-parser/src/errors.rs @@ -151,4 +151,12 @@ pub enum ParseError { #[error("{0}")] #[diagnostic(code(nu::parser::assignment_mismatch), url(docsrs))] AssignmentMismatch(String, String, #[label("{1}")] Span), + + #[error("Missing import pattern.")] + #[diagnostic(code(nu::parser::missing_import_pattern), url(docsrs))] + MissingImportPattern(#[label = "needs an import pattern"] Span), + + #[error("Module export not found.")] + #[diagnostic(code(nu::parser::export_not_found), url(docsrs))] + ExportNotFound(#[label = "could not find imports"] Span), } diff --git a/crates/nu-parser/src/flatten.rs b/crates/nu-parser/src/flatten.rs index ba3fde5db..7b94d007c 100644 --- a/crates/nu-parser/src/flatten.rs +++ b/crates/nu-parser/src/flatten.rs @@ -79,10 +79,10 @@ pub fn flatten_expression( Expr::Float(_) => { vec![(expr.span, FlatShape::Float)] } - Expr::FullCellPath(column_path) => { + Expr::FullCellPath(cell_path) => { let mut output = vec![]; - output.extend(flatten_expression(working_set, &column_path.head)); - for path_element in &column_path.tail { + output.extend(flatten_expression(working_set, &cell_path.head)); + for path_element in &cell_path.tail { match path_element { PathMember::String { span, .. } => output.push((*span, FlatShape::String)), PathMember::Int { span, .. } => output.push((*span, FlatShape::Int)), diff --git a/crates/nu-parser/src/lib.rs b/crates/nu-parser/src/lib.rs index cfd9535f7..0fd2f3ec8 100644 --- a/crates/nu-parser/src/lib.rs +++ b/crates/nu-parser/src/lib.rs @@ -2,6 +2,7 @@ mod errors; mod flatten; mod lex; mod lite_parse; +mod parse_keywords; mod parser; mod type_check; @@ -9,4 +10,7 @@ pub use errors::ParseError; pub use flatten::{flatten_block, FlatShape}; pub use lex::{lex, Token, TokenContents}; pub use lite_parse::{lite_parse, LiteBlock}; +pub use parse_keywords::{ + parse_alias, parse_def, parse_def_predecl, parse_let, parse_module, parse_use, +}; pub use parser::{parse, Import, VarDecl}; diff --git a/crates/nu-parser/src/parse_keywords.rs b/crates/nu-parser/src/parse_keywords.rs new file mode 100644 index 000000000..e7bcb38a3 --- /dev/null +++ b/crates/nu-parser/src/parse_keywords.rs @@ -0,0 +1,484 @@ +use nu_protocol::{ + ast::{Block, Call, Expr, Expression, ImportPatternMember, Pipeline, Statement}, + engine::StateWorkingSet, + span, DeclId, Span, SyntaxShape, Type, +}; + +use crate::{ + lex, lite_parse, + parser::{ + check_name, garbage, garbage_statement, parse_block_expression, parse_import_pattern, + parse_internal_call, parse_signature, parse_string, + }, + ParseError, +}; + +pub fn parse_def_predecl(working_set: &mut StateWorkingSet, spans: &[Span]) { + let name = working_set.get_span_contents(spans[0]); + + if name == b"def" && spans.len() >= 4 { + let (name_expr, ..) = parse_string(working_set, spans[1]); + let name = name_expr.as_string(); + + working_set.enter_scope(); + // FIXME: because parse_signature will update the scope with the variables it sees + // we end up parsing the signature twice per def. The first time is during the predecl + // so that we can see the types that are part of the signature, which we need for parsing. + // The second time is when we actually parse the body itworking_set. + // We can't reuse the first time because the variables that are created during parse_signature + // are lost when we exit the scope below. + let (sig, ..) = parse_signature(working_set, spans[2]); + let signature = sig.as_signature(); + working_set.exit_scope(); + + if let (Some(name), Some(mut signature)) = (name, signature) { + signature.name = name; + let decl = signature.predeclare(); + + working_set.add_decl(decl); + } + } +} + +pub fn parse_def( + working_set: &mut StateWorkingSet, + spans: &[Span], +) -> (Statement, Option) { + let mut error = None; + let name = working_set.get_span_contents(spans[0]); + + if name == b"def" && spans.len() >= 4 { + //FIXME: don't use expect here + let (name_expr, err) = parse_string(working_set, spans[1]); + error = error.or(err); + + working_set.enter_scope(); + let (sig, err) = parse_signature(working_set, spans[2]); + error = error.or(err); + + let (block, err) = + parse_block_expression(working_set, &SyntaxShape::Block(Some(vec![])), spans[3]); + error = error.or(err); + working_set.exit_scope(); + + if error.is_some() { + return ( + Statement::Pipeline(Pipeline::from_vec(vec![garbage(span(spans))])), + error, + ); + } + + let name = name_expr.as_string(); + + let signature = sig.as_signature(); + + let block_id = block.as_block(); + + match (name, signature, block_id) { + (Some(name), Some(mut signature), Some(block_id)) => { + let decl_id = working_set + .find_decl(name.as_bytes()) + .expect("internal error: predeclaration failed to add definition"); + + let declaration = working_set.get_decl_mut(decl_id); + + signature.name = name; + + *declaration = signature.into_block_command(block_id); + + let def_decl_id = working_set + .find_decl(b"def") + .expect("internal error: missing def command"); + + let call = Box::new(Call { + head: spans[0], + decl_id: def_decl_id, + positional: vec![name_expr, sig, block], + named: vec![], + }); + + ( + Statement::Pipeline(Pipeline::from_vec(vec![Expression { + expr: Expr::Call(call), + span: span(spans), + ty: Type::Unknown, + custom_completion: None, + }])), + error, + ) + } + _ => ( + Statement::Pipeline(Pipeline::from_vec(vec![garbage(span(spans))])), + error, + ), + } + } else { + ( + garbage_statement(spans), + Some(ParseError::UnknownState( + "Expected structure: def [] {}".into(), + span(spans), + )), + ) + } +} + +pub fn parse_alias( + working_set: &mut StateWorkingSet, + spans: &[Span], +) -> (Statement, Option) { + let name = working_set.get_span_contents(spans[0]); + + if name == b"alias" { + if let Some((span, err)) = check_name(working_set, spans) { + return ( + Statement::Pipeline(Pipeline::from_vec(vec![garbage(*span)])), + Some(err), + ); + } + + if let Some(decl_id) = working_set.find_decl(b"alias") { + let (call, call_span, _) = + parse_internal_call(working_set, spans[0], &spans[1..], decl_id); + + if spans.len() >= 4 { + let alias_name = working_set.get_span_contents(spans[1]); + + let alias_name = if alias_name.starts_with(b"\"") + && alias_name.ends_with(b"\"") + && alias_name.len() > 1 + { + alias_name[1..(alias_name.len() - 1)].to_vec() + } else { + alias_name.to_vec() + }; + let _equals = working_set.get_span_contents(spans[2]); + + let replacement = spans[3..].to_vec(); + + //println!("{:?} {:?}", alias_name, replacement); + + working_set.add_alias(alias_name, replacement); + } + + return ( + Statement::Pipeline(Pipeline::from_vec(vec![Expression { + expr: Expr::Call(call), + span: call_span, + ty: Type::Unknown, + custom_completion: None, + }])), + None, + ); + } + } + + ( + garbage_statement(spans), + Some(ParseError::UnknownState( + "internal error: alias statement unparseable".into(), + span(spans), + )), + ) +} + +pub fn parse_module( + working_set: &mut StateWorkingSet, + spans: &[Span], +) -> (Statement, Option) { + // TODO: Currently, module is closing over its parent scope (i.e., defs in the parent scope are + // visible and usable in this module's scope). We might want to disable that. How? + + let mut error = None; + let bytes = working_set.get_span_contents(spans[0]); + + // parse_def() equivalent + if bytes == b"module" && spans.len() >= 3 { + 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"); + + // parse_block_expression() equivalent + let block_span = spans[2]; + let block_bytes = working_set.get_span_contents(block_span); + let mut start = block_span.start; + let mut end = block_span.end; + + if block_bytes.starts_with(b"{") { + start += 1; + } else { + return ( + garbage_statement(spans), + Some(ParseError::Expected("block".into(), block_span)), + ); + } + + if block_bytes.ends_with(b"}") { + end -= 1; + } else { + error = error.or_else(|| { + Some(ParseError::Unclosed( + "}".into(), + Span { + start: end, + end: end + 1, + }, + )) + }); + } + + let block_span = Span { start, end }; + + let source = working_set.get_span_contents(block_span); + + let (output, err) = lex(source, start, &[], &[]); + error = error.or(err); + + working_set.enter_scope(); + + // Do we need block parameters? + + let (output, err) = lite_parse(&output); + error = error.or(err); + + // We probably don't need $it + + // we're doing parse_block() equivalent + // let (mut output, err) = parse_block(working_set, &output, false); + + for pipeline in &output.block { + if pipeline.commands.len() == 1 { + parse_def_predecl(working_set, &pipeline.commands[0].parts); + } + } + + let mut exports: Vec<(Vec, DeclId)> = vec![]; + + let block: Block = output + .block + .iter() + .map(|pipeline| { + if pipeline.commands.len() == 1 { + // this one here is doing parse_statement() equivalent + // let (stmt, err) = parse_statement(working_set, &pipeline.commands[0].parts); + let name = working_set.get_span_contents(pipeline.commands[0].parts[0]); + + let (stmt, err) = match name { + // TODO: Here we can add other stuff that's alowed for modules + b"def" => { + let (stmt, err) = parse_def(working_set, &pipeline.commands[0].parts); + + if err.is_none() { + let decl_name = + working_set.get_span_contents(pipeline.commands[0].parts[1]); + + let decl_id = working_set + .find_decl(decl_name) + .expect("internal error: failed to find added declaration"); + + // TODO: Later, we want to put this behind 'export' + exports.push((decl_name.into(), decl_id)); + } + + (stmt, err) + } + _ => ( + garbage_statement(&pipeline.commands[0].parts), + Some(ParseError::Expected("def".into(), block_span)), + ), + }; + + if error.is_none() { + error = err; + } + + stmt + } else { + error = Some(ParseError::Expected("not a pipeline".into(), block_span)); + garbage_statement(spans) + } + }) + .into(); + + let block = block.with_exports(exports); + + working_set.exit_scope(); + + let block_id = working_set.add_module(&module_name, block); + + let block_expr = Expression { + expr: Expr::Block(block_id), + span: block_span, + ty: Type::Block, + custom_completion: None, + }; + + let module_decl_id = working_set + .find_decl(b"module") + .expect("internal error: missing module command"); + + let call = Box::new(Call { + head: spans[0], + decl_id: module_decl_id, + positional: vec![module_name_expr, block_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: module {}".into(), + span(spans), + )), + ) + } +} + +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 (import_pattern, err) = parse_import_pattern(working_set, spans[1]); + error = error.or(err); + + 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() + } else { + return ( + garbage_statement(spans), + Some(ParseError::ModuleNotFound(spans[1])), + ); + }; + + let exports = if import_pattern.members.is_empty() { + exports + .into_iter() + .map(|(name, id)| { + let mut new_name = import_pattern.head.to_vec(); + new_name.push(b'.'); + new_name.extend(&name); + (new_name, id) + }) + .collect() + } else { + match &import_pattern.members[0] { + ImportPatternMember::Glob { .. } => exports, + ImportPatternMember::Name { name, span } => { + let new_exports: Vec<(Vec, usize)> = + exports.into_iter().filter(|x| &x.0 == name).collect(); + + if new_exports.is_empty() { + error = error.or(Some(ParseError::ExportNotFound(*span))) + } + + new_exports + } + } + }; + + // Extend the current scope with the module's exports + working_set.activate_overlay(exports); + + // Create the Use command call + 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], +) -> (Statement, Option) { + let name = working_set.get_span_contents(spans[0]); + + if name == b"let" { + if let Some((span, err)) = check_name(working_set, spans) { + return ( + Statement::Pipeline(Pipeline::from_vec(vec![garbage(*span)])), + Some(err), + ); + } + + if let Some(decl_id) = working_set.find_decl(b"let") { + let (call, call_span, err) = + parse_internal_call(working_set, spans[0], &spans[1..], decl_id); + + // Update the variable to the known type if we can. + if err.is_none() { + let var_id = call.positional[0] + .as_var() + .expect("internal error: expected variable"); + let rhs_type = call.positional[1].ty.clone(); + + working_set.set_variable_type(var_id, rhs_type); + } + + return ( + Statement::Pipeline(Pipeline::from_vec(vec![Expression { + expr: Expr::Call(call), + span: call_span, + ty: Type::Unknown, + custom_completion: None, + }])), + err, + ); + } + } + ( + garbage_statement(spans), + Some(ParseError::UnknownState( + "internal error: let statement unparseable".into(), + span(spans), + )), + ) +} diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs index 805c63748..a794b1a29 100644 --- a/crates/nu-parser/src/parser.rs +++ b/crates/nu-parser/src/parser.rs @@ -6,11 +6,15 @@ use crate::{ use nu_protocol::{ ast::{ - Block, Call, Expr, Expression, FullCellPath, Operator, PathMember, Pipeline, - RangeInclusion, RangeOperator, Statement, + Block, Call, Expr, Expression, FullCellPath, ImportPattern, ImportPatternMember, Operator, + PathMember, Pipeline, RangeInclusion, RangeOperator, Statement, }, engine::StateWorkingSet, - span, DeclId, Flag, PositionalArg, Signature, Span, SyntaxShape, Type, VarId, + span, Flag, PositionalArg, Signature, Span, SyntaxShape, Type, VarId, +}; + +use crate::parse_keywords::{ + parse_alias, parse_def, parse_def_predecl, parse_let, parse_module, parse_use, }; #[derive(Debug, Clone)] @@ -22,11 +26,11 @@ pub struct VarDecl { expression: Expression, } -fn garbage(span: Span) -> Expression { +pub fn garbage(span: Span) -> Expression { Expression::garbage(span) } -fn garbage_statement(spans: &[Span]) -> Statement { +pub fn garbage_statement(spans: &[Span]) -> Statement { Statement::Pipeline(Pipeline::from_vec(vec![garbage(span(spans))])) } @@ -63,7 +67,7 @@ fn check_call(command: Span, sig: &Signature, call: &Call) -> Option } } -fn check_name<'a>( +pub fn check_name<'a>( working_set: &mut StateWorkingSet, spans: &'a [Span], ) -> Option<(&'a Span, ParseError)> { @@ -963,7 +967,7 @@ pub(crate) fn parse_dollar_expr( } else if let (expr, None) = parse_range(working_set, span) { (expr, None) } else { - parse_full_column_path(working_set, None, span) + parse_full_cell_path(working_set, None, span) } } @@ -1035,7 +1039,7 @@ pub fn parse_string_interpolation( end: b + 1, }; - let (expr, err) = parse_full_column_path(working_set, None, span); + let (expr, err) = parse_full_cell_path(working_set, None, span); error = error.or(err); output.push(expr); } @@ -1071,7 +1075,7 @@ pub fn parse_string_interpolation( end, }; - let (expr, err) = parse_full_column_path(working_set, None, span); + let (expr, err) = parse_full_cell_path(working_set, None, span); error = error.or(err); output.push(expr); } @@ -1153,13 +1157,13 @@ pub fn parse_variable_expr( } } -pub fn parse_full_column_path( +pub fn parse_full_cell_path( working_set: &mut StateWorkingSet, implicit_head: Option, span: Span, ) -> (Expression, Option) { // FIXME: assume for now a paren expr, but needs more - let full_column_span = span; + let full_cell_span = span; let source = working_set.get_span_contents(span); let mut error = None; @@ -1294,7 +1298,7 @@ pub fn parse_full_column_path( Expression { expr: Expr::FullCellPath(Box::new(FullCellPath { head, tail })), ty: Type::Unknown, - span: full_column_span, + span: full_cell_span, custom_completion: None, }, error, @@ -1371,6 +1375,63 @@ pub fn parse_type(_working_set: &StateWorkingSet, bytes: &[u8]) -> Type { } } +pub fn parse_import_pattern( + working_set: &StateWorkingSet, + span: Span, +) -> (ImportPattern, Option) { + let source = working_set.get_span_contents(span); + let mut error = None; + + let (tokens, err) = lex(source, span.start, &[], &[b'.']); + error = error.or(err); + + if tokens.is_empty() { + return ( + ImportPattern { + head: vec![], + members: vec![], + }, + Some(ParseError::MissingImportPattern(span)), + ); + } + + let head = working_set.get_span_contents(tokens[0].span).to_vec(); + + if let Some(tail) = tokens.get(2) { + // FIXME: expand this to handle deeper imports once we support module imports + let tail_span = tail.span; + let tail = working_set.get_span_contents(tail.span); + if tail == b"*" { + ( + ImportPattern { + head, + members: vec![ImportPatternMember::Glob { span: tail_span }], + }, + error, + ) + } else { + ( + ImportPattern { + head, + members: vec![ImportPatternMember::Name { + name: tail.to_vec(), + span: tail_span, + }], + }, + error, + ) + } + } else { + ( + ImportPattern { + head, + members: vec![], + }, + None, + ) + } +} + pub fn parse_var_with_opt_type( working_set: &mut StateWorkingSet, spans: &[Span], @@ -1436,7 +1497,7 @@ pub fn expand_to_cell_path( } = expression { // Re-parse the string as if it were a cell-path - let (new_expression, _err) = parse_full_column_path(working_set, Some(var_id), *span); + let (new_expression, _err) = parse_full_cell_path(working_set, Some(var_id), *span); *expression = new_expression; } @@ -2180,7 +2241,7 @@ pub fn parse_value( if let (expr, None) = parse_range(working_set, span) { return (expr, None); } else { - return parse_full_column_path(working_set, None, span); + return parse_full_cell_path(working_set, None, span); } } else if bytes.starts_with(b"{") { if matches!(shape, SyntaxShape::Block(_)) || matches!(shape, SyntaxShape::Any) { @@ -2480,453 +2541,6 @@ pub fn parse_variable( } } -pub fn parse_def_predecl(working_set: &mut StateWorkingSet, spans: &[Span]) { - let name = working_set.get_span_contents(spans[0]); - - if name == b"def" && spans.len() >= 4 { - let (name_expr, ..) = parse_string(working_set, spans[1]); - let name = name_expr.as_string(); - - working_set.enter_scope(); - // FIXME: because parse_signature will update the scope with the variables it sees - // we end up parsing the signature twice per def. The first time is during the predecl - // so that we can see the types that are part of the signature, which we need for parsing. - // The second time is when we actually parse the body itworking_set. - // We can't reuse the first time because the variables that are created during parse_signature - // are lost when we exit the scope below. - let (sig, ..) = parse_signature(working_set, spans[2]); - let signature = sig.as_signature(); - working_set.exit_scope(); - - if let (Some(name), Some(mut signature)) = (name, signature) { - signature.name = name; - let decl = signature.predeclare(); - - working_set.add_decl(decl); - } - } -} - -pub fn parse_def( - working_set: &mut StateWorkingSet, - spans: &[Span], -) -> (Statement, Option) { - let mut error = None; - let name = working_set.get_span_contents(spans[0]); - - if name == b"def" && spans.len() >= 4 { - //FIXME: don't use expect here - let (name_expr, err) = parse_string(working_set, spans[1]); - error = error.or(err); - - working_set.enter_scope(); - let (sig, err) = parse_signature(working_set, spans[2]); - error = error.or(err); - - let (block, err) = - parse_block_expression(working_set, &SyntaxShape::Block(Some(vec![])), spans[3]); - error = error.or(err); - working_set.exit_scope(); - - if error.is_some() { - return ( - Statement::Pipeline(Pipeline::from_vec(vec![garbage(span(spans))])), - error, - ); - } - - let name = name_expr.as_string(); - - let signature = sig.as_signature(); - - let block_id = block.as_block(); - - match (name, signature, block_id) { - (Some(name), Some(mut signature), Some(block_id)) => { - let decl_id = working_set - .find_decl(name.as_bytes()) - .expect("internal error: predeclaration failed to add definition"); - - let declaration = working_set.get_decl_mut(decl_id); - - signature.name = name; - - *declaration = signature.into_block_command(block_id); - - let def_decl_id = working_set - .find_decl(b"def") - .expect("internal error: missing def command"); - - let call = Box::new(Call { - head: spans[0], - decl_id: def_decl_id, - positional: vec![name_expr, sig, block], - named: vec![], - }); - - ( - Statement::Pipeline(Pipeline::from_vec(vec![Expression { - expr: Expr::Call(call), - span: span(spans), - ty: Type::Unknown, - custom_completion: None, - }])), - error, - ) - } - _ => ( - Statement::Pipeline(Pipeline::from_vec(vec![garbage(span(spans))])), - error, - ), - } - } else { - ( - garbage_statement(spans), - Some(ParseError::UnknownState( - "Expected structure: def [] {}".into(), - span(spans), - )), - ) - } -} - -pub fn parse_alias( - working_set: &mut StateWorkingSet, - spans: &[Span], -) -> (Statement, Option) { - let name = working_set.get_span_contents(spans[0]); - - if name == b"alias" { - if let Some((span, err)) = check_name(working_set, spans) { - return ( - Statement::Pipeline(Pipeline::from_vec(vec![garbage(*span)])), - Some(err), - ); - } - - if let Some(decl_id) = working_set.find_decl(b"alias") { - let (call, call_span, _) = - parse_internal_call(working_set, spans[0], &spans[1..], decl_id); - - if spans.len() >= 4 { - let alias_name = working_set.get_span_contents(spans[1]); - - let alias_name = if alias_name.starts_with(b"\"") - && alias_name.ends_with(b"\"") - && alias_name.len() > 1 - { - alias_name[1..(alias_name.len() - 1)].to_vec() - } else { - alias_name.to_vec() - }; - let _equals = working_set.get_span_contents(spans[2]); - - let replacement = spans[3..].to_vec(); - - //println!("{:?} {:?}", alias_name, replacement); - - working_set.add_alias(alias_name, replacement); - } - - return ( - Statement::Pipeline(Pipeline::from_vec(vec![Expression { - expr: Expr::Call(call), - span: call_span, - ty: Type::Unknown, - custom_completion: None, - }])), - None, - ); - } - } - - ( - garbage_statement(spans), - Some(ParseError::UnknownState( - "internal error: alias statement unparseable".into(), - span(spans), - )), - ) -} - -pub fn parse_module( - working_set: &mut StateWorkingSet, - spans: &[Span], -) -> (Statement, Option) { - // TODO: Currently, module is closing over its parent scope (i.e., defs in the parent scope are - // visible and usable in this module's scope). We might want to disable that. How? - - let mut error = None; - let bytes = working_set.get_span_contents(spans[0]); - - // parse_def() equivalent - if bytes == b"module" && spans.len() >= 3 { - 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"); - - // parse_block_expression() equivalent - let block_span = spans[2]; - let block_bytes = working_set.get_span_contents(block_span); - let mut start = block_span.start; - let mut end = block_span.end; - - if block_bytes.starts_with(b"{") { - start += 1; - } else { - return ( - garbage_statement(spans), - Some(ParseError::Expected("block".into(), block_span)), - ); - } - - if block_bytes.ends_with(b"}") { - end -= 1; - } else { - error = error.or_else(|| { - Some(ParseError::Unclosed( - "}".into(), - Span { - start: end, - end: end + 1, - }, - )) - }); - } - - let block_span = Span { start, end }; - - let source = working_set.get_span_contents(block_span); - - let (output, err) = lex(source, start, &[], &[]); - error = error.or(err); - - working_set.enter_scope(); - - // Do we need block parameters? - - let (output, err) = lite_parse(&output); - error = error.or(err); - - // We probably don't need $it - - // we're doing parse_block() equivalent - // let (mut output, err) = parse_block(working_set, &output, false); - - for pipeline in &output.block { - if pipeline.commands.len() == 1 { - parse_def_predecl(working_set, &pipeline.commands[0].parts); - } - } - - let mut exports: Vec<(Vec, DeclId)> = vec![]; - - let block: Block = output - .block - .iter() - .map(|pipeline| { - if pipeline.commands.len() == 1 { - // this one here is doing parse_statement() equivalent - // let (stmt, err) = parse_statement(working_set, &pipeline.commands[0].parts); - let name = working_set.get_span_contents(pipeline.commands[0].parts[0]); - - let (stmt, err) = match name { - // TODO: Here we can add other stuff that's alowed for modules - b"def" => { - let (stmt, err) = parse_def(working_set, &pipeline.commands[0].parts); - - if err.is_none() { - let decl_name = - working_set.get_span_contents(pipeline.commands[0].parts[1]); - - let decl_id = working_set - .find_decl(decl_name) - .expect("internal error: failed to find added declaration"); - - // TODO: Later, we want to put this behind 'export' - exports.push((decl_name.into(), decl_id)); - } - - (stmt, err) - } - _ => ( - garbage_statement(&pipeline.commands[0].parts), - Some(ParseError::Expected("def".into(), block_span)), - ), - }; - - if error.is_none() { - error = err; - } - - stmt - } else { - error = Some(ParseError::Expected("not a pipeline".into(), block_span)); - garbage_statement(spans) - } - }) - .into(); - - let block = block.with_exports(exports); - - working_set.exit_scope(); - - let block_id = working_set.add_module(&module_name, block); - - let block_expr = Expression { - expr: Expr::Block(block_id), - span: block_span, - ty: Type::Block, - custom_completion: None, - }; - - let module_decl_id = working_set - .find_decl(b"module") - .expect("internal error: missing module command"); - - let call = Box::new(Call { - head: spans[0], - decl_id: module_decl_id, - positional: vec![module_name_expr, block_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: module {}".into(), - span(spans), - )), - ) - } -} - -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 exports = if let Some(block_id) = working_set.find_module(&module_name_bytes) { - // 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() - } else { - return ( - garbage_statement(spans), - Some(ParseError::ModuleNotFound(spans[1])), - ); - }; - - // Extend the current scope with the module's exports - working_set.activate_overlay(exports); - - // Create the Use command call - 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], -) -> (Statement, Option) { - let name = working_set.get_span_contents(spans[0]); - - if name == b"let" { - if let Some((span, err)) = check_name(working_set, spans) { - return ( - Statement::Pipeline(Pipeline::from_vec(vec![garbage(*span)])), - Some(err), - ); - } - - if let Some(decl_id) = working_set.find_decl(b"let") { - let (call, call_span, err) = - parse_internal_call(working_set, spans[0], &spans[1..], decl_id); - - // Update the variable to the known type if we can. - if err.is_none() { - let var_id = call.positional[0] - .as_var() - .expect("internal error: expected variable"); - let rhs_type = call.positional[1].ty.clone(); - - working_set.set_variable_type(var_id, rhs_type); - } - - return ( - Statement::Pipeline(Pipeline::from_vec(vec![Expression { - expr: Expr::Call(call), - span: call_span, - ty: Type::Unknown, - custom_completion: None, - }])), - err, - ); - } - } - ( - garbage_statement(spans), - Some(ParseError::UnknownState( - "internal error: let statement unparseable".into(), - span(spans), - )), - ) -} - pub fn parse_statement( working_set: &mut StateWorkingSet, spans: &[Span], diff --git a/crates/nu-protocol/src/ast/import_pattern.rs b/crates/nu-protocol/src/ast/import_pattern.rs new file mode 100644 index 000000000..c5183d75e --- /dev/null +++ b/crates/nu-protocol/src/ast/import_pattern.rs @@ -0,0 +1,13 @@ +use crate::Span; + +#[derive(Debug, Clone)] +pub enum ImportPatternMember { + Glob { span: Span }, + Name { name: Vec, span: Span }, +} + +#[derive(Debug, Clone)] +pub struct ImportPattern { + pub head: Vec, + pub members: Vec, +} diff --git a/crates/nu-protocol/src/ast/mod.rs b/crates/nu-protocol/src/ast/mod.rs index 4bd33ec76..67c9ce76e 100644 --- a/crates/nu-protocol/src/ast/mod.rs +++ b/crates/nu-protocol/src/ast/mod.rs @@ -3,6 +3,7 @@ mod call; mod cell_path; mod expr; mod expression; +mod import_pattern; mod operator; mod pipeline; mod statement; @@ -12,6 +13,7 @@ pub use call::*; pub use cell_path::*; pub use expr::*; pub use expression::*; +pub use import_pattern::*; pub use operator::*; pub use pipeline::*; pub use statement::*; diff --git a/crates/nu-protocol/src/syntax_shape.rs b/crates/nu-protocol/src/syntax_shape.rs index b0c1b9659..daed48c0c 100644 --- a/crates/nu-protocol/src/syntax_shape.rs +++ b/crates/nu-protocol/src/syntax_shape.rs @@ -33,6 +33,9 @@ pub enum SyntaxShape { /// A glob pattern is allowed, eg `foo*` GlobPattern, + /// A module path pattern used for imports + ImportPattern, + /// A block is allowed, eg `{start this thing}` Block(Option>), @@ -87,6 +90,7 @@ impl SyntaxShape { SyntaxShape::Filesize => Type::Filesize, SyntaxShape::FullCellPath => Type::Unknown, SyntaxShape::GlobPattern => Type::String, + SyntaxShape::ImportPattern => Type::Unknown, SyntaxShape::Int => Type::Int, SyntaxShape::List(x) => { let contents = x.to_type(); diff --git a/crates/nu-protocol/src/value/mod.rs b/crates/nu-protocol/src/value/mod.rs index bdb31de39..ce1a18a74 100644 --- a/crates/nu-protocol/src/value/mod.rs +++ b/crates/nu-protocol/src/value/mod.rs @@ -177,9 +177,9 @@ impl Value { } } - pub fn follow_cell_path(self, column_path: &[PathMember]) -> Result { + pub fn follow_cell_path(self, cell_path: &[PathMember]) -> Result { let mut current = self; - for member in column_path { + for member in cell_path { // FIXME: this uses a few extra clones for simplicity, but there may be a way // to traverse the path without them match member { diff --git a/src/tests.rs b/src/tests.rs index 90cb52013..c95d8e615 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -342,3 +342,35 @@ fn better_block_types() -> TestResult { "1 is 2", ) } + +#[test] +fn module_imports_1() -> TestResult { + run_test( + r#"module foo { def a [] { 1 }; def b [] { 2 } }; use foo; foo.a"#, + "1", + ) +} + +#[test] +fn module_imports_2() -> TestResult { + run_test( + r#"module foo { def a [] { 1 }; def b [] { 2 } }; use foo.a; a"#, + "1", + ) +} + +#[test] +fn module_imports_3() -> TestResult { + run_test( + r#"module foo { def a [] { 1 }; def b [] { 2 } }; use foo.*; b"#, + "2", + ) +} + +#[test] +fn module_imports_4() -> TestResult { + fail_test( + r#"module foo { def a [] { 1 }; def b [] { 2 } }; use foo.c"#, + "not find import", + ) +}