it does calls!!!

This commit is contained in:
Devyn Cairns 2024-06-18 22:16:21 -07:00
parent b0d24ea434
commit 3c4877b059
2 changed files with 93 additions and 15 deletions

View File

@ -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<D: DebugContext>(
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::<D>(
&mut EvalContext {
engine_state,
stack,
args_base,
registers: &mut registers[..],
},
&block_span,
@ -52,6 +54,8 @@ pub fn eval_ir_block<D: DebugContext>(
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<Value, ShellError> {
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<D: DebugContext>(
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<PipelineData, ShellError> {
// 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
}

View File

@ -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,