rune/core/env/
symbol_map.rs1use crate::core::{
2 gc::{Block, Context},
3 object::{CloneIn, Function, LispBuffer, Symbol, WithLifetime},
4};
5use anyhow::Result;
6use rune_core::hashmap::HashMap;
7
8pub(crate) struct SymbolMap {
9 map: SymbolMapCore,
10 block: Block<true>,
11}
12
13struct SymbolMapCore {
14 map: HashMap<&'static str, Symbol<'static>>,
15}
16
17impl SymbolMapCore {
18 fn with_capacity(cap: usize) -> Self {
19 Self {
20 map: HashMap::with_capacity_and_hasher(cap, std::hash::BuildHasherDefault::default()),
21 }
22 }
23
24 fn get(&self, name: &str) -> Option<Symbol> {
25 self.map.get(name).map(|x| unsafe { x.with_lifetime() })
26 }
27
28 fn intern<'ob>(&mut self, name: &str, block: &Block<true>, cx: &'ob Context) -> Symbol<'ob> {
29 match self.get(name) {
30 Some(x) => cx.bind(x),
31 None => {
32 let name = name.to_owned();
33 let static_name: &'static str = unsafe {
35 let name_ptr: *const str = Box::into_raw(name.into_boxed_str());
36 &*name_ptr
37 };
38 let sym = Symbol::new(static_name, block);
39 self.map.insert(static_name, unsafe { sym.with_lifetime() });
40 cx.bind(sym)
41 }
42 }
43 }
44
45 fn pre_init(&mut self, sym: Symbol<'static>) {
46 use std::collections::hash_map::Entry;
47 let name = sym.get().name();
48 let entry = self.map.entry(name);
49 assert!(matches!(entry, Entry::Vacant(_)), "Attempt to intitalize {name} twice");
50 entry.or_insert_with(|| sym);
51 }
52}
53
54impl SymbolMap {
55 pub(crate) fn intern<'ob>(&mut self, name: &str, cx: &'ob Context) -> Symbol<'ob> {
56 self.map.intern(name, &self.block, cx)
57 }
58
59 pub(crate) fn set_func(&self, symbol: Symbol, func: Function) -> Result<()> {
60 let new_func = func.clone_in(&self.block);
61 self.block.uninterned_symbol_map.clear();
62 unsafe { symbol.set_func(new_func) }
65 }
66
67 pub(crate) fn global_block(&self) -> &Block<true> {
68 &self.block
69 }
70
71 pub(crate) fn create_buffer(&self, name: &str) -> &LispBuffer {
72 LispBuffer::create(name.to_owned(), &self.block)
73 }
74
75 pub(crate) fn get(&self, name: &str) -> Option<Symbol> {
76 self.map.get(name)
77 }
78}
79
80include!(concat!(env!("OUT_DIR"), "/sym.rs"));
82
83pub(crate) fn intern<'ob>(name: &str, cx: &'ob Context) -> Symbol<'ob> {
85 INTERNED_SYMBOLS.lock().unwrap().intern(name, cx)
86}
87
88#[cfg(test)]
89mod test {
90 use super::*;
91 use crate::core::gc::RootSet;
92 use crate::core::object::FunctionType;
93 use crate::core::{cons::Cons, env::Env, object::Object};
94 use rune_core::macros::{list, root};
95
96 #[test]
97 fn size() {
98 assert_eq!(size_of::<isize>(), size_of::<Symbol>());
99 assert_eq!(size_of::<isize>(), size_of::<Function>());
100 }
101
102 #[test]
103 fn init() {
104 let roots = &RootSet::default();
105 let cx = &Context::new(roots);
106 intern("foo", cx);
107 }
108
109 #[test]
110 fn symbol_func() {
111 let roots = &RootSet::default();
112 let cx = &Context::new(roots);
113 sym::init_symbols();
114 let sym = Symbol::new_uninterned("foo", cx);
115 assert_eq!("foo", sym.name());
116 assert!(sym.func(cx).is_none());
117 let func1 = Cons::new1(1, cx);
118 unsafe {
119 sym.set_func(func1.into()).unwrap();
120 }
121 let cell1 = sym.func(cx).unwrap();
122 let FunctionType::Cons(before) = cell1.untag() else {
123 unreachable!("Type should be a lisp function")
124 };
125 assert_eq!(before.car(), 1);
126 let func2 = Cons::new1(2, cx);
127 unsafe {
128 sym.set_func(func2.into()).unwrap();
129 }
130 let cell2 = sym.func(cx).unwrap();
131 let FunctionType::Cons(after) = cell2.untag() else {
132 unreachable!("Type should be a lisp function")
133 };
134 assert_eq!(after.car(), 2);
135 assert_eq!(before.car(), 1);
136
137 unsafe {
138 sym.set_func(sym::NIL.into()).unwrap();
139 }
140 assert!(!sym.has_func());
141 }
142
143 #[test]
144 fn test_mutability() {
145 let roots = &RootSet::default();
146 let cx = &Context::new(roots);
147 let cons = list!(1, 2, 3; cx);
148 assert_eq!(cons, list!(1, 2, 3; cx));
149 if let crate::core::object::ObjectType::Cons(cons) = cons.untag() {
151 cons.set_car(4.into()).unwrap();
152 } else {
153 unreachable!();
154 }
155 assert_eq!(cons, list!(4, 2, 3; cx));
156 let sym = intern("cons-test", cx);
157 crate::data::fset(sym, cons).unwrap();
158 if let FunctionType::Cons(cons) = sym.func(cx).unwrap().untag() {
160 assert!(cons.set_car(5.into()).is_err());
161 let obj: Object = cons.into();
162 assert_eq!(obj, list!(4, 2, 3; cx));
163 } else {
164 unreachable!();
165 }
166 }
167
168 #[test]
169 fn test_init_variables() {
170 let roots = &RootSet::default();
171 let cx = &Context::new(roots);
172 root!(env, new(Env), cx);
173 init_variables(cx, env);
174 }
175}