diff --git a/crates/nu-engine/src/eval_ir.rs b/crates/nu-engine/src/eval_ir.rs index de50df10eb..ece243543f 100644 --- a/crates/nu-engine/src/eval_ir.rs +++ b/crates/nu-engine/src/eval_ir.rs @@ -1,9 +1,9 @@ use nu_protocol::{ ast::{Bits, Block, Boolean, CellPath, Comparison, Math, Operator}, debugger::DebugContext, - engine::{Closure, EngineState, Stack}, - ir::{Instruction, IrBlock, Literal}, - PipelineData, RegId, ShellError, Span, Value, + engine::{Argument, Closure, EngineState, Stack}, + ir::{Call, Instruction, IrBlock, Literal}, + DeclId, PipelineData, RegId, ShellError, Span, Value, }; /// Evaluate the compiled representation of a [`Block`]. @@ -18,12 +18,14 @@ pub fn eval_ir_block( let block_span = block.span; + let args_base = stack.argument_stack.get_base(); let mut registers = stack.register_buf_cache.acquire(ir_block.register_count); let result = eval_ir_block_impl::( &mut EvalContext { engine_state, stack, + args_base, registers: &mut registers[..], }, &block_span, @@ -52,6 +54,8 @@ pub fn eval_ir_block( struct EvalContext<'a> { engine_state: &'a EngineState, stack: &'a mut Stack, + /// Base index on the argument stack to reset to after a call + args_base: usize, registers: &'a mut [PipelineData], } @@ -66,6 +70,13 @@ impl<'a> EvalContext<'a> { fn take_reg(&mut self, reg_id: RegId) -> PipelineData { self.put_reg(reg_id, PipelineData::Empty) } + + /// Take and implicitly collect a register to a value + fn collect_reg(&mut self, reg_id: RegId) -> Result { + let pipeline_data = self.take_reg(reg_id); + let span = pipeline_data.span().unwrap_or(Span::unknown()); + pipeline_data.into_value(span) + } } /// Eval an IR block on the provided slice of registers. @@ -86,7 +97,7 @@ fn eval_ir_block_impl( let instruction = &ir_block.instructions[pc]; let span = &ir_block.spans[pc]; log::trace!("{pc:-4}: {}", instruction.display(ctx.engine_state)); - match do_instruction(ctx, instruction, span)? { + match eval_instruction(ctx, instruction, span)? { InstructionResult::Continue => { pc += 1; } @@ -121,7 +132,7 @@ enum InstructionResult { } /// Perform an instruction -fn do_instruction( +fn eval_instruction( ctx: &mut EvalContext<'_>, instruction: &Instruction, span: &Span, @@ -159,13 +170,50 @@ fn do_instruction( Instruction::LoadEnv { dst, key } => todo!(), Instruction::LoadEnvOpt { dst, key } => todo!(), Instruction::StoreEnv { key, src } => todo!(), - Instruction::PushPositional { src } => todo!(), - Instruction::AppendRest { src } => todo!(), - Instruction::PushFlag { name } => todo!(), - Instruction::PushNamed { name, src } => todo!(), - Instruction::RedirectOut { mode } => todo!(), - Instruction::RedirectErr { mode } => todo!(), - Instruction::Call { decl_id, src_dst } => todo!(), + Instruction::PushPositional { src } => { + let val = ctx.collect_reg(*src)?; + ctx.stack + .argument_stack + .push(Argument::Positional { span: *span, val }); + Ok(InstructionResult::Continue) + } + Instruction::AppendRest { src } => { + let vals = ctx.collect_reg(*src)?; + ctx.stack + .argument_stack + .push(Argument::Spread { span: *span, vals }); + Ok(InstructionResult::Continue) + } + Instruction::PushFlag { name } => { + ctx.stack.argument_stack.push(Argument::Flag { + name: name.clone(), + span: *span, + }); + Ok(InstructionResult::Continue) + } + Instruction::PushNamed { name, src } => { + let val = ctx.collect_reg(*src)?; + ctx.stack.argument_stack.push(Argument::Named { + name: name.clone(), + span: *span, + val, + }); + Ok(InstructionResult::Continue) + } + Instruction::RedirectOut { mode } => { + log::warn!("TODO: RedirectOut"); + Ok(InstructionResult::Continue) + } + Instruction::RedirectErr { mode } => { + log::warn!("TODO: RedirectErr"); + Ok(InstructionResult::Continue) + } + Instruction::Call { decl_id, src_dst } => { + let input = ctx.take_reg(*src_dst); + let result = eval_call(ctx, *decl_id, *span, input)?; + ctx.put_reg(*src_dst, result); + Ok(InstructionResult::Continue) + } Instruction::BinaryOp { lhs_dst, op, rhs } => binary_op(ctx, *lhs_dst, op, *rhs, *span), Instruction::FollowCellPath { src_dst, path } => todo!(), Instruction::CloneCellPath { dst, src, path } => todo!(), @@ -309,3 +357,33 @@ fn binary_op( Ok(InstructionResult::Continue) } + +/// Evaluate a call +fn eval_call( + ctx: &mut EvalContext<'_>, + decl_id: DeclId, + head: Span, + input: PipelineData, +) -> Result { + // TODO: handle block eval + let args_len = ctx.stack.argument_stack.get_len(ctx.args_base); + let decl = ctx.engine_state.get_decl(decl_id); + // should this be precalculated? ideally we just use the call builder... + let span = ctx + .stack + .argument_stack + .get_args(ctx.args_base, args_len) + .into_iter() + .fold(head, |span, arg| span.append(arg.span())); + let call = Call { + decl_id, + head, + span, + args_base: ctx.args_base, + args_len, + }; + let result = decl.run(ctx.engine_state, ctx.stack, &(&call).into(), input); + // Important that this runs: + ctx.stack.argument_stack.leave_frame(ctx.args_base); + result +} diff --git a/crates/nu-protocol/src/ir/call.rs b/crates/nu-protocol/src/ir/call.rs index d4cee88f1f..52b73b45f9 100644 --- a/crates/nu-protocol/src/ir/call.rs +++ b/crates/nu-protocol/src/ir/call.rs @@ -1,13 +1,13 @@ use crate::{ engine::{self, Argument, Stack}, - ShellError, Span, Spanned, Value, + DeclId, ShellError, Span, Spanned, Value, }; /// Contains the information for a call being made to a declared command. #[derive(Debug, Clone)] pub struct Call { /// The declaration ID of the command to be invoked. - pub decl_id: usize, + pub decl_id: DeclId, /// The span encompassing the command name, before the arguments. pub head: Span, /// The span encompassing the command name and all arguments. @@ -23,7 +23,7 @@ pub struct Call { impl Call { /// Build a new call with arguments. - pub fn build(decl_id: usize, head: Span) -> CallBuilder { + pub fn build(decl_id: DeclId, head: Span) -> CallBuilder { CallBuilder { inner: Call { decl_id,