use serde::{Deserialize, Serialize};
use std::hash::Hash;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Arena {
pub name: String,
pub types: Vec<TypeDef>,
pub functions: Vec<Function>,
pub children: Vec<Arena>,
}
impl Arena {
pub fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
types: Vec::new(),
functions: Vec::new(),
children: Vec::new(),
}
}
pub fn add_type(&mut self, typedef: TypeDef) {
self.types.push(typedef);
}
pub fn add_function(&mut self, func: Function) {
self.functions.push(func);
}
pub fn add_child(&mut self, child: Arena) {
self.children.push(child);
}
pub fn find_type(&self, name: &str) -> Option<&TypeDef> {
self.types.iter().find(|t| t.name() == name)
}
pub fn find_function(&self, name: &str) -> Option<&Function> {
self.functions.iter().find(|f| f.name == name)
}
pub fn imports(&self) -> Vec<Function> {
self.children
.iter()
.find(|c| c.name == "imports")
.map(|imports_arena| {
imports_arena
.children
.iter()
.flat_map(|interface| {
interface.functions.iter().map(|f| {
let mut func = f.clone();
func.interface = interface.name.clone();
func
})
})
.collect()
})
.unwrap_or_default()
}
pub fn exports(&self) -> Vec<Function> {
self.children
.iter()
.find(|c| c.name == "exports")
.map(|exports_arena| {
exports_arena
.children
.iter()
.flat_map(|interface| {
interface.functions.iter().map(|f| {
let mut func = f.clone();
func.interface = interface.name.clone();
func
})
})
.collect()
})
.unwrap_or_default()
}
pub fn imported_function_names(&self, interface_name: &str) -> Vec<String> {
self.imports()
.into_iter()
.filter(|f| f.interface == interface_name)
.map(|f| f.name)
.collect()
}
pub fn exported_function_names(&self, interface_name: &str) -> Vec<String> {
self.exports()
.into_iter()
.filter(|f| f.interface == interface_name)
.map(|f| f.name)
.collect()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Function {
pub name: String,
#[serde(default)]
pub interface: String,
pub types: Vec<TypeDef>,
pub params: Vec<Param>,
pub results: Vec<Type>,
}
impl Function {
pub fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
interface: String::new(),
types: Vec::new(),
params: Vec::new(),
results: Vec::new(),
}
}
pub fn with_signature(name: impl Into<String>, params: Vec<Param>, results: Vec<Type>) -> Self {
Self {
name: name.into(),
interface: String::new(),
types: Vec::new(),
params,
results,
}
}
pub fn with_interface(
name: impl Into<String>,
interface: impl Into<String>,
params: Vec<Param>,
results: Vec<Type>,
) -> Self {
Self {
name: name.into(),
interface: interface.into(),
types: Vec::new(),
params,
results,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Param {
pub name: String,
pub ty: Type,
}
impl Param {
pub fn new(name: impl Into<String>, ty: Type) -> Self {
Self {
name: name.into(),
ty,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum TypeDef {
Alias { name: String, ty: Type },
Record { name: String, fields: Vec<Field> },
Variant { name: String, cases: Vec<Case> },
Enum { name: String, cases: Vec<String> },
Flags { name: String, flags: Vec<String> },
}
impl TypeDef {
pub fn name(&self) -> &str {
match self {
TypeDef::Alias { name, .. } => name,
TypeDef::Record { name, .. } => name,
TypeDef::Variant { name, .. } => name,
TypeDef::Enum { name, .. } => name,
TypeDef::Flags { name, .. } => name,
}
}
pub fn alias(name: impl Into<String>, ty: Type) -> Self {
TypeDef::Alias {
name: name.into(),
ty,
}
}
pub fn record(name: impl Into<String>, fields: Vec<Field>) -> Self {
TypeDef::Record {
name: name.into(),
fields,
}
}
pub fn variant(name: impl Into<String>, cases: Vec<Case>) -> Self {
TypeDef::Variant {
name: name.into(),
cases,
}
}
pub fn enumeration(name: impl Into<String>, cases: Vec<String>) -> Self {
TypeDef::Enum {
name: name.into(),
cases,
}
}
pub fn flags(name: impl Into<String>, flags: Vec<String>) -> Self {
TypeDef::Flags {
name: name.into(),
flags,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Field {
pub name: String,
pub ty: Type,
}
impl Field {
pub fn new(name: impl Into<String>, ty: Type) -> Self {
Self {
name: name.into(),
ty,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Case {
pub name: String,
pub payload: Type,
}
impl Case {
pub fn new(name: impl Into<String>, payload: Type) -> Self {
Self {
name: name.into(),
payload,
}
}
pub fn unit(name: impl Into<String>) -> Self {
Self {
name: name.into(),
payload: Type::Unit,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Type {
Unit,
Bool,
U8,
U16,
U32,
U64,
S8,
S16,
S32,
S64,
F32,
F64,
Char,
String,
List(Box<Type>),
Option(Box<Type>),
Result { ok: Box<Type>, err: Box<Type> },
Tuple(Vec<Type>),
Ref(TypePath),
Value,
}
impl Type {
pub fn list(inner: Type) -> Self {
Type::List(Box::new(inner))
}
pub fn option(inner: Type) -> Self {
Type::Option(Box::new(inner))
}
pub fn result(ok: Type, err: Type) -> Self {
Type::Result {
ok: Box::new(ok),
err: Box::new(err),
}
}
pub fn tuple(types: Vec<Type>) -> Self {
Type::Tuple(types)
}
pub fn named(name: impl Into<String>) -> Self {
Type::Ref(TypePath::simple(name))
}
pub fn self_ref() -> Self {
Type::Ref(TypePath::self_ref())
}
pub fn is_unit(&self) -> bool {
matches!(self, Type::Unit)
}
pub fn is_self_ref(&self) -> bool {
matches!(self, Type::Ref(path) if path.is_self_ref())
}
pub fn contains_recursion(&self) -> bool {
match self {
Type::Ref(path) if path.is_self_ref() => true,
Type::List(inner) | Type::Option(inner) => inner.contains_recursion(),
Type::Result { ok, err } => ok.contains_recursion() || err.contains_recursion(),
Type::Tuple(types) => types.iter().any(|t| t.contains_recursion()),
_ => false,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct TypePath {
pub segments: Vec<String>,
pub absolute: bool,
}
impl TypePath {
pub fn simple(name: impl Into<String>) -> Self {
Self {
segments: vec![name.into()],
absolute: false,
}
}
pub fn absolute(segments: Vec<String>) -> Self {
Self {
segments,
absolute: true,
}
}
pub fn relative(segments: Vec<String>) -> Self {
Self {
segments,
absolute: false,
}
}
pub fn self_ref() -> Self {
Self {
segments: Vec::new(),
absolute: false,
}
}
pub fn is_self_ref(&self) -> bool {
self.segments.is_empty() && !self.absolute
}
pub fn is_simple(&self) -> bool {
self.segments.len() == 1 && !self.absolute
}
pub fn as_simple(&self) -> Option<&str> {
if self.is_simple() {
self.segments.first().map(|s| s.as_str())
} else {
None
}
}
pub fn name(&self) -> Option<&str> {
self.segments.last().map(|s| s.as_str())
}
}
impl std::fmt::Display for TypePath {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.is_self_ref() {
write!(f, "self")
} else if self.absolute {
write!(f, "::{}", self.segments.join("::"))
} else {
write!(f, "{}", self.segments.join("::"))
}
}
}
pub fn sexpr_type() -> TypeDef {
TypeDef::Variant {
name: "sexpr".to_string(),
cases: vec![
Case::new("sym", Type::String),
Case::new("num", Type::S64),
Case::new("flt", Type::F64),
Case::new("str", Type::String),
Case::new("lst", Type::list(Type::self_ref())),
],
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashSet;
#[test]
fn test_arena_creation() {
let mut arena = Arena::new("test");
arena.add_type(TypeDef::alias("count", Type::U32));
arena.add_function(Function::with_signature(
"add",
vec![Param::new("a", Type::S32), Param::new("b", Type::S32)],
vec![Type::S32],
));
assert_eq!(arena.name, "test");
assert_eq!(arena.types.len(), 1);
assert_eq!(arena.functions.len(), 1);
assert!(arena.find_type("count").is_some());
assert!(arena.find_function("add").is_some());
}
#[test]
fn test_type_path() {
let simple = TypePath::simple("expr");
assert!(simple.is_simple());
assert_eq!(simple.as_simple(), Some("expr"));
assert!(!simple.is_self_ref());
let self_ref = TypePath::self_ref();
assert!(self_ref.is_self_ref());
assert!(!self_ref.is_simple());
let absolute = TypePath::absolute(vec!["wasi".into(), "cli".into(), "stdout".into()]);
assert!(absolute.absolute);
assert_eq!(absolute.name(), Some("stdout"));
}
#[test]
fn test_sexpr_type() {
let sexpr = sexpr_type();
assert_eq!(sexpr.name(), "sexpr");
if let TypeDef::Variant { cases, .. } = &sexpr {
assert_eq!(cases.len(), 5);
assert_eq!(cases[0].name, "sym");
assert_eq!(cases[4].name, "lst");
if let Type::List(inner) = &cases[4].payload {
assert!(inner.is_self_ref());
} else {
panic!("Expected list type");
}
} else {
panic!("Expected variant");
}
}
#[test]
fn test_contains_recursion() {
assert!(Type::self_ref().contains_recursion());
assert!(Type::list(Type::self_ref()).contains_recursion());
assert!(!Type::list(Type::S32).contains_recursion());
assert!(!Type::String.contains_recursion());
assert!(Type::result(Type::self_ref(), Type::String).contains_recursion());
}
#[test]
fn test_type_hashing() {
let mut set = HashSet::new();
set.insert(Type::S32);
assert!(!set.insert(Type::S32));
assert!(set.insert(Type::S64));
assert!(set.insert(Type::String));
assert!(set.insert(Type::list(Type::S32)));
}
#[test]
fn test_arena_hashing() {
let mut set = HashSet::new();
let arena1 = Arena::new("test");
let arena2 = Arena::new("test");
let arena3 = Arena::new("other");
set.insert(arena1.clone());
assert!(!set.insert(arena2)); assert!(set.insert(arena3)); }
#[test]
fn test_typedef_name() {
assert_eq!(TypeDef::alias("foo", Type::S32).name(), "foo");
assert_eq!(TypeDef::record("bar", vec![]).name(), "bar");
assert_eq!(TypeDef::variant("baz", vec![]).name(), "baz");
assert_eq!(TypeDef::enumeration("qux", vec![]).name(), "qux");
assert_eq!(TypeDef::flags("quux", vec![]).name(), "quux");
}
#[test]
fn test_case_constructors() {
let with_payload = Case::new("data", Type::String);
assert_eq!(with_payload.name, "data");
assert_eq!(with_payload.payload, Type::String);
let without_payload = Case::unit("empty");
assert_eq!(without_payload.name, "empty");
assert_eq!(without_payload.payload, Type::Unit);
}
#[test]
fn test_unit_type() {
assert!(Type::Unit.is_unit());
assert!(!Type::S32.is_unit());
assert!(!Type::String.is_unit());
}
#[test]
fn test_type_display() {
assert_eq!(TypePath::self_ref().to_string(), "self");
assert_eq!(TypePath::simple("expr").to_string(), "expr");
assert_eq!(
TypePath::absolute(vec!["wasi".into(), "cli".into()]).to_string(),
"::wasi::cli"
);
}
}