Browse Source

Add checks to idents, vars, and var boundaries

main
raffitz 3 years ago
parent
commit
dc64500432
Signed by: raffitz
GPG Key ID: BB3596BD0A31252D
  1. 118
      src/astree.rs
  2. 20
      src/error.rs
  3. 131
      src/main.rs

118
src/astree.rs

@ -1,15 +1,5 @@
use std::collections::HashMap; use crate::error::Error;
use std::collections::{HashMap, HashSet};
#[derive(Debug, PartialEq)]
pub enum Error {
UnrecognisedBinaryOperator,
UnrecognisedCondition,
UnrecognisedJunction,
MissingVarValue,
MissingIdentAssignment,
MissingVarMap,
MissingIdentMap,
}
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum FunctionType { pub enum FunctionType {
@ -41,8 +31,8 @@ impl FunctionData {
pub fn eval( pub fn eval(
&self, &self,
idents: Option<&HashMap<String, Expression>>, idents: &Option<&HashMap<String, Expression>>,
vars: Option<&HashMap<char, f64>>, vars: &Option<&HashMap<char, f64>>,
) -> Result<f64, Error> { ) -> Result<f64, Error> {
let value = self.arg.eval(idents, vars)?; let value = self.arg.eval(idents, vars)?;
@ -79,8 +69,8 @@ impl OperationData {
pub fn eval( pub fn eval(
&self, &self,
idents: Option<&HashMap<String, Expression>>, idents: &Option<&HashMap<String, Expression>>,
vars: Option<&HashMap<char, f64>>, vars: &Option<&HashMap<char, f64>>,
) -> Result<f64, Error> { ) -> Result<f64, Error> {
let left = self.left.eval(idents, vars)?; let left = self.left.eval(idents, vars)?;
let right = self.right.eval(idents, vars)?; let right = self.right.eval(idents, vars)?;
@ -130,29 +120,97 @@ impl Expression {
pub fn eval( pub fn eval(
&self, &self,
idents: Option<&HashMap<String, Expression>>, idents: &Option<&HashMap<String, Expression>>,
vars: Option<&HashMap<char, f64>>, vars: &Option<&HashMap<char, f64>>,
) -> Result<f64, Error> { ) -> Result<f64, Error> {
match self { match self {
Expression::Float(f) => Ok(*f), Expression::Float(f) => Ok(*f),
Expression::Function(f) => f.eval(idents, vars), Expression::Function(f) => f.eval(idents, vars),
Expression::Operation(o) => o.eval(idents, vars), Expression::Operation(o) => o.eval(idents, vars),
Expression::Var(c) => { Expression::Var(c) => {
let value = vars let var_values = vars.as_ref().ok_or::<Error>(Error::MissingVarMap)?;
.ok_or::<Error>(Error::MissingVarMap)? let value = var_values.get(c).ok_or::<Error>(Error::MissingVarValue)?;
.get(c)
.ok_or::<Error>(Error::MissingVarValue)?;
Ok(*value) Ok(*value)
} }
Expression::Ident(s) => { Expression::Ident(s) => {
let referred = idents let ident_exps = idents.as_ref().ok_or::<Error>(Error::MissingIdentMap)?;
.ok_or::<Error>(Error::MissingIdentMap)? let referred = ident_exps
.get(s) .get(s)
.ok_or::<Error>(Error::MissingIdentAssignment)?; .ok_or::<Error>(Error::MissingIdentAssignment)?;
referred.eval(idents, vars) referred.eval(idents, vars)
} }
} }
} }
pub fn var_dependencies(
&self,
idents: &Option<&HashMap<String, Expression>>,
) -> Result<HashSet<char>, 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<char> = 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<char> = HashSet::new();
result.insert(*c);
Ok(result)
}
Expression::Ident(s) => {
let ident_exps = idents.as_ref().ok_or::<Error>(Error::MissingIdentMap)?;
let referred = ident_exps
.get(s)
.ok_or::<Error>(Error::MissingIdentAssignment)?;
referred.var_dependencies(idents)
}
}
}
pub fn ident_dependencies(
&self,
idents: &Option<&HashMap<String, Expression>>,
) -> Result<HashSet<String>, 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<String> = 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<String> = HashSet::new();
result.insert(s.to_owned());
let ident_exps = idents.as_ref().ok_or::<Error>(Error::MissingIdentMap)?;
let referred = ident_exps
.get(s)
.ok_or::<Error>(Error::MissingIdentAssignment)?;
let recursive = referred.ident_dependencies(idents)?;
for dep in recursive {
result.insert(dep);
}
Ok(result)
}
}
}
} }
#[derive(Debug)] #[derive(Debug)]
@ -171,8 +229,8 @@ impl Condition {
pub fn eval( pub fn eval(
&self, &self,
idents: Option<&HashMap<String, Expression>>, idents: &Option<&HashMap<String, Expression>>,
vars: Option<&HashMap<char, f64>>, vars: &Option<&HashMap<char, f64>>,
) -> Result<bool, Error> { ) -> Result<bool, Error> {
let left_val = self.left.eval(idents, vars)?; let left_val = self.left.eval(idents, vars)?;
let right_val = self.right.eval(idents, vars)?; let right_val = self.right.eval(idents, vars)?;
@ -203,8 +261,8 @@ impl JunctionData {
pub fn eval( pub fn eval(
&self, &self,
idents: Option<&HashMap<String, Expression>>, idents: &Option<&HashMap<String, Expression>>,
vars: Option<&HashMap<char, f64>>, vars: &Option<&HashMap<char, f64>>,
) -> Result<bool, Error> { ) -> Result<bool, Error> {
let left_val = self.left.eval(idents, vars)?; let left_val = self.left.eval(idents, vars)?;
let right_val = self.right.eval(idents, vars)?; let right_val = self.right.eval(idents, vars)?;
@ -237,8 +295,8 @@ impl Junction {
pub fn eval( pub fn eval(
&self, &self,
idents: Option<&HashMap<String, Expression>>, idents: &Option<&HashMap<String, Expression>>,
vars: Option<&HashMap<char, f64>>, vars: &Option<&HashMap<char, f64>>,
) -> Result<bool, Error> { ) -> Result<bool, Error> {
match self { match self {
Junction::Meta(meta) => meta.eval(idents, vars), Junction::Meta(meta) => meta.eval(idents, vars),

20
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
}
}

131
src/main.rs

@ -1,10 +1,12 @@
use clap::{crate_authors, crate_description, crate_name, crate_version, App, Arg}; use clap::{crate_authors, crate_description, crate_name, crate_version, App, Arg};
use logos::Logos; use logos::Logos;
use pomelo::pomelo; use pomelo::pomelo;
use std::collections::HashMap;
use std::fs; use std::fs;
use std::io::{Error, ErrorKind, Read}; use std::io::{Error, ErrorKind, Read};
mod astree; mod astree;
mod error;
macro_rules! scale_message { macro_rules! scale_message {
($n:ident) => { ($n:ident) => {
@ -23,17 +25,17 @@ fn io_error(err: Error, path: &str) -> String {
pomelo! { pomelo! {
%include { %include {
use logos::{Lexer, Logos}; use logos::{Lexer, Logos};
use crate::astree::{Condition, Expression, FunctionType, Junction, Error}; use crate::astree::{Condition, Expression, FunctionType, Junction};
use std::collections::HashMap; use std::collections::HashMap;
#[derive(Debug)] #[derive(Debug)]
pub struct Limit{ pub struct Boundary{
var: char, pub var: char,
min: Expression, pub min: Expression,
max: Expression, pub max: Expression,
} }
impl Limit{ impl Boundary{
pub fn new( pub fn new(
l: Expression, l: Expression,
lcond: char, lcond: char,
@ -48,14 +50,14 @@ pomelo! {
if rcond == '<' || rcond == '≤'{ if rcond == '<' || rcond == '≤'{
let min = l; let min = l;
let max = r; let max = r;
return Ok(Limit{var,min,max}); return Ok(Boundary{var,min,max});
} }
return Err(()); return Err(());
}else if lcond == '>' || lcond == '≥'{ }else if lcond == '>' || lcond == '≥'{
if rcond == '>' || rcond == '≥'{ if rcond == '>' || rcond == '≥'{
let min = r; let min = r;
let max = l; let max = l;
return Ok(Limit{var,min,max}); return Ok(Boundary{var,min,max});
} }
return Err(()); return Err(());
} }
@ -63,9 +65,9 @@ pomelo! {
} }
} }
type Limits = (Limit,Limit,Limit); type Boundaries = [Boundary; 3];
type Return = (Option<HashMap<String,Expression>>,Limits,Junction); type Return = (Option<HashMap<String,Expression>>, Boundaries, Junction);
fn read_var(lex: &mut Lexer<Token>) -> Option<char> { fn read_var(lex: &mut Lexer<Token>) -> Option<char> {
lex.slice().chars().next() lex.slice().chars().next()
@ -174,16 +176,16 @@ pomelo! {
%left LineEnd; %left LineEnd;
%type input Return; %type input Return;
input ::= limits(L) metajuncture(J) { (None,L,J) } input ::= boundaries(L) metajuncture(J) { (None,L,J) }
input ::= LineEnd limits(L) metajuncture(J) { (None,L,J) } input ::= LineEnd boundaries(L) metajuncture(J) { (None,L,J) }
input ::= assignments(A) limits(L) metajuncture(J) { (Some(A),L,J) } input ::= assignments(A) boundaries(L) metajuncture(J) { (Some(A),L,J) }
input ::= LineEnd assignments(A) limits(L) metajuncture(J) { (Some(A),L,J) } input ::= LineEnd assignments(A) boundaries(L) metajuncture(J) { (Some(A),L,J) }
%type limit Limit; %type boundary Boundary;
limit ::= expr(L) Qualifier(F) Var(V) Qualifier(S) expr(R) { Limit::new(L,F,V,S,R)? } boundary ::= expr(L) Qualifier(F) Var(V) Qualifier(S) expr(R) { Boundary::new(L,F,V,S,R)? }
%type limits Limits; %type boundaries Boundaries;
limits ::= limit(A) LineEnd limit(B) LineEnd limit(C) LineEnd { (A,B,C) } boundaries ::= boundary(A) LineEnd boundary(B) LineEnd boundary(C) LineEnd { [A,B,C] }
%type assignment (String,Expression); %type assignment (String,Expression);
assignment ::= Assign Ident(S) expr(E) { (S,E) } assignment ::= Assign Ident(S) expr(E) { (S,E) }
@ -192,12 +194,22 @@ pomelo! {
assignments ::= assignment(A) LineEnd { assignments ::= assignment(A) LineEnd {
let (k,v) = A; let (k,v) = A;
let mut m = HashMap::new(); 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 m
} }
assignments ::= assignment(A) LineEnd assignments(mut M){ assignments ::= assignments(mut M) assignment(A) LineEnd {
let (k,v) = A; 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 M
} }
%type quality Condition; %type quality Condition;
@ -226,7 +238,9 @@ pomelo! {
expr ::= Ident(S) { Expression::ident(S) } expr ::= Ident(S) { Expression::ident(S) }
} }
fn main() -> Result<(), ()> { fn main() -> Result<(), error::Error> {
use error::Error;
let matches = App::new(crate_name!()) let matches = App::new(crate_name!())
.version(crate_version!()) .version(crate_version!())
.author(crate_authors!()) .author(crate_authors!())
@ -266,7 +280,7 @@ fn main() -> Result<(), ()> {
) )
.get_matches(); .get_matches();
//let scale = matches.value_of("scale").map(|s| s.parse::<i32>().unwrap()); let scale = matches.value_of("scale").map(|s| s.parse::<i32>().unwrap());
let mut object_description = fs::File::open(matches.value_of("FILE").unwrap()).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; let mut line_ends = false;
for token in lex { for token in lex {
println!("{:?}", token); //println!("{:?}", token);
if token == parser::Token::LineEnd { if token == parser::Token::LineEnd {
if line_ends { if line_ends {
continue; continue;
@ -293,9 +307,74 @@ fn main() -> Result<(), ()> {
p.parse(token)?; p.parse(token)?;
} }
let tree = p.end_of_input()?; let (assigns, limits, tree) = p.end_of_input()?;
println!("\n{:?}", tree);
let idents = assigns.unwrap_or_default();
let ident_arg = Some(&idents);
//println!("\n{:?}", tree);
//println!("\nRead {} bytes, scale is {}", size, scale.unwrap_or(1)); //println!("\nRead {} bytes, scale is {}", size, scale.unwrap_or(1));
let mut min_x: Option<i64> = None;
let mut max_x: Option<i64> = None;
let mut min_y: Option<i64> = None;
let mut max_y: Option<i64> = None;
let mut min_z: Option<i64> = None;
let mut max_z: Option<i64> = 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(()) Ok(())

Loading…
Cancel
Save