1use core::fmt;
10use core::cell::{RefCell, Ref, RefMut};
11use core::hash::{Hash, Hasher, BuildHasher};
12use std::path::PathBuf;
13use once_cell::sync::Lazy;
14use crate::source::ModuleSource;
15use crate::language::{FloatType, Access};
16use crate::runtime::{Variant, HashMap, DefaultBuildHasher};
17use crate::runtime::gc::{Gc, GcTrace};
18use crate::runtime::strings::StringSymbol;
19use crate::runtime::errors::{ExecResult, RuntimeError};
20
21pub use crate::codegen::{ProgramData, Constant, Chunk, FunctionProto, ConstID, FunctionID};
22
23
24#[derive(Debug, Clone)]
25pub struct Variable {
26 access: Access,
27 value: Variant,
28}
29
30#[derive(Debug, Clone)]
31pub struct Namespace {
32 store: HashMap<StringSymbol, Variable>,
33}
34
35impl Default for Namespace {
36 fn default() -> Self { Self::new() }
37}
38
39impl Namespace {
40 pub fn new() -> Self {
41 Self {
42 store: HashMap::with_hasher(DefaultBuildHasher::default()),
43 }
44 }
45
46 pub fn names(&self) -> impl Iterator<Item=&StringSymbol> {
47 self.store.keys()
48 }
49
50 pub fn values(&self) -> impl Iterator<Item=&Variant> {
51 self.store.values().map(|var| &var.value)
52 }
53
54 pub fn create(&mut self, name: StringSymbol, access: Access, value: Variant) {
56 self.store.insert(name, Variable { access, value });
57 }
58
59 pub fn delete(&mut self, name: &StringSymbol) -> ExecResult<()> {
60 if self.store.remove(name).is_none() {
61 return Err(RuntimeError::name_not_defined(*name))
62 }
63 Ok(())
64 }
65
66 pub fn lookup<'a>(&'a self, name: &StringSymbol) -> ExecResult<&'a Variant> {
67 self.store.get(name)
68 .map(|var| &var.value)
69 .ok_or_else(|| RuntimeError::name_not_defined(*name))
70 }
71
72 pub fn lookup_mut<'a>(&'a mut self, name: &StringSymbol) -> ExecResult<&'a mut Variant> {
73 let variable = self.store.get_mut(name)
74 .ok_or_else(|| RuntimeError::name_not_defined(*name))?;
75
76 if variable.access != Access::ReadWrite {
77 return Err(RuntimeError::cant_assign_immutable(*name));
78 }
79
80 Ok(&mut variable.value)
81 }
82
83 pub fn extend(&mut self, other: &Namespace) {
84 for (name, variable) in other.store.iter() {
85 self.create(*name, variable.access, variable.value)
86 }
87 }
88}
89
90
91#[derive(Debug, Default, Clone)]
93pub struct NamespaceEnv {
94 namespace: RefCell<Namespace>,
95}
96
97impl From<Namespace> for NamespaceEnv {
98 fn from(namespace: Namespace) -> Self {
99 Self { namespace: RefCell::new(namespace) }
100 }
101}
102
103impl NamespaceEnv {
104 pub fn new() -> Gc<Self> {
105 Gc::new(Self::default())
106 }
107
108 pub fn take(self) -> Namespace {
109 self.namespace.take()
110 }
111
112 pub fn get_mut(&mut self) -> &mut Namespace {
113 self.namespace.get_mut()
114 }
115
116 pub fn borrow(&self) -> Ref<Namespace> {
117 self.namespace.borrow()
118 }
119
120 pub fn borrow_mut(&self) -> RefMut<Namespace> {
121 self.namespace.borrow_mut()
122 }
123}
124
125unsafe impl GcTrace for NamespaceEnv {
126 fn trace(&self) {
127 for value in self.namespace.borrow().values() {
128 value.trace();
129 }
130 }
131}
132
133
134#[derive(Debug)]
135pub struct Module {
136 ident: ModuleIdent,
137 display: String,
138 source: Option<ModuleSource>,
139 data: ProgramData,
140 globals: Gc<NamespaceEnv>,
141}
142
143unsafe impl GcTrace for Module {
144 fn trace(&self) {
145 self.globals.mark_trace()
146 }
147}
148
149impl Module {
150 pub fn allocate(source: Option<ModuleSource>, data: ProgramData) -> Gc<Self> {
151 let globals = NamespaceEnv::new();
152 Self::with_env(source, data, globals)
153 }
154
155 pub fn with_env(source: Option<ModuleSource>, data: ProgramData, globals: Gc<NamespaceEnv>) -> Gc<Self> {
156 let ident =
157 if let Some(source) = source.as_ref() { ModuleIdent::from(source) }
158 else { ModuleIdent::from(globals) };
159
160 let display = ident.to_string();
161
162 let module = Self {
163 ident,
164 display,
165 source,
166 data,
167 globals,
168 };
169
170 Gc::new(module)
171 }
172
173 pub fn ident(&self) -> &ModuleIdent { &self.ident }
174
175 pub fn source(&self) -> Option<&ModuleSource> { self.source.as_ref() }
176
177 #[inline(always)]
178 pub fn data(&self) -> &ProgramData { &self.data }
179
180 #[inline(always)]
181 pub fn globals(&self) -> Gc<NamespaceEnv> { self.globals }
182
183 #[inline]
184 pub fn get_const(&self, cid: ConstID) -> Variant {
185 match self.data.get_const(cid) {
186 Constant::Integer(value) => Variant::from(*value),
187
188 Constant::Float(bytes) => FloatType::from_le_bytes(*bytes).into(),
189
190 Constant::String(idx) => Variant::from(*self.data.get_string(*idx)),
191
192 Constant::Error { error, message: idx } => {
193 let message = *self.data.get_string(*idx);
194 Variant::Error(Gc::new(RuntimeError::new(*error, message.into())))
195 }
196 }
197 }
198
199 #[inline]
200 pub fn get_function(&self, fun_id: FunctionID) -> &FunctionProto {
201 self.data.get_function(fun_id)
202 }
203}
204
205impl fmt::Display for Module {
206 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
207 fmt.write_str(&self.display)
208 }
209}
210
211
212#[derive(Debug, PartialEq, Eq, Hash)]
214pub enum ModuleIdent {
215 SourcePath(PathBuf), SourceHash(u64), RefHash(u64), }
219
220
221static MODULE_IDENT_HASH: Lazy<DefaultBuildHasher> = Lazy::new(DefaultBuildHasher::default);
223
224impl From<&ModuleSource> for ModuleIdent {
225 fn from(source: &ModuleSource) -> Self {
226 match source {
227 ModuleSource::File(path) => {
228 Self::SourcePath(path.canonicalize().expect("invalid source path"))
229 },
230
231 ModuleSource::String(text) => {
232 let mut state = MODULE_IDENT_HASH.build_hasher();
233 text.hash(&mut state);
234 let hash = state.finish();
235 Self::SourceHash(hash)
236 }
237 }
238 }
239}
240
241impl From<Gc<NamespaceEnv>> for ModuleIdent {
242 fn from(env: Gc<NamespaceEnv>) -> Self {
243 let mut state = MODULE_IDENT_HASH.build_hasher();
244 <Gc<NamespaceEnv> as Hash>::hash(&env, &mut state);
245 let hash = state.finish();
246 Self::RefHash(hash)
247 }
248}
249
250impl fmt::Display for ModuleIdent {
251 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
252 match self {
253 Self::SourcePath(path) => {
254 let prefix = std::env::current_dir().ok()
255 .and_then(|pwd| pwd.canonicalize().ok());
256
257 let path = prefix.and_then(|prefix| path.strip_prefix(prefix).ok())
258 .unwrap_or(path);
259
260 write!(fmt, "\"{}\"", path.display())
261 },
262
263 Self::SourceHash(hash) => write!(fmt, "#{:016X}", hash),
264
265 Self::RefHash(hash) => write!(fmt, "&{:016X}", hash),
266 }
267 }
268}