Fix temporaries breaking (let ...) blocks, (gensym)
This commit is contained in:
@@ -1 +0,0 @@
|
||||
(print (explain explain))
|
||||
+16
-6
@@ -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")
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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>,
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -52,6 +52,7 @@ impl_keyword! {
|
||||
Break => "break",
|
||||
Continue => "continue",
|
||||
Declare => "declare",
|
||||
Error => "&error",
|
||||
// Cons => "cons",
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user