use crate::runtime::op::{Operation, ScopePersister, SigStatement};
use crate::runtime::{Environment, Event, Scope};
use crate::{ast, lex, lint};
use regex::Regex;
use std::fs::File;
use std::io::{BufRead, Read, Write};
pub struct Interpreter {
node: ast::Seq,
sep: Regex,
implicit_print: bool,
}
pub struct Builder {
filename: Option<String>,
expression: Option<String>,
sep: Option<Regex>,
print: Option<bool>,
}
impl Interpreter {
pub fn process<R: BufRead, W: Write>(&self, sin: &mut R, sout: &mut W) {
let mut iter = sin.lines();
let mut env = Environment::new(sout, &self.node, self.sep.clone());
if cfg!(feature = "envvar") {
env.push(Scope::env());
}
let implicit_print = !self.node.significant();
env.event = Event::Begin;
self.node.perform(&mut env);
while let Some(Ok(line)) = iter.next() {
env.lineno += 1;
env.event = Event::Line(line);
self.node.persist_scope(&mut env);
self.node.perform(&mut env);
if env.finished() {
return;
}
if implicit_print && self.implicit_print {
env.print_event();
}
}
env.event = Event::End;
self.node.perform(&mut env);
}
pub fn lint(&self) -> Vec<lint::LintMessage> {
lint::lint(&self.node)
}
pub fn builder() -> Builder {
Builder {
filename: None,
expression: None,
sep: None,
print: None,
}
}
}
impl Builder {
pub fn filename(&mut self, filename: String) -> &mut Self {
self.filename = Some(filename);
self
}
pub fn expression(&mut self, expression: String) -> &mut Self {
self.expression = Some(expression);
self
}
pub fn sep(&mut self, sep: Regex) -> &mut Self {
self.sep = Some(sep);
self
}
pub fn print(&mut self, print: bool) -> &mut Self {
self.print = Some(print);
self
}
pub fn build(&mut self) -> Result<Interpreter, String> {
let node = match (&self.filename, &self.expression) {
(None, None) => return Err(String::from("Neither an expression or a file was given")),
(Some(_), Some(_)) => {
return Err(String::from(
"Both expression and file should not be given at the same time",
))
}
(Some(ref file), None) => {
let mut file = match File::open(file) {
Ok(f) => f,
Err(err) => {
return Err(format!(
"unable to open file romulus file '{}': {}",
file, err
))
}
};
let mut buf = String::new();
if let Err(err) = file.read_to_string(&mut buf) {
return Err(format!("unable to read romulus file: {}", err));
}
let tokens = lex::lex(buf.as_ref())?;
ast::parse(tokens)?
}
(None, Some(expr)) => {
let tokens = lex::lex(expr.as_ref())?;
ast::parse(tokens)?
}
};
let sep = self
.sep
.clone()
.unwrap_or_else(|| Regex::new(" +").unwrap());
let implicit_print = self.print.unwrap_or(true);
Ok(Interpreter {
node,
sep,
implicit_print,
})
}
}