use alloc::vec::Vec;
use core::fmt;
use crate::ids::{FuncId, StrId, TypeId};
use crate::policy::{FloatTy, IntTy};
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum ValType {
Bool,
I8,
I16,
I32,
I64,
U8,
U16,
U32,
U64,
F32,
F64,
Char,
Str,
Dyn,
Ref(TypeId),
}
impl ValType {
#[must_use]
pub const fn int(ty: IntTy) -> Self {
match ty {
IntTy::I8 => ValType::I8,
IntTy::I16 => ValType::I16,
IntTy::I32 => ValType::I32,
IntTy::I64 => ValType::I64,
IntTy::U8 => ValType::U8,
IntTy::U16 => ValType::U16,
IntTy::U32 => ValType::U32,
IntTy::U64 => ValType::U64,
}
}
#[must_use]
pub const fn float(ty: FloatTy) -> Self {
match ty {
FloatTy::F32 => ValType::F32,
FloatTy::F64 => ValType::F64,
}
}
#[must_use]
pub const fn as_int(self) -> Option<IntTy> {
match self {
ValType::I8 => Some(IntTy::I8),
ValType::I16 => Some(IntTy::I16),
ValType::I32 => Some(IntTy::I32),
ValType::I64 => Some(IntTy::I64),
ValType::U8 => Some(IntTy::U8),
ValType::U16 => Some(IntTy::U16),
ValType::U32 => Some(IntTy::U32),
ValType::U64 => Some(IntTy::U64),
_ => None,
}
}
#[must_use]
pub const fn is_reference(self) -> bool {
matches!(self, ValType::Str | ValType::Dyn | ValType::Ref(_))
}
pub(crate) const fn tag(self) -> u8 {
match self {
ValType::Bool => 0,
ValType::I8 => 1,
ValType::I16 => 2,
ValType::I32 => 3,
ValType::I64 => 4,
ValType::U8 => 5,
ValType::U16 => 6,
ValType::U32 => 7,
ValType::U64 => 8,
ValType::F32 => 9,
ValType::F64 => 10,
ValType::Char => 11,
ValType::Str => 12,
ValType::Dyn => 13,
ValType::Ref(_) => 14,
}
}
pub(crate) const fn from_simple_tag(tag: u8) -> Option<ValType> {
Some(match tag {
0 => ValType::Bool,
1 => ValType::I8,
2 => ValType::I16,
3 => ValType::I32,
4 => ValType::I64,
5 => ValType::U8,
6 => ValType::U16,
7 => ValType::U32,
8 => ValType::U64,
9 => ValType::F32,
10 => ValType::F64,
11 => ValType::Char,
12 => ValType::Str,
13 => ValType::Dyn,
_ => return None,
})
}
}
impl fmt::Display for ValType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = match self {
ValType::Bool => "bool",
ValType::I8 => "i8",
ValType::I16 => "i16",
ValType::I32 => "i32",
ValType::I64 => "i64",
ValType::U8 => "u8",
ValType::U16 => "u16",
ValType::U32 => "u32",
ValType::U64 => "u64",
ValType::F32 => "f32",
ValType::F64 => "f64",
ValType::Char => "char",
ValType::Str => "str",
ValType::Dyn => "dyn",
ValType::Ref(ty) => return write!(f, "ref {ty}"),
};
f.write_str(name)
}
}
code_enum! {
Kind {
Nil = 0 => "nil",
Bool = 1 => "bool",
Int = 2 => "int",
Float = 3 => "float",
Char = 4 => "char",
Str = 5 => "str",
Array = 6 => "array",
Map = 7 => "map",
Object = 8 => "object",
Function = 9 => "function",
Cell = 10 => "cell",
Iter = 11 => "iter",
Error = 12 => "error",
Coroutine = 13 => "coroutine",
Reference = 14 => "reference",
}
}
code_enum! {
CoroState {
Created = 0 => "created",
Running = 1 => "running",
Yielded = 2 => "yielded",
Awaiting = 3 => "awaiting",
Returned = 4 => "returned",
Failed = 5 => "failed",
}
}
impl CoroState {
#[must_use]
pub const fn is_resumable(self) -> bool {
matches!(
self,
CoroState::Created | CoroState::Yielded | CoroState::Awaiting
)
}
}
code_enum! {
Prim {
Bool = 0 => "bool",
I8 = 1 => "i8",
I16 = 2 => "i16",
I32 = 3 => "i32",
I64 = 4 => "i64",
U8 = 5 => "u8",
U16 = 6 => "u16",
U32 = 7 => "u32",
U64 = 8 => "u64",
F32 = 9 => "f32",
F64 = 10 => "f64",
Char = 11 => "char",
Str = 12 => "str",
Ref = 13 => "ref",
}
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum TypeDef {
Func(FuncType),
Struct(StructDef),
Array(ValType),
Map {
key: ValType,
value: ValType,
},
Cell(ValType),
Iter {
key: ValType,
value: ValType,
},
Coroutine,
}
impl TypeDef {
pub(crate) const fn tag(&self) -> u8 {
match self {
TypeDef::Func(_) => 0,
TypeDef::Struct(_) => 1,
TypeDef::Array(_) => 2,
TypeDef::Map { .. } => 3,
TypeDef::Cell(_) => 4,
TypeDef::Iter { .. } => 5,
TypeDef::Coroutine => 6,
}
}
pub(crate) const fn is_structural(&self) -> bool {
!matches!(self, TypeDef::Struct(_))
}
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
pub struct FuncType {
pub params: Vec<ValType>,
pub results: Vec<ValType>,
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
pub struct StructDef {
pub name: StrId,
pub parent: Option<TypeId>,
pub fields: Vec<Field>,
pub methods: Vec<Method>,
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct Field {
pub name: StrId,
pub ty: ValType,
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct Method {
pub name: StrId,
pub func: FuncId,
}
fn write_list(f: &mut fmt::Formatter<'_>, types: &[ValType]) -> fmt::Result {
f.write_str("(")?;
for (i, ty) in types.iter().enumerate() {
if i > 0 {
f.write_str(", ")?;
}
write!(f, "{ty}")?;
}
f.write_str(")")
}
impl fmt::Display for FuncType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("func ")?;
write_list(f, &self.params)?;
f.write_str(" -> ")?;
write_list(f, &self.results)
}
}
impl fmt::Display for StructDef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "struct {}", self.name)?;
if let Some(parent) = self.parent {
write!(f, " : {parent}")?;
}
f.write_str(" {")?;
for (i, field) in self.fields.iter().enumerate() {
let sep = if i == 0 { " " } else { ", " };
write!(f, "{sep}#{i} {}: {}", field.name, field.ty)?;
}
f.write_str(" }")?;
if !self.methods.is_empty() {
f.write_str(" methods {")?;
for (i, method) in self.methods.iter().enumerate() {
let sep = if i == 0 { " " } else { ", " };
write!(f, "{sep}{} = {}", method.name, method.func)?;
}
f.write_str(" }")?;
}
Ok(())
}
}
impl fmt::Display for TypeDef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TypeDef::Func(sig) => write!(f, "{sig}"),
TypeDef::Struct(def) => write!(f, "{def}"),
TypeDef::Array(elem) => write!(f, "array {elem}"),
TypeDef::Map { key, value } => write!(f, "map {key} -> {value}"),
TypeDef::Cell(elem) => write!(f, "cell {elem}"),
TypeDef::Iter { key, value } => write!(f, "iter {key} -> {value}"),
TypeDef::Coroutine => f.write_str("coroutine"),
}
}
}
#[cfg(test)]
mod tests {
use alloc::string::ToString;
use alloc::vec;
use super::*;
#[test]
fn simple_tags_round_trip() {
for tag in 0..=13u8 {
let ty = ValType::from_simple_tag(tag).map(ValType::tag);
assert_eq!(ty, Some(tag));
}
assert_eq!(ValType::from_simple_tag(14), None);
assert_eq!(ValType::Ref(TypeId(9)).tag(), 14);
}
#[test]
fn int_value_types_map_back() {
for &ty in IntTy::ALL {
assert_eq!(ValType::int(ty).as_int(), Some(ty));
}
}
#[test]
fn struct_display_lists_fields_and_methods() {
let def = StructDef {
name: StrId(1),
parent: Some(TypeId(0)),
fields: vec![Field {
name: StrId(2),
ty: ValType::I64,
}],
methods: vec![Method {
name: StrId(3),
func: FuncId(4),
}],
};
assert_eq!(
TypeDef::Struct(def).to_string(),
"struct s1 : t0 { #0 s2: i64 } methods { s3 = f4 }"
);
}
}