Fix up calls and pipelines

This commit is contained in:
JT 2021-09-03 14:15:01 +12:00
parent 7c8504ea24
commit df63490266
14 changed files with 145 additions and 61 deletions

View File

@ -34,7 +34,7 @@ impl Command for Benchmark {
let state = context.enter_scope(); let state = context.enter_scope();
let start_time = Instant::now(); let start_time = Instant::now();
eval_block(&state, block)?; eval_block(&state, block, Value::nothing())?;
let end_time = Instant::now(); let end_time = Instant::now();
println!("{} ms", (end_time - start_time).as_millis()); println!("{} ms", (end_time - start_time).as_millis());
Ok(Value::Nothing { Ok(Value::Nothing {

View File

@ -5,7 +5,7 @@ use nu_protocol::{
Signature, SyntaxShape, 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<RefCell<EngineState>> { pub fn create_default_context() -> Rc<RefCell<EngineState>> {
let engine_state = Rc::new(RefCell::new(EngineState::new())); let engine_state = Rc::new(RefCell::new(EngineState::new()));
@ -33,6 +33,8 @@ pub fn create_default_context() -> Rc<RefCell<EngineState>> {
working_set.add_decl(Box::new(Benchmark)); working_set.add_decl(Box::new(Benchmark));
working_set.add_decl(Box::new(Length));
let sig = Signature::build("exit"); let sig = Signature::build("exit");
working_set.add_decl(sig.predeclare()); working_set.add_decl(sig.predeclare());
let sig = Signature::build("vars"); let sig = Signature::build("vars");

View File

@ -33,7 +33,7 @@ impl Command for For {
&self, &self,
context: &EvaluationContext, context: &EvaluationContext,
call: &Call, call: &Call,
_input: Value, input: Value,
) -> Result<nu_protocol::Value, nu_protocol::ShellError> { ) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
let var_id = call.positional[0] let var_id = call.positional[0]
.as_var() .as_var()
@ -62,7 +62,7 @@ impl Command for For {
break; break;
} else { } else {
state.add_var(var_id, x.clone()); 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 { if let Value::Int { ref mut val, .. } = x {
*val += 1 *val += 1

View File

@ -29,7 +29,7 @@ impl Command for If {
&self, &self,
context: &EvaluationContext, context: &EvaluationContext,
call: &Call, call: &Call,
_input: Value, input: Value,
) -> Result<nu_protocol::Value, nu_protocol::ShellError> { ) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
let cond = &call.positional[0]; let cond = &call.positional[0];
let then_block = call.positional[1] let then_block = call.positional[1]
@ -44,13 +44,13 @@ impl Command for If {
if val { if val {
let block = engine_state.get_block(then_block); let block = engine_state.get_block(then_block);
let state = context.enter_scope(); let state = context.enter_scope();
eval_block(&state, block) eval_block(&state, block, input)
} else if let Some(else_case) = else_case { } else if let Some(else_case) = else_case {
if let Some(else_expr) = else_case.as_keyword() { if let Some(else_expr) = else_case.as_keyword() {
if let Some(block_id) = else_expr.as_block() { if let Some(block_id) = else_expr.as_block() {
let block = engine_state.get_block(block_id); let block = engine_state.get_block(block_id);
let state = context.enter_scope(); let state = context.enter_scope();
eval_block(&state, block) eval_block(&state, block, input)
} else { } else {
eval_expression(context, else_expr) eval_expression(context, else_expr)
} }

View File

@ -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<nu_protocol::Value, nu_protocol::ShellError> {
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,
}),
}
}
}

View File

@ -5,6 +5,7 @@ mod def;
mod default_context; mod default_context;
mod for_; mod for_;
mod if_; mod if_;
mod length;
mod let_; mod let_;
mod let_env; mod let_env;
@ -15,5 +16,6 @@ pub use def::Def;
pub use default_context::create_default_context; pub use default_context::create_default_context;
pub use for_::For; pub use for_::For;
pub use if_::If; pub use if_::If;
pub use length::Length;
pub use let_::Let; pub use let_::Let;
pub use let_env::LetEnv; pub use let_env::LetEnv;

View File

@ -1,6 +1,6 @@
use nu_protocol::ast::{Block, Call, Expr, Expression, Operator, Statement}; use nu_protocol::ast::{Block, Call, Expr, Expression, Operator, Statement};
use nu_protocol::engine::EvaluationContext; 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<Operator, ShellError> { pub fn eval_operator(op: &Expression) -> Result<Operator, ShellError> {
match op { match op {
@ -12,7 +12,7 @@ pub fn eval_operator(op: &Expression) -> Result<Operator, ShellError> {
} }
} }
fn eval_call(context: &EvaluationContext, call: &Call) -> Result<Value, ShellError> { fn eval_call(context: &EvaluationContext, call: &Call, input: Value) -> Result<Value, ShellError> {
let engine_state = context.engine_state.borrow(); let engine_state = context.engine_state.borrow();
let decl = engine_state.get_decl(call.decl_id); 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_custom_command() {
@ -32,15 +32,9 @@ fn eval_call(context: &EvaluationContext, call: &Call) -> Result<Value, ShellErr
} }
let engine_state = state.engine_state.borrow(); let engine_state = state.engine_state.borrow();
let block = engine_state.get_block(block_id); let block = engine_state.get_block(block_id);
eval_block(&state, block) eval_block(&state, block, input)
} else { } else {
decl.run( decl.run(context, call, input)
context,
call,
Value::Nothing {
span: Span::unknown(),
},
)
} }
} }
@ -64,7 +58,7 @@ pub fn eval_expression(
Expr::Var(var_id) => context Expr::Var(var_id) => context
.get_var(*var_id) .get_var(*var_id)
.map_err(move |_| ShellError::VariableNotFound(expr.span)), .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::ExternalCall(_, _) => Err(ShellError::Unsupported(expr.span)),
Expr::Operator(_) => Ok(Value::Nothing { span: expr.span }), Expr::Operator(_) => Ok(Value::Nothing { span: expr.span }),
Expr::BinaryOp(lhs, op, rhs) => { Expr::BinaryOp(lhs, op, rhs) => {
@ -93,7 +87,7 @@ pub fn eval_expression(
let block = engine_state.get_block(*block_id); let block = engine_state.get_block(*block_id);
let state = context.enter_scope(); let state = context.enter_scope();
eval_block(&state, block) eval_block(&state, block, Value::nothing())
} }
Expr::Block(block_id) => Ok(Value::Block { Expr::Block(block_id) => Ok(Value::Block {
val: *block_id, val: *block_id,
@ -139,16 +133,29 @@ pub fn eval_expression(
} }
} }
pub fn eval_block(state: &EvaluationContext, block: &Block) -> Result<Value, ShellError> { pub fn eval_block(
let mut last = Ok(Value::Nothing { context: &EvaluationContext,
span: Span { start: 0, end: 0 }, block: &Block,
}); mut input: Value,
) -> Result<Value, ShellError> {
for stmt in &block.stmts { for stmt in &block.stmts {
if let Statement::Expression(expression) = stmt { if let Statement::Pipeline(pipeline) = stmt {
last = Ok(eval_expression(state, expression)?); 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)
} }

View File

@ -29,7 +29,6 @@ pub fn flatten_statement(
stmt: &Statement, stmt: &Statement,
) -> Vec<(Span, FlatShape)> { ) -> Vec<(Span, FlatShape)> {
match stmt { match stmt {
Statement::Expression(expr) => flatten_expression(working_set, expr),
Statement::Pipeline(pipeline) => flatten_pipeline(working_set, pipeline), Statement::Pipeline(pipeline) => flatten_pipeline(working_set, pipeline),
_ => vec![], _ => vec![],
} }

View File

@ -2066,30 +2066,30 @@ pub fn parse_def(
}); });
( (
Statement::Expression(Expression { Statement::Pipeline(Pipeline::from_vec(vec![Expression {
expr: Expr::Call(call), expr: Expr::Call(call),
span: span(spans), span: span(spans),
ty: Type::Unknown, ty: Type::Unknown,
}), }])),
error, error,
) )
} }
_ => ( _ => (
Statement::Expression(Expression { Statement::Pipeline(Pipeline::from_vec(vec![Expression {
expr: Expr::Garbage, expr: Expr::Garbage,
span: span(spans), span: span(spans),
ty: Type::Unknown, ty: Type::Unknown,
}), }])),
error, error,
), ),
} }
} else { } else {
( (
Statement::Expression(Expression { Statement::Pipeline(Pipeline::from_vec(vec![Expression {
expr: Expr::Garbage, expr: Expr::Garbage,
span: span(spans), span: span(spans),
ty: Type::Unknown, ty: Type::Unknown,
}), }])),
Some(ParseError::UnknownState( Some(ParseError::UnknownState(
"internal error: definition unparseable".into(), "internal error: definition unparseable".into(),
span(spans), span(spans),
@ -2130,22 +2130,22 @@ pub fn parse_alias(
} }
return ( return (
Statement::Expression(Expression { Statement::Pipeline(Pipeline::from_vec(vec![Expression {
expr: Expr::Call(call), expr: Expr::Call(call),
span: call_span, span: call_span,
ty: Type::Unknown, ty: Type::Unknown,
}), }])),
None, None,
); );
} }
} }
( (
Statement::Expression(Expression { Statement::Pipeline(Pipeline::from_vec(vec![Expression {
expr: Expr::Garbage, expr: Expr::Garbage,
span: span(spans), span: span(spans),
ty: Type::Unknown, ty: Type::Unknown,
}), }])),
Some(ParseError::UnknownState( Some(ParseError::UnknownState(
"internal error: let statement unparseable".into(), "internal error: let statement unparseable".into(),
span(spans), span(spans),
@ -2175,21 +2175,21 @@ pub fn parse_let(
} }
return ( return (
Statement::Expression(Expression { Statement::Pipeline(Pipeline::from_vec(vec![Expression {
expr: Expr::Call(call), expr: Expr::Call(call),
span: call_span, span: call_span,
ty: Type::Unknown, ty: Type::Unknown,
}), }])),
err, err,
); );
} }
} }
( (
Statement::Expression(Expression { Statement::Pipeline(Pipeline::from_vec(vec![Expression {
expr: Expr::Garbage, expr: Expr::Garbage,
span: span(spans), span: span(spans),
ty: Type::Unknown, ty: Type::Unknown,
}), }])),
Some(ParseError::UnknownState( Some(ParseError::UnknownState(
"internal error: let statement unparseable".into(), "internal error: let statement unparseable".into(),
span(spans), span(spans),
@ -2210,7 +2210,7 @@ pub fn parse_statement(
(stmt, None) (stmt, None)
} else { } else {
let (expr, err) = parse_expression(working_set, spans); let (expr, err) = parse_expression(working_set, spans);
(Statement::Expression(expr), err) (Statement::Pipeline(Pipeline::from_vec(vec![expr])), err)
} }
} }

View File

@ -1,7 +1,7 @@
use nu_parser::ParseError; use nu_parser::ParseError;
use nu_parser::*; use nu_parser::*;
use nu_protocol::{ use nu_protocol::{
ast::{Expr, Expression, Statement}, ast::{Expr, Expression, Pipeline, Statement},
engine::{EngineState, StateWorkingSet}, engine::{EngineState, StateWorkingSet},
Signature, SyntaxShape, Signature, SyntaxShape,
}; };
@ -15,13 +15,21 @@ pub fn parse_int() {
assert!(err.is_none()); assert!(err.is_none());
assert!(block.len() == 1); assert!(block.len() == 1);
assert!(matches!( match &block[0] {
block[0], Statement::Pipeline(Pipeline {
Statement::Expression(Expression { expressions: expressions,
expr: Expr::Int(3), }) => {
.. assert!(expressions.len() == 1);
}) assert!(matches!(
)); expressions[0],
Expression {
expr: Expr::Int(3),
..
}
))
}
_ => panic!("No match"),
}
} }
#[test] #[test]
@ -38,11 +46,16 @@ pub fn parse_call() {
assert!(block.len() == 1); assert!(block.len() == 1);
match &block[0] { match &block[0] {
Statement::Expression(Expression { Statement::Pipeline(Pipeline { expressions }) => {
expr: Expr::Call(call), assert_eq!(expressions.len(), 1);
..
}) => { if let Expression {
assert_eq!(call.decl_id, 0); expr: Expr::Call(call),
..
} = &expressions[0]
{
assert_eq!(call.decl_id, 0);
}
} }
_ => panic!("not a call"), _ => panic!("not a call"),
} }

View File

@ -17,4 +17,8 @@ impl Pipeline {
expressions: vec![], expressions: vec![],
} }
} }
pub fn from_vec(expressions: Vec<Expression>) -> Pipeline {
Self { expressions }
}
} }

View File

@ -1,9 +1,8 @@
use super::{Expression, Pipeline}; use super::Pipeline;
use crate::DeclId; use crate::DeclId;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Statement { pub enum Statement {
Declaration(DeclId), Declaration(DeclId),
Pipeline(Pipeline), Pipeline(Pipeline),
Expression(Expression),
} }

View File

@ -197,6 +197,12 @@ impl Value {
Value::Nothing { .. } => String::new(), Value::Nothing { .. } => String::new(),
} }
} }
pub fn nothing() -> Value {
Value::Nothing {
span: Span::unknown(),
}
}
} }
impl PartialEq for Value { impl PartialEq for Value {

View File

@ -2,7 +2,10 @@ use nu_cli::{report_parsing_error, report_shell_error, NuHighlighter};
use nu_command::create_default_context; use nu_command::create_default_context;
use nu_engine::eval_block; use nu_engine::eval_block;
use nu_parser::parse_file; use nu_parser::parse_file;
use nu_protocol::engine::{EngineState, EvaluationContext, StateWorkingSet}; use nu_protocol::{
engine::{EngineState, EvaluationContext, StateWorkingSet},
Value,
};
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
@ -32,7 +35,7 @@ fn main() -> std::io::Result<()> {
stack: nu_protocol::engine::Stack::new(), stack: nu_protocol::engine::Stack::new(),
}; };
match eval_block(&state, &block) { match eval_block(&state, &block, Value::nothing()) {
Ok(value) => { Ok(value) => {
println!("{}", value.into_string()); println!("{}", value.into_string());
} }
@ -106,7 +109,7 @@ fn main() -> std::io::Result<()> {
stack: stack.clone(), stack: stack.clone(),
}; };
match eval_block(&state, &block) { match eval_block(&state, &block, Value::nothing()) {
Ok(value) => { Ok(value) => {
println!("{}", value.into_string()); println!("{}", value.into_string());
} }