Skip to content

Commit

Permalink
Add the intern macro and a add_special_form/add_macro method (#71)
Browse files Browse the repository at this point in the history
  • Loading branch information
shsms authored Feb 15, 2024
2 parents 2738729 + b3d32bd commit d1868d1
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 2 deletions.
26 changes: 24 additions & 2 deletions src/context.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use std::{collections::HashMap, fs};
use std::{collections::HashMap, fs, rc::Rc};

use crate::{
builtin,
error::Error,
eval::{eval, eval_and_then, eval_basic, funcall, DummyEval},
list,
parse::parse,
TulispObject,
TulispObject, TulispValue,
};

#[derive(Debug, Default, Clone)]
Expand Down Expand Up @@ -80,6 +80,28 @@ impl TulispContext {
self.obarray.get(name).map(|x| x.clone_without_span())
}

#[inline(always)]
pub fn add_special_form(
&mut self,
name: &str,
func: impl Fn(&mut TulispContext, &TulispObject) -> Result<TulispObject, Error> + 'static,
) {
self.intern(name)
.set_scope(TulispValue::Func(Rc::new(func)).into_ref(None))
.unwrap();
}

#[inline(always)]
pub fn add_macro(
&mut self,
name: &str,
func: impl Fn(&mut TulispContext, &TulispObject) -> Result<TulispObject, Error> + 'static,
) {
self.intern(name)
.set_scope(TulispValue::Macro(Rc::new(func)).into_ref(None))
.unwrap();
}

/// Evaluates the given value and returns the result.
pub fn eval(&mut self, value: &TulispObject) -> Result<TulispObject, Error> {
eval(self, value)
Expand Down
75 changes: 75 additions & 0 deletions src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,3 +164,78 @@ macro_rules! destruct_bind {
destruct_bind!(@impl ($($rest)*) = $vv);
};
}

/**
Creates a struct that holds interned symbols.
## Example
```rust
use tulisp::{TulispContext, intern};
intern!{
#[derive(Clone)]
pub(crate) struct Keywords {
name: ":name",
scale: ":scale",
pos: ":pos",
}
}
fn main() {
let ctx = &mut TulispContext::new();
let kw = Keywords::new(ctx);
assert!(kw.name.eq(&ctx.intern(":name")));
assert!(kw.scale.eq(&ctx.intern(":scale")));
assert!(kw.pos.eq(&ctx.intern(":pos")));
}
```
It can also be used to create an instance of the struct directly:
```rust
use tulisp::{TulispContext, intern};
fn main() {
let ctx = &mut TulispContext::new();
let kw = intern!(ctx => {
name: ":name",
scale: ":scale",
pos: ":pos",
});
assert!(kw.name.eq(&ctx.intern(":name")));
assert!(kw.scale.eq(&ctx.intern(":scale")));
assert!(kw.pos.eq(&ctx.intern(":pos")));
}
```
*/
#[macro_export]
macro_rules! intern {
($( #[$meta:meta] )*
$vis:vis struct $struct_name:ident {
$($name:ident : $symbol:literal),+ $(,)?
}) => {
$( #[$meta] )*
$vis struct $struct_name {
$(pub $name: $crate::TulispObject),+
}

impl $struct_name {
fn new(ctx: &mut $crate::TulispContext) -> Self {
let ret = $struct_name {
$($name: ctx.intern($symbol),)+
};
ret
}
}
};

($ctx: ident => {$($name:ident : $symbol:literal),+ $(,)?}) => {{
intern!(pub(crate) struct Keywords {$($name : $symbol),+});
Keywords::new($ctx)
}};
}

0 comments on commit d1868d1

Please sign in to comment.