freinn
freinn

Reputation: 1079

How to divide my program in modules?

I'm having problems dividing my code into modules, I have a lot of compilation errors but I've tried something. The single file program works and evaluates expressions with the four basic math operators (+-*/), nested with parentheses or not.

I'm new to Rust so I'm having trouble understanding the module system and I want you to help me understand it and do it well from the beginning.

Here is the code (all files are in the src/ folder of the cargo project):

Operator.rs

// An attribute to hide warnings for unused code.
#![allow(dead_code)]

pub enum Operator {
    Plus,
    Minus,
    Mul,
    Div,
}

RuntimeContext.rs

pub struct RuntimeContext;

Token.rs

// An attribute to hide warnings for unused code.
#![allow(dead_code)]

#[derive(Debug, PartialEq)]
pub enum Token {
    IllegalToken, // invalid token, error
    TokPlus, // +
    TokMinus, // -
    TokMul, // *
    TokDiv, // /
    TokOparen, // (
    TokCparen, // )
    TokFloating, // number
    TokEos, // end of string
}

lexer.rs

pub use Token;

mod lexer {
    use Token::Token;
    use Token::Token::*;
    use regex::Regex;

    pub fn lexer(input: &str) -> Vec<(Token, Option<f64>)> {
        let re = Regex::new(r"([-+*/)(])|([0-9]+\.[0-9]+)|([0-9]+)").unwrap();
        let mut tokens_and_values: Vec<(Token, Option<f64>)> = Vec::new();
        let mut number: f64;

        for cap in re.captures_iter(&input) {
            let cap1 = cap.get(1);
            let cap2 = cap.get(2);
            let cap3 = cap.get(3);
            if cap1.is_some() {
                tokens_and_values.push((match cap1.unwrap().as_str() {
                                            "+" => Token::TokPlus,
                                            "-" => Token::TokMinus,
                                            "*" => Token::TokMul,
                                            "/" => Token::TokDiv,
                                            "(" => Token::TokOparen,
                                            ")" => Token::TokCparen,
                                            _ => Token::IllegalToken,
                                        },
                                        None));
            } else if cap2.is_some() {
                number = cap2.unwrap().as_str().parse().unwrap();
                tokens_and_values.push((Token::TokFloating, Some(number)));
            } else if cap3.is_some() {
                number = cap3.unwrap().as_str().parse().unwrap();
                tokens_and_values.push((Token::TokFloating, Some(number)));
            }
            //println!("{:?}", cap);
        }
        tokens_and_values.push((TokEos, None));
        tokens_and_values
    }
}

recursive_descent_parser.rs

pub use Token;
pub use Operator;

mod recursive_descent_parser {
    use Token::Token;
    use Token::Token::*;
    use Operator::Operator::*;

    pub fn recursive_descent_parser(input: &str) -> Box<Exp> {
        let tokens_and_values = lexer(&input);
        fn expr(tokens_and_values: &Vec<(Token, Option<f64>)>, mut index: &mut usize) -> Box<Exp> {
            // println!("index = {}", index);
            let term = term(&tokens_and_values, index);

            match tokens_and_values[*index] {
                (TokPlus, None) => {
                    *index += 1;
                    Box::new(BinaryExp {
                        exp1: term,
                        exp2: expr(&tokens_and_values, &mut index),
                        op: Plus,
                    })
                }
                (TokMinus, None) => {
                    *index += 1;
                    Box::new(BinaryExp {
                        exp1: term,
                        exp2: expr(&tokens_and_values, &mut index),
                        op: Minus,
                    })
                }
                _ => term, 
            }
        }

        // <Term> ::= <Factor> | <Factor> {*|/} <Term>
        fn term(tokens_and_values: &Vec<(Token, Option<f64>)>, mut index: &mut usize) -> Box<Exp> {
            // println!("index = {}", index);
            let factor = factor(&tokens_and_values, index);
            //*index += 1;
            match tokens_and_values[*index] {
                (TokMul, None) => {
                    *index += 1;
                    Box::new(BinaryExp {
                        exp1: factor,
                        exp2: term(&tokens_and_values, &mut index),
                        op: Mul,
                    })
                }
                (TokDiv, None) => {
                    *index += 1;
                    Box::new(BinaryExp {
                        exp1: factor,
                        exp2: term(&tokens_and_values, &mut index),
                        op: Div,
                    })
                }
                _ => factor,
            }
        }

        // <Factor>::= <number> | ( <Expr> ) | {+|-} <Factor>
        fn factor(tokens_and_values: &Vec<(Token, Option<f64>)>,
                  mut index: &mut usize)
                  -> Box<Exp> {
            //println!("index = {}", index);

            match tokens_and_values[*index].1 {
                Some(num) => {
                    *index += 1;
                    return Box::new(NumericConstant { value: num });
                }
                None => {}
            };

            //println!("number = {}", number);
            match tokens_and_values[*index] {
                (TokOparen, None) => {
                    // println!("oparen");
                    *index += 1;
                    let result = Box::new(expr(&tokens_and_values, &mut index));

                    if tokens_and_values[*index].0 != TokCparen {
                        println!("unclosed paren");
                    }

                    *index += 1;
                    result
                }
                (TokPlus, None) => {
                    *index += 1;
                    Box::new(UnaryExp {
                        exp1: factor(&tokens_and_values, &mut index),
                        op: Plus,
                    })
                }
                (TokMinus, None) => {
                    *index += 1;
                    Box::new(UnaryExp {
                        exp1: factor(&tokens_and_values, index),
                        op: Minus,
                    })
                }
                _ => return Box::new(NumericConstant { value: 288 as f64 }),
            }
        }
        let mut index = 0;
        expr(&tokens_and_values, &mut index)
    }
}

