support parser info
This commit is contained in:
parent
67bd88535c
commit
dca2798d4d
|
@ -61,14 +61,13 @@ impl Command for OverlayUse {
|
|||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let call = call.assert_ast_call()?; // FIXME
|
||||
let mut name_arg: Spanned<String> = call.req(engine_state, caller_stack, 0)?;
|
||||
name_arg.item = trim_quotes_str(&name_arg.item).to_string();
|
||||
|
||||
let maybe_origin_module_id =
|
||||
if let Some(overlay_expr) = call.get_parser_info("overlay_expr") {
|
||||
if let Some(overlay_expr) = call.get_parser_info(caller_stack, "overlay_expr") {
|
||||
if let Expr::Overlay(module_id) = &overlay_expr.expr {
|
||||
module_id
|
||||
module_id.clone()
|
||||
} else {
|
||||
return Err(ShellError::NushellFailedSpanned {
|
||||
msg: "Not an overlay".to_string(),
|
||||
|
@ -111,7 +110,7 @@ impl Command for OverlayUse {
|
|||
// a) adding a new overlay
|
||||
// b) refreshing an active overlay (the origin module changed)
|
||||
|
||||
let module = engine_state.get_module(*module_id);
|
||||
let module = engine_state.get_module(module_id);
|
||||
|
||||
// Evaluate the export-env block (if any) and keep its environment
|
||||
if let Some(block_id) = module.env_block {
|
||||
|
@ -119,7 +118,7 @@ impl Command for OverlayUse {
|
|||
&name_arg.item,
|
||||
engine_state,
|
||||
caller_stack,
|
||||
get_dirs_var_from_call(call),
|
||||
get_dirs_var_from_call(caller_stack, call),
|
||||
)?;
|
||||
|
||||
let block = engine_state.get_block(block_id);
|
||||
|
|
|
@ -54,11 +54,10 @@ This command is a parser keyword. For details, check:
|
|||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let call = call.assert_ast_call()?; // FIXME
|
||||
let Some(Expression {
|
||||
expr: Expr::ImportPattern(import_pattern),
|
||||
..
|
||||
}) = call.get_parser_info("import_pattern")
|
||||
}) = call.get_parser_info(caller_stack, "import_pattern")
|
||||
else {
|
||||
return Err(ShellError::GenericError {
|
||||
error: "Unexpected import".into(),
|
||||
|
@ -69,6 +68,9 @@ This command is a parser keyword. For details, check:
|
|||
});
|
||||
};
|
||||
|
||||
// Necessary so that we can modify the stack.
|
||||
let import_pattern = import_pattern.clone();
|
||||
|
||||
if let Some(module_id) = import_pattern.head.id {
|
||||
// Add constants
|
||||
for var_id in &import_pattern.constants {
|
||||
|
@ -100,7 +102,7 @@ This command is a parser keyword. For details, check:
|
|||
&module_arg_str,
|
||||
engine_state,
|
||||
caller_stack,
|
||||
get_dirs_var_from_call(call),
|
||||
get_dirs_var_from_call(caller_stack, call),
|
||||
)?;
|
||||
let maybe_parent = maybe_file_path
|
||||
.as_ref()
|
||||
|
|
|
@ -48,12 +48,9 @@ impl Command for BytesBuild {
|
|||
call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let call = call.assert_ast_call()?; // FIXME
|
||||
let mut output = vec![];
|
||||
for val in call.rest_iter_flattened(0, |expr| {
|
||||
let eval_expression = get_eval_expression(engine_state);
|
||||
eval_expression(engine_state, stack, expr)
|
||||
})? {
|
||||
let eval_expression = get_eval_expression(engine_state);
|
||||
for val in call.rest_iter_flattened(engine_state, stack, eval_expression, 0)? {
|
||||
let val_span = val.span();
|
||||
match val {
|
||||
Value::Binary { mut val, .. } => output.append(&mut val),
|
||||
|
|
3
crates/nu-command/src/env/source_env.rs
vendored
3
crates/nu-command/src/env/source_env.rs
vendored
|
@ -45,7 +45,6 @@ impl Command for SourceEnv {
|
|||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let call = call.assert_ast_call()?; // FIXME
|
||||
let source_filename: Spanned<String> = call.req(engine_state, caller_stack, 0)?;
|
||||
|
||||
// Note: this hidden positional is the block_id that corresponded to the 0th position
|
||||
|
@ -57,7 +56,7 @@ impl Command for SourceEnv {
|
|||
&source_filename.item,
|
||||
engine_state,
|
||||
caller_stack,
|
||||
get_dirs_var_from_call(call),
|
||||
get_dirs_var_from_call(caller_stack, call),
|
||||
)? {
|
||||
PathBuf::from(&path)
|
||||
} else {
|
||||
|
|
|
@ -87,7 +87,7 @@ impl Command for NuCheck {
|
|||
&path_str.item,
|
||||
engine_state,
|
||||
stack,
|
||||
get_dirs_var_from_call(call.assert_ast_call()?), // FIXME
|
||||
get_dirs_var_from_call(stack, call), // FIXME
|
||||
) {
|
||||
Ok(path) => {
|
||||
if let Some(path) = path {
|
||||
|
|
|
@ -293,7 +293,21 @@ impl CallExt for ir::Call {
|
|||
stack: &mut Stack,
|
||||
name: &str,
|
||||
) -> Result<T, ShellError> {
|
||||
todo!("req_parser_info is not yet implemented on ir::Call")
|
||||
// FIXME: this depends on the AST evaluator. We can fix this by making the parser info an
|
||||
// enum rather than using expressions. It's not clear that evaluation of this is ever really
|
||||
// needed.
|
||||
if let Some(expr) = self.get_parser_info(stack, name) {
|
||||
let expr = expr.clone();
|
||||
let stack = &mut stack.use_call_arg_out_dest();
|
||||
let result = eval_expression::<WithoutDebug>(engine_state, stack, &expr)?;
|
||||
FromValue::from_value(result)
|
||||
} else {
|
||||
Err(ShellError::CantFindColumn {
|
||||
col_name: name.into(),
|
||||
span: None,
|
||||
src_span: self.head,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn has_positional_args(&self, stack: &Stack, starting_pos: usize) -> bool {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use crate::ClosureEvalOnce;
|
||||
use nu_path::canonicalize_with;
|
||||
use nu_protocol::{
|
||||
ast::{Call, Expr},
|
||||
engine::{EngineState, Stack, StateWorkingSet},
|
||||
ast::Expr,
|
||||
engine::{Call, EngineState, Stack, StateWorkingSet},
|
||||
Config, ShellError, Span, Value, VarId,
|
||||
};
|
||||
use std::{
|
||||
|
@ -244,15 +244,15 @@ pub fn path_str(
|
|||
}
|
||||
|
||||
pub const DIR_VAR_PARSER_INFO: &str = "dirs_var";
|
||||
// FIXME: this should be possible on IR calls
|
||||
pub fn get_dirs_var_from_call(call: &Call) -> Option<VarId> {
|
||||
call.get_parser_info(DIR_VAR_PARSER_INFO).and_then(|x| {
|
||||
if let Expr::Var(id) = x.expr {
|
||||
Some(id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
pub fn get_dirs_var_from_call(stack: &Stack, call: &Call) -> Option<VarId> {
|
||||
call.get_parser_info(stack, DIR_VAR_PARSER_INFO)
|
||||
.and_then(|x| {
|
||||
if let Expr::Var(id) = x.expr {
|
||||
Some(id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// This helper function is used to find files during eval
|
||||
|
|
|
@ -662,7 +662,11 @@ fn eval_call(
|
|||
.argument_stack
|
||||
.get_args(*args_base, args_len)
|
||||
.into_iter()
|
||||
.fold(head, |span, arg| span.append(arg.span()));
|
||||
.fold(head, |span, arg| {
|
||||
arg.span()
|
||||
.map(|arg_span| span.append(arg_span))
|
||||
.unwrap_or(span)
|
||||
});
|
||||
let call = Call {
|
||||
decl_id,
|
||||
head,
|
||||
|
|
|
@ -189,7 +189,9 @@ fn ir_call_to_extern_call(
|
|||
extern_call.add_argument(stack, name_arg);
|
||||
extern_call.add_argument(stack, val_arg);
|
||||
}
|
||||
a @ (engine::Argument::Positional { .. } | engine::Argument::Spread { .. }) => {
|
||||
a @ (engine::Argument::Positional { .. }
|
||||
| engine::Argument::Spread { .. }
|
||||
| engine::Argument::ParserInfo { .. }) => {
|
||||
let argument = a.clone();
|
||||
extern_call.add_argument(stack, argument);
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@ impl MatchPattern {
|
|||
pub enum Pattern {
|
||||
Record(Vec<(String, MatchPattern)>),
|
||||
List(Vec<MatchPattern>),
|
||||
// TODO: it would be nice if this didn't depend on AST
|
||||
// maybe const evaluation can get us to a Value instead?
|
||||
Value(Expression),
|
||||
Variable(VarId),
|
||||
Or(Vec<MatchPattern>),
|
||||
|
|
|
@ -1,19 +1,14 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use crate::{ir::DataSlice, Span, Value};
|
||||
use crate::{ast::Expression, ir::DataSlice, Span, Value};
|
||||
|
||||
/// Represents a fully evaluated argument to a call.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Argument {
|
||||
/// A positional argument
|
||||
Positional {
|
||||
span: Span,
|
||||
val: Value,
|
||||
},
|
||||
Spread {
|
||||
span: Span,
|
||||
vals: Value,
|
||||
},
|
||||
Positional { span: Span, val: Value },
|
||||
/// A spread argument, e.g. `...$args`
|
||||
Spread { span: Span, vals: Value },
|
||||
/// A named argument with no value, e.g. `--flag`
|
||||
Flag {
|
||||
data: Arc<[u8]>,
|
||||
|
@ -27,17 +22,27 @@ pub enum Argument {
|
|||
span: Span,
|
||||
val: Value,
|
||||
},
|
||||
/// Information generated by the parser for use by certain keyword commands
|
||||
ParserInfo {
|
||||
data: Arc<[u8]>,
|
||||
name: DataSlice,
|
||||
// TODO: rather than `Expression`, this would probably be best served by a specific enum
|
||||
// type for this purpose.
|
||||
expr: Box<Expression>,
|
||||
},
|
||||
}
|
||||
|
||||
impl Argument {
|
||||
/// The span encompassing the argument's usage within the call, distinct from the span of the
|
||||
/// actual value of the argument.
|
||||
pub fn span(&self) -> Span {
|
||||
pub fn span(&self) -> Option<Span> {
|
||||
match self {
|
||||
Argument::Positional { span, .. } => *span,
|
||||
Argument::Spread { span, .. } => *span,
|
||||
Argument::Flag { span, .. } => *span,
|
||||
Argument::Named { span, .. } => *span,
|
||||
Argument::Positional { span, .. } => Some(*span),
|
||||
Argument::Spread { span, .. } => Some(*span),
|
||||
Argument::Flag { span, .. } => Some(*span),
|
||||
Argument::Named { span, .. } => Some(*span),
|
||||
// Because `ParserInfo` is generated, its span shouldn't be used
|
||||
Argument::ParserInfo { .. } => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
use crate::{ast, ir, DeclId, FromValue, ShellError, Span, Value};
|
||||
use crate::{
|
||||
ast::{self, Expression},
|
||||
ir, DeclId, FromValue, ShellError, Span, Value,
|
||||
};
|
||||
|
||||
use super::{EngineState, Stack, StateWorkingSet};
|
||||
|
||||
|
@ -117,6 +120,16 @@ impl Call<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Get a parser info argument by name.
|
||||
pub fn get_parser_info<'a>(&'a self, stack: &'a Stack, name: &str) -> Option<&'a Expression> {
|
||||
match &self.inner {
|
||||
CallImpl::AstRef(call) => call.get_parser_info(name),
|
||||
CallImpl::AstBox(call) => call.get_parser_info(name),
|
||||
CallImpl::IrRef(call) => call.get_parser_info(stack, name),
|
||||
CallImpl::IrBox(call) => call.get_parser_info(stack, name),
|
||||
}
|
||||
}
|
||||
|
||||
/// Evaluator-agnostic implementation of `rest_iter_flattened()`. Evaluates or gets all of the
|
||||
/// positional and spread arguments, flattens spreads, and then returns one list of values.
|
||||
pub fn rest_iter_flattened(
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use crate::{
|
||||
ast::Expression,
|
||||
engine::{self, Argument, Stack},
|
||||
DeclId, ShellError, Span, Spanned, Value,
|
||||
};
|
||||
|
@ -55,6 +56,7 @@ impl Call {
|
|||
Span::new(past.start, self.span.end)
|
||||
}
|
||||
|
||||
/// The number of named arguments, with or without values.
|
||||
pub fn named_len(&self, stack: &Stack) -> usize {
|
||||
self.arguments(stack)
|
||||
.iter()
|
||||
|
@ -62,6 +64,7 @@ impl Call {
|
|||
.count()
|
||||
}
|
||||
|
||||
/// Iterate through named arguments, with or without values.
|
||||
pub fn named_iter<'a>(
|
||||
&'a self,
|
||||
stack: &'a Stack,
|
||||
|
@ -97,6 +100,8 @@ impl Call {
|
|||
)
|
||||
}
|
||||
|
||||
/// Get a named argument's value by name. Returns [`None`] for named arguments with no value as
|
||||
/// well.
|
||||
pub fn get_named_arg<'a>(&self, stack: &'a Stack, flag_name: &str) -> Option<&'a Value> {
|
||||
// Optimized to avoid str::from_utf8()
|
||||
self.arguments(stack)
|
||||
|
@ -115,6 +120,7 @@ impl Call {
|
|||
.flatten()
|
||||
}
|
||||
|
||||
/// The number of positional arguments, excluding spread arguments.
|
||||
pub fn positional_len(&self, stack: &Stack) -> usize {
|
||||
self.arguments(stack)
|
||||
.iter()
|
||||
|
@ -122,6 +128,7 @@ impl Call {
|
|||
.count()
|
||||
}
|
||||
|
||||
/// Iterate through positional arguments. Does not include spread arguments.
|
||||
pub fn positional_iter<'a>(&self, stack: &'a Stack) -> impl Iterator<Item = &'a Value> {
|
||||
self.arguments(stack).iter().filter_map(|arg| match arg {
|
||||
Argument::Positional { val, .. } => Some(val),
|
||||
|
@ -129,6 +136,7 @@ impl Call {
|
|||
})
|
||||
}
|
||||
|
||||
/// Get a positional argument by index. Does not include spread arguments.
|
||||
pub fn positional_nth<'a>(&self, stack: &'a Stack, index: usize) -> Option<&'a Value> {
|
||||
self.positional_iter(stack).nth(index)
|
||||
}
|
||||
|
@ -150,6 +158,8 @@ impl Call {
|
|||
.skip(start)
|
||||
}
|
||||
|
||||
/// Returns all of the positional arguments including and after `start`, with spread arguments
|
||||
/// flattened into a single `Vec`.
|
||||
pub fn rest_iter_flattened(
|
||||
&self,
|
||||
stack: &Stack,
|
||||
|
@ -174,6 +184,20 @@ impl Call {
|
|||
Ok(acc)
|
||||
}
|
||||
|
||||
/// Get a parser info argument by name.
|
||||
pub fn get_parser_info<'a>(&self, stack: &'a Stack, name: &str) -> Option<&'a Expression> {
|
||||
self.arguments(stack)
|
||||
.iter()
|
||||
.find_map(|argument| match argument {
|
||||
Argument::ParserInfo {
|
||||
data,
|
||||
name: name_slice,
|
||||
expr,
|
||||
} if &data[*name_slice] == name.as_bytes() => Some(expr.as_ref()),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns a span encompassing the entire call.
|
||||
pub fn span(&self) -> Span {
|
||||
self.span
|
||||
|
@ -197,7 +221,9 @@ impl CallBuilder {
|
|||
self.inner.args_base = stack.argument_stack.get_base();
|
||||
}
|
||||
self.inner.args_len += 1;
|
||||
self.inner.span = self.inner.span.append(argument.span());
|
||||
if let Some(span) = argument.span() {
|
||||
self.inner.span = self.inner.span.append(span);
|
||||
}
|
||||
stack.argument_stack.push(argument);
|
||||
self
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user