use std::sync::Arc;
use thiserror::Error;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct LibId(pub u32);
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct ClassId(pub u32);
pub const CORE_CLASS_CLASS_ID: ClassId = ClassId(0);
pub const CORE_NIL_CLASS_ID: ClassId = ClassId(1);
pub const CORE_BOOL_CLASS_ID: ClassId = ClassId(2);
pub const CORE_NUMBER_CLASS_ID: ClassId = ClassId(3);
pub const CORE_SYMBOL_CLASS_ID: ClassId = ClassId(4);
pub const CORE_STRING_CLASS_ID: ClassId = ClassId(5);
pub const CORE_BYTES_CLASS_ID: ClassId = ClassId(6);
pub const CORE_LIST_CLASS_ID: ClassId = ClassId(7);
pub const CORE_TABLE_CLASS_ID: ClassId = ClassId(8);
pub const CORE_EXPR_CLASS_ID: ClassId = ClassId(9);
pub const CORE_FUNCTION_CLASS_ID: ClassId = ClassId(10);
pub const CORE_SHAPE_CLASS_ID: ClassId = ClassId(11);
pub const CORE_THUNK_CLASS_ID: ClassId = ClassId(12);
pub const CORE_EVAL_REQUEST_CLASS_ID: ClassId = ClassId(13);
pub const CORE_EVAL_REPLY_CLASS_ID: ClassId = ClassId(14);
pub const CORE_MACRO_CLASS_ID: ClassId = ClassId(15);
pub const CORE_SHAPE_MATCH_CLASS_ID: ClassId = ClassId(16);
pub const CORE_CODEC_CLASS_ID: ClassId = ClassId(17);
pub const CORE_HELP_CLASS_ID: ClassId = ClassId(18);
pub const CORE_TEST_CLASS_ID: ClassId = ClassId(19);
pub const CORE_NUMBER_DOMAIN_CLASS_ID: ClassId = ClassId(20);
pub const CORE_LOCAL_EVAL_FABRIC_CLASS_ID: ClassId = ClassId(21);
pub const CORE_SEQUENCE_CLASS_ID: ClassId = ClassId(22);
pub const CORE_CARD_CLASS_ID: ClassId = ClassId(23);
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct FunctionId(pub u32);
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct MacroId(pub u32);
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct CaseId(pub u32);
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct ShapeId(pub u32);
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct CodecId(pub u32);
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct NumberDomainId(pub u32);
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct SiteId(pub u32);
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct ValueId(pub u64);
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum RuntimeId {
Class(ClassId),
Function(FunctionId),
Macro(MacroId),
Shape(ShapeId),
Codec(CodecId),
NumberDomain(NumberDomainId),
Site(SiteId),
Value,
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct Symbol {
pub namespace: Option<Arc<str>>,
pub name: Arc<str>,
}
impl Symbol {
pub fn new(name: impl Into<Arc<str>>) -> Self {
Self {
namespace: None,
name: name.into(),
}
}
pub fn checked(name: impl Into<Arc<str>>) -> Result<Self, SymbolError> {
let name = name.into();
validate_name(&name)?;
Ok(Self {
namespace: None,
name,
})
}
pub fn qualified(namespace: impl Into<Arc<str>>, name: impl Into<Arc<str>>) -> Self {
Self {
namespace: Some(namespace.into()),
name: name.into(),
}
}
pub fn as_qualified_str(&self) -> String {
match &self.namespace {
Some(namespace) => format!("{namespace}/{}", self.name),
None => self.name.to_string(),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Error)]
pub enum SymbolError {
#[error("symbol name {0:?} contains the reserved namespace separator '/'")]
ContainsSeparator(String),
#[error("symbol name {0:?} contains a control character")]
ContainsControl(String),
}
fn validate_name(name: &str) -> Result<(), SymbolError> {
if name.contains('/') {
return Err(SymbolError::ContainsSeparator(name.to_owned()));
}
if name.chars().any(|ch| ch.is_control()) {
return Err(SymbolError::ContainsControl(name.to_owned()));
}
Ok(())
}
impl core::fmt::Display for Symbol {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(&self.as_qualified_str())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn core_class_ids_are_unique() {
let ids = [
CORE_CLASS_CLASS_ID,
CORE_NIL_CLASS_ID,
CORE_BOOL_CLASS_ID,
CORE_NUMBER_CLASS_ID,
CORE_SYMBOL_CLASS_ID,
CORE_STRING_CLASS_ID,
CORE_BYTES_CLASS_ID,
CORE_LIST_CLASS_ID,
CORE_TABLE_CLASS_ID,
CORE_EXPR_CLASS_ID,
CORE_FUNCTION_CLASS_ID,
CORE_SHAPE_CLASS_ID,
CORE_THUNK_CLASS_ID,
CORE_EVAL_REQUEST_CLASS_ID,
CORE_EVAL_REPLY_CLASS_ID,
CORE_MACRO_CLASS_ID,
CORE_SHAPE_MATCH_CLASS_ID,
CORE_CODEC_CLASS_ID,
CORE_HELP_CLASS_ID,
CORE_TEST_CLASS_ID,
CORE_NUMBER_DOMAIN_CLASS_ID,
CORE_LOCAL_EVAL_FABRIC_CLASS_ID,
CORE_SEQUENCE_CLASS_ID,
CORE_CARD_CLASS_ID,
];
let unique = ids
.iter()
.copied()
.collect::<std::collections::BTreeSet<_>>();
assert_eq!(ids.len(), unique.len());
}
#[test]
fn checked_accepts_a_plain_name() {
let symbol = Symbol::checked("car").expect("plain name is valid");
assert_eq!(symbol, Symbol::new("car"));
}
#[test]
fn checked_rejects_the_namespace_separator() {
assert_eq!(
Symbol::checked("a/b"),
Err(SymbolError::ContainsSeparator("a/b".to_owned()))
);
}
#[test]
fn checked_rejects_nul_and_control_characters() {
assert_eq!(
Symbol::checked("a\0b"),
Err(SymbolError::ContainsControl("a\0b".to_owned()))
);
assert_eq!(
Symbol::checked("a\tb"),
Err(SymbolError::ContainsControl("a\tb".to_owned()))
);
}
}