use super::*;
pub fn try_into_command_name(token: &Token) -> Result<smartstring::alias::CompactString, ParseError> {
match token.content.clone() {
TokenContent::Remainder(r )
=> Err(ParseError::Unrecognized(token.start, r)),
TokenContent::Symbol(s ) if !s.is_operator()
=> Err(ParseError::ExpectButGot("a command name".into(), format!("a '{}'", s).into())),
TokenContent::Symbol(s) => Ok((&s).into()),
TokenContent::Literal(Literal::Str(s)) => Ok(s),
TokenContent::Literal(l)
=> Err(ParseError::ExpectButGot("a command name".into(), format!("a {}", l.get_type_str()).into())),
TokenContent::Group(_, _)
=> Err(ParseError::ExpectButGot("a command name".into(), "a group".to_string().into())),
}
}
pub fn parse_command(
tokens: &mut PeekableTokenStream<impl TokenStream>,
terminator: Option<Symbol>
) -> Result<Invoke, ParseError> {
let name = match tokens.next() {
Some(n) => n,
None => return Err(ParseError::ExpectButEnd("a command name")),
};
let name: CompactString = try_into_command_name(&name)?;
parse_command_body(name, tokens, terminator)
}
pub fn parse_command_body(
name: CompactString,
tokens: &mut PeekableTokenStream<impl TokenStream>,
terminator: Option<Symbol>
) -> Result<Invoke, ParseError> {
let mut cmd = Invoke {
name,
pos_args: Default::default(),
nom_args: Default::default(),
};
let mut no_more_pos_args = false;
loop {
if let Some(terminator) = terminator {
if match_symbol(tokens, terminator) {
break; }
}
if match_symbol(tokens, Symbol::Semicolon) {
break; }
if consume_symbol(tokens, Symbol::DoubleDot) {
let subcommand = parse_command(tokens, None)?;
cmd.pos_args.push(subcommand.into());
break; }
if consume_symbol(tokens, Symbol::DoubleAmpersand) {
let previous = std::mem::replace(&mut cmd, Invoke {
name: "if-then".into(),
pos_args: Default::default(),
nom_args: Default::default(),
});
cmd.pos_args.push(previous.into());
let subcommand = parse_command(tokens, None)?;
cmd.pos_args.push(subcommand.into());
break; }
if consume_symbol(tokens, Symbol::DoublePipe) {
let previous = std::mem::replace(&mut cmd, Invoke {
name: "if-else".into(),
pos_args: Default::default(),
nom_args: Default::default(),
});
cmd.pos_args.push(previous.into());
let subcommand = parse_command(tokens, None)?;
cmd.pos_args.push(subcommand.into());
break; }
if consume_if(tokens, |tc|
matches!(tc, TokenContent::Symbol(peeked) if peeked.is_end_delimiter())
) {
break }
if match_symbol(tokens, Symbol::Pipe) {
break; }
if consume_symbol(tokens, Symbol::Dash) {
if let Some(s) = consume_string(tokens) {
cmd.nom_args.insert(s, Expression::Value(Literal::Bool(false)));
no_more_pos_args = true;
continue;
} else {
return Err(ParseError::ExpectButGot("a flag".into(), "something else".into()))
}
}
if consume_symbol(tokens, Symbol::Plus) {
if let Some(s) = consume_string(tokens) {
cmd.nom_args.insert(s, Expression::Value(Literal::Bool(true)));
no_more_pos_args = true;
continue;
} else {
return Err(ParseError::ExpectButGot("a flag".into(), "something else".into()))
}
}
if let Some(token) = tokens.peek().cloned() {
let expr = parse_expression(tokens, false, false)?;
if consume_symbol(tokens, Symbol::EqualSign) {
let lexpr = match expr {
Expression::Value(val) => match val {
Literal::Str(s) => s,
val => return Err(ParseError::ExpectButGot("a parameter name".into(), format!("{:?}", val).into())),
},
_ => return Err(ParseError::ExpectButGot("a parameter name".into(), format!("{expr:?}").into())),
};
let rexpr = parse_expression(tokens, false, false)?;
cmd.nom_args.insert(lexpr, rexpr);
no_more_pos_args = true;
} else {
if no_more_pos_args {
return Err(ParseError::PosArgAfterNomArg(token.start))
}
cmd.pos_args.push(expr);
}
} else {
break }
}
Ok(cmd)
}