parser: fix recursive subshells

This commit is contained in:
Michael Sippel 2023-11-08 04:22:08 +01:00
parent 903d6dd64f
commit c49577e924
Signed by: senvas
GPG key ID: F96CF119C34B64A6
2 changed files with 44 additions and 46 deletions

View file

@ -38,7 +38,7 @@ fn main() {
let stdin = std::io::stdin(); let stdin = std::io::stdin();
for line in std::io::BufReader::new(stdin).lines() { for line in std::io::BufReader::new(stdin).lines() {
if let Ok(line) = line { if let Ok(line) = line {
let cmd = sh::parse::parse_cmd( &mut line.chars().peekable() ); let cmd = sh::parse::parse_cmd( &mut line.chars().peekable(), 0 );
eprintln!("parsed cmd: {:?}", cmd); eprintln!("parsed cmd: {:?}", cmd);
/* /*
let mut lex = parse::WordLexer::from( line.chars() ); let mut lex = parse::WordLexer::from( line.chars() );

View file

@ -30,7 +30,8 @@ where It: Iterator<Item = char> {
(None, true), (None, true),
(Some(' '), true), (Some(' '), true),
(Some('\t'), true), (Some('\t'), true),
(Some('\n'), true) (Some('\n'), true),
(Some(')'), false),
]) ])
} }
@ -43,6 +44,8 @@ where It: Iterator<Item = char> {
(Some('|'), false), (Some('|'), false),
(Some('&'), false), (Some('&'), false),
(Some(';'), false), (Some(';'), false),
(Some(')'), false),
(Some('$'), false),
(Some('\"'), false), (Some('\"'), false),
(Some('\''), false) (Some('\''), false)
]) ])
@ -58,6 +61,7 @@ where It: Iterator<Item = char> {
(Some('|'), false), (Some('|'), false),
(Some('&'), false), (Some('&'), false),
(Some(';'), false), (Some(';'), false),
(Some(')'), false),
(Some('\"'), false), (Some('\"'), false),
(Some('\''), false) (Some('\''), false)
]) ])
@ -92,7 +96,8 @@ where It: 'a + Iterator<Item = char> {
pub struct SubstLexer<'a, It> pub struct SubstLexer<'a, It>
where It: 'a + Iterator<Item = char> { where It: 'a + Iterator<Item = char> {
chars: &'a mut Peekable<It> chars: &'a mut Peekable<It>,
depth: usize
} }
pub fn skip_whitespace<It>(chars: &mut Peekable<It>) pub fn skip_whitespace<It>(chars: &mut Peekable<It>)
@ -124,12 +129,14 @@ pub fn parse_doublequoted<It>(chars: &mut Peekable<It>) -> Result<WordSegment, L
where It: Iterator<Item = char> where It: Iterator<Item = char>
{ {
assert_eq!( chars.next(), Some('\"')); assert_eq!( chars.next(), Some('\"'));
// todo: allow escaped \"
let quoted = DelimIter::new(chars, vec![(Some('\"'), true)]).try_collect::<String>(); let quoted = DelimIter::new(chars, vec![(Some('\"'), true)]).try_collect::<String>();
match quoted { match quoted {
Ok(s) => { Ok(s) => {
let word = Word { let word = Word {
segments: SubstLexer { chars: &mut s.chars().peekable() } segments: SubstLexer { chars: &mut s.chars().peekable(), depth: 1 }
.try_collect::<Vec<_>>()? .try_collect::<Vec<_>>()?
}; };
@ -213,14 +220,14 @@ where It: Iterator<Item = char>
} }
} }
pub fn parse_cmd<It>(chars: &mut Peekable<It>) -> Result<Option<Command>, LexError> pub fn parse_cmd<It>(chars: &mut Peekable<It>, depth: usize) -> Result<Option<Command>, LexError>
where It: Iterator<Item = char> where It: Iterator<Item = char>
{ {
skip_whitespace(chars); skip_whitespace(chars);
match chars.peek() { match chars.peek() {
Some('!') => { Some('!') => {
chars.next(); chars.next();
if let Some(cmd) = parse_cmd(chars)? { if let Some(cmd) = parse_cmd(chars, depth)? {
Ok(Some(Command::Negation(Box::new(cmd)))) Ok(Some(Command::Negation(Box::new(cmd))))
} else { } else {
Err(LexError::UnexpectedEnd(vec![])) Err(LexError::UnexpectedEnd(vec![]))
@ -234,18 +241,14 @@ where It: Iterator<Item = char>
Some(';') => { Some(';') => {
chars.next(); chars.next();
let tail = parse_cmd( chars ) ?; let tail = parse_cmd( chars, depth ) ?;
match tail { match tail {
Some(Command::Sequence(mut s)) => { Some(Command::Sequence(mut s)) => {
s.insert(0, head); s.insert(0, head);
Ok(Some(Command::Sequence(s))) Ok(Some(Command::Sequence(s)))
} }
Some(tail) => { Some(tail) => Ok(Some(Command::Sequence(vec![ head, tail ]))),
Ok(Some(Command::Sequence(vec![ head, tail ]))) None => Ok(Some(head))
}
None => {
Ok(Some(head))
}
} }
} }
Some('|') => { Some('|') => {
@ -254,33 +257,25 @@ where It: Iterator<Item = char>
Some('|') => { Some('|') => {
chars.next(); chars.next();
let tail = parse_cmd( chars ) ?; let tail = parse_cmd( chars, depth ) ?;
match tail { match tail {
Some(Command::ShortCircuitDisjunction(mut s)) => { Some(Command::ShortCircuitDisjunction(mut s)) => {
s.insert(0, head); s.insert(0, head);
Ok(Some(Command::ShortCircuitDisjunction(s))) Ok(Some(Command::ShortCircuitDisjunction(s)))
} }
Some(tail) => { Some(tail) => Ok(Some(Command::ShortCircuitDisjunction(vec![ head, tail ]))),
Ok(Some(Command::ShortCircuitDisjunction(vec![ head, tail ]))) None => Err(LexError::UnexpectedEnd(vec![Some('|')]))
}
None => {
Err(LexError::UnexpectedEnd(vec![Some('|')]))
}
} }
} }
_ => { _ => {
let tail = parse_cmd( chars ) ?; let tail = parse_cmd( chars, depth ) ?;
match tail { match tail {
Some(Command::Pipeline(mut s)) => { Some(Command::Pipeline(mut s)) => {
s.insert(0, head); s.insert(0, head);
Ok(Some(Command::Pipeline(s))) Ok(Some(Command::Pipeline(s)))
} }
Some(tail) => { Some(tail) => Ok(Some(Command::Pipeline(vec![ head, tail ]))),
Ok(Some(Command::Pipeline(vec![ head, tail ]))) None => Err(LexError::UnexpectedEnd(vec![]))
}
None => {
Err(LexError::UnexpectedEnd(vec![]))
}
} }
} }
} }
@ -291,23 +286,17 @@ where It: Iterator<Item = char>
Some('&') => { Some('&') => {
chars.next(); chars.next();
let tail = parse_cmd( chars ) ?; let tail = parse_cmd( chars, depth ) ?;
match tail { match tail {
Some(Command::ShortCircuitConjunction(mut s)) => { Some(Command::ShortCircuitConjunction(mut s)) => {
s.insert(0, head); s.insert(0, head);
Ok(Some(Command::ShortCircuitConjunction(s))) Ok(Some(Command::ShortCircuitConjunction(s)))
} }
Some(tail) => { Some(tail) => Ok(Some(Command::ShortCircuitConjunction(vec![ head, tail ]))),
Ok(Some(Command::ShortCircuitConjunction(vec![ head, tail ]))) None => Err(LexError::UnexpectedEnd(vec![Some('&'), Some('&')]))
}
None => {
Err(LexError::UnexpectedEnd(vec![Some('&'), Some('&')]))
}
} }
} }
Some(c) => { Some(c) => Err(LexError::UnexpectedToken(*c)),
Err(LexError::UnexpectedToken(*c))
}
None => { None => {
// todo: // todo:
// background job // background job
@ -315,12 +304,17 @@ where It: Iterator<Item = char>
} }
} }
} }
Some(c) => { Some(')') => {
Err(LexError::UnexpectedToken(*c)) eprintln!("got )");
} chars.next();
None => { if depth > 0 {
Ok(Some(head)) Ok(Some(head))
} else {
Err(LexError::UnexpectedToken(')'))
}
} }
Some(c) => Err(LexError::UnexpectedToken(*c)),
None => Ok(Some(head))
} }
} else { } else {
Ok(None) Ok(None)
@ -351,20 +345,23 @@ where It: 'a + Iterator<Item = char> {
// Subshell // Subshell
Some('(') => { Some('(') => {
self.chars.next(); self.chars.next();
/*
let subcmd_str = DelimIter::new(&mut self.chars, vec![(Some(')'), true)]).try_collect::<String>(); let subcmd_str = DelimIter::new(&mut self.chars, vec![(Some(')'), true)]).try_collect::<String>();
match subcmd_str { match subcmd_str {
Ok(subcmd_str) => { Ok(subcmd_str) => {
match parse_cmd(&mut subcmd_str.chars().peekable()) { */
match parse_cmd(&mut self.chars, 1) {
Ok(Some(subcmd)) => { Ok(Some(subcmd)) => {
Some(Ok(WordSegment::Subshell(subcmd))) Some(Ok(WordSegment::Subshell(subcmd)))
} }
Ok(None) => None, Ok(None) => None,
Err(err) => Some(Err(err)) Err(err) => Some(Err(err))
} }
/*
} }
Err(err) => Some(Err(err)) Err(err) => Some(Err(err))
} }
*/
} }
// plain parameter name e.g. `$PARAM` // plain parameter name e.g. `$PARAM`
@ -426,8 +423,9 @@ where It: 'a + Iterator<Item = char> {
Some('"') => { Some(parse_doublequoted(self.chars)) }, Some('"') => { Some(parse_doublequoted(self.chars)) },
Some('\'') => { Some(parse_quoted(self.chars)) }, Some('\'') => { Some(parse_quoted(self.chars)) },
Some('$') => { Some('$') => {
SubstLexer{ chars: &mut self.chars }.next() SubstLexer{ chars: &mut self.chars, depth: 1 }.next()
} }
Some(')') => { None }
Some(c) => { Some(c) => {
let s : Result<String, LexError> = DelimIter::new_shell_word(self.chars).collect(); let s : Result<String, LexError> = DelimIter::new_shell_word(self.chars).collect();
match s { match s {