1 //! An example of using `peg` with `codespan_reporting`.
2 //!
3 //! To run this example, execute the following command from the top level of
4 //! this repository:
5 //!
6 //! ```sh
7 //! cargo run --example peg_calculator
8 //! ```
9
10 use codespan_reporting::diagnostic::{Diagnostic, Label};
11 use codespan_reporting::files::SimpleFile;
12 use codespan_reporting::term;
13 use codespan_reporting::term::termcolor::{ColorChoice, StandardStream};
14 use rustyline::error::ReadlineError;
15 use rustyline::Editor;
16
17 peg::parser! {
18 grammar arithmetic() for str {
19 rule number() -> i64
20 = n:$(['0'..='9']+) { n.parse().unwrap() }
21
22 pub rule calculate() -> i64 = precedence!{
23 x:(@) "+" y:@ { x + y }
24 x:(@) "-" y:@ { x - y }
25 "-" v:@ { - v }
26 --
27 x:(@) "*" y:@ { x * y }
28 x:(@) "/" y:@ { x / y }
29 --
30 x:@ "^" y:(@) { i64::pow(x, y as u32) }
31 v:@ "!" { (1..v+1).product() }
32 --
33 "(" v:calculate() ")" { v }
34 n:number() { n }
35 }
36 }
37 }
38
main() -> anyhow::Result<()>39 fn main() -> anyhow::Result<()> {
40 let writer = StandardStream::stderr(ColorChoice::Always);
41 let config = codespan_reporting::term::Config::default();
42 let mut editor = Editor::<()>::new();
43
44 loop {
45 let line = match editor.readline("> ") {
46 Ok(line) => line,
47 Err(ReadlineError::Interrupted) | Err(ReadlineError::Eof) => return Ok(()),
48 Err(error) => return Err(error.into()),
49 };
50
51 match arithmetic::calculate(&line) {
52 Ok(number) => println!("{}", number),
53 Err(error) => {
54 let file = SimpleFile::new("<repl>", line);
55
56 let start = error.location.offset;
57 let diagnostic = Diagnostic::error()
58 .with_message("parse error")
59 .with_labels(vec![
60 Label::primary((), start..start).with_message("parse error")
61 ])
62 .with_notes(vec![format!("expected: {}", error.expected)]);
63
64 term::emit(&mut writer.lock(), &config, &file, &diagnostic)?;
65 }
66 }
67 }
68 }
69