extern crate nom; use nom::{ branch::alt, bytes::complete::tag, character::complete::{digit1, multispace0}, combinator::map, sequence::{preceded, tuple}, Err, error, }; 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 { Parse, 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), 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)?; if let Ok(int) = digits.fragment.parse() { Ok(( i, (digits, Expr::Num(int)), )) } else { Err(Err::Failure(Error( i, Some(digits), ErrorKind::Parse, ))) } } fn parse_expr(i: Span) -> IResult<Span, SpanExpr> { alt(( map( tuple((parse_i32, preceded(multispace0, parse_op), parse_expr_ms)), |(l, op, r)| (i, Expr::BinOp(Box::new(l), op, Box::new(r))), ), parse_i32, ))(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::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.