• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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