use core::{
hint::{ unreachable_unchecked, },
};
use std::{
fmt::{ Display, Debug, Formatter, Result as FMTResult, },
collections::{ HashMap, hash_map::{ Iter as HashMapIter, IterMut as HashMapIterMut, Keys as HashMapKeys, Values as HashMapValues, }, },
};
use crate::{
util::{ make_key_type, Unref, UnwrapUnchecked, },
collections::{ SlotMap, },
source::{ SourceRegion, },
common::{ Identifier, },
ir,
};
#[derive(Debug)]
pub struct Context {
pub items: SlotMap<GlobalKey, GlobalItem>,
pub core_ns: GlobalNamespace,
pub core_mod: GlobalKey,
pub lib_mod: GlobalKey,
pub err_ty: GlobalKey,
pub void_ty: GlobalKey,
pub bool_ty: GlobalKey,
pub int_ty: GlobalKey,
pub concrete_int_ty: GlobalKey,
pub float_ty: GlobalKey,
pub concrete_float_ty: GlobalKey,
pub anon_types: HashMap<TypeData, GlobalKey>,
}
impl Default for Context {
#[inline] fn default () -> Self { Self::new() }
}
impl Context {
pub fn new () -> Self {
const PRIMITIVE_TYPES: &[(&str, TypeData)] = &[
("void", TypeData::Primitive(PrimitiveType::Void)),
("bool", TypeData::Primitive(PrimitiveType::Bool)),
("u8", TypeData::Primitive(PrimitiveType::Integer { signed: false, bit_size: 8 })),
("u16", TypeData::Primitive(PrimitiveType::Integer { signed: false, bit_size: 16 })),
("u32", TypeData::Primitive(PrimitiveType::Integer { signed: false, bit_size: 32 })),
("u64", TypeData::Primitive(PrimitiveType::Integer { signed: false, bit_size: 64 })),
("s8", TypeData::Primitive(PrimitiveType::Integer { signed: true, bit_size: 8 })),
("s16", TypeData::Primitive(PrimitiveType::Integer { signed: true, bit_size: 16 })),
("s32", TypeData::Primitive(PrimitiveType::Integer { signed: true, bit_size: 32 })),
("s64", TypeData::Primitive(PrimitiveType::Integer { signed: true, bit_size: 64 })),
("f32", TypeData::Primitive(PrimitiveType::FloatingPoint { bit_size: 32 })),
("f64", TypeData::Primitive(PrimitiveType::FloatingPoint { bit_size: 64 })),
];
let mut items = SlotMap::default();
let mut core_mod = Module::new(None, "core".into(), SourceRegion::ANONYMOUS);
let mut core_ns = Namespace::default();
for &(name, ref td) in PRIMITIVE_TYPES.iter() {
let key = items.insert(Type::new(Some(name.into()), SourceRegion::ANONYMOUS, Some(td.clone())).into());
core_ns.set_entry_bound(name, key, SourceRegion::ANONYMOUS);
core_mod.local_bindings.set_entry_bound(name, key, SourceRegion::ANONYMOUS);
core_mod.export_bindings.set_entry_bound(name, key, SourceRegion::ANONYMOUS);
}
let err_ty = items.insert(Type::new(Some("err_ty".into()), SourceRegion::ANONYMOUS, Some(TypeData::Error)).into());
let void_ty = core_ns.get_entry("void").unwrap();
let bool_ty = core_ns.get_entry("bool").unwrap();
let int_ty = items.insert(Type::new(Some("int".into()), SourceRegion::ANONYMOUS, Some(TypeData::Coercible(CoercibleType::Integer))).into());
let concrete_int_ty = core_ns.get_entry("s32").unwrap();
let float_ty = items.insert(Type::new(Some("float".into()), SourceRegion::ANONYMOUS, Some(TypeData::Coercible(CoercibleType::FloatingPoint))).into());
let concrete_float_ty = core_ns.get_entry("f32").unwrap();
let core_key = items.insert(core_mod.into());
core_ns.set_entry_bound("core", core_key, SourceRegion::ANONYMOUS);
let lib_mod = Module::new(None, "lib".into(), SourceRegion::ANONYMOUS);
let lib_key = items.insert(lib_mod.into());
core_ns.set_entry_bound("lib", lib_key, SourceRegion::ANONYMOUS);
Self {
items,
core_ns,
core_mod: core_key,
lib_mod: lib_key,
err_ty,
void_ty,
bool_ty,
int_ty,
concrete_int_ty,
float_ty,
concrete_float_ty,
anon_types: HashMap::default(),
}
}
}
#[derive(Debug, Clone)]
pub struct Namespace<K: std::hash::Hash + Eq + Copy> {
entries: HashMap<Identifier, K>,
bind_locations: HashMap<K, SourceRegion>,
}
impl<K: std::hash::Hash + Eq + Copy> Namespace<K> {
pub fn new () -> Self {
Self {
entries: HashMap::default(),
bind_locations: HashMap::default(),
}
}
}
impl<K: std::hash::Hash + Eq + Copy> Default for Namespace<K> { #[inline] fn default () -> Self { Self::new() } }
pub type GlobalNamespace = Namespace<GlobalKey>;
pub type LocalNamespace = Namespace<MultiKey>;
impl<K: std::hash::Hash + Eq + Copy> Namespace<K> {
pub fn get_bind_location (&self, key: K) -> Option<SourceRegion> {
self.bind_locations.get(&key).unref()
}
pub fn has_bind_location (&self, key: K) -> bool {
self.bind_locations.contains_key(&key)
}
pub fn set_bind_location (&mut self, key: K, location: SourceRegion) {
self.bind_locations.insert(key, location);
}
pub fn get_entry<I: AsRef<str> + ?Sized> (&self, ident: &I) -> Option<K> {
self.entries.get(ident.as_ref()).unref()
}
pub unsafe fn get_entry_unchecked<I: AsRef<str> + ?Sized> (&self, ident: &I) -> K {
*self.entries.get(ident.as_ref()).unwrap_unchecked()
}
pub fn has_entry<I: AsRef<str> + ?Sized> (&self, ident: &I) -> bool {
self.entries.contains_key(ident.as_ref())
}
pub fn get_entry_ident (&self, key: K) -> Option<&str> {
for (i, k) in self.entries.iter() {
if k == &key { return Some(i.as_str()) }
}
None
}
pub fn has_entry_key (&self, key: K) -> bool {
self.get_entry_ident(key).is_some()
}
pub fn set_entry<I: Into<Identifier> + AsRef<str>> (&mut self, ident: I, key: K) {
self.entries.insert(ident.into(), key);
}
pub fn set_entry_bound<I: Into<Identifier> + AsRef<str>> (&mut self, ident: I, key: K, location: SourceRegion) {
self.set_entry(ident, key);
self.set_bind_location(key, location);
}
pub fn bind_iter (&self) -> HashMapIter<K, SourceRegion> {
self.bind_locations.iter()
}
pub fn bind_iter_mut (&mut self) -> HashMapIterMut<K, SourceRegion> {
self.bind_locations.iter_mut()
}
pub fn ident_iter (&self) -> HashMapKeys<Identifier, K> {
self.entries.keys()
}
pub fn key_iter (&self) -> HashMapValues<Identifier, K> {
self.entries.values()
}
pub fn entry_iter (&self) -> HashMapIter<Identifier, K> {
self.entries.iter()
}
pub fn entry_iter_mut (&mut self) -> HashMapIterMut<Identifier, K> {
self.entries.iter_mut()
}
pub fn iter (&self) -> NamespaceIter<K> {
NamespaceIter {
bind_locations: &self.bind_locations,
entry_iter: self.entries.iter()
}
}
pub fn iter_mut (&mut self) -> NamespaceIterMut<K> {
NamespaceIterMut {
bind_locations: &self.bind_locations,
entry_iter: self.entries.iter_mut()
}
}
}
pub struct NamespaceIter<'a, K: std::hash::Hash + Eq + Copy> {
bind_locations: &'a HashMap<K, SourceRegion>,
entry_iter: HashMapIter<'a, Identifier, K>,
}
impl<'a, K: std::hash::Hash + Eq + Copy> Iterator for NamespaceIter<'a, K> {
type Item = (&'a Identifier, &'a K, &'a SourceRegion);
fn next (&mut self) -> Option<Self::Item> {
let (ident, key) = self.entry_iter.next()?;
Some((ident, key, self.bind_locations.get(key)?))
}
}
pub struct NamespaceIterMut<'a, K: std::hash::Hash + Eq + Copy> {
bind_locations: &'a HashMap<K, SourceRegion>,
entry_iter: HashMapIterMut<'a, Identifier, K>,
}
impl<'a, K: std::hash::Hash + Eq + Copy> Iterator for NamespaceIterMut<'a, K> {
type Item = (&'a Identifier, &'a mut K, &'a SourceRegion);
fn next (&mut self) -> Option<Self::Item> {
let (ident, key) = self.entry_iter.next()?;
let owned_key = *key;
Some((ident, key, self.bind_locations.get(&owned_key)?))
}
}
#[derive(Debug, Clone)]
pub struct Module {
pub parent: Option<GlobalKey>,
pub canonical_name: Identifier,
pub local_bindings: GlobalNamespace,
pub export_bindings: GlobalNamespace,
pub origin: SourceRegion,
}
impl Module {
pub fn new (parent: Option<GlobalKey>, canonical_name: Identifier, origin: SourceRegion) -> Self {
Self {
parent,
canonical_name,
local_bindings: Namespace::default(),
export_bindings: Namespace::default(),
origin,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum PrimitiveType {
Void,
Bool,
Integer {
signed: bool,
bit_size: usize
},
FloatingPoint {
bit_size: usize
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum CoercibleType {
Integer,
FloatingPoint,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum TypeData {
Error,
Primitive(PrimitiveType),
Coercible(CoercibleType),
Pointer(GlobalKey),
Function {
parameter_types: Vec<GlobalKey>,
return_type: Option<GlobalKey>
},
}
impl TypeData {
pub fn is_anon (&self) -> bool {
match self {
| TypeData::Error { .. }
| TypeData::Pointer { .. }
| TypeData::Function { .. }
=> true,
| TypeData::Primitive { .. }
| TypeData::Coercible { .. }
=> false,
}
}
}
#[derive(Debug, Clone)]
pub struct Type {
pub canonical_name: Option<Identifier>,
pub data: Option<TypeData>,
pub origin: SourceRegion,
}
impl Type {
pub fn new (canonical_name: Option<Identifier>, origin: SourceRegion, data: Option<TypeData>) -> Self {
Self {
canonical_name,
data,
origin,
}
}
pub fn is_anon (&self) -> Option<bool> {
self.data.as_ref().map(|td| td.is_anon())
}
}
pub struct TypeDisplay<'a> {
pub ty_key: GlobalKey,
pub context: &'a Context,
}
impl<'a> TypeDisplay<'a> {
pub fn descend (&self, ty_key: GlobalKey) -> Self {
Self {
ty_key,
context: self.context,
}
}
}
impl<'a> Display for TypeDisplay<'a> {
fn fmt (&self, f: &mut Formatter) -> FMTResult {
let ty: &Type =
self.context.items
.get(self.ty_key)
.expect("Internal error, tried to display invalid type")
.ref_type()
.expect("Internal error, cannot use TypeDisplay for other variants of GlobalItem");
if let Some(canonical_name) = &ty.canonical_name {
write!(f, "{}", canonical_name)?;
} else if let Some(data) = &ty.data {
match data {
TypeData::Pointer(ty_key) => { write!(f, "^{}", self.descend(*ty_key))?; },
TypeData::Error => { write!(f, "err ty")?; },
TypeData::Coercible(CoercibleType::Integer) => { write!(f, "int")?; },
TypeData::Coercible(CoercibleType::FloatingPoint) => { write!(f, "float")?; },
TypeData::Primitive(prim) => match prim {
PrimitiveType::Void => { write!(f, "void")?; },
PrimitiveType::Bool => { write!(f, "bool")?; },
PrimitiveType::Integer { bit_size, signed } => { write!(f, "{}{}", if *signed { "s" } else { "u" }, bit_size)?; },
PrimitiveType::FloatingPoint { bit_size } => { write!(f, "f{}", bit_size)?; },
}
TypeData::Function { parameter_types, return_type } => {
write!(f, "fn (")?;
let mut iter = parameter_types.iter().peekable();
while let Some(param_ty) = iter.next() {
write!(f, "{}", self.descend(*param_ty))?;
if iter.peek().is_some() { write!(f, ", ")?; }
}
write!(f, ")")?;
if let Some(return_ty) = return_type {
write!(f, " -> {}", self.descend(*return_ty))?;
}
}
}
} else {
write!(f, "UndefinedType")?;
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct Global {
pub canonical_name: Identifier,
pub ty: Option<GlobalKey>,
pub origin: SourceRegion,
pub initializer: Option<ir::Expression>
}
impl Global {
pub fn new (canonical_name: Identifier, origin: SourceRegion, ty: Option<GlobalKey>) -> Self {
Self {
canonical_name,
ty,
origin,
initializer: None,
}
}
}
#[derive(Debug, Clone)]
pub struct Function {
pub canonical_name: Identifier,
pub params: Vec<(Identifier, GlobalKey, SourceRegion)>,
pub return_ty: Option<GlobalKey>,
pub ty: Option<GlobalKey>,
pub origin: SourceRegion,
pub body: Option<ir::Block>
}
impl Function {
pub fn new (canonical_name: Identifier, origin: SourceRegion, ty: Option<GlobalKey>) -> Self {
Self {
canonical_name,
params: Vec::new(),
return_ty: None,
ty,
origin,
body: None
}
}
}
#[allow(missing_docs)]
#[derive(Debug, Clone)]
pub enum GlobalItem {
Module(Module),
Type(Type),
Global(Global),
Function(Function),
}
#[allow(missing_docs)]
#[derive(Debug, Clone)]
pub struct LocalItem {
pub canonical_name: Identifier,
pub ty: GlobalKey,
pub is_parameter: bool,
pub index: usize,
}
#[allow(missing_docs)]
pub enum MultiRef<'a> {
LocalItem(&'a LocalItem),
GlobalItem(&'a GlobalItem),
}
impl<'a> From<&'a LocalItem> for MultiRef<'a> { #[inline] fn from (item: &'a LocalItem) -> Self { Self::LocalItem(item) } }
impl<'a> From<&'a GlobalItem> for MultiRef<'a> { #[inline] fn from (item: &'a GlobalItem) -> Self { Self::GlobalItem(item) } }
#[allow(missing_docs)]
pub enum MultiMut<'a> {
LocalItem(&'a mut LocalItem),
GlobalItem(&'a mut GlobalItem),
}
impl<'a> From<&'a mut LocalItem> for MultiMut<'a> { #[inline] fn from (item: &'a mut LocalItem) -> Self { Self::LocalItem(item) } }
impl<'a> From<&'a mut GlobalItem> for MultiMut<'a> { #[inline] fn from (item: &'a mut GlobalItem) -> Self { Self::GlobalItem(item) } }
make_key_type! {
pub struct LocalKey;
}
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum MultiKey {
LocalKey(LocalKey),
GlobalKey(GlobalKey),
}
impl MultiKey {
pub fn local (self) -> Option<LocalKey> { if let Self::LocalKey(key) = self { Some(key) } else { None } }
pub unsafe fn local_unchecked (self) -> LocalKey { if cfg!(debug_assertions) { self.local().unwrap() } else if let Self::LocalKey(key) = self { key } else { unreachable_unchecked() } }
pub fn namespace (self) -> Option<GlobalKey> { if let Self::GlobalKey(key) = self { Some(key) } else { None } }
pub unsafe fn namespace_unchecked (self) -> GlobalKey { if cfg!(debug_assertions) { self.namespace().unwrap() } else if let Self::GlobalKey(key) = self { key } else { unreachable_unchecked() } }
}
impl PartialEq<LocalKey> for MultiKey { #[inline] fn eq (&self, other: &LocalKey) -> bool { if let Self::LocalKey(key) = self { key == other } else { false } } }
impl PartialEq<GlobalKey> for MultiKey { #[inline] fn eq (&self, other: &GlobalKey) -> bool { if let Self::GlobalKey(key) = self { key == other } else { false } } }
impl From<LocalKey> for MultiKey { #[inline] fn from (key: LocalKey) -> Self { Self::LocalKey(key) } }
impl From<GlobalKey> for MultiKey { #[inline] fn from (key: GlobalKey) -> Self { Self::GlobalKey(key) } }
#[derive(Debug, Clone)]
pub struct LocalContext {
pub variables: SlotMap<LocalKey, LocalItem>,
pub parameter_count: usize,
pub local_count: usize,
pub stack_frames: Vec<LocalNamespace>,
}
impl Default for LocalContext { #[inline] fn default () -> Self { Self::new() } }
impl LocalContext {
pub fn new () -> Self {
Self {
variables: SlotMap::default(),
parameter_count: 0,
local_count: 0,
stack_frames: vec![ Namespace::default() ]
}
}
pub fn create_variable (&mut self,
canonical_name: Identifier,
ty: GlobalKey,
is_parameter: bool,
origin: SourceRegion,
) -> LocalKey {
let count = if is_parameter { &mut self.parameter_count } else { &mut self.local_count };
let index = *count;
*count += 1;
let key = self.variables.insert(LocalItem {
canonical_name: canonical_name.clone(),
ty,
is_parameter,
index
});
self.set_variable(canonical_name, key.into(), origin);
key
}
pub fn set_variable<I: Into<Identifier> + AsRef<str>> (&mut self, ident: I, value: MultiKey, origin: SourceRegion) {
self.stack_frames.last_mut().unwrap().set_entry_bound(ident, value, origin)
}
pub fn get_variable<I: AsRef<str>> (&self, ident: &I) -> Option<MultiKey> {
for ns in self.stack_frames.iter().rev() {
let ns_entry = ns.get_entry(ident);
if ns_entry.is_some() { return ns_entry }
}
None
}
pub fn push_stack_frame (&mut self) {
self.stack_frames.push(Namespace::default())
}
pub fn pop_stack_frame (&mut self) -> LocalNamespace {
assert!(self.stack_frames.len() > 1, "Internal error, cannot pop final stack frame of LocalContext");
self.stack_frames.pop().unwrap()
}
}
impl GlobalItem {
pub fn kind (&self) -> NamespaceKind {
match self {
Self::Module(_) => NamespaceKind::Module,
Self::Type(_) => NamespaceKind::Type,
Self::Global(_) => NamespaceKind::Global,
Self::Function(_) => NamespaceKind::Function,
}
}
#[inline] pub fn ref_module (&self) -> Option<&Module> { match self { Self::Module(item) => Some(item), _ => None } }
#[inline] pub unsafe fn ref_module_unchecked (&self) -> &Module { if cfg!(debug_assertions) { self.ref_module().unwrap() } else if let Self::Module(item) = self { item } else { unreachable_unchecked() } }
#[inline] pub fn mut_module (&mut self) -> Option<&mut Module> { match self { Self::Module(item) => Some(item), _ => None } }
#[inline] pub unsafe fn mut_module_unchecked (&mut self) -> &mut Module { if cfg!(debug_assertions) { self.mut_module().unwrap() } else if let Self::Module(item) = self { item } else { unreachable_unchecked() } }
#[inline] pub fn ref_type (&self) -> Option<&Type> { match self { Self::Type(item) => Some(item), _ => None } }
#[inline] pub unsafe fn ref_type_unchecked (&self) -> &Type { if cfg!(debug_assertions) { self.ref_type().unwrap() } else if let Self::Type(item) = self { item } else { unreachable_unchecked() } }
#[inline] pub fn mut_type (&mut self) -> Option<&mut Type> { match self { Self::Type(item) => Some(item), _ => None } }
#[inline] pub unsafe fn mut_type_unchecked (&mut self) -> &mut Type { if cfg!(debug_assertions) { self.mut_type().unwrap() } else if let Self::Type(item) = self { item } else { unreachable_unchecked() } }
#[inline] pub fn ref_global (&self) -> Option<&Global> { match self { Self::Global(item) => Some(item), _ => None } }
#[inline] pub unsafe fn ref_global_unchecked (&self) -> &Global { if cfg!(debug_assertions) { self.ref_global().unwrap() } else if let Self::Global(item) = self { item } else { unreachable_unchecked() } }
#[inline] pub fn mut_global (&mut self) -> Option<&mut Global> { match self { Self::Global(item) => Some(item), _ => None } }
#[inline] pub unsafe fn mut_global_unchecked (&mut self) -> &mut Global { if cfg!(debug_assertions) { self.mut_global().unwrap() } else if let Self::Global(item) = self { item } else { unreachable_unchecked() } }
#[inline] pub fn ref_function (&self) -> Option<&Function> { match self { Self::Function(item) => Some(item), _ => None } }
#[inline] pub unsafe fn ref_function_unchecked (&self) -> &Function { if cfg!(debug_assertions) { self.ref_function().unwrap() } else if let Self::Function(item) = self { item } else { unreachable_unchecked() } }
#[inline] pub fn mut_function (&mut self) -> Option<&mut Function> { match self { Self::Function(item) => Some(item), _ => None } }
#[inline] pub unsafe fn mut_function_unchecked (&mut self) -> &mut Function { if cfg!(debug_assertions) { self.mut_function().unwrap() } else if let Self::Function(item) = self { item } else { unreachable_unchecked() } }
}
impl From<Module> for GlobalItem { #[inline] fn from (item: Module) -> GlobalItem { Self::Module(item) } }
impl From<Type> for GlobalItem { #[inline] fn from (item: Type) -> GlobalItem { Self::Type(item) } }
impl From<Global> for GlobalItem { #[inline] fn from (item: Global) -> GlobalItem { Self::Global(item) } }
impl From<Function> for GlobalItem { #[inline] fn from (item: Function) -> GlobalItem { Self::Function(item) } }
#[repr(u8)]
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum NamespaceKind {
Module,
Type,
Global,
Function,
}
impl Display for NamespaceKind {
fn fmt (&self, f: &mut Formatter) -> FMTResult {
write!(f, "{}", match self {
Self::Module => "Module",
Self::Type => "Type",
Self::Global => "Global",
Self::Function => "Function",
})
}
}
make_key_type! {
pub struct GlobalKey;
}