#![forbid(unsafe_code)]
#[macro_use]
extern crate pomelo;
pub mod concrete;
mod lexer;
mod parser;
pub mod renaming;
pub mod rewriter;
pub mod stats;
pub mod visitors;
pub type Numeral = num::bigint::BigUint;
pub type Decimal = num::rational::BigRational;
pub type Nibble = u8;
pub type Hexadecimal = Vec<Nibble>;
pub type Binary = Vec<bool>;
pub use concrete::Error;
pub use lexer::Position;
pub struct CommandStream<R, T>
where
R: std::io::BufRead,
T: visitors::Smt2Visitor,
{
lexer: lexer::Lexer<R>,
visitor: T,
position: Position,
}
impl<R, T> CommandStream<R, T>
where
R: std::io::BufRead,
T: visitors::Smt2Visitor,
{
pub fn new(reader: R, visitor: T, path: Option<String>) -> Self {
Self {
lexer: lexer::Lexer::new(reader),
visitor,
position: Position {
path,
..Position::default()
},
}
}
pub fn visitor(&self) -> &T {
&self.visitor
}
pub fn visitor_mut(&mut self) -> &mut T {
&mut self.visitor
}
pub fn into_visitor(self) -> T {
self.visitor
}
}
impl<R, T> Iterator for CommandStream<R, T>
where
R: std::io::BufRead,
T: visitors::Smt2Visitor,
{
type Item = Result<T::Command, T::Error>;
#[allow(clippy::while_let_on_iterator)]
fn next(&mut self) -> Option<Result<T::Command, T::Error>> {
let mut parser = parser::Parser::new((&mut self.visitor, &mut self.position));
let mut unmatched_paren = 0;
while let Some(token) = self.lexer.next() {
match &token {
parser::Token::LeftParen => unmatched_paren += 1,
parser::Token::RightParen => {
if unmatched_paren > 0 {
unmatched_paren -= 1;
}
}
_ => (),
}
self.lexer.update_position(parser.extra_mut().1);
if let Err(err) = parser.parse(token) {
return Some(Err(err));
}
if unmatched_paren == 0 {
return match parser.end_of_input() {
Ok((command, _)) => Some(Ok(command)),
Err(err) => Some(Err(err)),
};
}
}
if unmatched_paren > 0 {
let extra = parser.into_extra();
Some(Err(extra.0.parsing_error(
extra.1.clone(),
"unexpected end of input".into(),
)))
} else {
None
}
}
}
#[test]
fn test_command_stream_error() {
let input = b"(echo \"Hello world!\")(exit f)";
let builder = concrete::SyntaxBuilder::default();
let stream = CommandStream::new(&input[..], builder, None);
let commands = stream.collect::<Vec<_>>();
assert!(matches!(
commands[..],
[
Ok(concrete::Command::Echo { .. }),
Err(concrete::Error::SyntaxError(..)),
Err(concrete::Error::SyntaxError(..)),
]
));
assert_eq!(
commands[0].as_ref().unwrap().to_string(),
"(echo \"Hello world!\")"
);
}
#[test]
fn test_command_stream_invalid_token() {
let input = b"(echo \"Hello world!\")(exit \000)";
let builder = concrete::SyntaxBuilder::default();
let stream = CommandStream::new(&input[..], builder, None);
let commands = stream.collect::<Vec<_>>();
assert!(matches!(
commands[..],
[
Ok(concrete::Command::Echo { .. }),
Err(concrete::Error::ParsingError(..)),
]
));
assert_eq!(
commands[0].as_ref().unwrap().to_string(),
"(echo \"Hello world!\")"
);
}