Skip to content
Snippets Groups Projects
Select Git revision
  • home_exam
  • wip
  • master default protected
3 results

main_span_expr_custom_err2.rs

Blame
  • Forked from Per Lindgren / D7050E
    14 commits behind the upstream repository.
    main_span_expr_custom_err2.rs 4.17 KiB
    extern crate nom;
    
    use nom::{
        branch::alt,
        bytes::complete::tag,
        character::complete::{digit1, multispace0},
        combinator::map,
        error,
        sequence::{preceded, tuple},
        Err,
    };
    
    use nom_locate::LocatedSpan;
    
    type Span<'a> = LocatedSpan<&'a str>;
    
    #[derive(Debug)]
    pub struct Error<'a>(Span<'a>, Option<Span<'a>>, ErrorKind);
    type IResult<'a, I, O, E = Error<'a>> = Result<(I, O), Err<E>>;
    
    impl<'a> error::ParseError<Span<'a>> for Error<'a> {
        fn from_error_kind(input: Span<'a>, kind: error::ErrorKind) -> Self {
            Error(input, None, ErrorKind::Nom(kind))
        }
    
        fn append(_: Span<'a>, _: error::ErrorKind, other: Self) -> Self {
            other
        }
    }
    
    #[derive(Debug)]
    enum ErrorKind {
        ParseIntError(std::num::ParseIntError),
        Nom(error::ErrorKind),
    }
    
    #[derive(Debug, PartialEq)]
    pub enum Op {
        Add,
        Sub,
    }
    
    type SpanOp<'a> = (Span<'a>, Op);
    
    fn parse_op(i: Span) -> IResult<Span, SpanOp> {
        alt((
            map(tag("+"), |s| (s, Op::Add)),
            map(tag("-"), |s| (s, Op::Sub)),
        ))(i)
    }
    
    #[derive(Debug, PartialEq)]
    pub enum Expr<'a> {
        Num(i32),
        Num64(i64),
        BinOp(Box<SpanExpr<'a>>, SpanOp<'a>, Box<SpanExpr<'a>>),
    }
    
    type SpanExpr<'a> = (Span<'a>, Expr<'a>);
    
    pub fn parse_i32<'a>(i: Span<'a>) -> IResult<Span<'a>, SpanExpr> {
        let (i, digits) = digit1(i)?;
        match digits.fragment.parse() {
            Ok(int) => Ok((i, (digits, Expr::Num(int)))),
            Err(e) => Err(Err::Error(Error(
                i,
                Some(digits),
                ErrorKind::ParseIntError(e),
            ))),
        }
    }
    
    pub fn parse_i64<'a>(i: Span<'a>) -> IResult<Span<'a>, SpanExpr> {
        let (i, digits) = digit1(i)?;
        match digits.fragment.parse() {
            Ok(int) => Ok((i, (digits, Expr::Num64(int)))),
            Err(e) => Err(Err::Error(Error(
                i,
                Some(digits),
                ErrorKind::ParseIntError(e),
            ))),
        }
    }
    
    fn parse_expr(i: Span) -> IResult<Span, SpanExpr> {
        alt((
            map(
                tuple((
                    alt((parse_i32, parse_i64)),
                    preceded(multispace0, parse_op),
                    parse_expr_ms,
                )),
                |(l, op, r)| (i, Expr::BinOp(Box::new(l), op, Box::new(r))),
            ),
            parse_i32,
            parse_i64,
        ))(i)
    }
    
    fn parse_expr_ms(i: Span) -> IResult<Span, SpanExpr> {
        preceded(multispace0, parse_expr)(i)
    }
    
    // dumps a Span into a String
    fn dump_span(s: &Span) -> String {
        format!(
            "[line :{:?}, col:{:?}, {:?}]",
            s.line,
            s.get_column(),
            s.fragment
        )
    }
    
    // dumps a SpanExpr into a String
    fn dump_expr(se: &SpanExpr) -> String {
        let (s, e) = se;
        match e {
            Expr::Num(_) => dump_span(s),
            Expr::Num64(_) => dump_span(s),
            Expr::BinOp(l, (sop, _), r) => {
                format!("<{} {} {}>", dump_expr(l), dump_span(sop), dump_expr(r))
            }
        }
    }
    
    fn main() {
        let i = "\n    1+2+10000- \n3";
        // uncomment below for an error example
        let i = "\n    1+200000000000000000+a10000- \n3";
        let pe = parse_expr_ms(Span::new(i));
        println!("pe: {:?}\n", pe);
        match pe {
            Ok((_, (s, e))) => {
                println!(
                    "ok, span for expression: {:?}, \n\tline: {:?}, \n\tcolumn: {:?}\n",
                    s,
                    s.line,
                    s.get_column()
                );
                println!("raw e: {:?}\n", &e);
                println!("pretty e: {}\n", dump_expr(&(s, e)));
            }
            Err(Err::Failure(Error(_, Some(s), err))) => {
                println!(
                    "{:?} error at:\n\tline: {:?}\n\tcolumn: {:?}\n\tValue: {:?}\n",
                    err,
                    s.line,
                    s.get_column(),
                    s.fragment,
                );
                println!("raw s: {:?}", &s);
            }
            Err(err) => Err(err).unwrap(),
        }
    }
    
    // In this example, we have a `parse_expr_ms` is the "top" level parser.
    // It consumes white spaces, allowing the location information to reflect the exact
    // positions in the input file.
    //
    // The dump_expr will create a pretty printing of the expression with spans for
    // each terminal. This will be useful for later for precise type error reporting.
    //
    // The extra field is not used, it can be used for metadata, such as filename.