From dc6450043247bf25406012f93964d64fa097a7ec Mon Sep 17 00:00:00 2001 From: raffitz Date: Thu, 25 Mar 2021 16:59:20 +0000 Subject: [PATCH] Add checks to idents, vars, and var boundaries --- src/astree.rs | 118 +++++++++++++++++++++++++++++++++------------ src/error.rs | 20 ++++++++ src/main.rs | 131 ++++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 213 insertions(+), 56 deletions(-) create mode 100644 src/error.rs diff --git a/src/astree.rs b/src/astree.rs index dbd1832..57bd661 100644 --- a/src/astree.rs +++ b/src/astree.rs @@ -1,15 +1,5 @@ -use std::collections::HashMap; - -#[derive(Debug, PartialEq)] -pub enum Error { - UnrecognisedBinaryOperator, - UnrecognisedCondition, - UnrecognisedJunction, - MissingVarValue, - MissingIdentAssignment, - MissingVarMap, - MissingIdentMap, -} +use crate::error::Error; +use std::collections::{HashMap, HashSet}; #[derive(Debug, PartialEq)] pub enum FunctionType { @@ -41,8 +31,8 @@ impl FunctionData { pub fn eval( &self, - idents: Option<&HashMap>, - vars: Option<&HashMap>, + idents: &Option<&HashMap>, + vars: &Option<&HashMap>, ) -> Result { let value = self.arg.eval(idents, vars)?; @@ -79,8 +69,8 @@ impl OperationData { pub fn eval( &self, - idents: Option<&HashMap>, - vars: Option<&HashMap>, + idents: &Option<&HashMap>, + vars: &Option<&HashMap>, ) -> Result { let left = self.left.eval(idents, vars)?; let right = self.right.eval(idents, vars)?; @@ -130,29 +120,97 @@ impl Expression { pub fn eval( &self, - idents: Option<&HashMap>, - vars: Option<&HashMap>, + idents: &Option<&HashMap>, + vars: &Option<&HashMap>, ) -> Result { match self { Expression::Float(f) => Ok(*f), Expression::Function(f) => f.eval(idents, vars), Expression::Operation(o) => o.eval(idents, vars), Expression::Var(c) => { - let value = vars - .ok_or::(Error::MissingVarMap)? - .get(c) - .ok_or::(Error::MissingVarValue)?; + let var_values = vars.as_ref().ok_or::(Error::MissingVarMap)?; + let value = var_values.get(c).ok_or::(Error::MissingVarValue)?; Ok(*value) } Expression::Ident(s) => { - let referred = idents - .ok_or::(Error::MissingIdentMap)? + let ident_exps = idents.as_ref().ok_or::(Error::MissingIdentMap)?; + let referred = ident_exps .get(s) .ok_or::(Error::MissingIdentAssignment)?; referred.eval(idents, vars) } } } + + pub fn var_dependencies( + &self, + idents: &Option<&HashMap>, + ) -> Result, Error> { + match self { + Expression::Float(_) => Ok(HashSet::new()), + Expression::Function(f) => f.arg.var_dependencies(idents), + Expression::Operation(o) => { + let left = o.left.var_dependencies(idents)?; + let right = o.right.var_dependencies(idents)?; + let mut result: HashSet = HashSet::new(); + for dep in left { + result.insert(dep); + } + for dep in right { + result.insert(dep); + } + Ok(result) + } + Expression::Var(c) => { + let mut result: HashSet = HashSet::new(); + result.insert(*c); + Ok(result) + } + Expression::Ident(s) => { + let ident_exps = idents.as_ref().ok_or::(Error::MissingIdentMap)?; + let referred = ident_exps + .get(s) + .ok_or::(Error::MissingIdentAssignment)?; + referred.var_dependencies(idents) + } + } + } + + pub fn ident_dependencies( + &self, + idents: &Option<&HashMap>, + ) -> Result, Error> { + match self { + Expression::Float(_) => Ok(HashSet::new()), + Expression::Function(f) => f.arg.ident_dependencies(idents), + Expression::Operation(o) => { + let left = o.left.ident_dependencies(idents)?; + let right = o.right.ident_dependencies(idents)?; + let mut result: HashSet = HashSet::new(); + for dep in left { + result.insert(dep); + } + for dep in right { + result.insert(dep); + } + Ok(result) + } + Expression::Var(_) => Ok(HashSet::new()), + Expression::Ident(s) => { + let mut result: HashSet = HashSet::new(); + result.insert(s.to_owned()); + let ident_exps = idents.as_ref().ok_or::(Error::MissingIdentMap)?; + let referred = ident_exps + .get(s) + .ok_or::(Error::MissingIdentAssignment)?; + let recursive = referred.ident_dependencies(idents)?; + for dep in recursive { + result.insert(dep); + } + Ok(result) + } + } + } } #[derive(Debug)] @@ -171,8 +229,8 @@ impl Condition { pub fn eval( &self, - idents: Option<&HashMap>, - vars: Option<&HashMap>, + idents: &Option<&HashMap>, + vars: &Option<&HashMap>, ) -> Result { let left_val = self.left.eval(idents, vars)?; let right_val = self.right.eval(idents, vars)?; @@ -203,8 +261,8 @@ impl JunctionData { pub fn eval( &self, - idents: Option<&HashMap>, - vars: Option<&HashMap>, + idents: &Option<&HashMap>, + vars: &Option<&HashMap>, ) -> Result { let left_val = self.left.eval(idents, vars)?; let right_val = self.right.eval(idents, vars)?; @@ -237,8 +295,8 @@ impl Junction { pub fn eval( &self, - idents: Option<&HashMap>, - vars: Option<&HashMap>, + idents: &Option<&HashMap>, + vars: &Option<&HashMap>, ) -> Result { match self { Junction::Meta(meta) => meta.eval(idents, vars), diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..1d6d236 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,20 @@ +#[derive(Debug, PartialEq)] +pub enum Error { + ParserError, + UnrecognisedBinaryOperator, + UnrecognisedCondition, + UnrecognisedJunction, + MissingVarValue, + MissingIdentAssignment, + MissingVarMap, + MissingIdentMap, + IllegalVarInBoundary, + IllegarBoundedVar, + UnboundedVar, +} + +impl From<()> for Error { + fn from(_: ()) -> Self { + Error::ParserError + } +} diff --git a/src/main.rs b/src/main.rs index d41681a..33ea9f5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,12 @@ use clap::{crate_authors, crate_description, crate_name, crate_version, App, Arg}; use logos::Logos; use pomelo::pomelo; +use std::collections::HashMap; use std::fs; use std::io::{Error, ErrorKind, Read}; mod astree; +mod error; macro_rules! scale_message { ($n:ident) => { @@ -23,17 +25,17 @@ fn io_error(err: Error, path: &str) -> String { pomelo! { %include { use logos::{Lexer, Logos}; - use crate::astree::{Condition, Expression, FunctionType, Junction, Error}; + use crate::astree::{Condition, Expression, FunctionType, Junction}; use std::collections::HashMap; #[derive(Debug)] - pub struct Limit{ - var: char, - min: Expression, - max: Expression, + pub struct Boundary{ + pub var: char, + pub min: Expression, + pub max: Expression, } - impl Limit{ + impl Boundary{ pub fn new( l: Expression, lcond: char, @@ -48,14 +50,14 @@ pomelo! { if rcond == '<' || rcond == '≤'{ let min = l; let max = r; - return Ok(Limit{var,min,max}); + return Ok(Boundary{var,min,max}); } return Err(()); }else if lcond == '>' || lcond == '≥'{ if rcond == '>' || rcond == '≥'{ let min = r; let max = l; - return Ok(Limit{var,min,max}); + return Ok(Boundary{var,min,max}); } return Err(()); } @@ -63,9 +65,9 @@ pomelo! { } } - type Limits = (Limit,Limit,Limit); + type Boundaries = [Boundary; 3]; - type Return = (Option>,Limits,Junction); + type Return = (Option>, Boundaries, Junction); fn read_var(lex: &mut Lexer) -> Option { lex.slice().chars().next() @@ -174,16 +176,16 @@ pomelo! { %left LineEnd; %type input Return; - input ::= limits(L) metajuncture(J) { (None,L,J) } - input ::= LineEnd limits(L) metajuncture(J) { (None,L,J) } - input ::= assignments(A) limits(L) metajuncture(J) { (Some(A),L,J) } - input ::= LineEnd assignments(A) limits(L) metajuncture(J) { (Some(A),L,J) } + input ::= boundaries(L) metajuncture(J) { (None,L,J) } + input ::= LineEnd boundaries(L) metajuncture(J) { (None,L,J) } + input ::= assignments(A) boundaries(L) metajuncture(J) { (Some(A),L,J) } + input ::= LineEnd assignments(A) boundaries(L) metajuncture(J) { (Some(A),L,J) } - %type limit Limit; - limit ::= expr(L) Qualifier(F) Var(V) Qualifier(S) expr(R) { Limit::new(L,F,V,S,R)? } + %type boundary Boundary; + boundary ::= expr(L) Qualifier(F) Var(V) Qualifier(S) expr(R) { Boundary::new(L,F,V,S,R)? } - %type limits Limits; - limits ::= limit(A) LineEnd limit(B) LineEnd limit(C) LineEnd { (A,B,C) } + %type boundaries Boundaries; + boundaries ::= boundary(A) LineEnd boundary(B) LineEnd boundary(C) LineEnd { [A,B,C] } %type assignment (String,Expression); assignment ::= Assign Ident(S) expr(E) { (S,E) } @@ -192,12 +194,22 @@ pomelo! { assignments ::= assignment(A) LineEnd { let (k,v) = A; let mut m = HashMap::new(); - m.insert(k,v); + let ident_arg = Some(&m); + if v.ident_dependencies(&ident_arg).is_ok() { + m.insert(k,v); + }else{ + eprintln!("Undefined reference in {}",k); + } m } - assignments ::= assignment(A) LineEnd assignments(mut M){ + assignments ::= assignments(mut M) assignment(A) LineEnd { let (k,v) = A; - M.insert(k,v); + let ident_arg = Some(&M); + if v.ident_dependencies(&ident_arg).is_ok() { + M.insert(k,v); + }else{ + eprintln!("Undefined reference in {}",k); + } M } %type quality Condition; @@ -226,7 +238,9 @@ pomelo! { expr ::= Ident(S) { Expression::ident(S) } } -fn main() -> Result<(), ()> { +fn main() -> Result<(), error::Error> { + use error::Error; + let matches = App::new(crate_name!()) .version(crate_version!()) .author(crate_authors!()) @@ -266,7 +280,7 @@ fn main() -> Result<(), ()> { ) .get_matches(); - //let scale = matches.value_of("scale").map(|s| s.parse::().unwrap()); + let scale = matches.value_of("scale").map(|s| s.parse::().unwrap()); let mut object_description = fs::File::open(matches.value_of("FILE").unwrap()).unwrap(); @@ -280,7 +294,7 @@ fn main() -> Result<(), ()> { let mut line_ends = false; for token in lex { - println!("{:?}", token); + //println!("{:?}", token); if token == parser::Token::LineEnd { if line_ends { continue; @@ -293,9 +307,74 @@ fn main() -> Result<(), ()> { p.parse(token)?; } - let tree = p.end_of_input()?; - println!("\n{:?}", tree); + let (assigns, limits, tree) = p.end_of_input()?; + + let idents = assigns.unwrap_or_default(); + let ident_arg = Some(&idents); + //println!("\n{:?}", tree); //println!("\nRead {} bytes, scale is {}", size, scale.unwrap_or(1)); + let mut min_x: Option = None; + let mut max_x: Option = None; + let mut min_y: Option = None; + let mut max_y: Option = None; + let mut min_z: Option = None; + let mut max_z: Option = None; + + let mut vars = HashMap::new(); + vars.insert('s', scale.unwrap_or(1) as f64); + + for limit in &limits { + for dep in limit.min.var_dependencies(&ident_arg)? { + if dep != 's' { + eprintln!("Boundaries can only refer to s, not {}", dep); + return Err(Error::IllegalVarInBoundary); + } + } + for dep in limit.max.var_dependencies(&ident_arg)? { + if dep != 's' { + eprintln!("Boundaries can only refer to s, not {}", dep); + return Err(Error::IllegalVarInBoundary); + } + } + let var_arg = Some(&vars); + let min = limit.min.eval(&ident_arg, &var_arg)?.floor() as i64; + let max = limit.max.eval(&ident_arg, &var_arg)?.ceil() as i64; + match limit.var { + 'x' => { + min_x = Some(min); + max_x = Some(max); + } + 'y' => { + min_y = Some(min); + max_y = Some(max); + } + 'z' => { + min_z = Some(min); + max_z = Some(max); + } + c => { + eprintln!("Bounded variables are x,y,z only, not {}", c); + return Err(Error::IllegarBoundedVar); + } + } + } + + let mut unbounded = false; + if min_x.is_none() || max_x.is_none() { + unbounded = true; + eprintln!("x is unbounded"); + } + if min_y.is_none() || max_y.is_none() { + unbounded = true; + eprintln!("y is unbounded"); + } + if min_z.is_none() || max_z.is_none() { + unbounded = true; + eprintln!("z is unbounded"); + } + if unbounded { + return Err(Error::UnboundedVar); + } } Ok(())