Fix temporaries breaking (let ...) blocks, (gensym)

This commit is contained in:
2026-05-22 13:57:35 +03:00
parent 6936966455
commit 3cefd9ac4e
10 changed files with 143 additions and 15 deletions
-1
View File
@@ -1 +0,0 @@
(print (explain explain))
+16 -6
View File
@@ -19,14 +19,16 @@
(assert (= '(2 3 4 2 3 4) `(,@glob1 ,@glob1)))
(assert (= '((((2 3 4)))) `(((,glob1)))))
(defmacro debug (expression)
(print "DEBUG:" expression)
expression
)
; those are prelude, but defined in lysp itself:
(debug
(print "The previously printed expression is AFTER this one in the code")
(compile-debug
(when #t
(print "a")
(print "b")
)
)
(runtime-debug
(when #t
(print "a")
(print "b")
@@ -43,3 +45,11 @@
)
; catch macro
(print "Failing in a catch block...")
(catch
(print a)
e (print "Caught an error:" e)
)
(print "... doesn't fail the execution")
+23
View File
@@ -267,6 +267,29 @@ impl CompileContext {
}
}
pub fn guard_argument(&mut self) {
let depth = self.scope_depth as isize;
self.locals.push(Local {
name: "".into(),
depth,
is_captured: false,
});
}
pub fn unguard_argument(&mut self) {
let guard = self
.locals
.pop()
.expect("unguard_argument() called with an empty locals stack");
let stack_index = self.locals.len();
if guard.name.as_ref() != "" {
panic!(
"unguard_argument(): the local is not a guard: {:?} at stack index {}",
guard.name, stack_index,
);
}
}
pub fn push_scope(&mut self) {
self.scope_depth += 1;
if self.options.trace_compile {
+9
View File
@@ -53,13 +53,22 @@ impl Compile for CallExpression {
if instruction == Instruction::Call {
let callee = self.callee.compile(cx)?;
cx.push(callee)?;
cx.guard_argument();
}
for expression in self.arguments.iter() {
let value = expression.compile(cx)?;
cx.push(value)?;
cx.guard_argument();
}
cx.emit(instruction);
cx.emit(argument_count);
let mut unguard_count = usize::from(argument_count);
if instruction == Instruction::Call {
unguard_count += 1;
}
for _ in 0..unguard_count {
cx.unguard_argument();
}
Ok(CompileValue::Stack)
}
}
+1 -1
View File
@@ -121,7 +121,7 @@ impl Expression {
_ => Self::map_or(CallExpression::parse(cons, value), Expression::Call),
}
}
Value::Keyword(_) => todo!("Error here"),
Value::Keyword(keyword) => todo!("Error here: keyword {keyword}"),
Value::Closure(_) => todo!("Error here"),
Value::Function(_) => todo!("Error here"),
Value::NativeFunction(_) => todo!("Error here"),
+66 -4
View File
@@ -1,8 +1,70 @@
(defmacro when (condition &rest body)
`(if ,condition (progn ,@body))
; Convenience flow control macros
(defmacro when (condition body-head &rest body)
"If condition is true, evaluates the expressions in body, otherwise returns nil"
`(if ,condition (progn ,body-head ,@body))
)
(defmacro unless (condition &rest body)
`(if (not ,condition) (progn ,@body))
(defmacro unless (condition body-head &rest body)
"If condition is false, evaluates the expressions in body, otherwise returns nil"
`(if (not ,condition) (progn ,body-head ,@body))
)
; Result handling functions
(defun result/ok? (x)
"Returns #t if x is an ok"
(= 'ok (car x))
)
(defun result/err? (x)
"Returns #t if x is an error"
(= 'err (car x))
)
(defun result/map (f x)
"If x is an ok, applies f to its value, otherwise does nothing"
(if (result/ok? x)
`(ok ,(f (cadr x)))
x
)
)
(defun result/map-err (f x)
"If x is an error, applies f to its value, otherwise does nothing"
(if (result/err? x)
`(err ,(f (cadr x)))
x
)
)
; Convenience error handling macros
(defmacro catch (expression error-symbol handler)
"Evaluates expression, running the handler if evaluation fails, introducing error-symbol as error"
(let (eval-result-symbol (gensym))
`(let (,eval-result-symbol (eval (quote ,expression)))
(cond
((result/ok? ,eval-result-symbol) (cadr ,eval-result-symbol))
(&otherwise
(let (,error-symbol (cadr ,eval-result-symbol))
,handler
)
)
)
)
)
)
(defmacro compile-debug (expression)
"Prints the input expression during macro expansion/compile time and evaluates the expression in runtime"
(print expression)
expression
)
(defmacro runtime-debug (expression)
"Prints the input expression and evaluates it in runtime"
`(progn
(print (quote ,expression))
,expression
)
)
; Convenience list functions
(defun cadr (x) "Alias for (car (cdr x))" (car (cdr x)))
(defun cdar (x) "Alias for (cdr (car x))" (cdr (car x)))
(defun caddr (x) "Alias for (car (cdr (cdr x)))" (car (cdr (cdr x))))
(defun cadar (x) "Alias for (car (cdr (car x)))" (car (cdr (car x))))
+16 -1
View File
@@ -1,4 +1,12 @@
use std::{borrow::Borrow, cell::RefCell, collections::HashMap, fmt, hash::Hash, rc::Rc};
use std::{
borrow::Borrow,
cell::RefCell,
collections::HashMap,
fmt,
hash::Hash,
rc::Rc,
sync::atomic::{AtomicU32, Ordering},
};
use crate::{
error::MachineError,
@@ -22,6 +30,7 @@ pub struct Environment {
globals: RefCell<HashMap<IdentifierValue, Value>>,
macros: RefCell<HashMap<IdentifierValue, Macro>>,
parent: Option<Rc<Environment>>,
gensym_index: AtomicU32,
}
impl Environment {
@@ -32,6 +41,12 @@ impl Environment {
}
}
pub fn gensym(&self) -> IdentifierValue {
// TODO do this in a root environment?
let index = self.gensym_index.fetch_add(1, Ordering::SeqCst);
format!("___gensym{index}").into()
}
pub fn defun_native<S, D, F>(&self, identifier: S, docstring: D, function: F) -> Value
where
S: Into<IdentifierValue>,
+3
View File
@@ -6,6 +6,9 @@ use crate::{
};
pub fn load(env: &Rc<Environment>) {
env.defun_native("gensym", "Provides an unique symbol name", |_, env, _| {
Ok(env.gensym().into())
});
env.defun_native(
"explain",
"Provides an explanation for a given value",
+8 -2
View File
@@ -7,8 +7,8 @@ use crate::{
env::Environment,
machine::Machine,
value::{
BooleanValue, BytecodeFunction, ClosureValue, ConsCell, NativeFunction, NativeValue,
NumberValue, StringValue, Vector,
BooleanValue, BytecodeFunction, ClosureValue, ConsCell, IdentifierValue,
NativeFunction, NativeValue, NumberValue, StringValue, Vector,
},
},
};
@@ -126,6 +126,12 @@ impl From<Vec<u8>> for Value {
}
}
impl From<IdentifierValue> for Value {
fn from(value: IdentifierValue) -> Self {
Value::Identifier(value)
}
}
impl TryFromValue<'_> for AnyFunction {
fn try_from_value(value: &'_ Value) -> Result<Self, ValueConversionError> {
match value {
+1
View File
@@ -52,6 +52,7 @@ impl_keyword! {
Break => "break",
Continue => "continue",
Declare => "declare",
Error => "&error",
// Cons => "cons",
}
}