use crate::env::aliases::AliasStore;
use crate::error::{ParseErrorKind, ShellErrorKind};
use crate::parser::Parser;
use crate::parser::ast::CompleteCommand;
#[derive(Debug)]
pub enum ParseStatus {
Complete(Vec<CompleteCommand>),
Incomplete,
Empty,
Error(String),
}
const CLOSING_KEYWORDS: &[&str] = &[
"\n:\nfi\n",
"\n:\ndone\n",
"\n:\nesac\n",
"\n:\n}\n",
"\n:\n)\n",
"\n:\n;;\nesac\n",
"\ndo :\ndone\n",
];
pub fn classify_parse(input: &str, aliases: &AliasStore) -> ParseStatus {
if input.trim().is_empty() {
return ParseStatus::Empty;
}
if input.ends_with("\\\n") {
return ParseStatus::Incomplete;
}
let trimmed = input.trim_end_matches('\n').trim_end();
if trimmed.ends_with('|') || trimmed.ends_with("&&") || trimmed.ends_with("||") {
return ParseStatus::Incomplete;
}
let mut parser = Parser::new_with_aliases(input, aliases);
let mut commands = Vec::new();
if parser.is_at_end() {
return ParseStatus::Empty;
}
loop {
while !parser.is_at_end() && parser.current_token() == &crate::lexer::token::Token::Newline
{
if let Err(e) = parser.advance() {
if is_incomplete_error(&e.kind) {
return ParseStatus::Incomplete;
}
return ParseStatus::Error(e.message);
}
}
if parser.is_at_end() {
break;
}
match parser.parse_complete_command() {
Ok(cmd) => {
commands.push(cmd);
}
Err(e) => {
if is_incomplete_error(&e.kind) {
return ParseStatus::Incomplete;
}
if e.kind == ShellErrorKind::Parse(ParseErrorKind::UnexpectedToken)
&& parser.is_at_end()
&& is_completable(input, aliases)
{
return ParseStatus::Incomplete;
}
return ParseStatus::Error(e.message);
}
}
}
if commands.is_empty() {
return ParseStatus::Empty;
}
ParseStatus::Complete(commands)
}
fn is_completable(input: &str, aliases: &AliasStore) -> bool {
for suffix in CLOSING_KEYWORDS {
let candidate = format!("{}{}", input, suffix);
let mut p = Parser::new_with_aliases(&candidate, aliases);
if p.parse_program().is_ok() {
return true;
}
}
false
}
fn is_incomplete_error(kind: &ShellErrorKind) -> bool {
matches!(
kind,
ShellErrorKind::Parse(ParseErrorKind::UnterminatedSingleQuote)
| ShellErrorKind::Parse(ParseErrorKind::UnterminatedDoubleQuote)
| ShellErrorKind::Parse(ParseErrorKind::UnterminatedCommandSub)
| ShellErrorKind::Parse(ParseErrorKind::UnterminatedArithSub)
| ShellErrorKind::Parse(ParseErrorKind::UnterminatedParamExpansion)
| ShellErrorKind::Parse(ParseErrorKind::UnterminatedBacktick)
| ShellErrorKind::Parse(ParseErrorKind::UnterminatedDollarSingleQuote)
| ShellErrorKind::Parse(ParseErrorKind::UnexpectedEof)
)
}