1use std::sync::Arc;
10
11use thiserror::Error;
12
13#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
15pub struct LibId(pub u32);
16
17#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
19pub struct ClassId(pub u32);
20
21pub const CORE_CLASS_CLASS_ID: ClassId = ClassId(0);
23pub const CORE_NIL_CLASS_ID: ClassId = ClassId(1);
25pub const CORE_BOOL_CLASS_ID: ClassId = ClassId(2);
27pub const CORE_NUMBER_CLASS_ID: ClassId = ClassId(3);
29pub const CORE_SYMBOL_CLASS_ID: ClassId = ClassId(4);
31pub const CORE_STRING_CLASS_ID: ClassId = ClassId(5);
33pub const CORE_BYTES_CLASS_ID: ClassId = ClassId(6);
35pub const CORE_LIST_CLASS_ID: ClassId = ClassId(7);
37pub const CORE_TABLE_CLASS_ID: ClassId = ClassId(8);
39pub const CORE_EXPR_CLASS_ID: ClassId = ClassId(9);
41pub const CORE_FUNCTION_CLASS_ID: ClassId = ClassId(10);
43pub const CORE_SHAPE_CLASS_ID: ClassId = ClassId(11);
45pub const CORE_THUNK_CLASS_ID: ClassId = ClassId(12);
47pub const CORE_EVAL_REQUEST_CLASS_ID: ClassId = ClassId(13);
49pub const CORE_EVAL_REPLY_CLASS_ID: ClassId = ClassId(14);
51pub const CORE_MACRO_CLASS_ID: ClassId = ClassId(15);
53pub const CORE_SHAPE_MATCH_CLASS_ID: ClassId = ClassId(16);
55pub const CORE_CODEC_CLASS_ID: ClassId = ClassId(17);
57pub const CORE_HELP_CLASS_ID: ClassId = ClassId(18);
59pub const CORE_TEST_CLASS_ID: ClassId = ClassId(19);
61pub const CORE_NUMBER_DOMAIN_CLASS_ID: ClassId = ClassId(20);
63pub const CORE_LOCAL_EVAL_FABRIC_CLASS_ID: ClassId = ClassId(21);
65pub const CORE_SEQUENCE_CLASS_ID: ClassId = ClassId(22);
67pub const CORE_CARD_CLASS_ID: ClassId = ClassId(23);
69
70#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
72pub struct FunctionId(pub u32);
73
74#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
76pub struct MacroId(pub u32);
77
78#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
80pub struct CaseId(pub u32);
81
82#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
84pub struct ShapeId(pub u32);
85
86#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
88pub struct CodecId(pub u32);
89
90#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
92pub struct NumberDomainId(pub u32);
93
94#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
96pub struct SiteId(pub u32);
97
98#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
100pub struct ValueId(pub u64);
101
102#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
104pub enum RuntimeId {
105 Class(ClassId),
107 Function(FunctionId),
109 Macro(MacroId),
111 Shape(ShapeId),
113 Codec(CodecId),
115 NumberDomain(NumberDomainId),
117 Site(SiteId),
119 Value,
121}
122
123#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
155pub struct Symbol {
156 pub namespace: Option<Arc<str>>,
158 pub name: Arc<str>,
160}
161
162impl Symbol {
163 pub fn new(name: impl Into<Arc<str>>) -> Self {
165 Self {
166 namespace: None,
167 name: name.into(),
168 }
169 }
170
171 pub fn checked(name: impl Into<Arc<str>>) -> Result<Self, SymbolError> {
179 let name = name.into();
180 validate_name(&name)?;
181 Ok(Self {
182 namespace: None,
183 name,
184 })
185 }
186
187 pub fn qualified(namespace: impl Into<Arc<str>>, name: impl Into<Arc<str>>) -> Self {
189 Self {
190 namespace: Some(namespace.into()),
191 name: name.into(),
192 }
193 }
194
195 pub fn as_qualified_str(&self) -> String {
197 match &self.namespace {
198 Some(namespace) => format!("{namespace}/{}", self.name),
199 None => self.name.to_string(),
200 }
201 }
202}
203
204#[derive(Clone, Debug, PartialEq, Eq, Error)]
206pub enum SymbolError {
207 #[error("symbol name {0:?} contains the reserved namespace separator '/'")]
209 ContainsSeparator(String),
210 #[error("symbol name {0:?} contains a control character")]
212 ContainsControl(String),
213}
214
215fn validate_name(name: &str) -> Result<(), SymbolError> {
216 if name.contains('/') {
217 return Err(SymbolError::ContainsSeparator(name.to_owned()));
218 }
219 if name.chars().any(|ch| ch.is_control()) {
220 return Err(SymbolError::ContainsControl(name.to_owned()));
221 }
222 Ok(())
223}
224
225impl core::fmt::Display for Symbol {
226 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
227 f.write_str(&self.as_qualified_str())
228 }
229}
230
231#[cfg(test)]
232mod tests {
233 use super::*;
234
235 #[test]
236 fn core_class_ids_are_unique() {
237 let ids = [
238 CORE_CLASS_CLASS_ID,
239 CORE_NIL_CLASS_ID,
240 CORE_BOOL_CLASS_ID,
241 CORE_NUMBER_CLASS_ID,
242 CORE_SYMBOL_CLASS_ID,
243 CORE_STRING_CLASS_ID,
244 CORE_BYTES_CLASS_ID,
245 CORE_LIST_CLASS_ID,
246 CORE_TABLE_CLASS_ID,
247 CORE_EXPR_CLASS_ID,
248 CORE_FUNCTION_CLASS_ID,
249 CORE_SHAPE_CLASS_ID,
250 CORE_THUNK_CLASS_ID,
251 CORE_EVAL_REQUEST_CLASS_ID,
252 CORE_EVAL_REPLY_CLASS_ID,
253 CORE_MACRO_CLASS_ID,
254 CORE_SHAPE_MATCH_CLASS_ID,
255 CORE_CODEC_CLASS_ID,
256 CORE_HELP_CLASS_ID,
257 CORE_TEST_CLASS_ID,
258 CORE_NUMBER_DOMAIN_CLASS_ID,
259 CORE_LOCAL_EVAL_FABRIC_CLASS_ID,
260 CORE_SEQUENCE_CLASS_ID,
261 CORE_CARD_CLASS_ID,
262 ];
263 let unique = ids
264 .iter()
265 .copied()
266 .collect::<std::collections::BTreeSet<_>>();
267 assert_eq!(ids.len(), unique.len());
268 }
269
270 #[test]
271 fn checked_accepts_a_plain_name() {
272 let symbol = Symbol::checked("car").expect("plain name is valid");
273 assert_eq!(symbol, Symbol::new("car"));
274 }
275
276 #[test]
277 fn checked_rejects_the_namespace_separator() {
278 assert_eq!(
279 Symbol::checked("a/b"),
280 Err(SymbolError::ContainsSeparator("a/b".to_owned()))
281 );
282 }
283
284 #[test]
285 fn checked_rejects_nul_and_control_characters() {
286 assert_eq!(
287 Symbol::checked("a\0b"),
288 Err(SymbolError::ContainsControl("a\0b".to_owned()))
289 );
290 assert_eq!(
291 Symbol::checked("a\tb"),
292 Err(SymbolError::ContainsControl("a\tb".to_owned()))
293 );
294 }
295}