mod arena; pub(crate) mod eval; pub(crate) mod input_source; mod parser; #[cfg(test)] mod tests; pub(crate) mod ast; pub(crate) mod ast_writer; mod token_writer; use std::sync::Mutex; use crate::collections::{StringPool, StringRef}; pub use crate::protocol::ast::*; use crate::protocol::eval::*; use crate::protocol::input_source::*; use crate::protocol::parser::*; use crate::protocol::type_table::*; pub use parser::type_table::TypeId; /// A protocol description module pub struct Module { pub(crate) source: InputSource, pub(crate) root_id: RootId, pub(crate) name: Option>, } /// Description of a protocol object, used to configure new connectors. #[repr(C)] pub struct ProtocolDescription { pub(crate) modules: Vec, pub(crate) heap: Heap, pub(crate) types: TypeTable, pub(crate) pool: Mutex, } #[derive(Debug, Clone)] pub(crate) struct ComponentState { pub(crate) prompt: Prompt, } #[derive(Debug)] pub enum ComponentCreationError { ModuleDoesntExist, DefinitionDoesntExist, DefinitionNotComponent, InvalidNumArguments, InvalidArgumentType(usize), UnownedPort, InSync, } impl ProtocolDescription { pub fn parse(buffer: &[u8]) -> Result { let source = InputSource::new(String::new(), Vec::from(buffer)); let mut parser = Parser::new(None)?; parser.feed(source).expect("failed to feed source"); if let Err(err) = parser.parse() { println!("ERROR:\n{}", err); return Err(format!("{}", err)) } let modules: Vec = parser.modules.into_iter() .map(|module| Module{ source: module.source, root_id: module.root_id, name: module.name.map(|(_, name)| name) }) .collect(); return Ok(ProtocolDescription { modules, heap: parser.heap, types: parser.type_table, pool: Mutex::new(parser.string_pool), }); } pub(crate) fn new_component( &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_by_ident(&self.heap, identifier); if definition_id.is_none() { return Err(ComponentCreationError::DefinitionDoesntExist); } let definition_id = definition_id.unwrap(); let ast_definition = &self.heap[definition_id]; if !ast_definition.is_procedure() { return Err(ComponentCreationError::DefinitionNotComponent); } // Make sure that the types of the provided value group matches that of // the expected types. let ast_definition = ast_definition.as_procedure(); if !ast_definition.poly_vars.is_empty() || ast_definition.kind == ProcedureKind::Function { return Err(ComponentCreationError::DefinitionNotComponent); } // - check number of arguments by retrieving the one instantiated // monomorph let concrete_type = ConcreteType{ parts: vec![ConcreteTypePart::Component(ast_definition.this, 0)] }; let procedure_type_id = self.types.get_monomorph_type_id(&definition_id, &concrete_type.parts).unwrap(); let procedure_monomorph_index = self.types.get_monomorph(procedure_type_id).variant.as_procedure().monomorph_index; let monomorph_info = &ast_definition.monomorphs[procedure_monomorph_index as usize]; if monomorph_info.argument_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_id = monomorph_info.argument_types[arg_idx]; let expected_type = &self.types.get_monomorph(expected_type_id).concrete_type; 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(Prompt::new(&self.types, &self.heap, ast_definition.this, procedure_type_id, arguments)); } /// A somewhat temporary method. Can be used by components to lookup type /// definitions by their name (to have their implementation somewhat /// resistant to changes in the standard library) pub(crate) fn find_type<'a>(&'a self, module_name: &[u8], type_name: &[u8]) -> Option> { // Lookup type definition in module let root_id = self.lookup_module_root(module_name)?; let module = &self.heap[root_id]; let definition_id = module.get_definition_by_ident(&self.heap, type_name)?; let definition = &self.heap[definition_id]; // Make sure type is not polymorphic and is not a procedure if !definition.poly_vars().is_empty() { return None; } if definition.is_procedure() { return None; } // Lookup type in type table let type_parts = [ConcreteTypePart::Instance(definition_id, 0)]; let type_id = self.types.get_monomorph_type_id(&definition_id, &type_parts) .expect("type ID for non-polymorphic type"); let type_monomorph = self.types.get_monomorph(type_id); return Some(TypeInspector{ heap: definition, type_table: type_monomorph }); } /// Again a somewhat temporary method. Can be used by components to look up /// the definition of a particular procedure. Intended use is to find the /// DefinitionId/TypeId of builtin components. pub(crate) fn find_procedure(&self, module_name: &[u8], proc_name: &[u8]) -> Option<(ProcedureDefinitionId, TypeId)> { // Lookup type definition in module let root_id = self.lookup_module_root(module_name)?; let module = &self.heap[root_id]; let definition_id = module.get_definition_by_ident(&self.heap, proc_name)?; let definition = &self.heap[definition_id]; // Make sure the procedure is not polymorphic if !definition.poly_vars().is_empty() { return None; } if !definition.is_procedure() { return None; } // Lookup in type table let definition = definition.as_procedure(); let type_parts = [ConcreteTypePart::Component(definition.this, 0)]; let type_id = self.types.get_monomorph_type_id(&definition.this.upcast(), &type_parts) .expect("type ID for non-polymorphic procedure"); return Some((definition.this, type_id)); } fn lookup_module_root(&self, module_name: &[u8]) -> Option { for module in self.modules.iter() { match &module.name { Some(name) => if name.as_bytes() == module_name { return Some(module.root_id); }, None => if module_name.is_empty() { return Some(module.root_id); } } } return None; } fn verify_same_type(&self, expected: &ConcreteType, expected_idx: usize, arguments: &ValueGroup, argument: &Value) -> bool { use ConcreteTypePart as CTP; match &expected.parts[expected_idx] { CTP::Void | CTP::Message | CTP::Slice | CTP::Pointer | CTP::Function(_, _) | CTP::Component(_, _) => unreachable!(), CTP::Bool => if let Value::Bool(_) = argument { true } else { false }, CTP::UInt8 => if let Value::UInt8(_) = argument { true } else { false }, CTP::UInt16 => if let Value::UInt16(_) = argument { true } else { false }, CTP::UInt32 => if let Value::UInt32(_) = argument { true } else { false }, CTP::UInt64 => if let Value::UInt64(_) = argument { true } else { false }, CTP::SInt8 => if let Value::SInt8(_) = argument { true } else { false }, CTP::SInt16 => if let Value::SInt16(_) = argument { true } else { false }, CTP::SInt32 => if let Value::SInt32(_) = argument { true } else { false }, CTP::SInt64 => if let Value::SInt64(_) = argument { true } else { false }, CTP::Character => if let Value::Char(_) = argument { true } else { false }, 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 => if let Value::Input(_) = argument { true } else { false }, CTP::Output => if let Value::Output(_) = argument { true } else { false }, CTP::Tuple(_) => todo!("implement full type checking on user-supplied arguments"), CTP::Instance(definition_id, _num_embedded) => { let definition = self.types.get_base_definition(definition_id).unwrap(); match &definition.definition { DefinedTypeVariant::Enum(definition) => { if let Value::Enum(variant_value) = argument { let is_valid = definition.variants.iter() .any(|v| v.value == *variant_value); return is_valid; } }, _ => todo!("implement full type checking on user-supplied arguments"), } return false; }, } } } pub trait RunContext { fn performed_put(&mut self, port: PortId) -> bool; fn performed_get(&mut self, port: PortId) -> Option; // None if still waiting on message fn fires(&mut self, port: PortId) -> Option; // None if not yet branched fn performed_fork(&mut self) -> Option; // None if not yet forked fn created_channel(&mut self) -> Option<(Value, Value)>; // None if not yet prepared fn performed_select_wait(&mut self) -> Option; // None if not yet notified runtime of select blocker } pub struct ProtocolDescriptionBuilder { parser: Parser, } impl ProtocolDescriptionBuilder { pub fn new(std_lib_dir: Option) -> Result { let mut parser = Parser::new(std_lib_dir)?; return Ok(Self{ parser }) } pub fn add(&mut self, filename: String, buffer: Vec) -> Result<(), ParseError> { let input = InputSource::new(filename, buffer); self.parser.feed(input)?; return Ok(()) } pub fn compile(mut self) -> Result { self.parser.parse()?; let modules: Vec = self.parser.modules.into_iter() .map(|module| Module{ source: module.source, root_id: module.root_id, name: module.name.map(|(_, name)| name) }) .collect(); return Ok(ProtocolDescription { modules, heap: self.parser.heap, types: self.parser.type_table, pool: Mutex::new(self.parser.string_pool), }); } } pub struct TypeInspector<'a> { heap: &'a Definition, type_table: &'a MonoType, } impl<'a> TypeInspector<'a> { pub fn as_union(&'a self) -> UnionTypeInspector<'a> { let heap = self.heap.as_union(); let type_table = self.type_table.variant.as_union(); return UnionTypeInspector{ heap, type_table }; } pub fn as_struct(&'a self) -> StructTypeInspector<'a> { let heap = self.heap.as_struct(); let type_table = self.type_table.variant.as_struct(); return StructTypeInspector{ heap, type_table }; } } pub struct UnionTypeInspector<'a> { heap: &'a UnionDefinition, type_table: &'a UnionMonomorph, } impl UnionTypeInspector<'_> { /// Retrieves union variant tag value. pub fn get_variant_tag_value(&self, variant_name: &[u8]) -> Option { let variant_index = self.heap.variants.iter() .position(|v| v.identifier.value.as_bytes() == variant_name)?; return Some(variant_index as i64); } } pub struct StructTypeInspector<'a> { heap: &'a StructDefinition, type_table: &'a StructMonomorph, } impl StructTypeInspector<'_> { /// Retrieves number of struct fields pub fn get_num_struct_fields(&self) -> usize { return self.heap.fields.len(); } /// Retrieves struct field index pub fn get_struct_field_index(&self, field_name: &[u8]) -> Option { let field_index = self.heap.fields.iter() .position(|v| v.field.value.as_bytes() == field_name)?; return Some(field_index); } }