Split up code into multiple files
This commit is contained in:
		
							
								
								
									
										63
									
								
								src/gui.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								src/gui.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,63 @@
 | 
			
		||||
use imgui::{TableFlags, Ui};
 | 
			
		||||
use thousands::Separable;
 | 
			
		||||
use crate::state::{State, Variable};
 | 
			
		||||
 | 
			
		||||
pub fn draw_main_menu_bar(state: &mut State, ui: &Ui) {
 | 
			
		||||
    if let Some(t) = ui.begin_main_menu_bar() {
 | 
			
		||||
        if ui.menu_item("Clear") {
 | 
			
		||||
            *state = State::default();
 | 
			
		||||
        }
 | 
			
		||||
        if ui.menu_item("Open") {
 | 
			
		||||
            if let Some(f) = rfd::FileDialog::new().add_filter("json", &["json"]).pick_file() {
 | 
			
		||||
                let data =  std::fs::read_to_string(f).unwrap_or("}{".to_string());
 | 
			
		||||
                let vars: Result<Vec<Variable>, _> = serde_json::from_str(&data);
 | 
			
		||||
                match vars {
 | 
			
		||||
                    Ok(v) => {
 | 
			
		||||
                        *state = State::default();
 | 
			
		||||
                        state.variables = v;
 | 
			
		||||
                        state.eval_vars();
 | 
			
		||||
                    },
 | 
			
		||||
                    Err(e) => println!("Error while loading:\n{:#?}", e)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if ui.menu_item("Save") {
 | 
			
		||||
            if let Some(f) = rfd::FileDialog::new().add_filter("json", &["json"]).save_file() {
 | 
			
		||||
                if let Err(e) = std::fs::write(f, serde_json::to_string(&state.variables).unwrap()) {
 | 
			
		||||
                    println!("Error while saving:\n{:#?}", e);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        t.end();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn draw_table(state: &mut State, ui: &Ui) {
 | 
			
		||||
    if let Some(t) = ui.begin_table_with_flags("VariableTable", 4, TableFlags::BORDERS | TableFlags::ROW_BG | TableFlags::RESIZABLE) {
 | 
			
		||||
        ui.table_setup_column("Name");
 | 
			
		||||
        ui.table_setup_column("Value");
 | 
			
		||||
        ui.table_setup_column("Expression");
 | 
			
		||||
        ui.table_setup_column("Actions");
 | 
			
		||||
        ui.table_headers_row();
 | 
			
		||||
 | 
			
		||||
        let mut to_remove = vec![];
 | 
			
		||||
        for v in &state.variables {
 | 
			
		||||
            ui.table_next_row();
 | 
			
		||||
            ui.table_next_column();
 | 
			
		||||
            ui.text(&v.name);
 | 
			
		||||
            ui.table_next_column();
 | 
			
		||||
            ui.text(v.value.map_or("-".to_string(), |v| v.separate_with_spaces()));
 | 
			
		||||
            ui.table_next_column();
 | 
			
		||||
            ui.text(&v.expr);
 | 
			
		||||
            ui.table_next_column();
 | 
			
		||||
            if ui.button(format!("Remove##{}", v.name)) {
 | 
			
		||||
                to_remove.push(v.name.clone());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if !to_remove.is_empty() {
 | 
			
		||||
            state.variables.retain(|v| !to_remove.contains(&v.name));
 | 
			
		||||
            state.eval_vars();
 | 
			
		||||
        }
 | 
			
		||||
        t.end();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										162
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										162
									
								
								src/main.rs
									
									
									
									
									
								
							@@ -1,136 +1,16 @@
 | 
			
		||||
mod parser;
 | 
			
		||||
mod gui;
 | 
			
		||||
mod state;
 | 
			
		||||
 | 
			
		||||
use glium::Surface;
 | 
			
		||||
use imgui::{Condition, Context, FocusedWidget, TableFlags, Ui, WindowFlags};
 | 
			
		||||
use imgui::{Condition, Context, FocusedWidget, Ui, WindowFlags};
 | 
			
		||||
use lalrpop_util::lalrpop_mod;
 | 
			
		||||
use thousands::Separable;
 | 
			
		||||
use crate::parser::Node;
 | 
			
		||||
use crate::state::State;
 | 
			
		||||
 | 
			
		||||
lalrpop_mod!(grammar);
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
 | 
			
		||||
struct Variable {
 | 
			
		||||
    name: String,
 | 
			
		||||
    expr: String,
 | 
			
		||||
 | 
			
		||||
    #[serde(skip)]
 | 
			
		||||
    value: Option<f64>,
 | 
			
		||||
 | 
			
		||||
    parsed_expr: Node,
 | 
			
		||||
    used: Vec<String>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Default)]
 | 
			
		||||
struct State {
 | 
			
		||||
    inputs: Vec<String>,
 | 
			
		||||
    input_buf: String,
 | 
			
		||||
    error_msg: Option<String>,
 | 
			
		||||
    variables: Vec<Variable>,
 | 
			
		||||
    last_eval: Option<std::time::Duration>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn eval_vars(state: &mut State) {
 | 
			
		||||
    let start = std::time::Instant::now();
 | 
			
		||||
 | 
			
		||||
    let var_map = state.variables.iter()
 | 
			
		||||
        .map(|v| (v.name.clone(), v.parsed_expr.clone()))
 | 
			
		||||
        .collect();
 | 
			
		||||
 | 
			
		||||
    for v in &mut state.variables {
 | 
			
		||||
        let val = v.parsed_expr.eval(&var_map);
 | 
			
		||||
        v.value = val;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    state.last_eval = Some(start.elapsed());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn handle_input(input: String, state: &mut State) -> bool {
 | 
			
		||||
    state.error_msg.take();
 | 
			
		||||
 | 
			
		||||
    let node: Result<(String, Node), lalrpop_util::ParseError<usize, lalrpop_util::lexer::Token<'_>, &str>>
 | 
			
		||||
        = grammar::AssignParser::new().parse(&input);
 | 
			
		||||
    match node  {
 | 
			
		||||
        Ok((name, expr)) => {
 | 
			
		||||
            let used_vars = expr.used_vars();
 | 
			
		||||
            if used_vars.contains(&name) {
 | 
			
		||||
                state.error_msg = Some("Variable can't depend on itself".to_string());
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
            let seen = vec![name.clone()];
 | 
			
		||||
            if let Some(mut e) = used_vars.iter().filter_map(|v| find_dependency_recursive(state, v, seen.clone()).err()).next() {
 | 
			
		||||
                e.push(name.clone());
 | 
			
		||||
                e.reverse();
 | 
			
		||||
                e.push(name.clone());
 | 
			
		||||
                state.error_msg = Some(format!("Found recursive dependency:\n{}", e.join(" -> ")));
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
            if let Some(v) = state.variables.iter_mut().filter(|v| v.name == name).next() {
 | 
			
		||||
                v.parsed_expr = expr;
 | 
			
		||||
                v.expr = input.clone();
 | 
			
		||||
                v.used = used_vars;
 | 
			
		||||
            } else {
 | 
			
		||||
                state.variables.push(Variable {
 | 
			
		||||
                    name,
 | 
			
		||||
                    expr: input.clone(),
 | 
			
		||||
                    value: None,
 | 
			
		||||
                    parsed_expr: expr,
 | 
			
		||||
                    used: used_vars
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        Err(e) => {
 | 
			
		||||
            state.error_msg = Some(e.to_string());
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    eval_vars(state);
 | 
			
		||||
    state.inputs.push(input);
 | 
			
		||||
    true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn find_dependency_recursive(state: &State, name: &String, mut seen: Vec<String>) -> Result<(), Vec<String>> {
 | 
			
		||||
    if let Some(var) = state.variables.iter().filter(|v| v.name == *name).next() {
 | 
			
		||||
        seen.push(name.clone());
 | 
			
		||||
        if var.used.iter().any(|v| seen.contains(v)) {
 | 
			
		||||
            return Err(vec![name.clone()]);
 | 
			
		||||
        }
 | 
			
		||||
        if let Some(mut e) = var.used.iter().filter_map(|v| find_dependency_recursive(state, v, seen.clone()).err()).next() {
 | 
			
		||||
            e.push(name.clone());
 | 
			
		||||
            return Err(e);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn render(main_pos_size: ([f32; 2], [f32; 2]), state: &mut State, ui: &mut Ui) {
 | 
			
		||||
    if let Some(t) = ui.begin_main_menu_bar() {
 | 
			
		||||
        if ui.menu_item("Clear") {
 | 
			
		||||
            *state = State::default();
 | 
			
		||||
        }
 | 
			
		||||
        if ui.menu_item("Open") {
 | 
			
		||||
            if let Some(f) = rfd::FileDialog::new().add_filter("json", &["json"]).pick_file() {
 | 
			
		||||
                let data =  std::fs::read_to_string(f).unwrap_or("}{".to_string());
 | 
			
		||||
                let vars: Result<Vec<Variable>, _> = serde_json::from_str(&data);
 | 
			
		||||
                match vars {
 | 
			
		||||
                    Ok(v) => {
 | 
			
		||||
                        *state = State::default();
 | 
			
		||||
                        state.variables = v;
 | 
			
		||||
                        eval_vars(state);
 | 
			
		||||
                    },
 | 
			
		||||
                    Err(e) => println!("Error while loading:\n{:#?}", e)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if ui.menu_item("Save") {
 | 
			
		||||
            if let Some(f) = rfd::FileDialog::new().add_filter("json", &["json"]).save_file() {
 | 
			
		||||
                if let Err(e) = std::fs::write(f, serde_json::to_string(&state.variables).unwrap()) {
 | 
			
		||||
                    println!("Error while saving:\n{:#?}", e);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        t.end();
 | 
			
		||||
    }
 | 
			
		||||
    gui::draw_main_menu_bar(state, ui);
 | 
			
		||||
 | 
			
		||||
    let wflags = WindowFlags::NO_SCROLLBAR | WindowFlags::NO_MOVE | WindowFlags::NO_DECORATION | WindowFlags::NO_RESIZE | WindowFlags::NO_TITLE_BAR | WindowFlags::NO_COLLAPSE | WindowFlags::NO_DOCKING;
 | 
			
		||||
    if let Some(t) = ui.window("MainWindow")
 | 
			
		||||
@@ -155,9 +35,7 @@ fn render(main_pos_size: ([f32; 2], [f32; 2]), state: &mut State, ui: &mut Ui) {
 | 
			
		||||
        if ui.input_text("##Input", &mut state.input_buf)
 | 
			
		||||
            .enter_returns_true(true)
 | 
			
		||||
            .build() {
 | 
			
		||||
            if handle_input(state.input_buf.clone(), state) {
 | 
			
		||||
                state.input_buf.clear();
 | 
			
		||||
            }
 | 
			
		||||
            state.handle_input_buf();
 | 
			
		||||
            ui.set_keyboard_focus_here_with_offset(FocusedWidget::Previous);
 | 
			
		||||
        }
 | 
			
		||||
        if let Some(last_eval) = &state.last_eval {
 | 
			
		||||
@@ -170,33 +48,7 @@ fn render(main_pos_size: ([f32; 2], [f32; 2]), state: &mut State, ui: &mut Ui) {
 | 
			
		||||
        ui.spacing();
 | 
			
		||||
 | 
			
		||||
        if let Some(t) = ui.child_window("Variables").begin() {
 | 
			
		||||
            if let Some(t) = ui.begin_table_with_flags("VariableTable", 4, TableFlags::BORDERS | TableFlags::ROW_BG | TableFlags::RESIZABLE) {
 | 
			
		||||
                ui.table_setup_column("Name");
 | 
			
		||||
                ui.table_setup_column("Value");
 | 
			
		||||
                ui.table_setup_column("Expression");
 | 
			
		||||
                ui.table_setup_column("Actions");
 | 
			
		||||
                ui.table_headers_row();
 | 
			
		||||
 | 
			
		||||
                let mut to_remove = vec![];
 | 
			
		||||
                for v in &state.variables {
 | 
			
		||||
                    ui.table_next_row();
 | 
			
		||||
                    ui.table_next_column();
 | 
			
		||||
                    ui.text(&v.name);
 | 
			
		||||
                    ui.table_next_column();
 | 
			
		||||
                    ui.text(v.value.map_or("-".to_string(), |v| v.separate_with_spaces()));
 | 
			
		||||
                    ui.table_next_column();
 | 
			
		||||
                    ui.text(&v.expr);
 | 
			
		||||
                    ui.table_next_column();
 | 
			
		||||
                    if ui.button(format!("Remove##{}", v.name)) {
 | 
			
		||||
                        to_remove.push(v.name.clone());
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                if !to_remove.is_empty() {
 | 
			
		||||
                    state.variables.retain(|v| !to_remove.contains(&v.name));
 | 
			
		||||
                    eval_vars(state);
 | 
			
		||||
                }
 | 
			
		||||
                t.end();
 | 
			
		||||
            }
 | 
			
		||||
            gui::draw_table(state, ui);
 | 
			
		||||
            t.end();
 | 
			
		||||
        }
 | 
			
		||||
        t.end();
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										105
									
								
								src/state.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								src/state.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,105 @@
 | 
			
		||||
use crate::parser::Node;
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
 | 
			
		||||
pub struct Variable {
 | 
			
		||||
    pub name: String,
 | 
			
		||||
    pub expr: String,
 | 
			
		||||
 | 
			
		||||
    #[serde(skip)]
 | 
			
		||||
    pub value: Option<f64>,
 | 
			
		||||
 | 
			
		||||
    pub parsed_expr: Node,
 | 
			
		||||
    pub used: Vec<String>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Default)]
 | 
			
		||||
pub struct State {
 | 
			
		||||
    pub inputs: Vec<String>,
 | 
			
		||||
    pub input_buf: String,
 | 
			
		||||
    pub error_msg: Option<String>,
 | 
			
		||||
    pub variables: Vec<Variable>,
 | 
			
		||||
    pub last_eval: Option<std::time::Duration>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl State {
 | 
			
		||||
    pub fn eval_vars(&mut self) {
 | 
			
		||||
        let start = std::time::Instant::now();
 | 
			
		||||
 | 
			
		||||
        let var_map = self.variables.iter()
 | 
			
		||||
            .map(|v| (v.name.clone(), v.parsed_expr.clone()))
 | 
			
		||||
            .collect();
 | 
			
		||||
 | 
			
		||||
        for v in &mut self.variables {
 | 
			
		||||
            let val = v.parsed_expr.eval(&var_map);
 | 
			
		||||
            v.value = val;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        self.last_eval = Some(start.elapsed());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn handle_input_buf(&mut self) {
 | 
			
		||||
        let input = std::mem::take(&mut self.input_buf);
 | 
			
		||||
        if !self.handle_input(&input) {
 | 
			
		||||
            self.input_buf = input;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn handle_input(&mut self, input: &String) -> bool {
 | 
			
		||||
        self.error_msg.take();
 | 
			
		||||
 | 
			
		||||
        let node: Result<(String, Node), lalrpop_util::ParseError<usize, lalrpop_util::lexer::Token<'_>, &str>>
 | 
			
		||||
            = crate::grammar::AssignParser::new().parse(&input);
 | 
			
		||||
        match node {
 | 
			
		||||
            Ok((name, expr)) => {
 | 
			
		||||
                let used_vars = expr.used_vars();
 | 
			
		||||
                if used_vars.contains(&name) {
 | 
			
		||||
                    self.error_msg = Some("Variable can't depend on itself".to_string());
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
                let seen = vec![name.clone()];
 | 
			
		||||
                if let Some(mut e) = used_vars.iter().filter_map(|v| self.find_dependency_recursive(v, seen.clone()).err()).next() {
 | 
			
		||||
                    e.push(name.clone());
 | 
			
		||||
                    e.reverse();
 | 
			
		||||
                    e.push(name.clone());
 | 
			
		||||
                    self.error_msg = Some(format!("Found recursive dependency:\n{}", e.join(" -> ")));
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
                if let Some(v) = self.variables.iter_mut().filter(|v| v.name == name).next() {
 | 
			
		||||
                    v.parsed_expr = expr;
 | 
			
		||||
                    v.expr = input.clone();
 | 
			
		||||
                    v.used = used_vars;
 | 
			
		||||
                } else {
 | 
			
		||||
                    self.variables.push(Variable {
 | 
			
		||||
                        name,
 | 
			
		||||
                        expr: input.clone(),
 | 
			
		||||
                        value: None,
 | 
			
		||||
                        parsed_expr: expr,
 | 
			
		||||
                        used: used_vars
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            Err(e) => {
 | 
			
		||||
                self.error_msg = Some(e.to_string());
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        self.eval_vars();
 | 
			
		||||
        self.inputs.push(input.clone());
 | 
			
		||||
        true
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn find_dependency_recursive(&self, name: &String, mut seen: Vec<String>) -> Result<(), Vec<String>> {
 | 
			
		||||
        if let Some(var) = self.variables.iter().filter(|v| v.name == *name).next() {
 | 
			
		||||
            seen.push(name.clone());
 | 
			
		||||
            if var.used.iter().any(|v| seen.contains(v)) {
 | 
			
		||||
                return Err(vec![name.clone()]);
 | 
			
		||||
            }
 | 
			
		||||
            if let Some(mut e) = var.used.iter().filter_map(|v| self.find_dependency_recursive(v, seen.clone()).err()).next() {
 | 
			
		||||
                e.push(name.clone());
 | 
			
		||||
                return Err(e);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user