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
Loading items

Target

Select target project
  • mikala3/d7050e
  • 97gushan/d7050e
  • Abrikot/d7050e
  • Hammarkvast/d7050e
  • banunkers/d7050e
  • markhakansson/d7050e
  • inaule-6/d7050e
  • pln/d7050e
  • widforss/d7050e
  • arostr-5/d7050e
  • Grumme2/d7050e
  • brathen/d7050e
12 results
Select Git revision
Loading items
Show changes
Commits on Source (2)
......@@ -20,6 +20,7 @@ Practical assigment:
- Expressions (includig function calls)
- Primitive types (boolean, i32) and their literals
- Explicit types everywhere
- Explicit return(s)
- Begin writing a parser for expressions in Rust using `nom` (parser combinator library)
......
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!(),
}
}
......@@ -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>);
// lib
pub mod ast;
pub mod parse;
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!(),
}
}