2022-10-03 10:26:32 +02:00
|
|
|
use std::{
|
2022-10-04 22:58:21 +02:00
|
|
|
cell::RefCell,
|
2022-10-04 17:27:49 +02:00
|
|
|
collections::{BTreeMap, HashMap},
|
|
|
|
rc::Rc,
|
2022-10-03 10:26:32 +02:00
|
|
|
time::{SystemTime, UNIX_EPOCH},
|
|
|
|
};
|
|
|
|
|
|
|
|
use crate::{
|
2022-10-04 17:27:49 +02:00
|
|
|
errors::ErrorKind,
|
|
|
|
observer::NoOpObserver,
|
|
|
|
value::{Builtin, NixString, Thunk},
|
|
|
|
vm::VM,
|
|
|
|
SourceCode, Value,
|
2022-10-03 10:26:32 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
fn impure_builtins() -> Vec<Builtin> {
|
|
|
|
vec![]
|
|
|
|
}
|
2022-09-18 22:34:41 +02:00
|
|
|
|
2022-10-04 17:27:49 +02:00
|
|
|
/// Return all impure builtins, that is all builtins which may perform I/O
|
|
|
|
/// outside of the VM and so cannot be used in all contexts (e.g. WASM).
|
2022-10-03 10:26:32 +02:00
|
|
|
pub(super) fn builtins() -> BTreeMap<NixString, Value> {
|
|
|
|
let mut map: BTreeMap<NixString, Value> = impure_builtins()
|
|
|
|
.into_iter()
|
|
|
|
.map(|b| (b.name().into(), Value::Builtin(b)))
|
|
|
|
.collect();
|
|
|
|
|
2022-10-02 19:15:51 +02:00
|
|
|
// currentTime pins the time at which evaluation was started
|
|
|
|
{
|
|
|
|
let seconds = match SystemTime::now().duration_since(UNIX_EPOCH) {
|
|
|
|
Ok(dur) => dur.as_secs() as i64,
|
|
|
|
|
|
|
|
// This case is hit if the system time is *before* epoch.
|
|
|
|
Err(err) => -(err.duration().as_secs() as i64),
|
|
|
|
};
|
|
|
|
|
|
|
|
map.insert(NixString::from("currentTime"), Value::Integer(seconds));
|
|
|
|
}
|
|
|
|
|
2022-10-03 10:26:32 +02:00
|
|
|
map
|
2022-09-18 22:34:41 +02:00
|
|
|
}
|
2022-10-04 17:27:49 +02:00
|
|
|
|
|
|
|
/// Constructs and inserts the `import` builtin. This builtin is special in that
|
|
|
|
/// it needs to capture the [crate::SourceCode] structure to correctly track
|
|
|
|
/// source code locations while invoking a compiler.
|
|
|
|
// TODO: need to be able to pass through a CompilationObserver, too.
|
2022-10-04 22:58:21 +02:00
|
|
|
pub fn builtins_import(
|
|
|
|
globals: Rc<RefCell<HashMap<&'static str, Value>>>,
|
|
|
|
source: SourceCode,
|
|
|
|
) -> Builtin {
|
2022-10-04 17:27:49 +02:00
|
|
|
Builtin::new(
|
|
|
|
"import",
|
|
|
|
&[true],
|
2022-10-05 16:01:07 +02:00
|
|
|
move |mut args: Vec<Value>, vm: &mut VM| {
|
2022-10-04 22:21:49 +02:00
|
|
|
let path = super::coerce_value_to_path(&args.pop().unwrap(), vm)?;
|
2022-10-04 17:27:49 +02:00
|
|
|
|
|
|
|
let contents =
|
|
|
|
std::fs::read_to_string(&path).map_err(|err| ErrorKind::ReadFileError {
|
|
|
|
path: path.clone(),
|
|
|
|
error: Rc::new(err),
|
|
|
|
})?;
|
|
|
|
|
|
|
|
let parsed = rnix::ast::Root::parse(&contents);
|
|
|
|
let errors = parsed.errors();
|
|
|
|
|
|
|
|
if !errors.is_empty() {
|
|
|
|
return Err(ErrorKind::ImportParseError {
|
|
|
|
path,
|
|
|
|
errors: errors.to_vec(),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
let file = source.add_file(path.to_string_lossy().to_string(), contents);
|
|
|
|
|
|
|
|
let result = crate::compile(
|
|
|
|
&parsed.tree().expr().unwrap(),
|
|
|
|
Some(path.clone()),
|
|
|
|
file,
|
2022-10-04 22:58:21 +02:00
|
|
|
globals.clone(),
|
2022-10-04 17:27:49 +02:00
|
|
|
&mut NoOpObserver::default(),
|
|
|
|
)
|
|
|
|
.map_err(|err| ErrorKind::ImportCompilerError {
|
|
|
|
path: path.clone(),
|
|
|
|
errors: vec![err],
|
|
|
|
})?;
|
|
|
|
|
|
|
|
if !result.errors.is_empty() {
|
|
|
|
return Err(ErrorKind::ImportCompilerError {
|
|
|
|
path,
|
|
|
|
errors: result.errors,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-10-05 16:01:07 +02:00
|
|
|
for warning in result.warnings {
|
|
|
|
vm.push_warning(warning);
|
|
|
|
}
|
2022-10-04 17:27:49 +02:00
|
|
|
|
|
|
|
// Compilation succeeded, we can construct a thunk from whatever it spat
|
|
|
|
// out and return that.
|
|
|
|
Ok(Value::Thunk(Thunk::new(result.lambda)))
|
|
|
|
},
|
|
|
|
)
|
|
|
|
}
|