2019-06-30 08:14:40 +02:00
|
|
|
use crate::errors::{ArgumentError, ShellError};
|
2019-07-03 22:31:15 +02:00
|
|
|
use crate::parser::registry::{CommandConfig, CommandRegistry, NamedType, PositionalType};
|
2019-08-01 03:58:42 +02:00
|
|
|
use crate::parser::{baseline_parse_tokens, CallNode};
|
2019-06-22 03:36:57 +02:00
|
|
|
use crate::parser::{
|
|
|
|
hir::{self, NamedArguments},
|
|
|
|
Flag, RawToken, TokenNode,
|
|
|
|
};
|
2019-08-01 03:58:42 +02:00
|
|
|
use crate::{Span, Tag, Tagged, Text};
|
2019-06-22 03:36:57 +02:00
|
|
|
use log::trace;
|
|
|
|
|
|
|
|
pub fn parse_command(
|
|
|
|
config: &CommandConfig,
|
|
|
|
registry: &dyn CommandRegistry,
|
2019-08-01 03:58:42 +02:00
|
|
|
call: &Tagged<CallNode>,
|
2019-06-22 22:46:16 +02:00
|
|
|
source: &Text,
|
2019-06-22 03:36:57 +02:00
|
|
|
) -> Result<hir::Call, ShellError> {
|
2019-08-01 03:58:42 +02:00
|
|
|
let Tagged { item: raw_call, .. } = call;
|
2019-06-22 03:36:57 +02:00
|
|
|
|
|
|
|
trace!("Processing {:?}", config);
|
|
|
|
|
|
|
|
let head = parse_command_head(call.head())?;
|
|
|
|
|
2019-06-30 08:14:40 +02:00
|
|
|
let children: Option<Vec<TokenNode>> = raw_call.children().as_ref().map(|nodes| {
|
2019-06-22 03:36:57 +02:00
|
|
|
nodes
|
|
|
|
.iter()
|
|
|
|
.cloned()
|
|
|
|
.filter(|node| match node {
|
|
|
|
TokenNode::Whitespace(_) => false,
|
|
|
|
_ => true,
|
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
});
|
|
|
|
|
2019-08-01 03:58:42 +02:00
|
|
|
match parse_command_tail(&config, registry, children, source, call.span())? {
|
2019-06-22 03:36:57 +02:00
|
|
|
None => Ok(hir::Call::new(Box::new(head), None, None)),
|
|
|
|
Some((positional, named)) => Ok(hir::Call::new(Box::new(head), positional, named)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parse_command_head(head: &TokenNode) -> Result<hir::Expression, ShellError> {
|
|
|
|
match head {
|
|
|
|
TokenNode::Token(
|
2019-08-01 03:58:42 +02:00
|
|
|
spanned @ Tagged {
|
2019-06-22 03:36:57 +02:00
|
|
|
item: RawToken::Bare,
|
|
|
|
..
|
|
|
|
},
|
|
|
|
) => Ok(spanned.map(|_| hir::RawExpression::Literal(hir::Literal::Bare))),
|
|
|
|
|
2019-08-01 03:58:42 +02:00
|
|
|
TokenNode::Token(Tagged {
|
2019-06-22 03:36:57 +02:00
|
|
|
item: RawToken::String(inner_span),
|
2019-08-01 03:58:42 +02:00
|
|
|
tag: Tag { span },
|
|
|
|
}) => Ok(Tagged::from_item(
|
2019-06-22 03:36:57 +02:00
|
|
|
hir::RawExpression::Literal(hir::Literal::String(*inner_span)),
|
|
|
|
*span,
|
|
|
|
)),
|
|
|
|
|
|
|
|
other => Err(ShellError::unexpected(&format!(
|
|
|
|
"command head -> {:?}",
|
|
|
|
other
|
|
|
|
))),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parse_command_tail(
|
|
|
|
config: &CommandConfig,
|
|
|
|
registry: &dyn CommandRegistry,
|
|
|
|
tail: Option<Vec<TokenNode>>,
|
2019-06-22 22:46:16 +02:00
|
|
|
source: &Text,
|
2019-06-30 08:14:40 +02:00
|
|
|
command_span: Span,
|
2019-06-22 03:36:57 +02:00
|
|
|
) -> Result<Option<(Option<Vec<hir::Expression>>, Option<NamedArguments>)>, ShellError> {
|
2019-06-29 10:55:42 +02:00
|
|
|
let tail = &mut match &tail {
|
2019-06-30 08:14:40 +02:00
|
|
|
None => hir::TokensIterator::new(&[]),
|
2019-06-29 10:55:42 +02:00
|
|
|
Some(tail) => hir::TokensIterator::new(tail),
|
2019-06-22 03:36:57 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
let mut named = NamedArguments::new();
|
|
|
|
|
2019-06-29 10:55:42 +02:00
|
|
|
trace_remaining("nodes", tail.clone(), source);
|
|
|
|
|
2019-06-22 03:36:57 +02:00
|
|
|
for (name, kind) in config.named() {
|
2019-07-12 21:22:08 +02:00
|
|
|
trace!(target: "nu::parse", "looking for {} : {:?}", name, kind);
|
2019-06-22 03:36:57 +02:00
|
|
|
|
|
|
|
match kind {
|
|
|
|
NamedType::Switch => {
|
2019-06-29 10:55:42 +02:00
|
|
|
let flag = extract_switch(name, tail, source);
|
2019-06-22 03:36:57 +02:00
|
|
|
|
|
|
|
named.insert_switch(name, flag);
|
|
|
|
}
|
2019-07-15 23:16:27 +02:00
|
|
|
NamedType::Mandatory(syntax_type) => {
|
2019-07-03 22:31:15 +02:00
|
|
|
match extract_mandatory(config, name, tail, source, command_span) {
|
|
|
|
Err(err) => return Err(err), // produce a correct diagnostic
|
|
|
|
Ok((pos, flag)) => {
|
|
|
|
tail.move_to(pos);
|
|
|
|
|
|
|
|
if tail.at_end() {
|
2019-07-09 06:31:26 +02:00
|
|
|
return Err(ShellError::argument_error(
|
|
|
|
config.name.clone(),
|
|
|
|
ArgumentError::MissingValueForName(name.to_string()),
|
2019-08-01 03:58:42 +02:00
|
|
|
flag.span(),
|
2019-07-09 06:31:26 +02:00
|
|
|
));
|
2019-07-03 22:31:15 +02:00
|
|
|
}
|
|
|
|
|
2019-07-15 23:16:27 +02:00
|
|
|
let expr =
|
|
|
|
hir::baseline_parse_next_expr(tail, registry, source, *syntax_type)?;
|
2019-07-03 22:31:15 +02:00
|
|
|
|
|
|
|
tail.restart();
|
|
|
|
named.insert_mandatory(name, expr);
|
2019-06-30 08:14:40 +02:00
|
|
|
}
|
2019-06-22 03:36:57 +02:00
|
|
|
}
|
2019-07-03 22:31:15 +02:00
|
|
|
}
|
2019-07-15 23:16:27 +02:00
|
|
|
NamedType::Optional(syntax_type) => match extract_optional(name, tail, source) {
|
2019-06-22 03:36:57 +02:00
|
|
|
Err(err) => return Err(err), // produce a correct diagnostic
|
2019-06-30 08:14:40 +02:00
|
|
|
Ok(Some((pos, flag))) => {
|
2019-06-29 10:55:42 +02:00
|
|
|
tail.move_to(pos);
|
2019-06-30 08:14:40 +02:00
|
|
|
|
|
|
|
if tail.at_end() {
|
2019-07-09 06:31:26 +02:00
|
|
|
return Err(ShellError::argument_error(
|
|
|
|
config.name().clone(),
|
|
|
|
ArgumentError::MissingValueForName(name.to_string()),
|
2019-08-01 03:58:42 +02:00
|
|
|
flag.span(),
|
2019-07-09 06:31:26 +02:00
|
|
|
));
|
2019-06-30 08:14:40 +02:00
|
|
|
}
|
|
|
|
|
2019-07-15 23:16:27 +02:00
|
|
|
let expr = hir::baseline_parse_next_expr(tail, registry, source, *syntax_type)?;
|
2019-06-22 03:36:57 +02:00
|
|
|
|
2019-06-29 10:55:42 +02:00
|
|
|
tail.restart();
|
2019-06-22 03:36:57 +02:00
|
|
|
named.insert_optional(name, Some(expr));
|
|
|
|
}
|
|
|
|
|
2019-06-29 10:55:42 +02:00
|
|
|
Ok(None) => {
|
|
|
|
tail.restart();
|
2019-06-22 03:36:57 +02:00
|
|
|
named.insert_optional(name, None);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2019-06-29 10:55:42 +02:00
|
|
|
trace_remaining("after named", tail.clone(), source);
|
|
|
|
|
2019-06-22 03:36:57 +02:00
|
|
|
let mut positional = vec![];
|
|
|
|
|
2019-07-03 22:31:15 +02:00
|
|
|
for arg in config.positional() {
|
|
|
|
trace!("Processing positional {:?}", arg);
|
|
|
|
|
|
|
|
match arg {
|
|
|
|
PositionalType::Mandatory(..) => {
|
|
|
|
if tail.len() == 0 {
|
2019-07-09 06:31:26 +02:00
|
|
|
return Err(ShellError::argument_error(
|
|
|
|
config.name().clone(),
|
|
|
|
ArgumentError::MissingMandatoryPositional(arg.name().to_string()),
|
|
|
|
command_span,
|
|
|
|
));
|
2019-07-03 22:31:15 +02:00
|
|
|
}
|
|
|
|
}
|
2019-06-22 03:36:57 +02:00
|
|
|
|
2019-07-03 22:31:15 +02:00
|
|
|
PositionalType::Optional(..) => {
|
|
|
|
if tail.len() == 0 {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2019-06-22 03:36:57 +02:00
|
|
|
}
|
|
|
|
|
2019-07-15 23:16:27 +02:00
|
|
|
let result = hir::baseline_parse_next_expr(tail, registry, source, arg.syntax_type())?;
|
2019-06-22 03:36:57 +02:00
|
|
|
|
|
|
|
positional.push(result);
|
|
|
|
}
|
|
|
|
|
2019-07-03 22:31:15 +02:00
|
|
|
trace_remaining("after positional", tail.clone(), source);
|
2019-06-29 10:55:42 +02:00
|
|
|
|
2019-06-22 03:36:57 +02:00
|
|
|
// TODO: Only do this if rest params are specified
|
2019-06-29 10:55:42 +02:00
|
|
|
let remainder = baseline_parse_tokens(tail, registry, source)?;
|
2019-06-22 03:36:57 +02:00
|
|
|
positional.extend(remainder);
|
|
|
|
|
2019-06-29 10:55:42 +02:00
|
|
|
trace_remaining("after rest", tail.clone(), source);
|
|
|
|
|
2019-06-22 03:36:57 +02:00
|
|
|
trace!("Constructed positional={:?} named={:?}", positional, named);
|
|
|
|
|
|
|
|
let positional = match positional {
|
|
|
|
positional if positional.len() == 0 => None,
|
|
|
|
positional => Some(positional),
|
|
|
|
};
|
|
|
|
|
|
|
|
let named = match named {
|
|
|
|
named if named.named.is_empty() => None,
|
|
|
|
named => Some(named),
|
|
|
|
};
|
|
|
|
|
|
|
|
trace!("Normalized positional={:?} named={:?}", positional, named);
|
|
|
|
|
|
|
|
Ok(Some((positional, named)))
|
|
|
|
}
|
|
|
|
|
2019-06-29 10:55:42 +02:00
|
|
|
fn extract_switch(name: &str, tokens: &mut hir::TokensIterator<'_>, source: &Text) -> Option<Flag> {
|
|
|
|
tokens
|
|
|
|
.extract(|t| t.as_flag(name, source))
|
|
|
|
.map(|(_pos, flag)| flag.item)
|
2019-06-22 03:36:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
fn extract_mandatory(
|
2019-07-03 22:31:15 +02:00
|
|
|
config: &CommandConfig,
|
2019-06-22 03:36:57 +02:00
|
|
|
name: &str,
|
2019-06-29 10:55:42 +02:00
|
|
|
tokens: &mut hir::TokensIterator<'a>,
|
2019-06-22 22:46:16 +02:00
|
|
|
source: &Text,
|
2019-06-30 08:14:40 +02:00
|
|
|
span: Span,
|
2019-08-01 03:58:42 +02:00
|
|
|
) -> Result<(usize, Tagged<Flag>), ShellError> {
|
2019-06-29 10:55:42 +02:00
|
|
|
let flag = tokens.extract(|t| t.as_flag(name, source));
|
|
|
|
|
|
|
|
match flag {
|
2019-07-09 06:31:26 +02:00
|
|
|
None => Err(ShellError::argument_error(
|
|
|
|
config.name().clone(),
|
|
|
|
ArgumentError::MissingMandatoryFlag(name.to_string()),
|
2019-06-30 08:14:40 +02:00
|
|
|
span,
|
2019-07-09 06:31:26 +02:00
|
|
|
)),
|
2019-06-22 03:36:57 +02:00
|
|
|
|
2019-06-30 08:14:40 +02:00
|
|
|
Some((pos, flag)) => {
|
2019-06-22 03:36:57 +02:00
|
|
|
tokens.remove(pos);
|
2019-06-30 08:14:40 +02:00
|
|
|
Ok((pos, flag))
|
2019-06-22 03:36:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn extract_optional(
|
|
|
|
name: &str,
|
2019-06-29 10:55:42 +02:00
|
|
|
tokens: &mut hir::TokensIterator<'a>,
|
2019-06-22 22:46:16 +02:00
|
|
|
source: &Text,
|
2019-08-01 03:58:42 +02:00
|
|
|
) -> Result<(Option<(usize, Tagged<Flag>)>), ShellError> {
|
2019-06-29 10:55:42 +02:00
|
|
|
let flag = tokens.extract(|t| t.as_flag(name, source));
|
|
|
|
|
|
|
|
match flag {
|
|
|
|
None => Ok(None),
|
2019-06-22 03:36:57 +02:00
|
|
|
Some((pos, flag)) => {
|
|
|
|
tokens.remove(pos);
|
2019-06-30 08:14:40 +02:00
|
|
|
Ok(Some((pos, flag)))
|
2019-06-22 03:36:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-06-29 10:55:42 +02:00
|
|
|
|
|
|
|
pub fn trace_remaining(desc: &'static str, tail: hir::TokensIterator<'a>, source: &Text) {
|
|
|
|
trace!(
|
|
|
|
"{} = {:?}",
|
|
|
|
desc,
|
|
|
|
itertools::join(
|
|
|
|
tail.debug_remaining()
|
|
|
|
.iter()
|
|
|
|
.map(|i| format!("%{:?}%", i.debug(source))),
|
|
|
|
" "
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|