5 files + 193 − 176 Side-by-side Compare changes Side-by-side Inline Show whitespace changes Files 5 README.md +1 −0 Original line number Original line Diff line number Diff line Loading @@ -20,6 +20,7 @@ Practical assigment: - Expressions (includig function calls) - Expressions (includig function calls) - Primitive types (boolean, i32) and their literals - Primitive types (boolean, i32) and their literals - Explicit types everywhere - Explicit types everywhere - Explicit return(s) - Begin writing a parser for expressions in Rust using `nom` (parser combinator library) - Begin writing a parser for expressions in Rust using `nom` (parser combinator library) Loading examples/tmp.rs +1 −169 Original line number Original line Diff line number Diff line extern crate nom; extern crate nom; use std::iter::Peekable; use std::slice::Iter; use nom::{ use crust::parse::test; 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), } } fn main() { fn main() { test("- -1 + + 1", - -1 + 1); // rust does not allow + as a unary op (I do ;) test("- -1 + + 1", - -1 + 1); // rust does not allow + as a unary op (I do ;) Loading @@ -127,59 +14,4 @@ fn main() { test("1*2+3+3*21-a12+2", 1 * 2 + 3 + 3 * 21 - 12 + 2); 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!(), } } src/ast.rs +6 −6 Original line number Original line Diff line number Diff line Loading @@ -2,7 +2,7 @@ use nom_locate::LocatedSpan; use nom_locate::LocatedSpan; type Span<'a> = LocatedSpan<&'a str>; pub type Span<'a> = LocatedSpan<&'a str>; #[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)] pub enum Op { pub enum Op { Loading @@ -21,13 +21,13 @@ pub enum Op { type SpanOp<'a> = (Span<'a>, Op); type SpanOp<'a> = (Span<'a>, Op); #[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)] pub enum Expr { pub enum Expr<'a> { Num(i32), Num(i32), Par(Box<Expr>), Par(Box<SpanExpr<'a>>), // Identifier // Identifier // Function application // Function application BinOp(Op, Box<Expr>, Box<Expr>), BinOp(Op, Box<SpanExpr<'a>>, Box<SpanExpr<'a>>), UnaryOp(Op, Box<Expr>), UnaryOp(Op, Box<SpanExpr<'a>>), } } type SpanExpr<'a> = (Span<'a>, Expr); pub type SpanExpr<'a> = (Span<'a>, Expr<'a>); src/lib.rs +2 −1 Original line number Original line Diff line number Diff line // lib // lib pub mod ast; pub mod ast; pub mod parse; src/parse.rs 0 → 100644 +183 −0 Original line number Original line Diff line number Diff line 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!(), } }
README.md +1 −0 Original line number Original line Diff line number Diff line Loading @@ -20,6 +20,7 @@ Practical assigment: - Expressions (includig function calls) - Expressions (includig function calls) - Primitive types (boolean, i32) and their literals - Primitive types (boolean, i32) and their literals - Explicit types everywhere - Explicit types everywhere - Explicit return(s) - Begin writing a parser for expressions in Rust using `nom` (parser combinator library) - Begin writing a parser for expressions in Rust using `nom` (parser combinator library) Loading
examples/tmp.rs +1 −169 Original line number Original line Diff line number Diff line extern crate nom; extern crate nom; use std::iter::Peekable; use std::slice::Iter; use nom::{ use crust::parse::test; 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), } } fn main() { fn main() { test("- -1 + + 1", - -1 + 1); // rust does not allow + as a unary op (I do ;) test("- -1 + + 1", - -1 + 1); // rust does not allow + as a unary op (I do ;) Loading @@ -127,59 +14,4 @@ fn main() { test("1*2+3+3*21-a12+2", 1 * 2 + 3 + 3 * 21 - 12 + 2); 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!(), } }
src/ast.rs +6 −6 Original line number Original line Diff line number Diff line Loading @@ -2,7 +2,7 @@ use nom_locate::LocatedSpan; use nom_locate::LocatedSpan; type Span<'a> = LocatedSpan<&'a str>; pub type Span<'a> = LocatedSpan<&'a str>; #[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)] pub enum Op { pub enum Op { Loading @@ -21,13 +21,13 @@ pub enum Op { type SpanOp<'a> = (Span<'a>, Op); type SpanOp<'a> = (Span<'a>, Op); #[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)] pub enum Expr { pub enum Expr<'a> { Num(i32), Num(i32), Par(Box<Expr>), Par(Box<SpanExpr<'a>>), // Identifier // Identifier // Function application // Function application BinOp(Op, Box<Expr>, Box<Expr>), BinOp(Op, Box<SpanExpr<'a>>, Box<SpanExpr<'a>>), UnaryOp(Op, Box<Expr>), UnaryOp(Op, Box<SpanExpr<'a>>), } } type SpanExpr<'a> = (Span<'a>, Expr); pub type SpanExpr<'a> = (Span<'a>, Expr<'a>);
src/lib.rs +2 −1 Original line number Original line Diff line number Diff line // lib // lib pub mod ast; pub mod ast; pub mod parse;
src/parse.rs 0 → 100644 +183 −0 Original line number Original line Diff line number Diff line 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!(), } }