use std::collections::HashMap;
use super::load::FileId;
use crate::span::Span;
macro_rules! id {
($(#[$doc:meta])* $name:ident) => {
$(#[$doc])*
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) struct $name(pub(crate) u32);
impl $name {
pub(crate) fn new(index: usize) -> Self {
debug_assert!(u32::try_from(index).is_ok(), "fewer than 2^32 items exist");
Self(index as u32)
}
pub(crate) fn index(self) -> usize {
self.0 as usize
}
}
};
}
id!(
TypeId
);
id!(
StructId
);
id!(
SumId
);
id!(
SymbolId
);
id!(
RefinementId
);
id!(
GlobalId
);
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub(crate) enum Type {
Error,
Bool,
Int,
Float,
Str,
Path,
Data,
List(TypeId),
Set(TypeId),
Map(TypeId, TypeId),
Option(TypeId),
Ref(StructId),
Part(StructId),
Fn(Vec<TypeId>, TypeId),
Struct(StructId),
Sum(SumId),
Symbol(SymbolId),
Refined(TypeId, RefinementId),
}
#[derive(Debug, Clone)]
pub(crate) struct FieldDef {
pub(crate) name: String,
pub(crate) span: Span,
pub(crate) ty: TypeId,
pub(crate) ty_span: Span,
pub(crate) default: Option<usize>,
pub(crate) block: Option<String>,
pub(crate) doc: Option<String>,
}
impl FieldDef {
pub(crate) fn is_required(&self, types: &Types) -> bool {
self.default.is_none() && !matches!(types.get(types.peel(self.ty)), Type::Option(_))
}
}
#[derive(Debug, Clone)]
pub(crate) struct StructDef {
pub(crate) name: String,
pub(crate) file: FileId,
pub(crate) span: Span,
pub(crate) fields: Vec<FieldDef>,
pub(crate) doc: Option<String>,
}
impl StructDef {
pub(crate) fn field(&self, name: &str) -> Option<(usize, &FieldDef)> {
self.fields.iter().enumerate().find(|(_, f)| f.name == name)
}
}
#[derive(Debug, Clone)]
pub(crate) struct ParamDef {
pub(crate) name: String,
pub(crate) span: Span,
pub(crate) ty: TypeId,
pub(crate) ty_span: Span,
pub(crate) default: Option<usize>,
}
#[derive(Debug, Clone)]
pub(crate) struct VariantDef {
pub(crate) name: String,
pub(crate) fields: Option<Vec<ParamDef>>,
pub(crate) doc: Option<String>,
}
#[derive(Debug, Clone)]
pub(crate) struct SumDef {
pub(crate) name: String,
pub(crate) file: FileId,
pub(crate) span: Span,
pub(crate) variants: Vec<VariantDef>,
pub(crate) doc: Option<String>,
}
impl SumDef {
pub(crate) fn variant(&self, name: &str) -> Option<(usize, &VariantDef)> {
self.variants
.iter()
.enumerate()
.find(|(_, v)| v.name == name)
}
pub(crate) fn is_nullary(&self) -> bool {
self.variants.iter().all(|v| v.fields.is_none())
}
}
#[derive(Debug, Clone)]
pub(crate) struct SymbolDef {
pub(crate) name: String,
pub(crate) words: bool,
pub(crate) strings: bool,
pub(crate) constructor: String,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) enum Number {
Int(i64),
Float(f64),
}
#[derive(Debug, Clone, PartialEq)]
pub(crate) enum Predicate {
Range {
start: Option<Number>,
end: Option<Number>,
inclusive: bool,
length: bool,
},
NonEmpty,
Matches(String),
Call {
function: GlobalId,
args: Vec<usize>,
},
}
#[derive(Debug, Clone)]
pub(crate) struct Refinement {
pub(crate) file: FileId,
pub(crate) span: Span,
pub(crate) predicates: Vec<Predicate>,
}
#[derive(Debug, Clone)]
pub(crate) struct Types {
list: Vec<Type>,
index: HashMap<Type, TypeId>,
pub(crate) structs: Vec<StructDef>,
pub(crate) sums: Vec<SumDef>,
pub(crate) symbols: Vec<SymbolDef>,
pub(crate) refinements: Vec<Refinement>,
}
impl TypeId {
pub(crate) const ERROR: Self = Self(0);
pub(crate) const BOOL: Self = Self(1);
pub(crate) const INT: Self = Self(2);
pub(crate) const FLOAT: Self = Self(3);
pub(crate) const STR: Self = Self(4);
pub(crate) const PATH: Self = Self(5);
pub(crate) const DATA: Self = Self(6);
}
impl Types {
pub(crate) fn new() -> Self {
let mut types = Self {
list: Vec::new(),
index: HashMap::new(),
structs: Vec::new(),
sums: Vec::new(),
symbols: Vec::new(),
refinements: Vec::new(),
};
for ty in [
Type::Error,
Type::Bool,
Type::Int,
Type::Float,
Type::Str,
Type::Path,
Type::Data,
] {
types.intern(ty);
}
types
}
pub(crate) fn intern(&mut self, ty: Type) -> TypeId {
if let Some(&id) = self.index.get(&ty) {
return id;
}
let id = TypeId::new(self.list.len());
self.list.push(ty.clone());
self.index.insert(ty, id);
id
}
pub(crate) fn get(&self, id: TypeId) -> &Type {
self.list.get(id.index()).unwrap_or(&Type::Error)
}
pub(crate) fn list(&mut self, elem: TypeId) -> TypeId {
self.intern(Type::List(elem))
}
pub(crate) fn set(&mut self, elem: TypeId) -> TypeId {
self.intern(Type::Set(elem))
}
pub(crate) fn map(&mut self, key: TypeId, value: TypeId) -> TypeId {
self.intern(Type::Map(key, value))
}
pub(crate) fn option(&mut self, inner: TypeId) -> TypeId {
if matches!(self.get(self.peel(inner)), Type::Option(_)) || inner == TypeId::ERROR {
return inner;
}
self.intern(Type::Option(inner))
}
pub(crate) fn peel(&self, mut id: TypeId) -> TypeId {
while let Type::Refined(base, _) = self.get(id) {
id = *base;
}
id
}
pub(crate) fn unwrap_option(&self, id: TypeId) -> Option<TypeId> {
match self.get(self.peel(id)) {
Type::Option(inner) => Some(self.peel(*inner)),
_ => None,
}
}
pub(crate) fn has_error(&self, id: TypeId) -> bool {
match self.get(id) {
Type::Error => true,
Type::List(t) | Type::Set(t) | Type::Option(t) | Type::Refined(t, _) => {
self.has_error(*t)
}
Type::Map(k, v) => self.has_error(*k) || self.has_error(*v),
Type::Fn(params, ret) => {
params.iter().any(|p| self.has_error(*p)) || self.has_error(*ret)
}
_ => false,
}
}
pub(crate) fn same(&self, a: TypeId, b: TypeId) -> bool {
let (a, b) = (self.peel(a), self.peel(b));
if a == b {
return true;
}
match (self.get(a), self.get(b)) {
(Type::List(x), Type::List(y))
| (Type::Set(x), Type::Set(y))
| (Type::Option(x), Type::Option(y)) => self.same(*x, *y),
(Type::Map(k1, v1), Type::Map(k2, v2)) => self.same(*k1, *k2) && self.same(*v1, *v2),
(Type::Fn(p1, r1), Type::Fn(p2, r2)) => {
p1.len() == p2.len()
&& p1.iter().zip(p2).all(|(x, y)| self.same(*x, *y))
&& self.same(*r1, *r2)
}
_ => false,
}
}
pub(crate) fn fits(&self, found: TypeId, expected: TypeId) -> bool {
self.has_error(found) || self.has_error(expected) || self.same(found, expected)
}
pub(crate) fn is_key(&self, id: TypeId) -> bool {
match self.get(self.peel(id)) {
Type::Error | Type::Bool | Type::Int | Type::Str | Type::Path => true,
Type::Symbol(_) | Type::Ref(_) => true,
Type::Sum(s) => self.sums[s.index()].is_nullary(),
_ => false,
}
}
pub(crate) fn is_map_key(&self, id: TypeId) -> bool {
match self.get(self.peel(id)) {
Type::Error | Type::Str => true,
Type::Sum(s) => self.sums[s.index()].is_nullary(),
_ => false,
}
}
pub(crate) fn has_code(&self, id: TypeId) -> bool {
match self.get(id) {
Type::Fn(..) | Type::Part(_) => true,
Type::List(t) | Type::Set(t) | Type::Option(t) | Type::Refined(t, _) => {
self.has_code(*t)
}
Type::Map(k, v) => self.has_code(*k) || self.has_code(*v),
_ => false,
}
}
pub(crate) fn is_ordered(&self, id: TypeId) -> bool {
match self.get(self.peel(id)) {
Type::Error | Type::Int | Type::Float | Type::Str => true,
Type::Sum(s) => self.sums[s.index()].is_nullary(),
_ => false,
}
}
pub(crate) fn is_text(&self, id: TypeId) -> bool {
match self.get(self.peel(id)) {
Type::Error | Type::Str | Type::Int | Type::Float | Type::Bool => true,
Type::Symbol(_) | Type::Ref(_) => true,
Type::Sum(s) => self.sums[s.index()].is_nullary(),
_ => false,
}
}
pub(crate) fn as_struct(&self, id: TypeId) -> Option<StructId> {
match self.get(self.peel(id)) {
Type::Struct(s) => Some(*s),
_ => None,
}
}
pub(crate) fn as_sum(&self, id: TypeId) -> Option<SumId> {
match self.get(self.peel(id)) {
Type::Sum(s) => Some(*s),
_ => None,
}
}
pub(crate) fn structure(&self, id: StructId) -> &StructDef {
&self.structs[id.index()]
}
pub(crate) fn sum(&self, id: SumId) -> &SumDef {
&self.sums[id.index()]
}
pub(crate) fn render(&self, id: TypeId) -> String {
match self.get(id) {
Type::Error => "{unknown}".to_owned(),
Type::Bool => "Bool".to_owned(),
Type::Int => "Int".to_owned(),
Type::Float => "Float".to_owned(),
Type::Str => "Str".to_owned(),
Type::Path => "Path".to_owned(),
Type::Data => "Data".to_owned(),
Type::List(t) => format!("List<{}>", self.render(*t)),
Type::Set(t) => format!("Set<{}>", self.render(*t)),
Type::Map(k, v) => format!("Map<{}, {}>", self.render(*k), self.render(*v)),
Type::Option(t) => format!("Option<{}>", self.render(*t)),
Type::Ref(s) => format!("Ref<{}>", self.structure(*s).name),
Type::Part(s) => format!("Part<{}>", self.structure(*s).name),
Type::Fn(params, ret) => {
let params: Vec<String> = params.iter().map(|p| self.render(*p)).collect();
format!("Fn({}) -> {}", params.join(", "), self.render(*ret))
}
Type::Struct(s) => self.structure(*s).name.clone(),
Type::Sum(s) => self.sum(*s).name.clone(),
Type::Symbol(s) => self.symbols[s.index()].name.clone(),
Type::Refined(base, _) => format!("{} where ...", self.render(*base)),
}
}
pub(crate) fn render_plain(&self, id: TypeId) -> String {
match self.get(id) {
Type::Refined(base, _) => self.render_plain(*base),
Type::List(t) => format!("List<{}>", self.render_plain(*t)),
Type::Set(t) => format!("Set<{}>", self.render_plain(*t)),
Type::Map(k, v) => format!("Map<{}, {}>", self.render_plain(*k), self.render_plain(*v)),
Type::Option(t) => format!("Option<{}>", self.render_plain(*t)),
Type::Fn(params, ret) => {
let params: Vec<String> = params.iter().map(|p| self.render_plain(*p)).collect();
format!("Fn({}) -> {}", params.join(", "), self.render_plain(*ret))
}
_ => self.render(id),
}
}
pub(crate) fn origin(&self, id: TypeId) -> Option<FileId> {
match self.get(self.peel(id)) {
Type::Struct(s) => Some(self.structure(*s).file),
Type::Sum(s) => Some(self.sum(*s).file),
_ => None,
}
}
}