use std::{ collections::{HashMap, HashSet}, error::Error, fmt::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 { fn get_position(&self) -> ParserPosition; } #[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 Type { Int, Float, String, Bool, Bytes, Void, Custom(String), } impl ToString for Type { fn to_string(&self) -> String { match self { Type::Int => "int".to_string(), Type::Float => "float".to_string(), Type::String => "string".to_string(), Type::Bool => "bool".to_string(), Type::Bytes => "bytes".to_string(), Type::Void => "void".to_string(), Type::Custom(name) => name.clone(), } } } impl Hash for Type { fn hash(&self, state: &mut H) { self.to_string().hash(state); } } impl From<&String> for Type { fn from(value: &String) -> Self { Self::from(value.as_str()) } } impl From for Type { fn from(value: String) -> Self { Self::from(value.as_str()) } } impl From<&str> for Type { fn from(s: &str) -> Self { match s { "int" => Type::Int, "float" => Type::Float, "string" => Type::String, "bool" => Type::Bool, "boolean" => Type::Bool, "bytes" => Type::Bytes, "void" => Type::Void, _ => Type::Custom(s.to_string()), } } } #[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() } } #[derive(Debug, Clone)] pub struct Field { pub name: String, pub typ: Type, pub array: bool, pub optional: bool, pub map: Option, } #[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() } } #[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() } } #[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, pub array: bool, pub optional: bool, } #[derive(Debug, Clone)] pub struct MethodOutput { pub typ: Type, pub array: bool, } #[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 = Type::from(&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: typ.clone(), array: field.array, optional: field.optional, map: field.map.as_ref().map(|s| Type::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(&rt.fieldtype); if typ != Type::Void { servdef.depends.insert(typ.clone()); } MethodOutput { typ, array: rt.array, } }), decorators: MethodDecorators { description: None, parameter_descriptions: HashMap::new(), return_description: None, }, }; let mut optional_starts = false; for inp in &method.inputs { let typ = Type::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, array: inp.array, optional: inp.optional, }); } 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 Type::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 Type::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) } }