diff --git a/crates/nu-command/src/benchmark.rs b/crates/nu-command/src/benchmark.rs index f494da794..5bd5a6afc 100644 --- a/crates/nu-command/src/benchmark.rs +++ b/crates/nu-command/src/benchmark.rs @@ -34,7 +34,7 @@ impl Command for Benchmark { let state = context.enter_scope(); let start_time = Instant::now(); - eval_block(&state, block)?; + eval_block(&state, block, Value::nothing())?; let end_time = Instant::now(); println!("{} ms", (end_time - start_time).as_millis()); Ok(Value::Nothing { diff --git a/crates/nu-command/src/default_context.rs b/crates/nu-command/src/default_context.rs index f1bb536ff..4b4f38c5d 100644 --- a/crates/nu-command/src/default_context.rs +++ b/crates/nu-command/src/default_context.rs @@ -5,7 +5,7 @@ use nu_protocol::{ Signature, SyntaxShape, }; -use crate::{Alias, Benchmark, BuildString, Def, For, If, Let, LetEnv}; +use crate::{Alias, Benchmark, BuildString, Def, For, If, Length, Let, LetEnv}; pub fn create_default_context() -> Rc> { let engine_state = Rc::new(RefCell::new(EngineState::new())); @@ -33,6 +33,8 @@ pub fn create_default_context() -> Rc> { working_set.add_decl(Box::new(Benchmark)); + working_set.add_decl(Box::new(Length)); + let sig = Signature::build("exit"); working_set.add_decl(sig.predeclare()); let sig = Signature::build("vars"); diff --git a/crates/nu-command/src/for_.rs b/crates/nu-command/src/for_.rs index 8eaf393e7..3cede35e8 100644 --- a/crates/nu-command/src/for_.rs +++ b/crates/nu-command/src/for_.rs @@ -33,7 +33,7 @@ impl Command for For { &self, context: &EvaluationContext, call: &Call, - _input: Value, + input: Value, ) -> Result { let var_id = call.positional[0] .as_var() @@ -62,7 +62,7 @@ impl Command for For { break; } else { state.add_var(var_id, x.clone()); - eval_block(&state, block)?; + eval_block(&state, block, input.clone())?; } if let Value::Int { ref mut val, .. } = x { *val += 1 diff --git a/crates/nu-command/src/if_.rs b/crates/nu-command/src/if_.rs index 4124dbc7b..bb785c4c2 100644 --- a/crates/nu-command/src/if_.rs +++ b/crates/nu-command/src/if_.rs @@ -29,7 +29,7 @@ impl Command for If { &self, context: &EvaluationContext, call: &Call, - _input: Value, + input: Value, ) -> Result { let cond = &call.positional[0]; let then_block = call.positional[1] @@ -44,13 +44,13 @@ impl Command for If { if val { let block = engine_state.get_block(then_block); let state = context.enter_scope(); - eval_block(&state, block) + eval_block(&state, block, input) } else if let Some(else_case) = else_case { if let Some(else_expr) = else_case.as_keyword() { if let Some(block_id) = else_expr.as_block() { let block = engine_state.get_block(block_id); let state = context.enter_scope(); - eval_block(&state, block) + eval_block(&state, block, input) } else { eval_expression(context, else_expr) } diff --git a/crates/nu-command/src/length.rs b/crates/nu-command/src/length.rs index e69de29bb..79a92c778 100644 --- a/crates/nu-command/src/length.rs +++ b/crates/nu-command/src/length.rs @@ -0,0 +1,49 @@ +use nu_protocol::ast::Call; +use nu_protocol::engine::{Command, EvaluationContext}; +use nu_protocol::{Signature, Value}; + +pub struct Length; + +impl Command for Length { + fn name(&self) -> &str { + "length" + } + + fn usage(&self) -> &str { + "Count the number of elements in the input." + } + + fn signature(&self) -> nu_protocol::Signature { + Signature::build("length") + } + + fn run( + &self, + _context: &EvaluationContext, + call: &Call, + input: Value, + ) -> Result { + match input { + Value::List { val, .. } => { + let length = val.count(); + + Ok(Value::Int { + val: length as i64, + span: call.head, + }) + } + Value::Table { val, .. } => { + let length = val.count(); + + Ok(Value::Int { + val: length as i64, + span: call.head, + }) + } + _ => Ok(Value::Int { + val: 1, + span: call.head, + }), + } + } +} diff --git a/crates/nu-command/src/lib.rs b/crates/nu-command/src/lib.rs index ab4d11b5d..1d26da00f 100644 --- a/crates/nu-command/src/lib.rs +++ b/crates/nu-command/src/lib.rs @@ -5,6 +5,7 @@ mod def; mod default_context; mod for_; mod if_; +mod length; mod let_; mod let_env; @@ -15,5 +16,6 @@ pub use def::Def; pub use default_context::create_default_context; pub use for_::For; pub use if_::If; +pub use length::Length; pub use let_::Let; pub use let_env::LetEnv; diff --git a/crates/nu-engine/src/eval.rs b/crates/nu-engine/src/eval.rs index 662bb0f05..0a97143c2 100644 --- a/crates/nu-engine/src/eval.rs +++ b/crates/nu-engine/src/eval.rs @@ -1,6 +1,6 @@ use nu_protocol::ast::{Block, Call, Expr, Expression, Operator, Statement}; use nu_protocol::engine::EvaluationContext; -use nu_protocol::{IntoRowStream, IntoValueStream, ShellError, Span, Value}; +use nu_protocol::{IntoRowStream, IntoValueStream, ShellError, Value}; pub fn eval_operator(op: &Expression) -> Result { match op { @@ -12,7 +12,7 @@ pub fn eval_operator(op: &Expression) -> Result { } } -fn eval_call(context: &EvaluationContext, call: &Call) -> Result { +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() { @@ -32,15 +32,9 @@ fn eval_call(context: &EvaluationContext, call: &Call) -> Result context .get_var(*var_id) .map_err(move |_| ShellError::VariableNotFound(expr.span)), - Expr::Call(call) => eval_call(context, call), + Expr::Call(_) => panic!("Internal error: calls should be handled by eval_block"), Expr::ExternalCall(_, _) => Err(ShellError::Unsupported(expr.span)), Expr::Operator(_) => Ok(Value::Nothing { span: expr.span }), Expr::BinaryOp(lhs, op, rhs) => { @@ -93,7 +87,7 @@ pub fn eval_expression( let block = engine_state.get_block(*block_id); let state = context.enter_scope(); - eval_block(&state, block) + eval_block(&state, block, Value::nothing()) } Expr::Block(block_id) => Ok(Value::Block { val: *block_id, @@ -139,16 +133,29 @@ pub fn eval_expression( } } -pub fn eval_block(state: &EvaluationContext, block: &Block) -> Result { - let mut last = Ok(Value::Nothing { - span: Span { start: 0, end: 0 }, - }); - +pub fn eval_block( + context: &EvaluationContext, + block: &Block, + mut input: Value, +) -> Result { for stmt in &block.stmts { - if let Statement::Expression(expression) = stmt { - last = Ok(eval_expression(state, expression)?); + if let Statement::Pipeline(pipeline) = stmt { + for elem in &pipeline.expressions { + match elem { + Expression { + expr: Expr::Call(call), + .. + } => { + input = eval_call(context, call, input)?; + } + + elem => { + input = eval_expression(context, elem)?; + } + } + } } } - last + Ok(input) } diff --git a/crates/nu-parser/src/flatten.rs b/crates/nu-parser/src/flatten.rs index a2b490bf8..d2a55e9af 100644 --- a/crates/nu-parser/src/flatten.rs +++ b/crates/nu-parser/src/flatten.rs @@ -29,7 +29,6 @@ pub fn flatten_statement( stmt: &Statement, ) -> Vec<(Span, FlatShape)> { match stmt { - Statement::Expression(expr) => flatten_expression(working_set, expr), Statement::Pipeline(pipeline) => flatten_pipeline(working_set, pipeline), _ => vec![], } diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs index 60a3dbb0e..0c1810c22 100644 --- a/crates/nu-parser/src/parser.rs +++ b/crates/nu-parser/src/parser.rs @@ -2066,30 +2066,30 @@ pub fn parse_def( }); ( - Statement::Expression(Expression { + Statement::Pipeline(Pipeline::from_vec(vec![Expression { expr: Expr::Call(call), span: span(spans), ty: Type::Unknown, - }), + }])), error, ) } _ => ( - Statement::Expression(Expression { + Statement::Pipeline(Pipeline::from_vec(vec![Expression { expr: Expr::Garbage, span: span(spans), ty: Type::Unknown, - }), + }])), error, ), } } else { ( - Statement::Expression(Expression { + Statement::Pipeline(Pipeline::from_vec(vec![Expression { expr: Expr::Garbage, span: span(spans), ty: Type::Unknown, - }), + }])), Some(ParseError::UnknownState( "internal error: definition unparseable".into(), span(spans), @@ -2130,22 +2130,22 @@ pub fn parse_alias( } return ( - Statement::Expression(Expression { + Statement::Pipeline(Pipeline::from_vec(vec![Expression { expr: Expr::Call(call), span: call_span, ty: Type::Unknown, - }), + }])), None, ); } } ( - Statement::Expression(Expression { + Statement::Pipeline(Pipeline::from_vec(vec![Expression { expr: Expr::Garbage, span: span(spans), ty: Type::Unknown, - }), + }])), Some(ParseError::UnknownState( "internal error: let statement unparseable".into(), span(spans), @@ -2175,21 +2175,21 @@ pub fn parse_let( } return ( - Statement::Expression(Expression { + Statement::Pipeline(Pipeline::from_vec(vec![Expression { expr: Expr::Call(call), span: call_span, ty: Type::Unknown, - }), + }])), err, ); } } ( - Statement::Expression(Expression { + Statement::Pipeline(Pipeline::from_vec(vec![Expression { expr: Expr::Garbage, span: span(spans), ty: Type::Unknown, - }), + }])), Some(ParseError::UnknownState( "internal error: let statement unparseable".into(), span(spans), @@ -2210,7 +2210,7 @@ pub fn parse_statement( (stmt, None) } else { let (expr, err) = parse_expression(working_set, spans); - (Statement::Expression(expr), err) + (Statement::Pipeline(Pipeline::from_vec(vec![expr])), err) } } diff --git a/crates/nu-parser/tests/test_parser.rs b/crates/nu-parser/tests/test_parser.rs index 959925d96..0ffa726de 100644 --- a/crates/nu-parser/tests/test_parser.rs +++ b/crates/nu-parser/tests/test_parser.rs @@ -1,7 +1,7 @@ use nu_parser::ParseError; use nu_parser::*; use nu_protocol::{ - ast::{Expr, Expression, Statement}, + ast::{Expr, Expression, Pipeline, Statement}, engine::{EngineState, StateWorkingSet}, Signature, SyntaxShape, }; @@ -15,13 +15,21 @@ pub fn parse_int() { assert!(err.is_none()); assert!(block.len() == 1); - assert!(matches!( - block[0], - Statement::Expression(Expression { - expr: Expr::Int(3), - .. - }) - )); + match &block[0] { + Statement::Pipeline(Pipeline { + expressions: expressions, + }) => { + assert!(expressions.len() == 1); + assert!(matches!( + expressions[0], + Expression { + expr: Expr::Int(3), + .. + } + )) + } + _ => panic!("No match"), + } } #[test] @@ -38,11 +46,16 @@ pub fn parse_call() { assert!(block.len() == 1); match &block[0] { - Statement::Expression(Expression { - expr: Expr::Call(call), - .. - }) => { - assert_eq!(call.decl_id, 0); + Statement::Pipeline(Pipeline { expressions }) => { + assert_eq!(expressions.len(), 1); + + if let Expression { + expr: Expr::Call(call), + .. + } = &expressions[0] + { + assert_eq!(call.decl_id, 0); + } } _ => panic!("not a call"), } diff --git a/crates/nu-protocol/src/ast/pipeline.rs b/crates/nu-protocol/src/ast/pipeline.rs index fd4425290..1f5652fa4 100644 --- a/crates/nu-protocol/src/ast/pipeline.rs +++ b/crates/nu-protocol/src/ast/pipeline.rs @@ -17,4 +17,8 @@ impl Pipeline { expressions: vec![], } } + + pub fn from_vec(expressions: Vec) -> Pipeline { + Self { expressions } + } } diff --git a/crates/nu-protocol/src/ast/statement.rs b/crates/nu-protocol/src/ast/statement.rs index 24703a8cf..5fa7faef1 100644 --- a/crates/nu-protocol/src/ast/statement.rs +++ b/crates/nu-protocol/src/ast/statement.rs @@ -1,9 +1,8 @@ -use super::{Expression, Pipeline}; +use super::Pipeline; use crate::DeclId; #[derive(Debug, Clone)] pub enum Statement { Declaration(DeclId), Pipeline(Pipeline), - Expression(Expression), } diff --git a/crates/nu-protocol/src/value.rs b/crates/nu-protocol/src/value.rs index bd328eb71..3f3ce34e2 100644 --- a/crates/nu-protocol/src/value.rs +++ b/crates/nu-protocol/src/value.rs @@ -197,6 +197,12 @@ impl Value { Value::Nothing { .. } => String::new(), } } + + pub fn nothing() -> Value { + Value::Nothing { + span: Span::unknown(), + } + } } impl PartialEq for Value { diff --git a/src/main.rs b/src/main.rs index f1d595f89..0c649d274 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,10 @@ use nu_cli::{report_parsing_error, report_shell_error, NuHighlighter}; use nu_command::create_default_context; use nu_engine::eval_block; use nu_parser::parse_file; -use nu_protocol::engine::{EngineState, EvaluationContext, StateWorkingSet}; +use nu_protocol::{ + engine::{EngineState, EvaluationContext, StateWorkingSet}, + Value, +}; #[cfg(test)] mod tests; @@ -32,7 +35,7 @@ fn main() -> std::io::Result<()> { stack: nu_protocol::engine::Stack::new(), }; - match eval_block(&state, &block) { + match eval_block(&state, &block, Value::nothing()) { Ok(value) => { println!("{}", value.into_string()); } @@ -106,7 +109,7 @@ fn main() -> std::io::Result<()> { stack: stack.clone(), }; - match eval_block(&state, &block) { + match eval_block(&state, &block, Value::nothing()) { Ok(value) => { println!("{}", value.into_string()); }