Reputation: 1079
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
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