use super::Parser;
use super::ast::FunctionDef;
use super::word::is_valid_name;
use crate::error;
use crate::lexer::token::Token;
use std::rc::Rc;
impl Parser {
pub(super) fn try_parse_function_def(&mut self) -> error::Result<Option<FunctionDef>> {
let name = match &self.current.token {
Token::Word(word) => {
if let Some(lit) = word.as_literal() {
if is_valid_name(lit) {
lit.to_string()
} else {
return Ok(None);
}
} else {
return Ok(None);
}
}
_ => return Ok(None),
};
let saved_lexer_state = self.lexer.save_state();
let saved_current = self.current.clone();
self.advance()?;
if self.current.token != Token::LParen {
self.lexer.restore_state(saved_lexer_state);
self.current = saved_current;
return Ok(None);
}
self.advance()?;
if self.current.token != Token::RParen {
self.lexer.restore_state(saved_lexer_state);
self.current = saved_current;
return Ok(None);
}
self.advance()?;
self.skip_newlines()?;
let body = self.parse_compound_command()?;
let redirects = self.parse_redirect_list()?;
Ok(Some(FunctionDef {
name,
body: Rc::new(body),
redirects,
}))
}
}
#[cfg(test)]
mod tests {
use super::super::ast::Command;
use super::super::tests::parse;
#[test]
fn test_function_def() {
let prog = parse("myfunc() { echo hello; }");
let cmd = &prog.commands[0].items[0].0.first.commands[0];
match cmd {
Command::FunctionDef(fd) => assert_eq!(fd.name, "myfunc"),
_ => panic!(),
}
}
#[test]
fn test_function_def_with_redirect() {
let prog = parse("myfunc() { echo hello; } > out.txt");
let cmd = &prog.commands[0].items[0].0.first.commands[0];
match cmd {
Command::FunctionDef(fd) => {
assert_eq!(fd.name, "myfunc");
assert_eq!(fd.redirects.len(), 1);
}
_ => panic!(),
}
}
}