Skip to content

Commit

Permalink
implement named let, set-cdr and add better examples
Browse files Browse the repository at this point in the history
  • Loading branch information
maplant committed Dec 5, 2024
1 parent da752f7 commit 374f080
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 15 deletions.
22 changes: 15 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,33 @@ That is obviously a long way away.

- Records, and therefore conditions and error handling
- Ports and IO operations
- Generally, most API functions are not currently implemented
- set-car! and set-cdr!
- Most API functions are not implemented
- A large portion of lexical structures are missing; there's no way to specify recursive data structures
- Let loop
- And many more that I cannot think of off the top of my head.
- And many more that I cannot think of off the top of my head

## Usage:

### Running a REPL:

A REPL is the default entry point for scheme-rs at the current moment. You can access it by running `cargo run`
in the repo's root directory:
in the repo's root directory (examples taken from wikipedia):

```
~/scheme-rs> cargo run
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.03s
Running `target/debug/scheme-rs`
>>> (+ 5 5)
$1 = 10
>>> (let loop ((n 1))
... (if (> n 10)
... '()
... (cons n
... (loop (+ n 1)))))
$1 = (1 2 3 4 5 6 7 8 9 10)
>>> (let* ((yin
... ((lambda (cc) (display "@") cc) (call-with-current-continuation (lambda (c) c))))
... (yang
... ((lambda (cc) (display "*") cc) (call-with-current-continuation (lambda (c) c)))))
... (yin yang))
@*@**@***@****@*****@******@*******@********@*********@**********@***********@************@*************@**************@***************@****************@*****************@******************@*******************@********************@*********************@**********************@***********************@************************@*************************@**************************@***************************@****************************@*****************************@******************************@***********...^C
```

### Creating Builtin Functions:
Expand Down
32 changes: 26 additions & 6 deletions src/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,16 +126,21 @@ impl Compile for ast::Let {
span: &Span,
) -> Result<Self, CompileLetError> {
match expr {
[Syntax::Null { .. }, body @ ..] => compile_let(&[], body, env, cont, span).await,
[Syntax::Null { .. }, body @ ..] => compile_let(None, &[], body, env, cont, span).await,
[Syntax::List { list: bindings, .. }, body @ ..] => {
compile_let(bindings, body, env, cont, span).await
compile_let(None, bindings, body, env, cont, span).await
}
// Named let:
[Syntax::Identifier { ident, .. }, Syntax::List { list: bindings, .. }, body @ ..] => {
compile_let(Some(ident), bindings, body, env, cont, span).await
}
_ => Err(CompileLetError::BadForm(span.clone())),
}
}
}

async fn compile_let(
name: Option<&Identifier>,
bindings: &[Syntax],
body: &[Syntax],
env: &Env,
Expand All @@ -161,11 +166,26 @@ async fn compile_let(
let body = ast::Body::compile(body, &Env::from(env.clone()), cont, span)
.await
.map_err(CompileLetError::CompileBodyError)?;

let mut bindings: Vec<_> = compiled_bindings
.into_iter()
.map(|binding| (binding.ident, binding.expr))
.collect();

// If this is a named let, add a binding for a procedure with the same
// body and args of the formals:
if let Some(name) = name {
let lambda = ast::Lambda {
args: ast::Formals::FixedArgs(
bindings.iter().map(|(ident, _)| ident.clone()).collect(),
),
body: body.clone(),
};
bindings.push((name.clone(), Arc::new(lambda)));
}

Ok(ast::Let {
bindings: compiled_bindings
.into_iter()
.map(|binding| (binding.ident, binding.expr))
.collect(),
bindings: Arc::from(bindings),
body,
})
}
Expand Down
14 changes: 14 additions & 0 deletions src/lists.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,20 @@ pub async fn set_car(
Ok(vec![Gc::new(Value::Null)])
}

#[builtin("set-cdr!")]
pub async fn set_cdr(
_cont: &Option<Arc<Continuation>>,
var: &Gc<Value>,
val: &Gc<Value>,
) -> Result<Vec<Gc<Value>>, RuntimeError> {
let mut var = var.write().await;
match &mut *var {
Value::Pair(_car, ref mut cdr) => *cdr = val.clone(),
_ => todo!(),
}
Ok(vec![Gc::new(Value::Null)])
}

#[builtin("length")]
pub async fn length(
_cont: &Option<Arc<Continuation>>,
Expand Down
2 changes: 1 addition & 1 deletion src/syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ pub enum Syntax {
span: Span,
},
/// A nested grouping of pairs. If the expression is a proper list, then the
/// last element of expression will be Nil. This vector is guaranteed to contain
/// last element of expression will be Null. This vector is guaranteed to contain
/// at least two elements.
List {
list: Vec<Syntax>,
Expand Down
2 changes: 1 addition & 1 deletion src/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ impl Value {
Self::Boolean(true) => "#t".to_string(),
Self::Boolean(false) => "#f".to_string(),
Self::Number(number) => number.to_string(),
Self::String(string) => format!("\"{string}\""),
Self::String(string) => string.to_string(),
Self::Symbol(symbol) => symbol.clone(),
Self::Pair(car, cdr) => crate::lists::fmt_list(car, cdr).await,
Self::Vector(vec) => {
Expand Down

0 comments on commit 374f080

Please sign in to comment.