nu_protocol/engine/
call.rs

1use crate::{
2    DeclId, FromValue, ShellError, Span, Value,
3    ast::{self, Expression},
4    ir,
5};
6
7use super::{EngineState, Stack, StateWorkingSet};
8
9/// This is a HACK to help [`Command`](super::Command) support both the old AST evaluator and the
10/// new IR evaluator at the same time. It should be removed once we are satisfied with the new
11/// evaluator.
12#[derive(Debug, Clone)]
13pub struct Call<'a> {
14    pub head: Span,
15    pub decl_id: DeclId,
16    pub inner: CallImpl<'a>,
17}
18
19#[derive(Debug, Clone)]
20pub enum CallImpl<'a> {
21    AstRef(&'a ast::Call),
22    AstBox(Box<ast::Call>),
23    IrRef(&'a ir::Call),
24    IrBox(Box<ir::Call>),
25}
26
27impl Call<'_> {
28    /// Returns a new AST call with the given span. This is often used by commands that need an
29    /// empty call to pass to a command. It's not easily possible to add anything to this.
30    pub fn new(span: Span) -> Self {
31        // this is using the boxed variant, which isn't so efficient... but this is only temporary
32        // anyway.
33        Call {
34            head: span,
35            decl_id: DeclId::new(0),
36            inner: CallImpl::AstBox(Box::new(ast::Call::new(span))),
37        }
38    }
39
40    /// Convert the `Call` from any lifetime into `'static`, by cloning the data within onto the
41    /// heap.
42    pub fn to_owned(&self) -> Call<'static> {
43        Call {
44            head: self.head,
45            decl_id: self.decl_id,
46            inner: self.inner.to_owned(),
47        }
48    }
49
50    /// Assert that the call is `ast::Call`, and fail with an error if it isn't.
51    ///
52    /// Provided as a stop-gap for commands that can't work with `ir::Call`, or just haven't been
53    /// implemented yet. Eventually these issues should be resolved and then this can be removed.
54    pub fn assert_ast_call(&self) -> Result<&ast::Call, ShellError> {
55        match &self.inner {
56            CallImpl::AstRef(call) => Ok(call),
57            CallImpl::AstBox(call) => Ok(call),
58            _ => Err(ShellError::NushellFailedSpanned {
59                msg: "Can't be used in IR context".into(),
60                label: "this command is not yet supported by IR evaluation".into(),
61                span: self.head,
62            }),
63        }
64    }
65
66    /// FIXME: implementation asserts `ast::Call` and proxies to that
67    pub fn has_flag_const(
68        &self,
69        working_set: &StateWorkingSet,
70        flag_name: &str,
71    ) -> Result<bool, ShellError> {
72        self.assert_ast_call()?
73            .has_flag_const(working_set, flag_name)
74    }
75
76    /// FIXME: implementation asserts `ast::Call` and proxies to that
77    pub fn get_flag_const<T: FromValue>(
78        &self,
79        working_set: &StateWorkingSet,
80        name: &str,
81    ) -> Result<Option<T>, ShellError> {
82        self.assert_ast_call()?.get_flag_const(working_set, name)
83    }
84
85    /// FIXME: implementation asserts `ast::Call` and proxies to that
86    pub fn req_const<T: FromValue>(
87        &self,
88        working_set: &StateWorkingSet,
89        pos: usize,
90    ) -> Result<T, ShellError> {
91        self.assert_ast_call()?.req_const(working_set, pos)
92    }
93
94    /// FIXME: implementation asserts `ast::Call` and proxies to that
95    pub fn rest_const<T: FromValue>(
96        &self,
97        working_set: &StateWorkingSet,
98        starting_pos: usize,
99    ) -> Result<Vec<T>, ShellError> {
100        self.assert_ast_call()?
101            .rest_const(working_set, starting_pos)
102    }
103
104    /// Returns a span covering the call's arguments.
105    pub fn arguments_span(&self) -> Span {
106        match &self.inner {
107            CallImpl::AstRef(call) => call.arguments_span(),
108            CallImpl::AstBox(call) => call.arguments_span(),
109            CallImpl::IrRef(call) => call.arguments_span(),
110            CallImpl::IrBox(call) => call.arguments_span(),
111        }
112    }
113
114    /// Returns a span covering the whole call.
115    pub fn span(&self) -> Span {
116        match &self.inner {
117            CallImpl::AstRef(call) => call.span(),
118            CallImpl::AstBox(call) => call.span(),
119            CallImpl::IrRef(call) => call.span(),
120            CallImpl::IrBox(call) => call.span(),
121        }
122    }
123
124    /// Get a parser info argument by name.
125    pub fn get_parser_info<'a>(&'a self, stack: &'a Stack, name: &str) -> Option<&'a Expression> {
126        match &self.inner {
127            CallImpl::AstRef(call) => call.get_parser_info(name),
128            CallImpl::AstBox(call) => call.get_parser_info(name),
129            CallImpl::IrRef(call) => call.get_parser_info(stack, name),
130            CallImpl::IrBox(call) => call.get_parser_info(stack, name),
131        }
132    }
133
134    /// Evaluator-agnostic implementation of `rest_iter_flattened()`. Evaluates or gets all of the
135    /// positional and spread arguments, flattens spreads, and then returns one list of values.
136    pub fn rest_iter_flattened(
137        &self,
138        engine_state: &EngineState,
139        stack: &mut Stack,
140        eval_expression: fn(
141            &EngineState,
142            &mut Stack,
143            &ast::Expression,
144        ) -> Result<Value, ShellError>,
145        starting_pos: usize,
146    ) -> Result<Vec<Value>, ShellError> {
147        fn by_ast(
148            call: &ast::Call,
149            engine_state: &EngineState,
150            stack: &mut Stack,
151            eval_expression: fn(
152                &EngineState,
153                &mut Stack,
154                &ast::Expression,
155            ) -> Result<Value, ShellError>,
156            starting_pos: usize,
157        ) -> Result<Vec<Value>, ShellError> {
158            call.rest_iter_flattened(starting_pos, |expr| {
159                eval_expression(engine_state, stack, expr)
160            })
161        }
162
163        fn by_ir(
164            call: &ir::Call,
165            stack: &Stack,
166            starting_pos: usize,
167        ) -> Result<Vec<Value>, ShellError> {
168            call.rest_iter_flattened(stack, starting_pos)
169        }
170
171        match &self.inner {
172            CallImpl::AstRef(call) => {
173                by_ast(call, engine_state, stack, eval_expression, starting_pos)
174            }
175            CallImpl::AstBox(call) => {
176                by_ast(call, engine_state, stack, eval_expression, starting_pos)
177            }
178            CallImpl::IrRef(call) => by_ir(call, stack, starting_pos),
179            CallImpl::IrBox(call) => by_ir(call, stack, starting_pos),
180        }
181    }
182
183    /// Get the original AST expression for a positional argument. Does not usually work for IR
184    /// unless the decl specified `requires_ast_for_arguments()`
185    pub fn positional_nth<'a>(&'a self, stack: &'a Stack, index: usize) -> Option<&'a Expression> {
186        match &self.inner {
187            CallImpl::AstRef(call) => call.positional_nth(index),
188            CallImpl::AstBox(call) => call.positional_nth(index),
189            CallImpl::IrRef(call) => call.positional_ast(stack, index).map(|arc| arc.as_ref()),
190            CallImpl::IrBox(call) => call.positional_ast(stack, index).map(|arc| arc.as_ref()),
191        }
192    }
193}
194
195impl CallImpl<'_> {
196    pub fn to_owned(&self) -> CallImpl<'static> {
197        match self {
198            CallImpl::AstRef(call) => CallImpl::AstBox(Box::new((*call).clone())),
199            CallImpl::AstBox(call) => CallImpl::AstBox(call.clone()),
200            CallImpl::IrRef(call) => CallImpl::IrBox(Box::new((*call).clone())),
201            CallImpl::IrBox(call) => CallImpl::IrBox(call.clone()),
202        }
203    }
204}
205
206impl<'a> From<&'a ast::Call> for Call<'a> {
207    fn from(call: &'a ast::Call) -> Self {
208        Call {
209            head: call.head,
210            decl_id: call.decl_id,
211            inner: CallImpl::AstRef(call),
212        }
213    }
214}
215
216impl<'a> From<&'a ir::Call> for Call<'a> {
217    fn from(call: &'a ir::Call) -> Self {
218        Call {
219            head: call.head,
220            decl_id: call.decl_id,
221            inner: CallImpl::IrRef(call),
222        }
223    }
224}