diff --git a/examples/tmp.rs b/examples/tmp.rs index ec7393d6cf7d267561468839a8a9a05f1ff2f81e..c84931cd0b29054a2e8ad0c97a852dcb820257db 100644 --- a/examples/tmp.rs +++ b/examples/tmp.rs @@ -1,120 +1,7 @@ extern crate nom; -use std::iter::Peekable; -use std::slice::Iter; -use nom::{ - branch::alt, - bytes::complete::tag, - character::complete::char, - character::complete::{digit1, multispace0}, - combinator::{cut, map}, - error::ParseError, - multi::many1, - sequence::{delimited, preceded}, - IResult, -}; - -use crust::ast::{Op, Expr}; - - -pub fn parse_i32(i: &str) -> IResult<&str, i32> { - map(digit1, |digit_str: &str| digit_str.parse::<i32>().unwrap())(i) -} - -fn parse_op(i: &str) -> IResult<&str, Op> { - alt(( - map(tag("=="), |_| Op::Eq), - map(tag("!="), |_| Op::Neq), - map(tag("**"), |_| Op::Pow), - map(tag("&&"), |_| Op::And), - map(tag("||"), |_| Op::Or), - map(tag("+"), |_| Op::Add), - map(tag("-"), |_| Op::Sub), - map(tag("*"), |_| Op::Mul), - map(tag("/"), |_| Op::Div), - map(tag("!"), |_| Op::Not), - ))(i) -} - -#[derive(Debug, Clone, PartialEq)] -pub enum Token { - Num(i32), - Par(Vec<Token>), - Op(Op), -} - -fn parse_terminal(i: &str) -> IResult<&str, Token> { - alt(( - map(parse_i32, |v| Token::Num(v)), - map(parse_par(parse_tokens), |tokens| Token::Par(tokens)), - ))(i) -} - -fn parse_token(i: &str) -> IResult<&str, Token> { - preceded( - multispace0, - alt((map(parse_op, |op| Token::Op(op)), parse_terminal)), - )(i) -} - -fn parse_tokens(i: &str) -> IResult<&str, Vec<Token>> { - many1(parse_token)(i) -} - -fn compute_atom(t: &mut Peekable<Iter<Token>>) -> Expr { - match t.next() { - Some(Token::Num(i)) => Expr::Num(*i), - Some(Token::Par(v)) => climb(&mut v.iter().peekable(), 0), - Some(Token::Op(op)) => Expr::UnaryOp(*op, Box::new(climb(t, 4))), // assume highest precedence - _ => panic!("error in compute atom"), - } -} - -fn climb(t: &mut Peekable<Iter<Token>>, min_prec: u8) -> Expr { - let mut result = compute_atom(t); - - loop { - match t.peek() { - Some(Token::Op(op)) => { - let (prec, ass) = get_prec(op); - if prec < min_prec { - break; - }; - let next_prec = prec - + match ass { - Ass::Left => 1, - _ => 0, - }; - t.next(); - let rhs = climb(t, next_prec); - result = Expr::BinOp(*op, Box::new(result), Box::new(rhs)) - } - _ => { - break; - } - } - } - result -} - -fn test(s: &str, v: i32) { - match parse_tokens(s) { - Ok(("", t)) => { - let mut t = t.iter().peekable(); - println!("{:?}", &t); - let e = climb(&mut t, 0); - println!("{:?}", &e); - println!("eval {} {}", math_eval(&e), v); - assert_eq!(math_eval(&e), v); - } - Ok((s, t)) => println!( - "parse incomplete, \n parsed tokens \t{:?}, \n remaining \t{:?}", - t, s - ), - Err(err) => println!("{:?}", err), - } -} +use crust::parse::test; fn main() { test("- -1 + + 1", - -1 + 1); // rust does not allow + as a unary op (I do ;) @@ -127,59 +14,4 @@ fn main() { test("1*2+3+3*21-a12+2", 1 * 2 + 3 + 3 * 21 - 12 + 2); } -// helpers -fn parse_par<'a, O, F, E>( - inner: F, -) -> impl Fn(&'a str) -> IResult<&'a str, O, E> -where - F: Fn(&'a str) -> IResult<&'a str, O, E>, - E: ParseError<&'a str>, -{ - // delimited allows us to split up the input - // cut allwos us to consume the input (and prevent backtracking) - delimited(char('('), preceded(multispace0, inner), cut(char(')'))) -} - -fn math_eval(e: &Expr) -> i32 { - match e { - Expr::Num(i) => *i, - Expr::BinOp(op, l, r) => { - let lv = math_eval(l); - let rv = math_eval(r); - match op { - Op::Add => lv + rv, - Op::Sub => lv - rv, - Op::Mul => lv * rv, - Op::Div => lv / rv, - Op::Pow => lv.pow(rv as u32), - _ => unimplemented!(), - } - } - Expr::UnaryOp(op, e) => { - let e = math_eval(e); - match op { - Op::Add => e, - Op::Sub => -e, - _ => unimplemented!(), - } - } - _ => unimplemented!(), - } -} -#[derive(Debug, Copy, Clone, PartialEq)] -enum Ass { - Left, - Right, -} - -fn get_prec(op: &Op) -> (u8, Ass) { - match op { - Op::Add => (1, Ass::Left), - Op::Sub => (1, Ass::Left), - Op::Mul => (2, Ass::Left), - Op::Div => (2, Ass::Left), - Op::Pow => (3, Ass::Right), - _ => unimplemented!(), - } -} diff --git a/src/ast.rs b/src/ast.rs index 3c2f8b41459bac8bfe773ef894b26a9499f4fa64..bc4464e33e5158c2db7a2431dec5a2a71ed7f55d 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -2,7 +2,7 @@ use nom_locate::LocatedSpan; -type Span<'a> = LocatedSpan<&'a str>; +pub type Span<'a> = LocatedSpan<&'a str>; #[derive(Debug, Clone, Copy, PartialEq)] pub enum Op { @@ -21,13 +21,13 @@ pub enum Op { type SpanOp<'a> = (Span<'a>, Op); #[derive(Debug, Clone, PartialEq)] -pub enum Expr { +pub enum Expr<'a> { Num(i32), - Par(Box<Expr>), + Par(Box<SpanExpr<'a>>), // Identifier // Function application - BinOp(Op, Box<Expr>, Box<Expr>), - UnaryOp(Op, Box<Expr>), + BinOp(Op, Box<SpanExpr<'a>>, Box<SpanExpr<'a>>), + UnaryOp(Op, Box<SpanExpr<'a>>), } -type SpanExpr<'a> = (Span<'a>, Expr); +pub type SpanExpr<'a> = (Span<'a>, Expr<'a>); diff --git a/src/lib.rs b/src/lib.rs index 95787c26b88a62b43f78980ce41580778a495ae1..b5afecafe3d0a66ea3b66ae31359b41646bb88aa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ // lib -pub mod ast; \ No newline at end of file +pub mod ast; +pub mod parse; diff --git a/src/parse.rs b/src/parse.rs new file mode 100644 index 0000000000000000000000000000000000000000..77e8edc0807348a39b4e79deb9bf096086e193c6 --- /dev/null +++ b/src/parse.rs @@ -0,0 +1,183 @@ +extern crate nom; + +use std::iter::Peekable; +use std::slice::Iter; + +use nom::{ + branch::alt, + bytes::complete::tag, + character::complete::char, + character::complete::{digit1, multispace0}, + combinator::{cut, map}, + error::ParseError, + multi::many1, + sequence::{delimited, preceded}, + IResult, +}; + +use crate::ast::{Expr, Op, Span, SpanExpr}; + +pub fn parse_i32(i: Span) -> IResult<Span, (Span, i32)> { + map(digit1, |digit_str: Span| { + (digit_str, digit_str.fragment.parse::<i32>().unwrap()) + })(i) +} + +fn parse_op(i: Span) -> IResult<Span, (Span, Op)> { + alt(( + map(tag("=="), |s| (s, Op::Eq)), + map(tag("!="), |s| (s, Op::Neq)), + map(tag("**"), |s| (s, Op::Pow)), + map(tag("&&"), |s| (s, Op::And)), + map(tag("||"), |s| (s, Op::Or)), + map(tag("+"), |s| (s, Op::Add)), + map(tag("-"), |s| (s, Op::Sub)), + map(tag("*"), |s| (s, Op::Mul)), + map(tag("/"), |s| (s, Op::Div)), + map(tag("!"), |s| (s, Op::Not)), + ))(i) +} + +#[derive(Debug, Clone, PartialEq)] +pub enum Token<'a> { + Num(i32), + Par(Vec<SpanToken<'a>>), + Op(Op), +} + +type SpanToken<'a> = (Span<'a>, Token<'a>); + +fn parse_terminal(i: Span) -> IResult<Span, SpanToken> { + alt(( + map(parse_i32, |(s, v)| (s, Token::Num(v))), + map(parse_par(parse_tokens), |(s, tokens)| { + (s, Token::Par(tokens)) + }), + ))(i) +} + +fn parse_token(i: Span) -> IResult<Span, SpanToken> { + preceded( + multispace0, + alt((map(parse_op, |(s, op)| (s, Token::Op(op))), parse_terminal)), + )(i) +} + +// I think the outer span is wrong +fn parse_tokens(i: Span) -> IResult<Span, (Span, Vec<SpanToken>)> { + map(many1(parse_token), |tokens| (i, tokens))(i) +} + +fn compute_atom<'a>(t: &mut Peekable<Iter<SpanToken<'a>>>) -> SpanExpr<'a> { + match t.next() { + Some((s, Token::Num(i))) => (*s, Expr::Num(*i)), + Some((_, Token::Par(v))) => climb(&mut v.iter().peekable(), 0), + Some((s, Token::Op(op))) => (*s, Expr::UnaryOp(*op, Box::new(climb(t, 4)))), // assume highest precedence + _ => panic!("error in compute atom"), + } +} + +fn climb<'a>( + t: &mut Peekable<Iter<SpanToken<'a>>>, + min_prec: u8, +) -> SpanExpr<'a> { + let mut result: SpanExpr = compute_atom(t); + + loop { + match t.peek() { + Some((s, Token::Op(op))) => { + let (prec, ass) = get_prec(op); + if prec < min_prec { + break; + }; + let next_prec = prec + + match ass { + Ass::Left => 1, + _ => 0, + }; + t.next(); + let rhs = climb(t, next_prec); + result = (*s, Expr::BinOp(*op, Box::new(result), Box::new(rhs))) + } + _ => { + break; + } + } + } + result +} + +pub fn test(s: &str, v: i32) { + match parse_tokens(Span::new(s)) { + Ok((Span { fragment: "", .. }, (_, t))) => { + let mut t = t.iter().peekable(); + println!("{:?}", &t); + let e = climb(&mut t, 0); + println!("{:?}", &e); + println!("eval {} {}", math_eval(&e), v); + assert_eq!(math_eval(&e), v); + } + Ok((s, t)) => println!( + "parse incomplete, \n parsed tokens \t{:?}, \n remaining \t{:?}", + t, s + ), + Err(err) => println!("{:?}", err), + } +} + +// helpers +fn parse_par<'a, O, F, E>( + inner: F, +) -> impl Fn(Span<'a>) -> IResult<Span<'a>, O, E> +where + F: Fn(Span<'a>) -> IResult<Span<'a>, O, E>, + E: ParseError<Span<'a>>, +{ + // delimited allows us to split up the input + // cut allwos us to consume the input (and prevent backtracking) + delimited(char('('), preceded(multispace0, inner), cut(char(')'))) +} + +fn math_eval(e: &SpanExpr) -> i32 { + match e.clone().1 { + Expr::Num(i) => i, + Expr::BinOp(op, l, r) => { + let lv = math_eval(&l); + let rv = math_eval(&r); + match op { + Op::Add => lv + rv, + Op::Sub => lv - rv, + Op::Mul => lv * rv, + Op::Div => lv / rv, + Op::Pow => lv.pow(rv as u32), + _ => unimplemented!(), + } + } + Expr::UnaryOp(op, e) => { + let e = math_eval(&e); + match op { + Op::Add => e, + Op::Sub => -e, + _ => unimplemented!(), + } + } + _ => unimplemented!(), + } +} + +#[derive(Debug, Copy, Clone, PartialEq)] +enum Ass { + Left, + Right, +} + +fn get_prec(op: &Op) -> (u8, Ass) { + match op { + Op::Add => (1, Ass::Left), + Op::Sub => (1, Ass::Left), + Op::Mul => (2, Ass::Left), + Op::Div => (2, Ass::Left), + Op::Pow => (3, Ass::Right), + _ => unimplemented!(), + } +}