Files
yggdrasil/userspace/lib/lysp/src/vm/prelude/debug.rs
T

176 lines
6.1 KiB
Rust

use std::{
path::{Path, PathBuf},
rc::Rc,
};
use crate::{
error::MachineError,
vm::{
Value,
env::{Environment, Macro},
value::{Keyword, StringValue, convert::TryFromValue},
},
};
pub fn load(env: &Rc<Environment>) {
env.defun_native(
"import",
"Evaluates the given script file",
|vm, env, args| {
if args.is_empty() {
return Err(MachineError::InvalidArgumentCount);
}
let caller = vm
.read_call_stack(0)
.ok_or(MachineError::CallStackUnderflow)?;
let caller_directory = caller
.closure
.function
.script
.as_ref()
.and_then(|path| path.parent())
.map(Path::to_owned)
.unwrap_or_else(|| PathBuf::from("."));
for arg in args {
let path_str = StringValue::try_from_value(arg)?;
let mut path = PathBuf::from(&*path_str);
if path.is_relative() {
let in_caller_directory = caller_directory.join(&path);
if in_caller_directory.exists() {
path = in_caller_directory;
}
}
vm.load_file(Default::default(), env, &path)
.map_err(|error| MachineError::LoadError(path_str, error.into()))?;
}
Ok(Value::Nil)
},
);
env.defun_native("gensym", "Provides an unique symbol name", |_, env, _| {
Ok(env.gensym().into())
});
env.defmacro_native(
"explain",
"Provides an explanation for a given value",
|_, env, args| {
let [argument] = args else {
return Err(MachineError::InvalidArgumentCount);
};
if let Value::Identifier(identifier) = argument
&& let Some(mac) = env.global_macro(identifier)
{
return match mac {
Macro::Native(mac) => Ok(Value::String(
format!("built-in macro {}: {}", mac.name(), mac.docstring()).into(),
)),
Macro::Bytecode(mac) => Ok(Value::String(
format!("macro {}: {}", mac.name(), mac.docstring()).into(),
)),
};
}
Ok(Value::list_or_nil([
Value::Identifier("explain_".into()),
argument.clone(),
]))
},
);
env.defun_native(
"explain_",
"Provides an explanation for a given value",
|_, _, args| {
let [argument] = args else {
return Err(MachineError::InvalidArgumentCount);
};
let explanation = match argument {
Value::Nil => "an empty list".into(),
Value::Cons(_) => "a cons-cell".into(),
Value::Number(_) => "a number".into(),
Value::Boolean(_) => "a boolean value".into(),
Value::Quasi(_) => "a quasi-quoted value".into(),
Value::Quote(_) => "a quoted value".into(),
Value::Unquote(_) => "an unquoted value".into(),
Value::UnquoteSplice(_) => "an unquote-spliced value".into(),
Value::Identifier(identifier) => format!("an identifier {:?}", identifier.as_ref()),
Value::Vector(_) => "a vector".into(),
Value::HashTable(_) => "a hash table".into(),
Value::String(_) => "a string".into(),
Value::Keyword(_) => "a keyword".into(),
Value::Closure(closure) => {
format!(
"function {}: {}",
closure.function.name(),
closure.function.docstring()
)
}
Value::Function(function) => {
format!("function {}: {}", function.name(), function.docstring())
}
Value::NativeFunction(function) => {
format!(
"built-in function {}: {}",
function.name(),
function.docstring()
)
}
Value::NativeValue(_) => "a native value".into(),
};
Ok(Value::String(explanation.into()))
},
);
env.defun_native(
"type",
"Returns the data type of the argument",
|_, _, args| {
let [argument] = args else {
return Err(MachineError::InvalidArgumentCount);
};
Ok(argument.type_id())
},
);
env.defmacro_native(
"assert",
"Checks that the condition is true, otherwise aborts the execution",
|vm, _, args| match args {
[] => Err(MachineError::InvalidArgumentCount),
[cond] => {
let assertion_failure_msg = match vm.current_location() {
Some(ip) => format!("{ip}: assertion failed: {cond}"),
None => format!("<undefined>: assertion failed: {cond}"),
};
let assertion_failure = Value::list_or_nil([
Value::Identifier("abort".into()),
Value::String(assertion_failure_msg.into()),
]);
Ok(Value::list_or_nil([
Value::Keyword(Keyword::If),
cond.clone(),
Value::Nil,
assertion_failure,
]))
}
_ => Err(MachineError::InvalidArgumentCount),
},
);
env.defun_native(
"abort",
"Aborts the execution with the given reason message",
|_, _, args| {
let mut message = String::new();
for (i, arg) in args.iter().enumerate() {
if i != 0 {
message.push(' ');
}
message.push_str(&format!("{arg}"));
}
Err(MachineError::Abort(message.into()))
},
);
}