use panfix::{pattern, Grammar, GrammarError, ParseError, Parser, Source, Visitor};
fn make_parser() -> Result<Parser<&'static str>, GrammarError> {
let mut grammar = Grammar::new_with_unicode_whitespace()?;
grammar.regex("Number", r#"[0-9]+[.]?[0-9]*([eE][+-]?[0-9]+)?"#)?;
grammar.op("Parens", pattern!("(" ")"))?;
grammar.left_assoc();
grammar.op("Times", pattern!(_ "*" _))?;
grammar.op("Divide", pattern!(_ "/" _))?;
grammar.left_assoc();
grammar.op("Negative", pattern!("-" _))?;
grammar.op("Plus", pattern!(_ "+" _))?;
grammar.op("Minus", pattern!(_ "-" _))?;
grammar.left_assoc();
grammar.op("Log", pattern!("log" "of" _))?;
grammar.finish()
}
fn calc<'s>(expr: Visitor<'s, '_, '_, &'static str>) -> Result<f64, ParseError<'s>> {
match expr.token() {
"Blank" => Err(expr.error("missing expression", "Missing expression.")),
"Juxtapose" => Err(expr.error(
"extra expression",
"Multiple expressions. There can only be one.",
)),
"Number" => match expr.source().parse::<f64>() {
Ok(n) => Ok(n),
Err(err) => Err(expr.error("invalid number", &format!("Invalid number '{}'", err))),
},
"Times" => Ok(calc(expr.child(0))? * calc(expr.child(1))?),
"Divide" => Ok(calc(expr.child(0))? / calc(expr.child(1))?),
"Parens" => calc(expr.child(0)),
"Negative" => Ok(-calc(expr.child(0))?),
"Plus" => Ok(calc(expr.child(0))? + calc(expr.child(1))?),
"Minus" => Ok(calc(expr.child(0))? - calc(expr.child(1))?),
"Log" if expr.child(0).token() == "Blank" => Ok(f64::ln(calc(expr.child(1))?)),
"Log" => Ok(calc(expr.child(1))?.log(calc(expr.child(0))?)),
op => panic!("Bug: missing case in parser: {}", op),
}
}
fn main() {
eprintln!(
"Reading from stdin. If you're using this interactively, end your input with Ctrl-D."
);
let parser = make_parser().unwrap();
let source = Source::from_stdin().unwrap();
match parser.parse(&source) {
Ok(tree) => match calc(tree.visitor()) {
Ok(answer) => println!("{}", answer),
Err(err) => println!("{}", err),
},
Err(err) => println!("{}", err),
}
}