nu_protocol/engine/
call.rs

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