main.rs

use Operator;
use Token;
use RuntimeContext;
use lexer;
use recursive_descent_parser;

extern crate regex;
use regex::Regex;

use std::ops::Deref;
use std::cmp::PartialEq;

trait Exp {
    fn evaluate(&self, &mut RuntimeContext) -> f64;
}

impl Exp for Box<Exp> {
    fn evaluate(&self, runtime_context: &mut RuntimeContext) -> f64 {
        self.deref().evaluate(runtime_context)
    }
}

struct NumericConstant {
    value: f64, 
    // marker: PhantomData<T>,
}

impl Exp for NumericConstant {
    fn evaluate(&self, runtime_context: &mut RuntimeContext) -> f64 {
        self.value
    }
}

impl Exp for Box<NumericConstant> {
    fn evaluate(&self, runtime_context: &mut RuntimeContext) -> f64 {
        self.deref().value
    }
}

struct BinaryExp<T: Exp, U: Exp> {
    exp1: T,
    exp2: U,
    op: Operator,
}

impl<T: Exp, U: Exp> Exp for BinaryExp<T, U> {
    fn evaluate(&self, runtime_context: &mut RuntimeContext) -> f64 {
        use Operator::*;
        match self.op {
            Plus => self.exp1.evaluate(runtime_context) + self.exp2.evaluate(runtime_context),
            Minus => self.exp1.evaluate(runtime_context) - self.exp2.evaluate(runtime_context),
            Mul => self.exp1.evaluate(runtime_context) * self.exp2.evaluate(runtime_context),
            Div => self.exp1.evaluate(runtime_context) / self.exp2.evaluate(runtime_context),
        }
    }
}

struct UnaryExp<T: Exp> {
    exp1: T,
    op: Operator,
}

impl<T: Exp> Exp for UnaryExp<T> {
    fn evaluate(&self, runtime_context: &mut RuntimeContext) -> f64 {
        use Operator::*;
        match self.op {
            Minus => -self.exp1.evaluate(runtime_context),
            _ => self.exp1.evaluate(runtime_context),
        }
    }
}

fn main() {
    use Operator::*;

    let expressions = vec!["2+2",
                           "5*10",
                           "(2+5)*10",
                           "(10 + (30 + 50 ))",
                           "(100 / 25)",
                           "(25 * 4 * 8)",
                           "25 * 4 * 8 + 100 / 25",
                           "-2*3+3",
                           "(25 * 4 * 8) + (100 / 25)"];

    for expression in expressions {
        //println!("{}", );
        //println!("{:?}", lexer(&expression));
        let parsed = recursive_descent_parser(expression);
        let mut runtime_context = RuntimeContext;
        let result = parsed.evaluate(&mut runtime_context);
        println!("{} = {}", &expression, result);
    }
}

Upvotes: 0

Views: 157

Answers (1)

ljedrz
ljedrz

Reputation: 22273

At this point it's a bit of a mess. I would recommend starting with the creation of an additional lib.rs file in your src folder and declaring all the modules and external crates in it:

pub mod operator;
pub mod token;
pub mod runtime_context;
pub mod lexer;
pub mod recursive_descent_parser;

extern crate regex;

note the slightly modified module names (this also applies to their file names); as per The Rust Book:

Module names follow the conventions for other Rust identifiers: lower_snake_case.

Afterwards you can just use them in your main.rs. I recommend reading The Rust Book's chapter on Crates and Modules.

Upvotes: 4

Related Questions