Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • home_exam
  • master
  • wip
3 results

Target

Select target project
No results found
Select Git revision
  • home_exam
  • master
  • wip
3 results
Show changes

Commits on Source 2

5 files
+ 193
176
Compare changes
  • Side-by-side
  • Inline

Files

+1 −0
Original line number Original line Diff line number Diff line
@@ -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)


+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 ;)
@@ -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!(),
    }
}
+6 −6
Original line number Original line Diff line number Diff line
@@ -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 {
@@ -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>);
+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!(),
    }
}