From 39d7f4a123171a1dc5d5a8ec1c512599d4bec0f0 Mon Sep 17 00:00:00 2001 From: troido Date: Sun, 20 Sep 2020 12:55:40 +0200 Subject: renamed ComponentParameter to ParemeterExpression; List and Template variant for ParameterExpression --- src/assemblage.rs | 22 ++--- src/componentparameter.rs | 150 --------------------------------- src/main.rs | 2 +- src/parameterexpression.rs | 204 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 216 insertions(+), 162 deletions(-) delete mode 100644 src/componentparameter.rs create mode 100644 src/parameterexpression.rs diff --git a/src/assemblage.rs b/src/assemblage.rs index c88b39b..c706ed0 100644 --- a/src/assemblage.rs +++ b/src/assemblage.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use serde_json::{Value, json, value}; use crate::{ - componentparameter::ComponentParameter, + parameterexpression::ParameterExpression, parameter::{Parameter, ParameterType}, componentwrapper::{ComponentWrapper, ComponentType}, components::Serialise, @@ -18,7 +18,7 @@ type ArgumentDef = (String, ParameterType, Option); #[derive(Debug, PartialEq, Clone)] pub struct Assemblage { pub arguments: Vec, - pub components: Vec<(ComponentType, HashMap)>, + pub components: Vec<(ComponentType, HashMap)>, pub save: bool, pub extract: Vec<(String, ComponentType, String)> } @@ -47,7 +47,7 @@ impl Assemblage { Ok(arguments) } - fn parse_definition_components(comps: &[Value]) -> PResult)>> { + fn parse_definition_components(comps: &[Value]) -> PResult)>> { let mut components = Vec::new(); for tup in comps { if let Some(name) = tup.as_str() { @@ -55,9 +55,9 @@ impl Assemblage { } else { let (name, params) = value::from_value::<(String, HashMap)>(tup.clone()).map_err(|e| perr!("invalid component definition: {:?}", e))?; let comptype = ComponentType::from_str(&name).ok_or(perr!("{} not a valid componenttype", name))?; - let mut parameters: HashMap = HashMap::new(); + let mut parameters: HashMap = HashMap::new(); for (key, value) in params.into_iter() { - let param = ComponentParameter::from_json(&value)?; + let param = ParameterExpression::from_json(&value)?; parameters.insert(key, param); } components.push((comptype, parameters)); @@ -227,9 +227,9 @@ mod tests { arguments: vec![("sprite".to_string(), ParameterType::String, Some(Parameter::String("grass1".to_string())))], components: vec![ (ComponentType::Visible, hashmap!( - "sprite".to_string() => ComponentParameter::Argument("sprite".to_string()), - "height".to_string() => ComponentParameter::Constant(Parameter::Float(0.1)), - "name".to_string() => ComponentParameter::Constant(Parameter::String("grass".to_string())) + "sprite".to_string() => ParameterExpression::Argument("sprite".to_string()), + "height".to_string() => ParameterExpression::Constant(Parameter::Float(0.1)), + "name".to_string() => ParameterExpression::Constant(Parameter::String("grass".to_string())) )) ], save: true, @@ -346,9 +346,9 @@ mod tests { arguments: vec![("sprite".to_string(), ParameterType::String, None)], components: vec![ (ComponentType::Visible, hashmap!( - "sprite".to_string() => ComponentParameter::Argument("sprite".to_string()), - "height".to_string() => ComponentParameter::Constant(Parameter::Float(0.1)), - "name".to_string() => ComponentParameter::Argument("sprite".to_string()) + "sprite".to_string() => ParameterExpression::Argument("sprite".to_string()), + "height".to_string() => ParameterExpression::Constant(Parameter::Float(0.1)), + "name".to_string() => ParameterExpression::Argument("sprite".to_string()) )) ], save: true, diff --git a/src/componentparameter.rs b/src/componentparameter.rs deleted file mode 100644 index 08c3244..0000000 --- a/src/componentparameter.rs +++ /dev/null @@ -1,150 +0,0 @@ - -use std::collections::HashMap; -use rand::Rng; -use serde_json::Value; -use crate::{ - parameter::{Parameter, ParameterType}, - Template, - Result, - aerr, - PResult, - perr -}; - -const MAX_NESTING: usize = 5; - - -#[derive(Debug, PartialEq, Clone)] -pub enum ComponentParameter { - Constant(Parameter), - Argument(String), - Random(Vec), - Concat(Vec), - If(Box, Box, Box), - TemplateSelf, - TemplateName -} - -impl ComponentParameter { - - pub fn evaluate(&self, arguments: &HashMap<&str, Parameter>, template: &Template) -> Option { - self.evaluate_(arguments, template, 0) - } - - fn evaluate_(&self, arguments: &HashMap<&str, Parameter>, template: &Template, nesting: usize) -> Option { - if nesting > MAX_NESTING { - return None; - } - match self { - Self::Constant(val) => { - Some(val.clone()) - } - Self::Argument(argname) => { - Some(arguments.get(argname.as_str())?.clone()) - } - Self::Random(options) => { - let r = rand::thread_rng().gen_range(0, options.len()); - options[r].evaluate_(arguments, template, nesting + 1) - } - Self::Concat(options) => { - let mut string = String::new(); - for option in options { - if let Parameter::String(s) = option.evaluate_(arguments, template, nesting+1)? { - string.push_str(&s); - } else { - return None; - } - } - Some(Parameter::String(string)) - } - Self::If(condition, thenval, elseval) => { - if let Parameter::Bool(b) = condition.evaluate_(arguments, template, nesting+1)? { - if b { - thenval.evaluate_(arguments, template, nesting+1) - } else { - elseval.evaluate_(arguments, template, nesting+1) - } - } else { - None - } - } - Self::TemplateSelf => Some(Parameter::Template(template.clone())), - Self::TemplateName => Some(Parameter::String(template.name.0.clone())), - - } - } - - pub fn from_json(value: &Value) -> PResult { - if !value.is_array() { - return Ok(Self::Constant(Parameter::guess_from_json(value)?)); - } - let paramvalue = value.get(1).ok_or(perr!("index 1 not in component parameter"))?; - let typename = value.get(0).ok_or(perr!("index 0 not in component parameter"))?.as_str().ok_or(perr!("compparam type not a string"))?; - if let Some(paramtype) = ParameterType::from_str(typename) { - Ok(Self::Constant(Parameter::from_typed_json(paramtype, paramvalue)?)) - } else { - match typename { - "A" | "arg" => { - let argname = paramvalue.as_str().ok_or(perr!("argument parameter not a string"))?.to_string(); - Ok(Self::Argument(argname)) - }, - "random" => { - let optionvalues = paramvalue.as_array().ok_or(perr!("random argument not an array"))?; - let mut options = Vec::new(); - for option in optionvalues { - options.push(Self::from_json(option)?) - } - Ok(Self::Random(options)) - }, - "concat" => { - let values = paramvalue.as_array().ok_or(perr!("concat argument not an array"))?; - let mut options = Vec::new(); - for option in values { - options.push(Self::from_json(option)?) - } - Ok(Self::Concat(options)) - }, - "if" => { - Ok(Self::If( - Box::new(Self::from_json(paramvalue.get(0).ok_or(perr!("if does not have condition"))?)?), - Box::new(Self::from_json(paramvalue.get(1).ok_or(perr!("if does not have then value"))?)?), - Box::new(Self::from_json(paramvalue.get(2).ok_or(perr!("if does not have else value"))?)?) - )) - } - "self" => Ok(Self::TemplateSelf), - "name" => Ok(Self::TemplateName), - _ => Err(perr!("unknown compparam type '{}'", typename)) - } - } - } - - #[allow(dead_code)] - pub fn get_type(&self, arguments: &[(String, ParameterType, Option)]) -> Result{ - Ok(match self { - Self::Constant(param) => param.paramtype(), - Self::Argument(argname) => arguments.iter().find(|(n, _t, _d)| n == argname).ok_or(aerr!("unknown argument name {} in {:?}", argname, arguments))?.1, - Self::Random(options) => { - let typ: ParameterType = options.get(0).ok_or(aerr!("random has no options"))?.get_type(arguments)?; - for param in options { - if param.get_type(arguments)? != typ { - return Err(aerr!("inconsistent parameter types in random")); - } - } - typ - }, - Self::If(condition, thenval, elseval) => { - if condition.get_type(arguments)? != ParameterType::Bool { - return Err(aerr!("if condition is not a bool")); - } - let typ: ParameterType = thenval.get_type(arguments)?; - if elseval.get_type(arguments)? != typ { - return Err(aerr!("inconsistent parameter types in if")); - } - typ - }, - Self::Concat(_s) => ParameterType::String, - Self::TemplateSelf => ParameterType::Template, - Self::TemplateName => ParameterType::String - }) - } -} diff --git a/src/main.rs b/src/main.rs index cbc92a6..9020e3c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,7 +18,7 @@ mod pos; mod componentwrapper; mod parameter; mod assemblage; -mod componentparameter; +mod parameterexpression; mod encyclopedia; mod template; mod roomtemplate; diff --git a/src/parameterexpression.rs b/src/parameterexpression.rs new file mode 100644 index 0000000..7e2236e --- /dev/null +++ b/src/parameterexpression.rs @@ -0,0 +1,204 @@ + +use std::collections::HashMap; +use rand::Rng; +use serde_json::{Value, json}; +use crate::{ + parameter::{Parameter, ParameterType}, + Template, + template::{SaveOption, EntityType}, + Result, + aerr, + PResult, + perr +}; + +const MAX_NESTING: usize = 5; + + +#[derive(Debug, PartialEq, Clone)] +pub enum ParameterExpression { + Constant(Parameter), + List(Vec), + #[allow(dead_code)] // rustc bug does not know that this variant is used: https://github.com/rust-lang/rust/issues/68408 + Template{name: EntityType, kwargs: HashMap, save: SaveOption}, + Argument(String), + Random(Vec), + Concat(Vec), + If(Box, Box, Box), + TemplateSelf, + TemplateName +} + +impl ParameterExpression { + + pub fn evaluate(&self, arguments: &HashMap<&str, Parameter>, template: &Template) -> Option { + self.evaluate_(arguments, template, 0) + } + + fn evaluate_(&self, arguments: &HashMap<&str, Parameter>, template: &Template, nesting: usize) -> Option { + if nesting > MAX_NESTING { + return None; + } + match self { + Self::Constant(val) => { + Some(val.clone()) + } + Self::List(values) => { + Some(Parameter::List(values.iter().map(|v| v.evaluate_(arguments, template, nesting+1)).collect::>>()?)) + } + Self::Template{name, kwargs, save} => { + Some(Parameter::Template(Template{ + name: name.clone(), + args: Vec::new(), + save: *save, + kwargs: kwargs + .iter() + .map( + |(k, v)| + Some((k.clone(), v.evaluate_(arguments, template, nesting+1)?))) + .collect::>>()? + })) + } + Self::Argument(argname) => { + Some(arguments.get(argname.as_str())?.clone()) + } + Self::Random(options) => { + let r = rand::thread_rng().gen_range(0, options.len()); + options[r].evaluate_(arguments, template, nesting + 1) + } + Self::Concat(options) => { + let mut string = String::new(); + for option in options { + if let Parameter::String(s) = option.evaluate_(arguments, template, nesting+1)? { + string.push_str(&s); + } else { + return None; + } + } + Some(Parameter::String(string)) + } + Self::If(condition, thenval, elseval) => { + if let Parameter::Bool(b) = condition.evaluate_(arguments, template, nesting+1)? { + if b { + thenval.evaluate_(arguments, template, nesting+1) + } else { + elseval.evaluate_(arguments, template, nesting+1) + } + } else { + None + } + } + Self::TemplateSelf => Some(Parameter::Template(template.clone())), + Self::TemplateName => Some(Parameter::String(template.name.0.clone())), + + } + } + + pub fn from_json(value: &Value) -> PResult { + if !value.is_array() { + return Ok(Self::Constant(Parameter::guess_from_json(value)?)); + } + let paramvalue = value.get(1).ok_or(perr!("index 1 not in component parameter"))?; + let typename = value.get(0).ok_or(perr!("index 0 not in component parameter"))?.as_str().ok_or(perr!("compparam type not a string"))?; + match typename { + "string" | "int" | "float" | "bool" | "pos" | "interaction" => { + let paramtype = ParameterType::from_str(typename).expect(&format!("unknown parameter type {:?}", typename)); + Ok(Self::Constant(Parameter::from_typed_json(paramtype, paramvalue)?)) + } + "list" => { + let values = paramvalue.as_array().ok_or(perr!("random argument not an array"))?; + let mut entries = Vec::new(); + for entry in values { + entries.push(Self::from_json(entry)?) + } + Ok(Self::List(entries)) + } + "template" => { + match paramvalue { + Value::String(s) => Ok(Self::Template{ + name: EntityType(s.clone()), + kwargs: HashMap::new(), + save: SaveOption::Default + }), + Value::Object(o) => { + let name = EntityType(o.get("type").ok_or(perr!("template doesn't have 'type'"))?.as_str().ok_or(perr!("template type not a string"))?.to_string()); + let mut kwargs = HashMap::new(); + for (key, arg) in o.get("kwargs").unwrap_or(&json!({})).as_object().ok_or(perr!("template kwargs not a json object"))? { + kwargs.insert(key.to_string(), Self::from_json(arg)?); + } + let save = match o.get("save") { + Some(Value::Bool(b)) if *b => SaveOption::Always, + Some(Value::Bool(_b)) => SaveOption::False, + None => SaveOption::Default, + _ => {return Err(perr!("save not a bool"))} + }; + Ok(Self::Template{name, kwargs, save}) + } + _ => return Err(perr!("invalid template {:?}", paramvalue)) + } + } + "A" | "arg" => { + let argname = paramvalue.as_str().ok_or(perr!("argument parameter not a string"))?.to_string(); + Ok(Self::Argument(argname)) + } + "random" => { + let optionvalues = paramvalue.as_array().ok_or(perr!("random argument not an array"))?; + let mut options = Vec::new(); + for option in optionvalues { + options.push(Self::from_json(option)?) + } + Ok(Self::Random(options)) + } + "concat" => { + let values = paramvalue.as_array().ok_or(perr!("concat argument not an array"))?; + let mut options = Vec::new(); + for option in values { + options.push(Self::from_json(option)?) + } + Ok(Self::Concat(options)) + } + "if" => { + Ok(Self::If( + Box::new(Self::from_json(paramvalue.get(0).ok_or(perr!("if does not have condition"))?)?), + Box::new(Self::from_json(paramvalue.get(1).ok_or(perr!("if does not have then value"))?)?), + Box::new(Self::from_json(paramvalue.get(2).ok_or(perr!("if does not have else value"))?)?) + )) + } + "self" => Ok(Self::TemplateSelf), + "name" => Ok(Self::TemplateName), + _ => Err(perr!("unknown compparam type '{}'", typename)) + } + } + + #[allow(dead_code)] + pub fn get_type(&self, arguments: &[(String, ParameterType, Option)]) -> Result{ + Ok(match self { + Self::Constant(param) => param.paramtype(), + Self::List(_) => ParameterType::List, + Self::Template{name: _, kwargs: _, save: _} => ParameterType::Template, + Self::Argument(argname) => arguments.iter().find(|(n, _t, _d)| n == argname).ok_or(aerr!("unknown argument name {} in {:?}", argname, arguments))?.1, + Self::Random(options) => { + let typ: ParameterType = options.get(0).ok_or(aerr!("random has no options"))?.get_type(arguments)?; + for param in options { + if param.get_type(arguments)? != typ { + return Err(aerr!("inconsistent parameter types in random")); + } + } + typ + }, + Self::If(condition, thenval, elseval) => { + if condition.get_type(arguments)? != ParameterType::Bool { + return Err(aerr!("if condition is not a bool")); + } + let typ: ParameterType = thenval.get_type(arguments)?; + if elseval.get_type(arguments)? != typ { + return Err(aerr!("inconsistent parameter types in if")); + } + typ + }, + Self::Concat(_s) => ParameterType::String, + Self::TemplateSelf => ParameterType::Template, + Self::TemplateName => ParameterType::String + }) + } +} -- cgit