diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index fbe0fca45345572d5c03507ce1147e34341b357e..f8fa2a8c7997917373315e46cdab473febf83d45 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -44,6 +44,15 @@ pub(crate) enum EvalContext<'a> { } ////////////////////////////////////////////// +#[derive(Debug)] +pub enum ComponentCreationError { + ModuleDoesntExist, + DefinitionDoesntExist, + DefinitionNotComponent, + InvalidNumArguments, + InvalidArgumentType(usize), +} + impl std::fmt::Debug for ProtocolDescription { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "(An opaque protocol description)") @@ -79,6 +88,8 @@ impl ProtocolDescription { pool: Mutex::new(parser.string_pool), }); } + + #[deprecated] pub(crate) fn component_polarities( &self, module_name: &[u8], @@ -130,7 +141,9 @@ impl ProtocolDescription { } Ok(result) } + // expects port polarities to be correct + #[deprecated] pub(crate) fn new_component(&self, module_name: &[u8], identifier: &[u8], ports: &[PortId]) -> ComponentState { let mut args = Vec::new(); for (&x, y) in ports.iter().zip(self.component_polarities(module_name, identifier).unwrap()) { @@ -147,6 +160,59 @@ impl ProtocolDescription { ComponentState { prompt: Prompt::new(&self.types, &self.heap, def, 0, ValueGroup::new_stack(args)) } } + // TODO: Ofcourse, rename this at some point, perhaps even remove it in its + // entirety. Find some way to interface with the parameter's types. + pub(crate) fn new_component_v2( + &self, module_name: &[u8], identifier: &[u8], arguments: ValueGroup + ) -> Result { + // Find the module in which the definition can be found + let module_root = self.lookup_module_root(module_name); + if module_root.is_none() { + return Err(ComponentCreationError::ModuleDoesntExist); + } + let module_root = module_root.unwrap(); + + let root = &self.heap[module_root]; + let definition_id = root.get_definition_ident(&heap, identifier); + if definition_id.is_none() { + return Err(ComponentCreationError::DefinitionDoesntExist); + } + let definition_id = definition_id.unwrap(); + + let definition = &self.heap[definition_id]; + if !definition.is_component() { + return Err(ComponentCreationError::DefinitionNotComponent); + } + + // Make sure that the types of the provided value group matches that of + // the expected types. + let definition = definition.as_component(); + if !definition.poly_vars.is_empty() { + return Err(ComponentCreationError::DefinitionNotComponent); + } + + // - check number of arguments + let expr_data = self.types.get_procedure_expression_data(&definition_id, 0); + if expr_data.arg_types.len() != arguments.values.len() { + return Err(ComponentCreationError::InvalidNumArguments); + } + + // - for each argument try to make sure the types match + for arg_idx in 0..arguments.values.len() { + let expected_type = &expr_data.arg_types[arg_idx]; + let provided_value = &arguments.values[arg_idx]; + if !self.verify_same_type(expected_type, 0, &arguments, provided_value) { + return Err(ComponentCreationError::InvalidArgumentType(arg_idx)); + } + } + + // By now we're sure that all of the arguments are correct. So create + // the connector. + return Ok(ComponentState{ + prompt: Prompt::new(&self.types, &self.heap, def, 0, arguments), + }); + } + fn lookup_module_root(&self, module_name: &[u8]) -> Option { for module in self.modules.iter() { match &module.name { @@ -161,6 +227,63 @@ impl ProtocolDescription { return None; } + + fn verify_same_type(&self, expected: &ConcreteType, expected_idx: usize, arguments: &ValueGroup, argument: &Value) -> bool { + use ConcreteTypePart as CTP; + + macro_rules! match_variant { + ($value:expr, $variant:expr) => { + if let $variant(_) = $value { true } else { false } + }; + } + + match &expected.parts[expected_idx] { + CTP::Void | CTP::Message | CTP::Slice | CTP::Function(_, _) | CTP::Component(_, _) => unreachable!(), + CTP::Bool => match_variant!(argument, Value::Bool), + CTP::UInt8 => match_variant!(argument, Value::UInt8), + CTP::UInt16 => match_variant!(argument, Value::UInt16), + CTP::UInt32 => match_variant!(argument, Value::UInt32), + CTP::UInt64 => match_variant!(argument, Value::UInt64), + CTP::SInt8 => match_variant!(argument, Value::SInt8), + CTP::SInt16 => match_variant!(argument, Value::SInt16), + CTP::SInt32 => match_variant!(argument, Value::SInt32), + CTP::SInt64 => match_variant!(argument, Value::SInt64), + CTP::Character => match_variant!(argument, Value::Char), + CTP::String => { + // Match outer string type and embedded character types + if let Value::String(heap_pos) = argument { + for element in &arguments.regions[*heap_pos as usize] { + if let Value::Char(_) = element {} else { + return false; + } + } + } else { + return false; + } + + return true; + }, + CTP::Array => { + if let Value::Array(heap_pos) = argument { + let heap_pos = *heap_pos; + for element in &arguments.regions[heap_pos as usize] { + if !self.verify_same_type(expected, expected_idx + 1, arguments, element) { + return false; + } + } + return true; + } else { + return false; + } + }, + CTP::Input => match_variant!(argument, Value::Input), + CTP::Output => match_variant!(argument, Value::Output), + CTP::Instance(_definition_id, _num_embedded) => { + todo!("implement full type checking on user-supplied arguments"); + return false; + }, + } + } } // TODO: @temp Should just become a concrete thing that is passed in