diff --git a/crates/nu-cli/src/errors.rs b/crates/nu-cli/src/errors.rs index 4b2944f355..ef25181cad 100644 --- a/crates/nu-cli/src/errors.rs +++ b/crates/nu-cli/src/errors.rs @@ -252,61 +252,76 @@ pub fn report_shell_error( let writer = StandardStream::stderr(ColorChoice::Always); let config = codespan_reporting::term::Config::default(); - let diagnostic = match error { - ShellError::OperatorMismatch { - op_span, - lhs_ty, - lhs_span, - rhs_ty, - rhs_span, - } => { - let (lhs_file_id, lhs_range) = convert_span_to_diag(working_set, lhs_span)?; - let (rhs_file_id, rhs_range) = convert_span_to_diag(working_set, rhs_span)?; - let (op_file_id, op_range) = convert_span_to_diag(working_set, op_span)?; - Diagnostic::error() - .with_message("Type mismatch during operation") - .with_labels(vec![ - Label::primary(op_file_id, op_range).with_message("type mismatch for operator"), - Label::secondary(lhs_file_id, lhs_range).with_message(lhs_ty.to_string()), - Label::secondary(rhs_file_id, rhs_range).with_message(rhs_ty.to_string()), - ]) - } - ShellError::Unsupported(span) => { - let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; - Diagnostic::error() - .with_message("Unsupported operation") - .with_labels(vec![ - Label::primary(diag_file_id, diag_range).with_message("unsupported operation") - ]) - } - ShellError::InternalError(s) => { - Diagnostic::error().with_message(format!("Internal error: {}", s)) - } - ShellError::VariableNotFound(span) => { - let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; - Diagnostic::error() - .with_message("Variable not found") - .with_labels(vec![ - Label::primary(diag_file_id, diag_range).with_message("variable not found") - ]) - } - ShellError::CantConvert(s, span) => { - let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; - Diagnostic::error() - .with_message(format!("Can't convert to {}", s)) - .with_labels(vec![Label::primary(diag_file_id, diag_range) - .with_message(format!("can't convert to {}", s))]) - } - ShellError::DivisionByZero(span) => { - let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; + let diagnostic = + match error { + ShellError::OperatorMismatch { + op_span, + lhs_ty, + lhs_span, + rhs_ty, + rhs_span, + } => { + let (lhs_file_id, lhs_range) = convert_span_to_diag(working_set, lhs_span)?; + let (rhs_file_id, rhs_range) = convert_span_to_diag(working_set, rhs_span)?; + let (op_file_id, op_range) = convert_span_to_diag(working_set, op_span)?; + Diagnostic::error() + .with_message("Type mismatch during operation") + .with_labels(vec![ + Label::primary(op_file_id, op_range) + .with_message("type mismatch for operator"), + Label::secondary(lhs_file_id, lhs_range).with_message(lhs_ty.to_string()), + Label::secondary(rhs_file_id, rhs_range).with_message(rhs_ty.to_string()), + ]) + } + ShellError::UnsupportedOperator(op, span) => { + let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; + Diagnostic::error() + .with_message(format!("Unsupported operator: {}", op)) + .with_labels(vec![Label::primary(diag_file_id, diag_range) + .with_message("unsupported operator")]) + } + ShellError::UnknownOperator(op, span) => { + let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; + Diagnostic::error() + .with_message(format!("Unsupported operator: {}", op)) + .with_labels(vec![Label::primary(diag_file_id, diag_range) + .with_message("unsupported operator")]) + } + ShellError::ExternalNotSupported(span) => { + let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; + Diagnostic::error() + .with_message("External commands not yet supported") + .with_labels(vec![Label::primary(diag_file_id, diag_range) + .with_message("external not supported")]) + } + ShellError::InternalError(s) => { + Diagnostic::error().with_message(format!("Internal error: {}", s)) + } + ShellError::VariableNotFound(span) => { + let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; + Diagnostic::error() + .with_message("Variable not found") + .with_labels(vec![ + Label::primary(diag_file_id, diag_range).with_message("variable not found") + ]) + } + ShellError::CantConvert(s, span) => { + let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; + Diagnostic::error() + .with_message(format!("Can't convert to {}", s)) + .with_labels(vec![Label::primary(diag_file_id, diag_range) + .with_message(format!("can't convert to {}", s))]) + } + ShellError::DivisionByZero(span) => { + let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; - Diagnostic::error() - .with_message("Division by zero") - .with_labels(vec![ - Label::primary(diag_file_id, diag_range).with_message("division by zero") - ]) - } - }; + Diagnostic::error() + .with_message("Division by zero") + .with_labels(vec![ + Label::primary(diag_file_id, diag_range).with_message("division by zero") + ]) + } + }; // println!("DIAG"); // println!("{:?}", diagnostic); diff --git a/crates/nu-command/src/each.rs b/crates/nu-command/src/each.rs index 9807749293..c1e2a5cb80 100644 --- a/crates/nu-command/src/each.rs +++ b/crates/nu-command/src/each.rs @@ -15,13 +15,7 @@ impl Command for Each { } fn signature(&self) -> nu_protocol::Signature { - Signature::build("each") - .required( - "var_name", - SyntaxShape::Variable, - "name of the looping variable", - ) - .required("block", SyntaxShape::Block, "the block to run") + Signature::build("each").required("block", SyntaxShape::Block, "the block to run") } fn run( @@ -30,11 +24,7 @@ impl Command for Each { call: &Call, input: Value, ) -> Result { - let var_id = call.positional[0] - .as_var() - .expect("internal error: missing variable"); - - let block = call.positional[1] + let block_id = call.positional[0] .as_block() .expect("internal error: expected block"); let context = context.clone(); @@ -45,13 +35,19 @@ impl Command for Each { .into_iter() .map(move |x| { let engine_state = context.engine_state.borrow(); - let block = engine_state.get_block(block); + let block = engine_state.get_block(block_id); let state = context.enter_scope(); - state.add_var(var_id, x); + if let Some(var) = block.signature.required_positional.first() { + if let Some(var_id) = &var.var_id { + state.add_var(*var_id, x); + } + } - //FIXME: DON'T UNWRAP - eval_block(&state, block, Value::nothing()).unwrap() + match eval_block(&state, block, Value::nothing()) { + Ok(v) => v, + Err(err) => Value::Error { err }, + } }) .collect(), span: call.head, @@ -60,13 +56,19 @@ impl Command for Each { stream: stream .map(move |x| { let engine_state = context.engine_state.borrow(); - let block = engine_state.get_block(block); + let block = engine_state.get_block(block_id); let state = context.enter_scope(); - state.add_var(var_id, x); + if let Some(var) = block.signature.required_positional.first() { + if let Some(var_id) = &var.var_id { + state.add_var(*var_id, x); + } + } - //FIXME: DON'T UNWRAP - eval_block(&state, block, Value::nothing()).unwrap() + match eval_block(&state, block, Value::nothing()) { + Ok(v) => v, + Err(err) => Value::Error { err }, + } }) .into_value_stream(), span: call.head, diff --git a/crates/nu-engine/src/eval.rs b/crates/nu-engine/src/eval.rs index e7a9cef6bf..e629ab3034 100644 --- a/crates/nu-engine/src/eval.rs +++ b/crates/nu-engine/src/eval.rs @@ -8,14 +8,16 @@ pub fn eval_operator(op: &Expression) -> Result { expr: Expr::Operator(operator), .. } => Ok(operator.clone()), - Expression { span, .. } => Err(ShellError::Unsupported(*span)), + Expression { span, expr, .. } => { + Err(ShellError::UnknownOperator(format!("{:?}", expr), *span)) + } } } fn eval_call(context: &EvaluationContext, call: &Call, input: Value) -> Result { let engine_state = context.engine_state.borrow(); let decl = engine_state.get_decl(call.decl_id); - if let Some(block_id) = decl.get_custom_command() { + if let Some(block_id) = decl.get_block_id() { let state = context.enter_scope(); for (arg, param) in call.positional.iter().zip( decl.signature() @@ -101,7 +103,7 @@ pub fn eval_expression( .get_var(*var_id) .map_err(move |_| ShellError::VariableNotFound(expr.span)), Expr::Call(call) => eval_call(context, call, Value::nothing()), - Expr::ExternalCall(_, _) => Err(ShellError::Unsupported(expr.span)), + Expr::ExternalCall(_, _) => Err(ShellError::ExternalNotSupported(expr.span)), Expr::Operator(_) => Ok(Value::Nothing { span: expr.span }), Expr::BinaryOp(lhs, op, rhs) => { let op_span = op.span; @@ -120,7 +122,7 @@ pub fn eval_expression( Operator::GreaterThanOrEqual => lhs.gte(op_span, &rhs), Operator::Equal => lhs.eq(op_span, &rhs), Operator::NotEqual => lhs.ne(op_span, &rhs), - _ => Err(ShellError::Unsupported(op_span)), + x => Err(ShellError::UnsupportedOperator(x, op_span)), } } diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs index 1e8227fa67..d9206b1e0e 100644 --- a/crates/nu-parser/src/parser.rs +++ b/crates/nu-parser/src/parser.rs @@ -1223,16 +1223,6 @@ pub fn parse_signature( working_set: &mut StateWorkingSet, span: Span, ) -> (Expression, Option) { - enum ParseMode { - ArgMode, - TypeMode, - } - - enum Arg { - Positional(PositionalArg, bool), // bool - required - Flag(Flag), - } - let bytes = working_set.get_span_contents(span); let mut error = None; @@ -1256,7 +1246,34 @@ pub fn parse_signature( }); } - let span = Span { start, end }; + let (sig, err) = parse_signature_helper(working_set, Span { start, end }); + error = error.or(err); + + ( + Expression { + expr: Expr::Signature(sig), + span, + ty: Type::Unknown, + }, + error, + ) +} + +pub fn parse_signature_helper( + working_set: &mut StateWorkingSet, + span: Span, +) -> (Box, Option) { + enum ParseMode { + ArgMode, + TypeMode, + } + + enum Arg { + Positional(PositionalArg, bool), // bool - required + Flag(Flag), + } + + let mut error = None; let source = working_set.get_span_contents(span); let (output, err) = lex(source, span.start, &[b'\n', b','], &[b':']); @@ -1535,14 +1552,7 @@ pub fn parse_signature( } } - ( - Expression { - expr: Expr::Signature(Box::new(sig)), - span, - ty: Type::Unknown, - }, - error, - ) + (Box::new(sig), error) } pub fn parse_list_expression( @@ -1762,37 +1772,59 @@ pub fn parse_block_expression( error = error.or(err); // Check to see if we have parameters - let _params = if matches!( - output.first(), + let (signature, amt_to_skip): (Option>, usize) = match output.first() { Some(Token { contents: TokenContents::Pipe, - .. - }) - ) { - // We've found a parameter list - let mut param_tokens = vec![]; - let mut token_iter = output.iter().skip(1); - for token in &mut token_iter { - if matches!( - token, - Token { + span, + }) => { + // We've found a parameter list + let start_point = span.start; + let mut token_iter = output.iter().enumerate().skip(1); + let mut end_span = None; + let mut amt_to_skip = 1; + + for token in &mut token_iter { + if let Token { contents: TokenContents::Pipe, - .. + span, + } = token.1 + { + end_span = Some(span); + amt_to_skip = token.0; + break; } - ) { - break; - } else { - param_tokens.push(token); } + + let end_point = if let Some(span) = end_span { + span.end + } else { + end + }; + + let (signature, err) = parse_signature_helper( + working_set, + Span { + start: start_point, + end: end_point, + }, + ); + error = error.or(err); + + (Some(signature), amt_to_skip) } + _ => (None, 0), }; - let (output, err) = lite_parse(&output); + let (output, err) = lite_parse(&output[amt_to_skip..]); error = error.or(err); - let (output, err) = parse_block(working_set, &output, true); + let (mut output, err) = parse_block(working_set, &output, true); error = error.or(err); + if let Some(signature) = signature { + output.signature = signature; + } + let block_id = working_set.add_block(output); ( diff --git a/crates/nu-protocol/src/ast/block.rs b/crates/nu-protocol/src/ast/block.rs index 365d2e76c9..d36e43d8ec 100644 --- a/crates/nu-protocol/src/ast/block.rs +++ b/crates/nu-protocol/src/ast/block.rs @@ -1,9 +1,12 @@ use std::ops::{Index, IndexMut}; +use crate::Signature; + use super::Statement; #[derive(Debug, Clone)] pub struct Block { + pub signature: Box, pub stmts: Vec, } @@ -39,6 +42,9 @@ impl Default for Block { impl Block { pub fn new() -> Self { - Self { stmts: vec![] } + Self { + signature: Box::new(Signature::new("")), + stmts: vec![], + } } } diff --git a/crates/nu-protocol/src/engine/command.rs b/crates/nu-protocol/src/engine/command.rs index 246985fe38..0f22b7c247 100644 --- a/crates/nu-protocol/src/engine/command.rs +++ b/crates/nu-protocol/src/engine/command.rs @@ -50,8 +50,8 @@ pub trait Command { false } - // If command is a custom command i.e. def blah [] { }, get the block id - fn get_custom_command(&self) -> Option { + // If command is a block i.e. def blah [] { }, get the block id + fn get_block_id(&self) -> Option { None } } diff --git a/crates/nu-protocol/src/shell_error.rs b/crates/nu-protocol/src/shell_error.rs index a0786d599b..a769faacf8 100644 --- a/crates/nu-protocol/src/shell_error.rs +++ b/crates/nu-protocol/src/shell_error.rs @@ -1,6 +1,6 @@ -use crate::{Span, Type}; +use crate::{ast::Operator, Span, Type}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum ShellError { OperatorMismatch { op_span: Span, @@ -9,7 +9,9 @@ pub enum ShellError { rhs_ty: Type, rhs_span: Span, }, - Unsupported(Span), + UnsupportedOperator(Operator, Span), + UnknownOperator(String, Span), + ExternalNotSupported(Span), InternalError(String), VariableNotFound(Span), CantConvert(String, Span), diff --git a/crates/nu-protocol/src/signature.rs b/crates/nu-protocol/src/signature.rs index 27a8577bdc..2c12efd00b 100644 --- a/crates/nu-protocol/src/signature.rs +++ b/crates/nu-protocol/src/signature.rs @@ -350,7 +350,7 @@ impl Command for BlockCommand { } fn signature(&self) -> Signature { - self.signature.clone() + panic!("Internal error: can't get signature with 'signature', use block_id"); } fn usage(&self) -> &str { @@ -366,7 +366,7 @@ impl Command for BlockCommand { panic!("Internal error: can't run custom command with 'run', use block_id"); } - fn get_custom_command(&self) -> Option { + fn get_block_id(&self) -> Option { Some(self.block_id) } } diff --git a/crates/nu-protocol/src/ty.rs b/crates/nu-protocol/src/ty.rs index b0e291b414..e944682159 100644 --- a/crates/nu-protocol/src/ty.rs +++ b/crates/nu-protocol/src/ty.rs @@ -19,6 +19,7 @@ pub enum Type { RowStream, ValueStream, Unknown, + Error, } impl Display for Type { @@ -41,6 +42,7 @@ impl Display for Type { Type::ValueStream => write!(f, "value stream"), Type::RowStream => write!(f, "row stream"), Type::Unknown => write!(f, "unknown"), + Type::Error => write!(f, "error"), } } } diff --git a/crates/nu-protocol/src/value.rs b/crates/nu-protocol/src/value.rs index d834bd3209..74a78aff63 100644 --- a/crates/nu-protocol/src/value.rs +++ b/crates/nu-protocol/src/value.rs @@ -158,6 +158,9 @@ pub enum Value { Nothing { span: Span, }, + Error { + err: ShellError, + }, } impl Value { @@ -181,6 +184,7 @@ impl Value { Value::RowStream { span, .. } => *span, Value::ValueStream { span, .. } => *span, Value::Nothing { span, .. } => *span, + Value::Error { .. } => Span::unknown(), } } @@ -197,6 +201,7 @@ impl Value { Value::Table { span, .. } => *span = new_span, Value::Block { span, .. } => *span = new_span, Value::Nothing { span, .. } => *span = new_span, + Value::Error { .. } => {} } self @@ -215,6 +220,7 @@ impl Value { Value::Block { .. } => Type::Block, Value::ValueStream { .. } => Type::ValueStream, Value::RowStream { .. } => Type::RowStream, + Value::Error { .. } => Type::Error, } } @@ -264,6 +270,7 @@ impl Value { } => stream.into_string(headers), Value::Block { val, .. } => format!("", val), Value::Nothing { .. } => String::new(), + Value::Error { err } => format!("{:?}", err), } }