use std::{ collections::{HashMap, HashSet}, error::Error, fmt::{Debug, Display}, hash::{Hash, Hasher}, }; use anyhow::Result; use crate::parser::{ EnumStatement, Node, ParserPosition, RootNode, ServiceStatement, TypeStatement, }; static BUILT_INS: [&str; 6] = ["int", "float", "string", "boolean", "bytes", "void"]; pub trait Definition: Debug { fn get_position(&self) -> ParserPosition; fn get_name(&self) -> String; } #[derive(Debug, Clone)] pub struct IR { pub options: HashMap, pub steps: Vec, } #[derive(Debug, Clone)] pub enum Step { Type(TypeDefinition), Enum(EnumDefinition), Service(ServiceDefinition), } #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub enum BaseType { Int, Float, String, Bool, Bytes, Void, Custom(String), } impl ToString for BaseType { fn to_string(&self) -> String { match self { BaseType::Int => "int".to_string(), BaseType::Float => "float".to_string(), BaseType::String => "string".to_string(), BaseType::Bool => "bool".to_string(), BaseType::Bytes => "bytes".to_string(), BaseType::Void => "void".to_string(), BaseType::Custom(name) => name.clone(), } } } impl Hash for BaseType { fn hash(&self, state: &mut H) { self.to_string().hash(state); } } impl From<&String> for BaseType { fn from(value: &String) -> Self { Self::from(value.as_str()) } } impl From for BaseType { fn from(value: String) -> Self { Self::from(value.as_str()) } } impl From<&str> for BaseType { fn from(s: &str) -> Self { match s { "int" => BaseType::Int, "float" => BaseType::Float, "string" => BaseType::String, "bool" => BaseType::Bool, "boolean" => BaseType::Bool, "bytes" => BaseType::Bytes, "void" => BaseType::Void, _ => BaseType::Custom(s.to_string()), } } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum TypeModifier { None, Array, Optional, Map(BaseType), OptionalArray, OptionalMap(BaseType), OptionalMapArray(BaseType), MapArray(BaseType), } impl TypeModifier { pub fn from_flags(optional: bool, array: bool, map: Option) -> Self { match (optional, array, map) { (false, false, None) => Self::None, (false, true, None) => Self::Array, (true, false, None) => Self::Optional, (true, true, None) => Self::OptionalArray, (false, false, Some(map)) => Self::Map(map), (false, true, Some(map)) => Self::MapArray(map), (true, false, Some(map)) => Self::OptionalMap(map), (true, true, Some(map)) => Self::OptionalMapArray(map), } } pub fn get_flags(&self) -> (bool, bool, Option) { match self.clone() { Self::None => (false, false, None), Self::Array => (false, true, None), Self::Optional => (true, false, None), Self::OptionalArray => (true, true, None), Self::Map(map) => (false, false, Some(map)), Self::MapArray(map) => (false, true, Some(map)), Self::OptionalMap(map) => (true, false, Some(map)), Self::OptionalMapArray(map) => (true, true, Some(map)), } } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Type(pub BaseType, pub TypeModifier); impl Type { pub fn from_flags(base: BaseType, optional: bool, array: bool, map: Option) -> Self { Self(base, TypeModifier::from_flags(optional, array, map)) } /// Returns flags in the order of: (OPTIONAL, ARRAY, MAP) pub fn into_flags(self) -> (BaseType, bool, bool, Option) { let b = self.0.clone(); match self.1 { TypeModifier::None => (b, false, false, None), TypeModifier::Array => (b, false, true, None), TypeModifier::Optional => (b, true, false, None), TypeModifier::OptionalArray => (b, true, true, None), TypeModifier::Map(map) => (b, false, false, Some(map)), TypeModifier::MapArray(map) => (b, false, true, Some(map)), TypeModifier::OptionalMap(map) => (b, true, false, Some(map)), TypeModifier::OptionalMapArray(map) => (b, true, true, Some(map)), } } pub fn is_optional(&self) -> bool { self.1.get_flags().0 } pub fn is_array(&self) -> bool { self.1.get_flags().1 } pub fn is_map(&self) -> bool { self.1.get_flags().2.is_some() } pub fn get_map(&self) -> Option { self.1.get_flags().2.clone() } } // { // pub base: BaseType, // pub array: bool, // pub optional: bool, // pub map: Option, // } #[derive(Debug, Clone)] pub struct TypeDefinition { pub name: String, pub depends: HashSet, pub fields: Vec, pub position: ParserPosition, } impl Definition for TypeDefinition { fn get_position(&self) -> ParserPosition { self.position.clone() } fn get_name(&self) -> String { self.name.clone() } } #[derive(Debug, Clone)] pub struct Field { pub name: String, pub typ: Type, } #[derive(Debug, Clone)] pub struct EnumDefinition { pub name: String, pub values: Vec, pub position: ParserPosition, } impl Definition for EnumDefinition { fn get_position(&self) -> ParserPosition { self.position.clone() } fn get_name(&self) -> String { self.name.clone() } } #[derive(Debug, Clone)] pub struct EnumField { pub name: String, pub value: i32, } #[derive(Debug, Clone)] pub struct ServiceDefinition { pub name: String, pub depends: HashSet, pub methods: Vec, pub position: ParserPosition, } impl Definition for ServiceDefinition { fn get_position(&self) -> ParserPosition { self.position.clone() } fn get_name(&self) -> String { self.name.clone() } } #[derive(Debug, Clone)] pub struct Method { pub name: String, pub inputs: Vec, pub output: Option, pub decorators: MethodDecorators, } #[derive(Debug, Clone)] pub struct MethodInput { pub name: String, pub typ: Type, } #[derive(Debug, Clone)] pub struct MethodOutput { pub typ: Type, } #[derive(Debug, Clone)] pub struct MethodDecorators { pub description: Option, pub parameter_descriptions: HashMap, pub return_description: Option, } fn build_type(stmt: &TypeStatement) -> Result { let mut typedef = TypeDefinition { position: stmt.position.clone(), name: stmt.name.clone(), depends: HashSet::new(), fields: Vec::new(), }; for field in &stmt.fields { let typ = BaseType::from(&field.fieldtype); if stmt.name != field.fieldtype { typedef.depends.insert(typ.clone()); } if let Some(maptype) = &field.map { if maptype != "string" && maptype != "int" { return Err(IRError::new("Map type must be string or int", field).into()); } } typedef.fields.push(Field { name: field.name.clone(), typ: Type::from_flags( typ.clone(), field.optional, field.array, field.map.as_ref().map(|s| BaseType::from(s)), ), }); } Ok(typedef) } fn build_enum(stmt: &EnumStatement) -> Result { let mut enumdef = EnumDefinition { position: stmt.position.clone(), name: stmt.name.clone(), values: Vec::new(), }; let mut last = -1 as i32; for field in &stmt.values { let value = if let Some(value) = field.value { if value > std::i32::MAX as i64 { return Err(IRError::new("Enum value too large", field).into()); } let value = value as i32; if value <= last { return Err(IRError::new("Enum values must be increasing", field).into()); } last = value; value } else { last = last + 1; last }; enumdef.values.push(EnumField { name: field.name.clone(), value, }); } Ok(enumdef) } fn build_service(stmt: &ServiceStatement) -> Result { let mut servdef = ServiceDefinition { position: stmt.position.clone(), name: stmt.name.clone(), depends: HashSet::new(), methods: Vec::new(), }; for method in &stmt.methods { let mut methoddef = Method { name: method.name.clone(), inputs: Vec::new(), output: method.return_type.as_ref().map(|rt| { let typ = Type::from_flags(BaseType::from(&rt.fieldtype), false, rt.array, None); if typ.0 != BaseType::Void { servdef.depends.insert(typ.0.clone()); } MethodOutput { typ } }), decorators: MethodDecorators { description: None, parameter_descriptions: HashMap::new(), return_description: None, }, }; let mut optional_starts = false; for inp in &method.inputs { let typ = BaseType::from(&inp.fieldtype); servdef.depends.insert(typ.clone()); if optional_starts && !inp.optional { return Err( IRError::new("Optional fields must come after required fields", inp).into(), ); } if inp.optional { optional_starts = true; } methoddef.inputs.push(MethodInput { name: inp.name.clone(), typ: Type::from_flags(typ.clone(), inp.optional, inp.array, None), }); } for decorator in &method.decorators { match decorator.name.as_str() { "Description" => { if methoddef.decorators.description.is_some() { return Err(IRError::new("Duplicate description", decorator).into()); } if decorator.args.len() != 1 { return Err(IRError::new( "Description must have exactly one argument", decorator, ) .into()); } methoddef.decorators.description = Some(decorator.args[0].clone()); } "Returns" => { if methoddef.decorators.return_description.is_some() { return Err(IRError::new("Duplicate return description", decorator).into()); } if decorator.args.len() != 1 { return Err(IRError::new( "Returns must have exactly one argument", decorator, ) .into()); } methoddef.decorators.return_description = Some(decorator.args[0].clone()); } "Param" => { if decorator.args.len() != 2 { return Err(IRError::new( "Param must have exactly two arguments", decorator, ) .into()); } let name = decorator.args[0].clone(); let description = decorator.args[1].clone(); if methoddef .decorators .parameter_descriptions .contains_key(&name) { return Err( IRError::new("Duplicate parameter description", decorator).into() ); } if methoddef.inputs.iter().find(|i| i.name == name).is_none() { return Err(IRError::new("Parameter not found", decorator).into()); } methoddef .decorators .parameter_descriptions .insert(name, description); } _ => { return Err(IRError::new("Unknown decorator", decorator).into()); } } } servdef.methods.push(methoddef); } Ok(servdef) } pub fn build_ir(root: &Vec) -> Result { let mut options = HashMap::::new(); let mut steps = Vec::new(); for node in root { match node { RootNode::Type(stmt) => steps.push(Step::Type(build_type(stmt)?)), RootNode::Enum(stmt) => steps.push(Step::Enum(build_enum(stmt)?)), RootNode::Service(stmt) => steps.push(Step::Service(build_service(stmt)?)), RootNode::Define(stmt) => { if options.contains_key(&stmt.key) { return Err(IRError::new("Duplicate define", stmt).into()); } if (stmt.key == "use_messagepack" || stmt.key == "allow_bytes") && stmt.value == "true" { options.insert("allow_bytes".to_owned(), "true".to_owned()); } options.insert(stmt.key.clone(), stmt.value.clone()); } RootNode::Import(_) => { panic!("Import not supported at this stage!"); } } } let mut all_types = HashSet::::new(); let mut serv_types = HashSet::::new(); for bi in &BUILT_INS { all_types.insert(bi.to_string()); } for step in &steps { match step { Step::Type(typedef) => { if all_types.contains(&typedef.name) { return Err(IRError::new_from_def("Duplicate type", typedef).into()); } all_types.insert(typedef.name.clone()); } Step::Enum(enumdef) => { if all_types.contains(&enumdef.name) { return Err(IRError::new_from_def("Duplicate type", enumdef).into()); } all_types.insert(enumdef.name.clone()); } Step::Service(servdef) => { if serv_types.contains(&servdef.name) { return Err(IRError::new_from_def("Duplicate type", servdef).into()); } serv_types.insert(servdef.name.clone()); } } } // Verify dependencies for step in &steps { match step { Step::Type(typedef) => { for dep in &typedef.depends { if let BaseType::Custom(dep) = dep { if !all_types.contains(dep) { return Err(IRError::new_from_def( &format!("Type {} depends on unknown type {}", typedef.name, dep), typedef, ) .into()); } } } } Step::Service(servdef) => { for dep in &servdef.depends { if let BaseType::Custom(dep) = dep { if !all_types.contains(dep) { return Err(IRError::new_from_def( &format!( "Service {} depends on unknown type {}", servdef.name, dep ), servdef, ) .into()); } } } } _ => {} } } Ok(IR { options, steps }) } #[derive(Debug, Clone)] pub struct IRError { pub message: String, pub position: ParserPosition, } impl IRError { fn new(msg: &str, node: &impl Node) -> IRError { IRError { message: format!("{}: {}", msg, node.get_name()), position: node.get_position(), } } fn new_from_def(msg: &str, def: &impl Definition) -> IRError { IRError { message: msg.to_string(), position: def.get_position(), } } } impl Error for IRError {} impl Display for IRError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "ParserError: {} at {:?}", self.message, self.position) } }