From 3af6ce5130b56812a51c9eb0280ec7f0450f28d1 Mon Sep 17 00:00:00 2001 From: elvis Date: Sat, 2 Aug 2025 06:50:01 +0200 Subject: [PATCH] Execute function for assert --- Cargo.toml | 1 + src/main.rs | 25 +- src/rsprocess/assert.rs | 636 +++++++++++++++++++++++++++++----- src/rsprocess/grammar.lalrpop | 1 + 4 files changed, 577 insertions(+), 86 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b39e97f..9a3c4b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ edition = "2024" lalrpop = "0.22" [dependencies] +rand = { version = "*" } regex = { version = "1", features = ["unicode-bool"] } lalrpop-util = { version = "*", features = ["lexer", "unicode"] } petgraph = { version = "*", features = ["serde-1"] } diff --git a/src/main.rs b/src/main.rs index 73aa7d4..38ad9f1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,27 @@ fn main() { let now = std::time::Instant::now(); - use reactionsystems::rsprocess::presets; - match presets::run("testing/first.system".into()) { - Ok(_) => {}, - Err(e) => {println!("{e}")} + // use reactionsystems::rsprocess::presets; + // match presets::run("testing/first.system".into()) { + // Ok(_) => {}, + // Err(e) => {println!("{e}")} + // } + + use reactionsystems::grammar::AssertParser; + use reactionsystems::rsprocess::translator::Translator; + + let contents = r#"label { if (substr("e", ('e').tostr)) then {return 'e'} else {return (("e").toel)} }"#; + let mut translator = Translator::new(); + let tree = AssertParser::new().parse(&mut translator, contents).unwrap(); + + println!("{tree}"); + match tree.typecheck() { + Ok(_) => println!("ok"), + Err(e) => println!("error: {e}") + } + match tree.execute(&reactionsystems::rsprocess::structure::RSlabel::new(), &mut translator) { + Ok(val) => println!("ok: {val}"), + Err(e) => println!("error: {e}") } println!("{} milliseconds elapsed", now.elapsed().as_millis()); diff --git a/src/rsprocess/assert.rs b/src/rsprocess/assert.rs index eb9b75c..6d3f910 100644 --- a/src/rsprocess/assert.rs +++ b/src/rsprocess/assert.rs @@ -53,6 +53,7 @@ pub enum Range { IterateInRange(Box, Box), } + #[derive(Debug, Clone, Copy)] pub enum Unary { Not, @@ -82,6 +83,60 @@ impl Unary { !self.is_prefix() } + fn associate( + &self, + type_exp: &AssertionTypes + ) -> Result { + match self { + Self::Not => { + if let AssertionTypes::Boolean = type_exp { + return Ok(AssertionTypes::Boolean) + } + }, + Self::Rand => { + if let AssertionTypes::Integer = type_exp { + return Ok(AssertionTypes::Integer) + } + }, + Self::Empty => { + if let AssertionTypes::Set = type_exp { + return Ok(AssertionTypes::Boolean) + } + }, + Self::Length => { + match type_exp { + AssertionTypes::Set | + AssertionTypes::String => { + return Ok(AssertionTypes::Integer) + } + _ => {} + } + }, + Self::ToStr => { + match type_exp { + AssertionTypes::Boolean | + AssertionTypes::Element | + AssertionTypes::Integer => { + return Ok(AssertionTypes::String) + } + _ => {} + } + }, + Self::Qualifier(_) => { + if let AssertionTypes::Label = type_exp { + return Ok(AssertionTypes::Set) + } + }, + Self::ToEl => { + if let AssertionTypes::String = type_exp { + return Ok(AssertionTypes::Element) + } + } + } + Err(format!("Expression has incompatible type with operation: \ + {type_exp} with operation {self}.")) + } + fn associated_types_unary(&self) -> Vec { match self { Unary::Not => vec![AssertionTypes::Boolean], @@ -109,6 +164,38 @@ pub enum QualifierRestricted { Products, } +impl QualifierRestricted { + pub fn referenced_mut<'a>( + &self, + label: &'a mut super::structure::RSlabel, + ) -> &'a mut super::structure::RSset { + match self { + Self::Entities => {&mut label.available_entities}, + Self::Context => {&mut label.context}, + Self::Reactants => {&mut label.reactants}, + Self::ReactantsAbsent => {&mut label.reactants_absent}, + Self::Inhibitors => {&mut label.inhibitors}, + Self::InhibitorsPresent => {&mut label.inhibitors_present}, + Self::Products => {&mut label.products}, + } + } + + pub fn referenced<'a>( + &self, + label: &'a super::structure::RSlabel, + ) -> &'a super::structure::RSset { + match self { + Self::Entities => {&label.available_entities}, + Self::Context => {&label.context}, + Self::Reactants => {&label.reactants}, + Self::ReactantsAbsent => {&label.reactants_absent}, + Self::Inhibitors => {&label.inhibitors}, + Self::InhibitorsPresent => {&label.inhibitors_present}, + Self::Products => {&label.products}, + } + } +} + #[derive(Debug, Clone, Copy)] pub enum Qualifier { AvailableEntities, @@ -117,6 +204,28 @@ pub enum Qualifier { Restricted(QualifierRestricted), } +impl Qualifier { + pub fn get( + &self, + l: &super::structure::RSlabel, + ) -> super::structure::RSset { + match self { + Qualifier::AvailableEntities => { + l.t.clone() + }, + Qualifier::AllReactants => { + l.reactants.union(&l.reactants_absent) + }, + Qualifier::AllInhibitors => { + l.inhibitors.union(&l.inhibitors_present) + }, + Qualifier::Restricted(q) => { + q.referenced(l).clone() + } + } + } +} + #[derive(Debug, Clone, Copy)] pub enum Binary { And, @@ -295,20 +404,20 @@ impl Binary { } -use core::fmt; // ----------------------------------------------------------------------------- use std::collections::HashMap; +use std::fmt; -struct Context { +struct TypeContext { data: HashMap, - ty_return: Option + return_ty: Option } -impl Context { +impl TypeContext { pub fn new() -> Self { - Context { + TypeContext { data: HashMap::new(), - ty_return: None + return_ty: None } } @@ -340,7 +449,7 @@ impl Context { &mut self, ty: AssertionTypes ) -> Result<(), String> { - if let Some(ty_return) = self.ty_return { + if let Some(ty_return) = self.return_ty { if ty_return == ty { Ok(()) } else { @@ -348,7 +457,7 @@ impl Context { {ty} found.")) } } else { - self.ty_return = Some(ty); + self.return_ty = Some(ty); Ok(()) } } @@ -383,23 +492,20 @@ impl Context { v: &AssignmentVariable, ) -> Result { match v { - AssignmentVariable::Var(v) => { - match v { - Variable::Label => Ok(AssertionTypes::Label), - Variable::Id(v) => { - if let Some(ty) = self.data.get(v) { - Ok(*ty) - } else { - Err(format!("Could not find variable {v}.")) - } - } + AssignmentVariable::Var(Variable::Label) => { + Ok(AssertionTypes::Label) + }, + AssignmentVariable::Var(Variable::Id(v)) => { + if let Some(ty) = self.data.get(v) { + Ok(*ty) + } else { + Err(format!("Could not find variable {v}.")) } }, - AssignmentVariable::QualifiedVar(var, _) => { - let var = match var { - Variable::Id(v) => v, - Variable::Label => return Ok(AssertionTypes::Set), - }; + AssignmentVariable::QualifiedVar(Variable::Label, _) => { + Ok(AssertionTypes::Set) + }, + AssignmentVariable::QualifiedVar(Variable::Id(var), _) => { if let Some(ty) = self.data.get(var) { if *ty == AssertionTypes::Label { Ok(AssertionTypes::Set) @@ -411,10 +517,108 @@ impl Context { } } } - } } +struct Context<'a> { + data: HashMap, + label: &'a super::structure::RSlabel, +} + +impl<'a> Context<'a> { + pub fn new(label: &'a super::structure::RSlabel) -> Self { + Self { data: HashMap::new(), label } + } + + pub fn assign( + &mut self, + v: &AssignmentVariable, + val: AssertReturnValue, + ) -> Result<(), String> { + match v { + AssignmentVariable::Var(v) => { + if let Variable::Id(v) = v { + self.data.insert(v.clone(), val); + } + Ok(()) + }, + AssignmentVariable::QualifiedVar(Variable::Label, _) => Ok(()), + AssignmentVariable::QualifiedVar(Variable::Id(v), q) => { + match self.data.entry(v.clone()) { + std::collections::hash_map::Entry::Vacant(_ve) => + Err(format!("Variable {v} has no value while trying to \ + assign a value to the field {q} of a label\ + ")), + std::collections::hash_map::Entry::Occupied(mut oe) => { + if let AssertReturnValue::Set(s) = val { + let l = oe.get_mut(); + match l { + AssertReturnValue::Label(l) => { + *q.referenced_mut(l) = s; + // if we modified available entities or the + // context we need to sync it with t + match q { + QualifierRestricted::Context | + QualifierRestricted::Entities => { + l.t = l.available_entities.union( + &l.context + ); + }, + _ => {}, + } + Ok(()) + }, + _ => { + Err(format!("Variable {v} does not have \ + type label while trying to \ + assign to qualification {q}")) + } + } + } else { + Err(format!("Value {val} is not a set while trying \ + to assign to qualification {q} of \ + variable {v}")) + } + } + } + } + } + } + + pub fn get( + &self, + v: &AssignmentVariable + ) -> Result { + match v { + AssignmentVariable::Var(Variable::Id(var)) => { + self.data.get(var) + .cloned() + .ok_or(format!("Variable {v} used, but no value assigned.")) + }, + AssignmentVariable::QualifiedVar(Variable::Id(var), q) => { + let val = self.data.get(var) + .ok_or(format!("Variable {v} used, but no value assigned." + ))?; + match val { + AssertReturnValue::Label(l) => { + Ok(AssertReturnValue::Set(q.referenced(l).clone())) + }, + _ => {Err(format!("Variable {v} is not a label but was \ + quantified with {q}."))} + } + }, + AssignmentVariable::Var(Variable::Label) => { + Ok(AssertReturnValue::Label(self.label.clone())) + }, + AssignmentVariable::QualifiedVar(Variable::Label, q) => { + Ok(AssertReturnValue::Set(q.referenced(self.label).clone())) + } + } + } +} + + + #[derive(Copy, Clone, PartialEq, Eq)] enum AssertionTypes { Boolean, @@ -428,23 +632,158 @@ enum AssertionTypes { RangeSet, } -impl fmt::Display for AssertionTypes { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - AssertionTypes::Boolean => write!(f, "boolean"), - AssertionTypes::Integer => write!(f, "integer"), - AssertionTypes::String => write!(f, "string"), - AssertionTypes::Label => write!(f, "label"), - AssertionTypes::Set => write!(f, "set"), - AssertionTypes::Element => write!(f, "element"), - AssertionTypes::NoType => write!(f, "no type"), - AssertionTypes::RangeInteger => write!(f, "range of integers"), - AssertionTypes::RangeSet => write!(f, "range of set"), +#[derive(Debug, Clone)] +pub enum AssertReturnValue { + Boolean(bool), + Integer(IntegerType), + String(String), + Label(super::structure::RSlabel), + Set(super::structure::RSset), + Element(super::translator::IdType), +} + +impl AssertReturnValue { + pub fn unary( + self, + u: &Unary, + translator: &mut super::translator::Translator + ) -> Result { + match (self, u) { + (AssertReturnValue::Boolean(b), Unary::Not) => { + Ok(AssertReturnValue::Boolean(!b)) + }, + (AssertReturnValue::Integer(i), Unary::Rand) => { + Ok(AssertReturnValue::Integer(rand::random_range(0..i))) + }, + (AssertReturnValue::Set(set), Unary::Empty) => { + Ok(AssertReturnValue::Boolean(set.is_empty())) + }, + (AssertReturnValue::String(s), Unary::Empty) => { + Ok(AssertReturnValue::Boolean(s.is_empty())) + }, + (AssertReturnValue::Set(set), Unary::Length) => { + Ok(AssertReturnValue::Integer(set.len() as i64)) + }, + (AssertReturnValue::String(s), Unary::Length) => { + Ok(AssertReturnValue::Integer(s.len() as i64)) + }, + (AssertReturnValue::Boolean(b), Unary::ToStr) => { + Ok(AssertReturnValue::String(format!("{b}"))) + }, + (AssertReturnValue::Integer(i), Unary::ToStr) => { + Ok(AssertReturnValue::String(format!("{i}"))) + }, + (AssertReturnValue::Element(el), Unary::ToStr) => { + Ok(AssertReturnValue::String( + translator.decode(el).ok_or( + format!("Could not find element {el}.") + )? + )) + }, + (AssertReturnValue::Label(l), Unary::Qualifier(q)) => { + Ok(AssertReturnValue::Set(q.get(&l))) + }, + (AssertReturnValue::String(s), Unary::ToEl) => { + Ok(AssertReturnValue::Element(translator.encode(s))) + }, + (val, u) => { + Err(format!("Incompatible unary operation {u} on value {val}")) + } } } + + pub fn binary( + self, + b: &Binary, + other: AssertReturnValue, + _translator: &mut super::translator::Translator + ) -> Result { + use AssertReturnValue::*; + Ok(match (b, self, other) { + (Binary::And, Boolean(b1), Boolean(b2)) => {Boolean(b1 && b2)}, + (Binary::Or, Boolean(b1), Boolean(b2)) => {Boolean(b1 || b2)}, + (Binary::Xor, Boolean(b1), Boolean(b2)) => {Boolean(b1 ^ b2)}, + (Binary::Xor, Set(s1), Set(s2)) => { + Set(s1.union(&s2).subtraction(&s1.intersection(&s2))) + }, + (Binary::Less, Integer(i1), Integer(i2)) => {Boolean(i1 < i2)}, + (Binary::Less, Set(s1), Set(s2)) => { + Boolean(s1.is_subset(&s2) && !s2.is_subset(&s1))}, + (Binary::LessEq, Integer(i1), Integer(i2)) => {Boolean(i1 <= i2)}, + (Binary::LessEq, Set(s1), Set(s2)) => { + Boolean(s1.is_subset(&s2))}, + (Binary::More, Integer(i1), Integer(i2)) => {Boolean(i1 > i2)}, + (Binary::More, Set(s1), Set(s2)) => { + Boolean(s2.is_subset(&s1) && !s1.is_subset(&s2))}, + (Binary::MoreEq, Integer(i1), Integer(i2)) => {Boolean(i1 >= i2)}, + (Binary::MoreEq, Set(s1), Set(s2)) => { + Boolean(s1.is_subset(&s2))}, + (Binary::Eq, Integer(i1), Integer(i2)) => {Boolean(i1 == i2)}, + (Binary::Eq, Boolean(b1), Boolean(b2)) => {Boolean(b1 == b2)}, + (Binary::Eq, Element(el1), Element(el2)) => {Boolean(el1 == el2)}, + (Binary::Eq, Label(l1), Label(l2)) => {Boolean(l1 == l2)}, + (Binary::Eq, String(s1), String(s2)) => {Boolean(s1 == s2)}, + (Binary::Eq, Set(set1), Set(set2)) => {Boolean(set1 == set2)}, + (Binary::NotEq, Integer(i1), Integer(i2)) => {Boolean(i1 != i2)}, + (Binary::NotEq, Boolean(b1), Boolean(b2)) => {Boolean(b1 != b2)}, + (Binary::NotEq, Element(el1), Element(el2)) => { + Boolean(el1 != el2)}, + (Binary::NotEq, Label(l1), Label(l2)) => {Boolean(l1 != l2)}, + (Binary::NotEq, String(s1), String(s2)) => {Boolean(s1 != s2)}, + (Binary::NotEq, Set(set1), Set(set2)) => {Boolean(set1 != set2)}, + (Binary::Plus, Integer(i1), Integer(i2)) => {Integer(i1 + i2)}, + (Binary::Plus, Set(set1), Set(set2)) => {Set(set1.union(&set2))}, + (Binary::Minus, Integer(i1), Integer(i2)) => {Integer(i1 - i2)}, + (Binary::Minus, Set(set1), Set(set2)) => { + Set(set1.subtraction(&set2))}, + (Binary::Times, Integer(i1), Integer(i2)) => {Integer(i1 * i2)}, + (Binary::Times, Set(set1), Set(set2)) => { + Set(set1.intersection(&set2))}, + (Binary::Exponential, Integer(i1), Integer(i2)) => { + if i2 < 0 { + Integer(0) + } else { + Integer(i1.pow(i2 as u32))} + }, + (Binary::Quotient, Integer(i1), Integer(i2)) => { + Integer(i1.div_euclid(i2))}, + (Binary::Reminder, Integer(i1), Integer(i2)) => { + Integer(i1.rem_euclid(i2))}, + (Binary::Concat, String(s1), String(s2)) => {String(s1 + &s2)}, + (Binary::SubStr, String(s1), String(s2)) => { + let mut len = s1.len() as i64; + for (p, c) in s1.chars().enumerate() { + if s2.chars().nth(p) != Some(c) { + len = p as i64; + break; + } + } + Integer(len) + }, + (Binary::Min, Integer(i1), Integer(i2)) => {Integer(i1.min(i2))}, + (Binary::Max, Integer(i1), Integer(i2)) => {Integer(i1.max(i2))}, + (Binary::CommonSubStr, String(s1), String(s2)) => { + let mut s = std::string::String::new(); + for (p, c) in s1.chars().enumerate() { + if s2.chars().nth(p) != Some(c) { + break; + } + s.push(c); + } + String(s) + }, + (b, val1, val2) => + return Err(format!("Operation {b} on values {val1} and {val2} \ + could not be executed.")) + }) + } } -fn typecheck(tree: &Tree, c: &mut Context) -> Result { +fn typecheck( + tree: &Tree, + c: &mut TypeContext +) -> Result +{ match tree { Tree::Concat(t1, t2) => { typecheck(t1, c)?; @@ -492,7 +831,7 @@ fn typecheck(tree: &Tree, c: &mut Context) -> Result { fn typecheck_expression( exp: &Expression, - c: &Context + c: &TypeContext ) -> Result { match exp { Expression::True | @@ -505,23 +844,8 @@ fn typecheck_expression( Expression::Var(v) => c.get(v), Expression::Unary(u, exp) => { - if let Unary::Qualifier(_) = u { - let type_exp = typecheck_expression(exp, c)?; - if type_exp == AssertionTypes::Label { - Ok(AssertionTypes::Set) - } else { - Err("Trying to get the field of a label, but value is not \ - a label.".into()) - } - } else { - let type_exp = typecheck_expression(exp, c)?; - let possible_types = u.associated_types_unary(); - if possible_types.contains(&type_exp) { - Ok(type_exp) - } else { - Err(format!("Cannot use operation {u} on type {type_exp}")) - } - } + let type_exp = typecheck_expression(exp, c)?; + u.associate(&type_exp) }, Expression::Binary(b, exp1, exp2) => { @@ -535,7 +859,7 @@ fn typecheck_expression( fn typecheck_range( range: &Range, - c: &mut Context + c: &mut TypeContext ) -> Result { match range { Range::IterateInRange(exp1, exp2) => { @@ -562,11 +886,137 @@ fn typecheck_range( } } +fn execute( + tree: &Tree, + c: &mut Context, + translator: &mut super::translator::Translator, +) -> Result { + match tree { + Tree::Concat(t1, t2) => { + execute(t1, c, translator)?; + execute(t2, c, translator) + }, + Tree::If(exp, t) => { + let guard = execute_exp(exp, c, translator)?; + if let AssertReturnValue::Boolean(true) = guard { + execute(t, c, translator) + } else { + Ok(AssertReturnValue::Boolean(true)) + } + }, + Tree::IfElse(exp, t1, t2) => { + let guard = execute_exp(exp, c, translator)?; + if let AssertReturnValue::Boolean(true) = guard { + execute(t1, c, translator) + } else { + execute(t2, c, translator) + } + }, + Tree::Assignment(v, exp) => { + let val = execute_exp(exp, c, translator)?; + c.assign(v, val)?; + Ok(AssertReturnValue::Boolean(true)) + }, + Tree::Return(exp) => { + execute_exp(exp, c, translator) + }, + Tree::For(v, r, t) => { + let range = range_into_iter(r, c, translator)?; + for val in range { + c.assign(&AssignmentVariable::Var(v.clone()), val)?; + execute(t, c, translator)?; + } + Ok(AssertReturnValue::Boolean(true)) + }, + } +} + +type RangeIterator = std::vec::IntoIter; + +fn range_into_iter( + range: &Range, + c: &mut Context, + translator: &mut super::translator::Translator, +) -> Result { + match range { + Range::IterateOverSet(exp) => { + let val = execute_exp(exp, c, translator)?; + if let AssertReturnValue::Set(set) = val { + Ok(set.into_iter().map(AssertReturnValue::Element) + .collect::>().into_iter()) + } else { + Err(format!("{val} is not a set in for cycle.")) + } + }, + Range::IterateInRange(exp1, exp2) => { + let val1 = execute_exp(exp1, c, translator)?; + let val2 = execute_exp(exp2, c, translator)?; + match (val1, val2) { + (AssertReturnValue::Integer(i1), + AssertReturnValue::Integer(i2)) => + Ok((i1..i2).map(AssertReturnValue::Integer) + .collect::>().into_iter()), + (val1, val2) => + Err(format!("{val1}..{val2} is not a valid integer range \ + in for cycle.")) + } + } + } +} + +fn execute_exp( + exp: &Expression, + c: &Context, + translator: &mut super::translator::Translator, +) -> Result { + match exp { + Expression::True => Ok(AssertReturnValue::Boolean(true)), + Expression::False => Ok(AssertReturnValue::Boolean(false)), + Expression::Integer(i) => Ok(AssertReturnValue::Integer(*i)), + Expression::Label(l) => Ok(AssertReturnValue::Label(*l.clone())), + Expression::Set(set) => Ok(AssertReturnValue::Set(set.clone())), + Expression::Element(el) => + Ok(AssertReturnValue::Element(*el)), + Expression::String(s) => Ok(AssertReturnValue::String(s.clone())), + Expression::Var(var) => c.get(var), + Expression::Unary(u, exp) => { + let val = execute_exp(exp, c, translator)?; + val.unary(u, translator) + }, + Expression::Binary(b, exp1, exp2) => { + let val1 = execute_exp(exp1, c, translator)?; + let val2 = execute_exp(exp2, c, translator)?; + val1.binary(b, val2, translator) + }, + } +} + impl RSassert { pub fn typecheck(&self) -> Result<(), String> { - let mut context = Context::new(); + let mut context = TypeContext::new(); typecheck(&self.tree, &mut context)?; - Ok(()) + let ty = context.return_ty.unwrap_or(AssertionTypes::NoType); + match ty { + AssertionTypes::Boolean | + AssertionTypes::Integer | + AssertionTypes::String | + AssertionTypes::Label | + AssertionTypes::Set | + AssertionTypes::Element => Ok(()), + AssertionTypes::NoType | + AssertionTypes::RangeInteger | + AssertionTypes::RangeSet => + Err(format!("Returned type {ty} is not a valid return type.")), + } + } + + pub fn execute( + &self, + label: &super::structure::RSlabel, + translator: &mut super::translator::Translator, + ) -> Result { + let mut context = Context::new(label); + execute(&self.tree, &mut context, translator) } } @@ -620,9 +1070,9 @@ impl fmt::Display for Expression { Self::True => {write!(f, "True")}, Self::False => {write!(f, "False")}, Self::Integer(i) => {write!(f, "{i}")}, - Self::Label(rslabel) => {write!(f, "{rslabel:?}")}, - Self::Set(set) => {write!(f, "{set:?}")}, - Self::Element(el) => {write!(f, "'{el}'")}, + Self::Label(rslabel) => {write!(f, "{{debug: {rslabel:?}}}")}, + Self::Set(set) => {write!(f, "{{debug: {set:?}}}")}, + Self::Element(el) => {write!(f, "'{{debug: {el:?}}}'")}, Self::String(s) => {write!(f, r#""{s}""#)}, Self::Var(v) => {write!(f, "{v}")}, Self::Unary(u, exp) => { @@ -661,13 +1111,13 @@ impl fmt::Display for Range { impl fmt::Display for Unary { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Unary::Not => write!(f, "not"), - Unary::Rand => write!(f, "rand"), - Unary::Empty => write!(f, ".empty"), - Unary::Length => write!(f, ".length"), - Unary::ToStr => write!(f, ".tostr"), - Unary::Qualifier(q) => write!(f, ".{q}"), - Unary::ToEl => write!(f, ".toel") + Self::Not => write!(f, "not"), + Self::Rand => write!(f, "rand"), + Self::Empty => write!(f, ".empty"), + Self::Length => write!(f, ".length"), + Self::ToStr => write!(f, ".tostr"), + Self::Qualifier(q) => write!(f, ".{q}"), + Self::ToEl => write!(f, ".toel") } } } @@ -675,20 +1125,13 @@ impl fmt::Display for Unary { impl fmt::Display for QualifierRestricted { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - QualifierRestricted::Entities => - write!(f, "Entities"), - QualifierRestricted::Context => - write!(f, "Context"), - QualifierRestricted::Reactants => - write!(f, "Reactants"), - QualifierRestricted::ReactantsAbsent => - write!(f, "ReactantsAbsent"), - QualifierRestricted::Inhibitors => - write!(f, "Inhibitors"), - QualifierRestricted::InhibitorsPresent => - write!(f, "InhibitorsPresent"), - QualifierRestricted::Products => - write!(f, "Products"), + Self::Entities => write!(f, "Entities"), + Self::Context => write!(f, "Context"), + Self::Reactants => write!(f, "Reactants"), + Self::ReactantsAbsent => write!(f, "ReactantsAbsent"), + Self::Inhibitors => write!(f, "Inhibitors"), + Self::InhibitorsPresent => write!(f, "InhibitorsPresent"), + Self::Products => write!(f, "Products"), } } } @@ -730,3 +1173,32 @@ impl fmt::Display for Binary { } } } + +impl fmt::Display for AssertReturnValue { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Boolean(b) => {write!(f, "{b}")}, + Self::Integer(i) => {write!(f, "{i}")}, + Self::String(s) => {write!(f, r#""{s}""#)}, + Self::Label(l) => {write!(f, "{{debug: {l:?}}}")}, + Self::Set(set) => {write!(f, "{{debug: {set:?}}}")}, + Self::Element(el) => {write!(f, "{{debug: {el:?}}}")}, + } + } +} + +impl fmt::Display for AssertionTypes { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Boolean => write!(f, "boolean"), + Self::Integer => write!(f, "integer"), + Self::String => write!(f, "string"), + Self::Label => write!(f, "label"), + Self::Set => write!(f, "set"), + Self::Element => write!(f, "element"), + Self::NoType => write!(f, "no type"), + Self::RangeInteger => write!(f, "range of integers"), + Self::RangeSet => write!(f, "range of set"), + } + } +} diff --git a/src/rsprocess/grammar.lalrpop b/src/rsprocess/grammar.lalrpop index 2e66bce..e74bee7 100644 --- a/src/rsprocess/grammar.lalrpop +++ b/src/rsprocess/grammar.lalrpop @@ -261,6 +261,7 @@ AssertUnarySuffix: assert::Unary = { ".empty" => assert::Unary::Empty, ".length" => assert::Unary::Length, ".tostr" => assert::Unary::ToStr, + ".toel" => assert::Unary::ToEl, "." => assert::Unary::Qualifier(q), }