lysp: fix (explain ...) for macros

This commit is contained in:
2026-06-04 13:15:25 +03:00
parent f5d3809f37
commit 60f3572fec
4 changed files with 57 additions and 16 deletions
+7
View File
@@ -59,3 +59,10 @@
)
(print "... doesn't fail the execution")
(defmacro nested-0 (x) `(list 2 ,x))
(defmacro nested-1 (x) `(list 1 (nested-0 ,x)))
(assert (= '(1 (2 3)) (nested-1 3)))
;; (explain ...) must work on macros
(assert (explain and))
+8
View File
@@ -31,6 +31,7 @@
; TODO most of those could be ported to Rust for performance
; TODO tail recursion
(defun filter (f xs)
"Returns a new list, formed from elements of xs matching predicate f"
(cond
((nil? xs) nil)
((f (car xs)) (cons (car xs) (filter f (cdr xs))))
@@ -38,30 +39,35 @@
)
)
(defun map (f xs)
"Returns a new list, with f applied to elements of xs"
(if (cons? xs)
(cons (f (car xs)) (map f (cdr xs)))
nil
)
)
(defun flatmap (f xs)
"Returns a new list, with f applied to list elements of xs, then appended together"
(let (ys nil)
(while (cons? xs)
(setq ys (append ys (f (car xs))))
(setq xs (cdr xs)))
ys))
(defun fold (f acc xs)
"Returns the value calculated by sequentially applying f to acc and each element of xs"
(while (cons? xs)
(setq acc (f acc (car xs)))
(setq xs (cdr xs)))
acc)
(defun reverse (xs)
"Returns the reversed list"
(let (ys nil)
(while (cons? xs)
(setq ys (cons (car xs) ys))
(setq xs (cdr xs)))
ys))
(defun unzip (xs)
"Converts a list of lists into a single-level list"
(flatmap identity xs))
; Logic functions
@@ -78,6 +84,7 @@
)
))
(defmacro or (&rest forms)
"Sequentially evaluates the forms, stopping and returning a form if it's trueish"
;; (or x y z) -> (cond (x x) (y y) (z z) (&otherwise #t))
(or_ forms))
(defun and_ (forms)
@@ -87,6 +94,7 @@
(&otherwise `(if ,(car forms) ,(and_ (cdr forms))))
))
(defmacro and (&rest forms)
"Sequentially evaluates the forms, returning the last form if all the forms are trueish"
;; (and x y z) -> (if x (if y (if z z)))
(and_ forms)
)
+14 -14
View File
@@ -32,8 +32,6 @@ impl MacroExpand for Value {
| Self::Boolean(_)
| Self::String(_)
| Self::Keyword(_)
// | Self::OpaqueValue(_)
// | Self::BytecodeFunction(_)
| Self::Quote(_)
| Self::Closure(_)
| Self::Function(_)
@@ -43,7 +41,6 @@ impl MacroExpand for Value {
| Self::HashTable(_)
| Self::UnquoteSplice(_)
| Self::Unquote(_) => Ok(self.clone()),
// | Self::NativeFunction(_) => Ok(self.clone()),
Self::Cons(cons) => {
let ConsCell(car, cdr) = cons.as_ref();
let car = car.macro_expand(vm, env, false)?;
@@ -65,17 +62,20 @@ impl MacroExpand for Value {
return Ok(Self::Cons(cons));
};
match mac {
Macro::Native(native) => {
let result = native.invoke(vm, env, &args[..]).expect("TODO");
Ok(result)
}
Macro::Bytecode(bytecode) => {
let closure = ClosureValue { function: bytecode.clone(), upvalues: vec![] };
let result = vm.evaluate_closure_args(env, closure, &args[..])?;
Ok(result)
}
}
match mac {
Macro::Native(native) => {
let result = native.invoke(vm, env, &args[..]).expect("TODO");
result.macro_expand(vm, env, tail)
}
Macro::Bytecode(bytecode) => {
let closure = ClosureValue {
function: bytecode.clone(),
upvalues: vec![],
};
let result = vm.evaluate_closure_args(env, closure, &args[..])?;
result.macro_expand(vm, env, tail)
}
}
}
Self::Quasi(value) => {
let value = expand_quasiquote(value);
+28 -2
View File
@@ -7,7 +7,7 @@ use crate::{
error::MachineError,
vm::{
Value,
env::Environment,
env::{Environment, Macro},
value::{Keyword, StringValue, convert::TryFromValue},
},
};
@@ -55,9 +55,35 @@ pub fn load(env: &Rc<Environment>) {
env.defun_native("gensym", "Provides an unique symbol name", |_, env, _| {
Ok(env.gensym().into())
});
env.defun_native(
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);