sphinx/runtime/
module.rs

1///! Modules are the top level environment in Sphinx.
2///! All top-level names live in a module, there are no "universal" global variables.
3///!
4///! They are also the top-level unit of execution. 
5///! All Sphinx programs produce a module as a result of execution (even if it is discarded). 
6///! Importing a Sphinx module simply means executing a Sphinx sub-program and binding the
7///! resulting module to a name.
8
9use 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    // if the variable already exists, it is overwritten
55    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/// Mutable container for storing a Namespace in a Gc.
92#[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/// Used to uniquely identify a module
213#[derive(Debug, PartialEq, Eq, Hash)]
214pub enum ModuleIdent {
215    SourcePath(PathBuf), // canonicalized path to the source file
216    SourceHash(u64),  // hash of source text
217    RefHash(u64),  // Gc address of globals, for Native Modules
218}
219
220
221// All module hashes must come from the same builder for consistency
222static 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}