use crate::Abi;
use std::collections::{HashMap, HashSet};
use std::rc::Rc;
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Id(String);
impl Id {
pub fn new<S: AsRef<str>>(s: S) -> Self {
Id(s.as_ref().to_string())
}
pub fn as_str(&self) -> &str {
self.0.as_str()
}
}
impl AsRef<str> for Id {
fn as_ref(&self) -> &str {
self.0.as_ref()
}
}
impl PartialEq<&str> for Id {
fn eq(&self, rhs: &&str) -> bool {
PartialEq::eq(self.as_ref(), *rhs)
}
}
impl PartialEq<Id> for &str {
fn eq(&self, rhs: &Id) -> bool {
PartialEq::eq(*self, rhs.as_ref())
}
}
impl From<&str> for Id {
fn from(s: &str) -> Self {
Self::new(s)
}
}
#[derive(Debug, Clone)]
pub struct Module {
name: Id,
module_id: ModuleId,
types: Vec<Rc<NamedType>>,
type_map: HashMap<Id, Rc<NamedType>>,
resources: Vec<Rc<Resource>>,
resource_map: HashMap<Id, Rc<Resource>>,
funcs: Vec<Rc<Function>>,
func_map: HashMap<Id, Rc<Function>>,
constants: Vec<Constant>,
}
#[derive(Debug, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct ModuleId(pub(crate) Rc<std::path::PathBuf>);
impl Module {
pub(crate) fn new(name: Id, module_id: ModuleId) -> Module {
Module {
name,
module_id,
types: Default::default(),
type_map: Default::default(),
resources: Default::default(),
resource_map: Default::default(),
funcs: Default::default(),
func_map: Default::default(),
constants: Default::default(),
}
}
pub fn name(&self) -> &Id {
&self.name
}
pub fn module_id(&self) -> &ModuleId {
&self.module_id
}
pub(crate) fn push_type(&mut self, ty: Rc<NamedType>) {
assert!(self.type_map.insert(ty.name.clone(), ty.clone()).is_none());
self.types.push(ty);
}
pub(crate) fn push_resource(&mut self, r: Rc<Resource>) {
assert!(self
.resource_map
.insert(r.name.clone(), r.clone())
.is_none());
self.resources.push(r);
}
pub(crate) fn push_func(&mut self, func: Rc<Function>) {
assert!(self
.func_map
.insert(func.name.clone(), func.clone())
.is_none());
self.funcs.push(func);
}
pub(crate) fn push_constant(&mut self, constant: Constant) {
self.constants.push(constant);
}
pub fn typename(&self, name: &Id) -> Option<Rc<NamedType>> {
self.type_map.get(name).cloned()
}
pub fn typenames<'a>(&'a self) -> impl Iterator<Item = &'a Rc<NamedType>> + 'a {
self.types.iter()
}
pub fn resource(&self, name: &Id) -> Option<Rc<Resource>> {
self.resource_map.get(name).cloned()
}
pub fn resources<'a>(&'a self) -> impl Iterator<Item = &'a Rc<Resource>> + 'a {
self.resources.iter()
}
pub fn error_types<'a>(&'a self) -> impl Iterator<Item = TypeRef> + 'a {
let errors: HashSet<TypeRef> = self
.funcs()
.filter_map(|f| {
if f.results.len() == 1 {
Some(f.results[0].tref.type_().clone())
} else {
None
}
})
.filter_map(|t| match &*t {
Type::Variant(v) => {
let (_ok, err) = v.as_expected()?;
Some(err?.clone())
}
_ => None,
})
.collect::<HashSet<TypeRef>>();
errors.into_iter()
}
pub fn func(&self, name: &Id) -> Option<Rc<Function>> {
self.func_map.get(&name).cloned()
}
pub fn funcs<'a>(&'a self) -> impl Iterator<Item = Rc<Function>> + 'a {
self.funcs.iter().cloned()
}
pub fn constants<'a>(&'a self) -> impl Iterator<Item = &'a Constant> + 'a {
self.constants.iter()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum TypeRef {
Name(Rc<NamedType>),
Value(Rc<Type>),
}
impl TypeRef {
pub fn type_(&self) -> &Rc<Type> {
match self {
TypeRef::Name(named) => named.type_(),
TypeRef::Value(v) => v,
}
}
pub fn name(&self) -> Option<&NamedType> {
match self {
TypeRef::Name(n) => Some(n),
TypeRef::Value(_) => None,
}
}
pub fn named(&self) -> bool {
match self {
TypeRef::Name(_) => true,
TypeRef::Value(_) => false,
}
}
pub fn type_equal(&self, other: &TypeRef) -> bool {
self.type_().type_equal(other.type_())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct NamedType {
pub name: Id,
pub module: ModuleId,
pub tref: TypeRef,
pub docs: String,
}
impl NamedType {
pub fn type_(&self) -> &Rc<Type> {
self.tref.type_()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Type {
Record(RecordDatatype),
Variant(Variant),
Handle(HandleDatatype),
List(TypeRef),
Pointer(TypeRef),
ConstPointer(TypeRef),
Buffer(Buffer),
Builtin(BuiltinType),
}
impl Type {
pub fn kind(&self) -> &'static str {
use Type::*;
match self {
Record(_) => "record",
Variant(_) => "variant",
Handle(_) => "handle",
List(_) => "list",
Pointer(_) => "pointer",
ConstPointer(_) => "constpointer",
Buffer(_) => "buffer",
Builtin(_) => "builtin",
}
}
pub fn all_bits_valid(&self) -> bool {
match self {
Type::Record(r) => r.members.iter().all(|t| t.tref.type_().all_bits_valid()),
Type::Builtin(BuiltinType::Char)
| Type::Variant(_)
| Type::Handle(_)
| Type::Buffer(_)
| Type::List(_) => false,
Type::Builtin(BuiltinType::U8 { .. })
| Type::Builtin(BuiltinType::S8)
| Type::Builtin(BuiltinType::U16)
| Type::Builtin(BuiltinType::S16)
| Type::Builtin(BuiltinType::U32 { .. })
| Type::Builtin(BuiltinType::S32)
| Type::Builtin(BuiltinType::U64)
| Type::Builtin(BuiltinType::S64)
| Type::Builtin(BuiltinType::F32)
| Type::Builtin(BuiltinType::F64)
| Type::Pointer(_)
| Type::ConstPointer(_) => true,
}
}
pub fn type_equal(&self, other: &Type) -> bool {
match self {
Type::Record(a) => match other {
Type::Record(b) => a.type_equal(b),
_ => false,
},
Type::Variant(a) => match other {
Type::Variant(b) => a.type_equal(b),
_ => false,
},
Type::Handle(a) => match other {
Type::Handle(b) => a.type_equal(b),
_ => false,
},
Type::List(a) => match other {
Type::List(b) => a.type_equal(b),
_ => false,
},
Type::Pointer(a) => match other {
Type::Pointer(b) => a.type_equal(b),
_ => false,
},
Type::ConstPointer(a) => match other {
Type::ConstPointer(b) => a.type_equal(b),
_ => false,
},
Type::Builtin(a) => match other {
Type::Builtin(b) => a == b,
_ => false,
},
Type::Buffer(a) => match other {
Type::Buffer(b) => a.type_equal(b),
_ => false,
},
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BuiltinType {
Char,
U8 {
lang_c_char: bool,
},
U16,
U32 {
lang_ptr_size: bool,
},
U64,
S8,
S16,
S32,
S64,
F32,
F64,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum IntRepr {
U8,
U16,
U32,
U64,
}
impl IntRepr {
pub fn to_builtin(&self) -> BuiltinType {
match self {
IntRepr::U8 => BuiltinType::U8 { lang_c_char: false },
IntRepr::U16 => BuiltinType::U16,
IntRepr::U32 => BuiltinType::U32 {
lang_ptr_size: false,
},
IntRepr::U64 => BuiltinType::U64,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct RecordDatatype {
pub kind: RecordKind,
pub members: Vec<RecordMember>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum RecordKind {
Tuple,
Bitflags(IntRepr),
Other,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct RecordMember {
pub name: Id,
pub tref: TypeRef,
pub docs: String,
}
impl RecordDatatype {
pub fn is_tuple(&self) -> bool {
match self.kind {
RecordKind::Tuple => true,
_ => false,
}
}
pub fn bitflags_repr(&self) -> Option<IntRepr> {
match self.kind {
RecordKind::Bitflags(i) => Some(i),
_ => None,
}
}
pub fn type_equal(&self, other: &RecordDatatype) -> bool {
self.members.len() == other.members.len()
&& self
.members
.iter()
.zip(&other.members)
.all(|(a, b)| a.type_equal(b))
}
}
impl RecordMember {
pub fn type_equal(&self, other: &RecordMember) -> bool {
self.name == other.name && self.tref.type_equal(&other.tref)
}
}
impl RecordKind {
pub fn infer(members: &[RecordMember]) -> RecordKind {
if members.len() == 0 {
return RecordKind::Other;
}
if members.iter().all(|t| is_bool(&t.tref)) {
match members.len() {
n if n <= 8 => return RecordKind::Bitflags(IntRepr::U8),
n if n <= 16 => return RecordKind::Bitflags(IntRepr::U16),
n if n <= 32 => return RecordKind::Bitflags(IntRepr::U32),
n if n <= 64 => return RecordKind::Bitflags(IntRepr::U64),
_ => {}
}
}
if members
.iter()
.enumerate()
.all(|(i, m)| m.name.as_str().parse().ok() == Some(i))
{
return RecordKind::Tuple;
}
return RecordKind::Other;
fn is_bool(t: &TypeRef) -> bool {
match &**t.type_() {
Type::Variant(v) => v.is_bool(),
_ => false,
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Variant {
pub tag_repr: IntRepr,
pub cases: Vec<Case>,
}
impl Variant {
pub fn infer_repr(cases: usize) -> IntRepr {
match cases {
n if n < u8::max_value() as usize => IntRepr::U8,
n if n < u16::max_value() as usize => IntRepr::U16,
n if n < u32::max_value() as usize => IntRepr::U32,
n if n < u64::max_value() as usize => IntRepr::U64,
_ => panic!("too many cases to fit in a repr"),
}
}
pub fn as_option(&self) -> Option<&TypeRef> {
if self.cases.len() != 2 {
return None;
}
if self.cases[0].name != "none" || self.cases[0].tref.is_some() {
return None;
}
if self.cases[1].name != "some" {
return None;
}
self.cases[1].tref.as_ref()
}
pub fn as_expected(&self) -> Option<(Option<&TypeRef>, Option<&TypeRef>)> {
if self.cases.len() != 2 {
return None;
}
if self.cases[0].name != "ok" {
return None;
}
if self.cases[1].name != "err" {
return None;
}
Some((self.cases[0].tref.as_ref(), self.cases[1].tref.as_ref()))
}
pub fn is_bool(&self) -> bool {
self.cases.len() == 2
&& self.cases[0].name == "false"
&& self.cases[1].name == "true"
&& self.cases[0].tref.is_none()
&& self.cases[1].tref.is_none()
}
pub fn is_enum(&self) -> bool {
self.cases.iter().all(|c| c.tref.is_none())
}
pub fn type_equal(&self, other: &Variant) -> bool {
self.tag_repr == other.tag_repr
&& self.cases.len() == other.cases.len()
&& self
.cases
.iter()
.zip(&other.cases)
.all(|(a, b)| a.type_equal(b))
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Case {
pub name: Id,
pub tref: Option<TypeRef>,
pub docs: String,
}
impl Case {
pub fn type_equal(&self, other: &Case) -> bool {
self.name == other.name
&& match (&self.tref, &other.tref) {
(Some(a), Some(b)) => a.type_equal(b),
(None, None) => true,
_ => false,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Resource {
pub name: Id,
pub resource_id: ResourceId,
pub docs: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ResourceId {
pub name: Id,
pub module_id: ModuleId,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct HandleDatatype {
pub resource_id: ResourceId,
}
impl HandleDatatype {
pub fn type_equal(&self, other: &HandleDatatype) -> bool {
self.resource_id == other.resource_id
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Function {
pub abi: Abi,
pub name: Id,
pub params: Vec<Param>,
pub results: Vec<Param>,
pub noreturn: bool,
pub docs: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Param {
pub name: Id,
pub tref: TypeRef,
pub docs: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Constant {
pub ty: Id,
pub name: Id,
pub value: u64,
pub docs: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Buffer {
pub out: bool,
pub tref: TypeRef,
}
impl Buffer {
pub fn type_equal(&self, other: &Buffer) -> bool {
self.out == other.out && self.tref.type_equal(&other.tref)
}
}