| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use { |
| crate::Rule, |
| failure::Fail, |
| pest::iterators::{Pair, Pairs}, |
| serde_derive::Serialize, |
| std::fmt, |
| std::{ |
| collections::{HashMap, HashSet, VecDeque}, |
| str::FromStr, |
| }, |
| }; |
| |
| #[derive(Debug, Fail)] |
| pub enum ParseError { |
| #[fail(display = "The primary namespace was already set")] |
| AlreadyPrimaryNamespace, |
| #[fail(display = "{} is not yet support", 0)] |
| NotYetSupported(String), |
| #[fail(display = "{:?} is not expected in this location", 0)] |
| UnexpectedToken(Rule), |
| #[fail(display = "{} was not included in the input libraries", 0)] |
| UnImported(String), |
| #[fail(display = "{:?} is an unknown type", 0)] |
| UnrecognizedType(String), |
| #[fail(display = "Failed to parse because {:?} is not an integer", 0)] |
| NotAnInteger(Rule), |
| #[fail(display = "Invalid dependencies: {}", 0)] |
| InvalidDeps(String), |
| } |
| |
| #[derive(PartialEq, Eq, Serialize, Default, Debug, Hash)] |
| pub struct Attr { |
| pub key: String, |
| pub val: Option<String>, |
| } |
| |
| #[derive(PartialEq, Eq, Serialize, Default, Debug, Hash)] |
| pub struct Attrs(pub Vec<Attr>); |
| pub type Ident = String; |
| |
| impl Attrs { |
| #[allow(dead_code)] |
| pub fn has_attributes(&self) -> bool { |
| self.0.len() > 0 |
| } |
| |
| pub fn has_attribute(&self, key: &str) -> bool { |
| self.0.iter().any(|attr| attr.key == key) |
| } |
| |
| pub fn get_attribute<'a>(&'a self, key: &str) -> Option<&'a String> { |
| if let Some(attr) = self.0.iter().find(|attr| attr.key == key) { |
| if let Some(ref val) = attr.val { |
| Some(val) |
| } else { |
| None |
| } |
| } else { |
| None |
| } |
| } |
| |
| pub fn from_pair(pair: Pair<'_, Rule>) -> Result<Attrs, ParseError> { |
| let mut attrs = Attrs::default(); |
| let mut doc_string: Option<String> = None; // String::default(); |
| for inner_pair in pair.into_inner() { |
| match inner_pair.as_rule() { |
| Rule::doc_comment => { |
| if doc_string.is_none() { |
| doc_string = Some(String::default()) |
| } |
| if let Some(ref mut doc_string) = doc_string { |
| *doc_string += inner_pair.as_str().split_at(3).1; |
| } |
| } |
| Rule::attribute_list => { |
| let attr_string = inner_pair |
| .as_str() |
| .trim_start_matches('[') |
| .trim_end_matches(']'); |
| let attr_pairs = attr_string.split(","); |
| for ap in attr_pairs { |
| if !ap.contains("=") { |
| attrs.0.push(Attr { |
| key: String::from(ap.trim()), |
| val: None, |
| }); |
| } else { |
| let split: Vec<&str> = ap.split("=").collect(); |
| attrs.0.push(Attr { |
| key: String::from(split[0].trim()), |
| val: Some(String::from(split[1])), |
| }); |
| } |
| } |
| } |
| _ => unreachable!(), |
| } |
| } |
| if doc_string.is_some() { |
| attrs.0.push(Attr { |
| key: String::from("Doc"), |
| val: doc_string, |
| }); |
| } |
| Ok(attrs) |
| } |
| } |
| |
| #[derive(PartialEq, Eq, Clone, Serialize, Debug, Hash)] |
| pub enum Constant { |
| SizedRaw(usize), |
| SizedConstant(Ident), |
| } |
| |
| impl Constant { |
| pub fn from_str(string: &str) -> Result<Self, ParseError> { |
| match usize::from_str(string) { |
| Ok(s) => Ok(Constant::SizedRaw(s)), |
| Err(_) => Ok(Constant::SizedConstant(string.to_string())), |
| } |
| } |
| } |
| |
| #[derive(PartialEq, Eq, Clone, Serialize, Debug, Hash)] |
| pub enum HandleTy { |
| Handle, |
| Process, |
| Thread, |
| Vmo, |
| Channel, |
| Event, |
| Port, |
| Interrupt, |
| Log, |
| Socket, |
| Resource, |
| EventPair, |
| Job, |
| Vmar, |
| Fifo, |
| Guest, |
| Timer, |
| Bti, |
| Profile, |
| } |
| |
| #[derive(PartialEq, Eq, Clone, Serialize, Debug, Hash)] |
| pub enum Ty { |
| Voidptr, |
| USize, |
| Bool, |
| Int8, |
| Int16, |
| Int32, |
| Int64, |
| UInt8, |
| UInt16, |
| UInt32, |
| UInt64, |
| Float32, |
| Float64, |
| Str { |
| size: Option<Constant>, |
| nullable: bool, |
| }, |
| Vector { |
| ty: Box<Ty>, |
| size: Option<Constant>, |
| nullable: bool, |
| }, |
| Array { |
| ty: Box<Ty>, |
| size: Constant, |
| }, |
| Interface, |
| Struct, |
| Union, |
| Enum, |
| Handle { |
| ty: HandleTy, |
| nullable: bool, |
| }, |
| Ident { |
| id: String, |
| nullable: bool, |
| }, |
| } |
| |
| impl fmt::Display for Ty { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| match self { |
| Ty::UInt32 => write!(f, "UInt32"), |
| _ => panic!("unknown type in Display conversion"), |
| } |
| } |
| } |
| |
| impl Ty { |
| pub fn is_primitive(&self) -> bool { |
| match self { |
| Ty::Ident { id, .. } => { |
| if id == "zx.status" { |
| return true; |
| } else { |
| return false; |
| } |
| } |
| Ty::Str { .. } | Ty::Vector { .. } | Ty::Array { .. } | Ty::Handle { .. } => false, |
| _ => true, |
| } |
| } |
| pub fn is_nullable(&self) -> bool { |
| match self { |
| Ty::Str { nullable, .. } => *nullable, |
| Ty::Vector { nullable, .. } => *nullable, |
| Ty::Ident { nullable, .. } => *nullable, |
| Ty::Handle { nullable, .. } => *nullable, |
| _ => false, |
| } |
| } |
| |
| pub fn from_pair(pair: &Pair<'_, Rule>) -> Result<Self, ParseError> { |
| let rule = pair.as_rule(); |
| match rule { |
| Rule::primitive_type => match pair.as_str() { |
| "usize" => Ok(Ty::USize), |
| "bool" => Ok(Ty::Bool), |
| "int8" => Ok(Ty::Int8), |
| "int16" => Ok(Ty::Int16), |
| "int32" => Ok(Ty::Int32), |
| "int64" => Ok(Ty::Int64), |
| "uint8" => Ok(Ty::UInt8), |
| "uint16" => Ok(Ty::UInt16), |
| "uint32" => Ok(Ty::UInt32), |
| "uint64" => Ok(Ty::UInt64), |
| "float32" => Ok(Ty::Float32), |
| "float64" => Ok(Ty::Float64), |
| "voidptr" => Ok(Ty::Voidptr), |
| _e => Err(ParseError::UnrecognizedType(pair.as_str().to_string())), |
| }, |
| Rule::handle_type => { |
| let mut ty = HandleTy::Handle; |
| let mut nullable = false; |
| for inner_pair in pair.clone().into_inner() { |
| match inner_pair.as_rule() { |
| Rule::handle_subtype => { |
| ty = match inner_pair.as_str() { |
| "process" => HandleTy::Process, |
| "thread" => HandleTy::Thread, |
| "vmo" => HandleTy::Vmo, |
| "channel" => HandleTy::Channel, |
| "event" => HandleTy::Event, |
| "port" => HandleTy::Port, |
| "interrupt" => HandleTy::Interrupt, |
| "log" => HandleTy::Log, |
| "socket" => HandleTy::Socket, |
| "resource" => HandleTy::Resource, |
| "eventpair" => HandleTy::EventPair, |
| "job" => HandleTy::Job, |
| "vmar" => HandleTy::Vmar, |
| "fifo" => HandleTy::Fifo, |
| "guest" => HandleTy::Guest, |
| "timer" => HandleTy::Timer, |
| "bti" => HandleTy::Bti, |
| "profile" => HandleTy::Profile, |
| _e => { |
| return Err(ParseError::UnrecognizedType( |
| inner_pair.as_str().to_string(), |
| )); |
| } |
| } |
| } |
| Rule::nullable => { |
| nullable = true; |
| } |
| _e => { |
| return Err(ParseError::UnrecognizedType( |
| inner_pair.as_str().to_string(), |
| )); |
| } |
| } |
| } |
| Ok(Ty::Handle { ty, nullable }) |
| } |
| Rule::integer_type => match pair.as_str() { |
| "usize" => Ok(Ty::USize), |
| "int8" => Ok(Ty::Int8), |
| "int16" => Ok(Ty::Int16), |
| "int32" => Ok(Ty::Int32), |
| "int64" => Ok(Ty::Int64), |
| "uint8" => Ok(Ty::UInt8), |
| "uint16" => Ok(Ty::UInt16), |
| "uint32" => Ok(Ty::UInt32), |
| "uint64" => Ok(Ty::UInt64), |
| _e => Err(ParseError::NotAnInteger(rule)), |
| }, |
| Rule::array_type => { |
| let vec_contents: Vec<Pair<'_, Rule>> = pair.clone().into_inner().collect(); |
| let ty = Box::new(Ty::from_pair(&vec_contents[0])?); |
| let size = Constant::from_str(vec_contents[1].as_str())?; |
| Ok(Ty::Array { ty, size }) |
| } |
| Rule::identifier_type => { |
| let mut iter = pair.clone().into_inner(); |
| let id = String::from(iter.next().unwrap().as_str()); |
| let nullable = if let Some(pair) = iter.next() { |
| match pair.as_rule() { |
| Rule::nullable => true, |
| e => { |
| return Err(ParseError::UnexpectedToken(e)); |
| } |
| } |
| } else { |
| false |
| }; |
| Ok(Ty::Ident { id, nullable }) |
| } |
| Rule::string_type => { |
| let mut size = None; |
| let mut nullable = false; |
| for inner_pair in pair.clone().into_inner() { |
| match inner_pair.as_rule() { |
| Rule::constant => { |
| size = Some(Constant::from_str(inner_pair.as_str())?); |
| } |
| Rule::nullable => { |
| nullable = true; |
| } |
| e => { |
| return Err(ParseError::UnexpectedToken(e)); |
| } |
| } |
| } |
| Ok(Ty::Str { size, nullable }) |
| } |
| Rule::vector_type => { |
| let mut iter = pair.clone().into_inner(); |
| let ty = Box::new(Ty::from_pair(&iter.next().unwrap())?); |
| let mut size = None; |
| let mut nullable = false; |
| for inner_pair in iter { |
| match inner_pair.as_rule() { |
| Rule::constant => { |
| size = Some(Constant::from_str(inner_pair.as_str())?); |
| } |
| Rule::nullable => { |
| nullable = true; |
| } |
| e => { |
| return Err(ParseError::UnexpectedToken(e)); |
| } |
| } |
| } |
| Ok(Ty::Vector { ty, size, nullable }) |
| } |
| _e => Err(ParseError::UnrecognizedType(pair.as_str().to_string())), |
| } |
| } |
| } |
| |
| // TODO string should be an Ident type |
| #[derive(PartialEq, Eq, Serialize, Debug, Hash)] |
| pub struct StructField { |
| pub attributes: Attrs, |
| pub ty: Ty, |
| pub ident: String, |
| } |
| impl StructField { |
| pub fn from_pair(pair: Pair<'_, Rule>) -> Result<Self, ParseError> { |
| let mut attributes = Attrs::default(); |
| let mut ty = None; |
| let mut ident = String::default(); |
| for inner_pair in pair.into_inner() { |
| match inner_pair.as_rule() { |
| Rule::attributes => { |
| attributes = Attrs::from_pair(inner_pair)?; |
| } |
| Rule::ident => { |
| ident = String::from(inner_pair.as_str()); |
| } |
| _ => { |
| ty = Some(Ty::from_pair(&inner_pair)?); |
| } |
| } |
| } |
| // TODO use the val field in structs |
| Ok(StructField { |
| attributes: attributes, |
| ty: ty.unwrap(), |
| ident: ident, |
| // val: String::from(fields[2].as_str()), |
| }) |
| } |
| } |
| |
| // TODO string should be an Ident type |
| #[derive(PartialEq, Eq, Serialize, Debug, Hash)] |
| pub struct UnionField { |
| pub attributes: Attrs, |
| pub ty: Ty, |
| pub ident: String, |
| } |
| impl UnionField { |
| pub fn from_pair(pair: Pair<'_, Rule>) -> Result<Self, ParseError> { |
| let fields: Vec<Pair<'_, Rule>> = pair.into_inner().collect(); |
| let ty = &fields[1]; |
| Ok(UnionField { |
| attributes: Attrs::from_pair(fields[0].clone())?, |
| ty: Ty::from_pair(ty)?, |
| ident: String::from(fields[2].as_str()), |
| }) |
| } |
| } |
| |
| // TODO string should be an Ident type |
| #[derive(PartialEq, Eq, Serialize, Debug, Hash)] |
| pub struct EnumVariant { |
| pub attributes: Attrs, |
| pub name: String, |
| pub size: Constant, |
| } |
| impl EnumVariant { |
| pub fn from_pair(pair: Pair<'_, Rule>) -> Result<Self, ParseError> { |
| let fields: Vec<Pair<'_, Rule>> = pair.into_inner().collect(); |
| Ok(EnumVariant { |
| attributes: Attrs::from_pair(fields[0].clone())?, |
| name: String::from(fields[1].as_str()), |
| size: Constant::from_str(fields[2].as_str())?, |
| }) |
| } |
| } |
| |
| #[derive(PartialEq, Eq, Serialize, Debug, Hash)] |
| pub struct Method { |
| pub attributes: Attrs, |
| pub name: String, |
| pub in_params: Vec<(String, Ty)>, |
| pub out_params: Vec<(String, Ty)>, |
| } |
| |
| impl Method { |
| pub fn from_pair(pair: Pair<'_, Rule>) -> Result<Self, ParseError> { |
| let mut attributes = Attrs::default(); |
| let mut name = String::default(); |
| let mut in_params = Vec::new(); |
| let mut out_params = Vec::new(); |
| for inner_pair in pair.into_inner() { |
| match inner_pair.as_rule() { |
| Rule::attributes => { |
| attributes = Attrs::from_pair(inner_pair)?; |
| } |
| Rule::interface_parameters => { |
| let mut fields: Vec<Pair<'_, Rule>> = inner_pair.into_inner().collect(); |
| name = String::from(fields[0].as_str()); |
| // TODO cleaner way of getting in/out params |
| let inner_params = fields.remove(1); |
| for in_pair in inner_params.into_inner() { |
| match in_pair.as_rule() { |
| Rule::parameter => { |
| let mut param = in_pair.into_inner(); |
| let ty = Ty::from_pair(¶m.next().unwrap())?; |
| let name = String::from(param.next().unwrap().as_str()); |
| in_params.push((name, ty)); |
| } |
| e => return Err(ParseError::UnexpectedToken(e)), |
| } |
| } |
| let inner_params = fields.remove(1); |
| for in_pair in inner_params.into_inner() { |
| match in_pair.as_rule() { |
| Rule::parameter => { |
| let mut param = in_pair.into_inner(); |
| let ty = Ty::from_pair(¶m.next().unwrap())?; |
| let name = String::from(param.next().unwrap().as_str()); |
| out_params.push((name, ty)); |
| } |
| e => return Err(ParseError::UnexpectedToken(e)), |
| } |
| } |
| } |
| e => return Err(ParseError::UnexpectedToken(e)), |
| } |
| } |
| Ok(Method { |
| attributes, |
| name, |
| in_params, |
| out_params, |
| }) |
| } |
| } |
| |
| #[derive(PartialEq, Eq, Serialize, Debug, Hash)] |
| pub enum Decl { |
| Struct { |
| attributes: Attrs, |
| name: String, |
| fields: Vec<StructField>, |
| }, |
| Interface { |
| attributes: Attrs, |
| name: String, |
| methods: Vec<Method>, |
| }, |
| Alias(String, String), |
| Constant { |
| attributes: Attrs, |
| name: String, |
| ty: Ty, |
| value: Constant, |
| }, |
| Union { |
| attributes: Attrs, |
| name: String, |
| fields: Vec<UnionField>, |
| }, |
| Enum { |
| attributes: Attrs, |
| name: String, |
| ty: Ty, |
| variants: Vec<EnumVariant>, |
| }, |
| } |
| |
| #[derive(PartialEq, Serialize, Debug)] |
| pub struct BanjoAst { |
| pub primary_namespace: String, |
| pub namespaces: HashMap<String, Vec<Decl>>, |
| } |
| |
| impl BanjoAst { |
| pub fn id_to_type(&self, fq_ident: &str) -> Ty { |
| // check if FQ |
| let v: Vec<&str> = fq_ident.rsplitn(2, '.').collect(); |
| let mut namespace = self.primary_namespace.as_str(); |
| let mut ident = fq_ident; |
| if v.len() > 1 { |
| namespace = v[1]; |
| ident = v[0]; |
| } |
| |
| match ident { |
| "usize" => return Ty::USize, |
| "bool" => return Ty::Bool, |
| "int8" => return Ty::Int8, |
| "int16" => return Ty::Int16, |
| "int32" => return Ty::Int32, |
| "int64" => return Ty::Int64, |
| "uint8" => return Ty::UInt8, |
| "uint16" => return Ty::UInt16, |
| "uint32" => return Ty::UInt32, |
| "uint64" => return Ty::UInt64, |
| "float32" => return Ty::Float32, |
| "float64" => return Ty::Float64, |
| "voidptr" => return Ty::Voidptr, |
| _ => {} |
| }; |
| |
| for decl in self.namespaces[namespace].iter() { |
| match decl { |
| Decl::Interface { name, .. } => { |
| if name == ident { |
| return Ty::Interface; |
| } |
| } |
| Decl::Struct { name, .. } => { |
| if name == ident { |
| return Ty::Struct; |
| } |
| } |
| Decl::Union { name, .. } => { |
| if name == ident { |
| return Ty::Union; |
| } |
| } |
| Decl::Enum { name, .. } => { |
| if name == ident { |
| return Ty::Enum; |
| } |
| } |
| Decl::Alias(to, from) => { |
| if to == ident { |
| return self.id_to_type(from); |
| } |
| } |
| Decl::Constant { name, ty, .. } => { |
| if name == ident { |
| return (*ty).clone(); |
| } |
| } |
| } |
| } |
| panic!("Unidentified {}", fq_ident); |
| } |
| |
| pub fn parse_decl( |
| pair: Pair<'_, Rule>, _namespaces: &HashMap<String, Vec<Decl>>, |
| ) -> Result<Decl, ParseError> { |
| match pair.as_rule() { |
| Rule::struct_declaration => { |
| let mut attributes = Attrs::default(); |
| let mut name = String::default(); |
| let mut fields = Vec::default(); |
| for inner_pair in pair.into_inner() { |
| match inner_pair.as_rule() { |
| Rule::attributes => { |
| attributes = Attrs::from_pair(inner_pair)?; |
| } |
| Rule::ident => { |
| name = String::from(inner_pair.as_str()); |
| } |
| Rule::struct_field => fields.push(StructField::from_pair(inner_pair)?), |
| e => return Err(ParseError::UnexpectedToken(e)), |
| } |
| } |
| Ok(Decl::Struct { |
| attributes, |
| name, |
| fields, |
| }) |
| } |
| Rule::enum_declaration => { |
| let mut attributes = Attrs::default(); |
| let mut name = String::default(); |
| let mut variants = Vec::default(); |
| let mut ty = Ty::UInt16; |
| for inner_pair in pair.into_inner() { |
| match inner_pair.as_rule() { |
| Rule::attributes => { |
| attributes = Attrs::from_pair(inner_pair)?; |
| } |
| Rule::ident => { |
| name = String::from(inner_pair.as_str()); |
| } |
| Rule::integer_type => { |
| ty = Ty::from_pair(&inner_pair)?; |
| } |
| Rule::enum_field => variants.push(EnumVariant::from_pair(inner_pair)?), |
| e => return Err(ParseError::UnexpectedToken(e)), |
| } |
| } |
| Ok(Decl::Enum { |
| attributes, |
| name, |
| ty, |
| variants, |
| }) |
| } |
| Rule::union_declaration => { |
| let mut attributes = Attrs::default(); |
| let mut name = String::default(); |
| let mut fields = Vec::default(); |
| for inner_pair in pair.into_inner() { |
| match inner_pair.as_rule() { |
| Rule::attributes => { |
| attributes = Attrs::from_pair(inner_pair)?; |
| } |
| Rule::ident => { |
| name = String::from(inner_pair.as_str()); |
| } |
| Rule::union_field => fields.push(UnionField::from_pair(inner_pair)?), |
| e => return Err(ParseError::UnexpectedToken(e)), |
| } |
| } |
| Ok(Decl::Union { |
| attributes, |
| name, |
| fields, |
| }) |
| } |
| // TODO extend to be more expressive for banjo |
| Rule::interface_declaration => { |
| let mut attributes = Attrs::default(); |
| let mut name = String::default(); |
| let mut methods = Vec::default(); |
| for inner_pair in pair.into_inner() { |
| match inner_pair.as_rule() { |
| Rule::attributes => { |
| attributes = Attrs::from_pair(inner_pair)?; |
| } |
| Rule::ident => { |
| name = String::from(inner_pair.as_str()); |
| } |
| Rule::interface_method => methods.push(Method::from_pair(inner_pair)?), |
| e => return Err(ParseError::UnexpectedToken(e)), |
| } |
| } |
| Ok(Decl::Interface { |
| attributes, |
| name, |
| methods, |
| }) |
| } |
| Rule::const_declaration => { |
| let mut attributes = Attrs::default(); |
| let mut name = String::default(); |
| let mut ty = Ty::UInt32; |
| let mut value = Constant::SizedRaw(0); |
| for inner_pair in pair.into_inner() { |
| match inner_pair.as_rule() { |
| Rule::attributes => { |
| attributes = Attrs::from_pair(inner_pair)?; |
| } |
| Rule::ident => { |
| name = String::from(inner_pair.as_str()); |
| } |
| Rule::primitive_type => { |
| ty = Ty::from_pair(&inner_pair)?; |
| } |
| Rule::constant => { |
| value = Constant::from_str(inner_pair.clone().into_span().as_str())?; |
| } |
| e => return Err(ParseError::UnexpectedToken(e)), |
| } |
| } |
| Ok(Decl::Constant { |
| attributes, |
| name, |
| ty, |
| value, |
| }) |
| } |
| e => Err(ParseError::UnexpectedToken(e)), |
| } |
| } |
| |
| fn type_to_decl(&self, ty: &Ty) -> Option<&Decl> { |
| match ty { |
| Ty::Array { ref ty, .. } => self.type_to_decl(ty), |
| Ty::Vector { ref ty, .. } => self.type_to_decl(ty), |
| Ty::Ident { id, .. } => { |
| // check if FQ |
| let v: Vec<&str> = id.rsplitn(2, '.').collect(); |
| let (namespace, ident) = if v.len() > 1 { |
| (v[1], v[0]) |
| } else { |
| (self.primary_namespace.as_str(), id.as_str()) |
| }; |
| for decl in self.namespaces[namespace].iter() { |
| match decl { |
| Decl::Interface { name, .. } |
| | Decl::Struct { name, .. } |
| | Decl::Union { name, .. } |
| | Decl::Enum { name, .. } |
| | Decl::Constant { name, .. } => { |
| if name == ident { |
| return Some(decl); |
| } |
| } |
| Decl::Alias(to, from) => { |
| if to == ident { |
| return self.type_to_decl(&self.id_to_type(from)); |
| } |
| } |
| } |
| } |
| None |
| } |
| _ => None, |
| } |
| } |
| |
| // An edge from D1 to D2 means that a C needs to see the declaration |
| // of D1 before the declaration of D2. For instance, given the banjo |
| // struct D2 { D1 d; }; |
| // struct D1 { int32 x; }; |
| // D1 has an edge pointing to D2. Note that struct and union pointers, |
| // unlike inline structs or unions, do not have dependency edges. |
| fn decl_dependencies(&self, decl: &Decl) -> Result<HashSet<&Decl>, ParseError> { |
| let mut edges = HashSet::new(); |
| |
| let mut maybe_add_decl = |ty| { |
| if let Some(type_decl) = self.type_to_decl(ty) { |
| edges.insert(type_decl); |
| } |
| }; |
| |
| match decl { |
| Decl::Interface { methods, .. } => { |
| for method in methods { |
| for (_, ty) in method.in_params.iter() { |
| maybe_add_decl(&ty); |
| } |
| for (_, ty) in method.out_params.iter() { |
| maybe_add_decl(&ty); |
| } |
| } |
| } |
| Decl::Struct { fields, .. } => { |
| for field in fields { |
| maybe_add_decl(&field.ty); |
| } |
| } |
| Decl::Union { fields, .. } => { |
| for field in fields { |
| maybe_add_decl(&field.ty); |
| } |
| } |
| Decl::Alias(_to, from) => { |
| maybe_add_decl(&self.id_to_type(from)); |
| } |
| // TODO(surajmalhtora): Implement constant. |
| Decl::Constant { .. } => (), |
| // Enum cannot have dependencies. |
| Decl::Enum { .. } => (), |
| }; |
| |
| Ok(edges) |
| } |
| |
| // Validates that the declarations are cycle free. |
| fn validate_declaration_deps(&self) -> Result<(), ParseError> { |
| // The number of undelcared dependencies for each decl. |
| let mut degrees: HashMap<&Decl, u32> = HashMap::new(); |
| // Records the decls that depend on each other. |
| let mut inverse_dependencies: HashMap<&Decl, Vec<&Decl>> = HashMap::new(); |
| |
| for decl in self.namespaces.iter().flat_map(|(_, decls)| decls.iter()) { |
| degrees.insert(&decl, 0); |
| } |
| |
| for decl in self.namespaces.iter().flat_map(|(_, decls)| decls.iter()) { |
| let deps = self.decl_dependencies(&decl)?; |
| for dep in deps.iter().filter(|&dep| dep != &decl) { |
| let entry = degrees.get_mut(&decl).unwrap(); |
| *entry += 1; |
| let entry = inverse_dependencies.entry(&dep).or_insert(Vec::new()); |
| entry.push(&decl); |
| } |
| } |
| // Remove mutability. |
| let inverse_dependencies = inverse_dependencies; |
| |
| // Start with all decls that have no incoming edges. |
| let mut decls_without_deps = degrees |
| .iter() |
| .filter(|(_, °rees)| degrees == 0) |
| .map(|(&decl, _)| decl) |
| .collect::<VecDeque<_>>(); |
| |
| let mut decl_order = Vec::new(); |
| // Pull one out of the queue. |
| while let Some(decl) = decls_without_deps.pop_front() { |
| assert_eq!(degrees.get(decl), Some(&0)); |
| decl_order.push(decl); |
| |
| // Decrement the incoming degree of all other decls it points to. |
| if let Some(inverse_deps) = inverse_dependencies.get(decl) { |
| for inverse_dep in inverse_deps { |
| let degree = degrees.get_mut(inverse_dep).unwrap(); |
| assert_ne!(*degree, 0); |
| *degree -= 1; |
| if *degree == 0 { |
| decls_without_deps.push_back(inverse_dep); |
| } |
| } |
| } |
| } |
| |
| if decl_order.len() != degrees.len() { |
| // We didn't visit all the edges! There was a cycle. |
| return Err(ParseError::InvalidDeps(String::from( |
| "There is a cycle in the declarations", |
| ))); |
| } |
| |
| Ok(()) |
| } |
| |
| pub fn parse(pair_vec: Vec<Pairs<'_, Rule>>) -> Result<Self, ParseError> { |
| let mut primary_namespace = None; |
| let mut namespaces = HashMap::default(); |
| |
| for pairs in pair_vec { |
| for pair in pairs { |
| let mut current_namespace = String::default(); |
| let mut namespace = Vec::default(); |
| for inner_pair in pair.into_inner() { |
| match inner_pair.as_rule() { |
| Rule::library_header => { |
| for token in inner_pair.into_inner() { |
| if Rule::compound_ident == token.as_rule() { |
| current_namespace = String::from(token.as_str()); |
| if let Some(primary_namespace) = primary_namespace { |
| if primary_namespace == current_namespace { |
| return Err(ParseError::AlreadyPrimaryNamespace); |
| } |
| } |
| primary_namespace = Some(String::from(token.as_str())); |
| } |
| } |
| } |
| Rule::using_decl => { |
| let contents: Vec<&str> = inner_pair |
| .clone() |
| .into_inner() |
| .map(|p| p.as_str()) |
| .collect(); |
| namespace.push(Decl::Alias( |
| contents[0].to_string(), |
| contents[1].to_string(), |
| )); |
| } |
| Rule::using => { |
| for (cnt, pair) in inner_pair.into_inner().enumerate() { |
| if cnt == 0 { |
| if !namespaces.contains_key(pair.as_str()) { |
| return Err(ParseError::UnImported(format!( |
| "{}", |
| pair.as_str() |
| ))); |
| } |
| } else { |
| return Err(ParseError::NotYetSupported(String::from( |
| "'as' in library imports", |
| ))); |
| } |
| } |
| } |
| Rule::struct_declaration |
| | Rule::union_declaration |
| | Rule::enum_declaration |
| | Rule::interface_declaration |
| | Rule::const_declaration => { |
| let decl = Self::parse_decl(inner_pair, &namespaces)?; |
| namespace.push(decl) |
| } |
| Rule::EOI => (), |
| e => return Err(ParseError::UnexpectedToken(e)), |
| }; |
| } |
| namespaces.insert(current_namespace, namespace); |
| } |
| } |
| |
| let ast = BanjoAst { |
| primary_namespace: primary_namespace.unwrap(), |
| namespaces, |
| }; |
| ast.validate_declaration_deps()?; |
| |
| Ok(ast) |
| } |
| } |