diff --git a/proc-macros/src/lib.rs b/proc-macros/src/lib.rs index d419b09..1e2a6f0 100644 --- a/proc-macros/src/lib.rs +++ b/proc-macros/src/lib.rs @@ -28,9 +28,9 @@ pub fn builtin(name: TokenStream, item: TokenStream) -> TokenStream { let arg_indices: Vec<_> = (0..num_args).into_iter().collect(); parse_quote! { fn #wrapper_name( - cont: Option>, - args: Vec> - ) -> futures::future::BoxFuture<'static, Result>, crate::error::RuntimeError>> { + cont: Option>, + args: Vec<::scheme_rs::gc::Gc<::scheme_rs::value::Value>> + ) -> futures::future::BoxFuture<'static, Result>, ::scheme_rs::error::RuntimeError>> { Box::pin( async move { #impl_name( @@ -45,9 +45,9 @@ pub fn builtin(name: TokenStream, item: TokenStream) -> TokenStream { let arg_indices: Vec<_> = (0..num_args).into_iter().collect(); parse_quote! { fn #wrapper_name( - cont: Option>, - mut required_args: Vec> - ) -> futures::future::BoxFuture<'static, Result>, crate::error::RuntimeError>> { + cont: Option>, + mut required_args: Vec<::scheme_rs::gc::Gc<::scheme_rs::value::Value>> + ) -> futures::future::BoxFuture<'static, Result>, ::scheme_rs::error::RuntimeError>> { let var_args = required_args.split_off(#num_args); Box::pin( async move { @@ -67,7 +67,7 @@ pub fn builtin(name: TokenStream, item: TokenStream) -> TokenStream { #wrapper inventory::submit! { - crate::builtin::Builtin::new(#name, #num_args, #is_variadic, #wrapper_name) + ::scheme_rs::builtin::Builtin::new(#name, #num_args, #is_variadic, #wrapper_name) } } .into() diff --git a/src/env.rs b/src/env.rs index c666ba4..67517d8 100644 --- a/src/env.rs +++ b/src/env.rs @@ -24,30 +24,6 @@ pub struct LexicalContour { } impl LexicalContour { - /* - fn strip<'a>(&self, ident: &'a Identifier) -> Cow<'a, Identifier> { - if ident.marks.contains(&self.mark) { - let mut stripped = ident.clone(); - stripped.mark(self.mark); - Cow::Owned(stripped) - } else { - Cow::Borrowed(ident) - } - } - - pub fn strip_unused_marks<'a>(&'a self, ident: &'a mut Identifier) -> BoxFuture<'a, ()> { - Box::pin(async move { - if ident.marks.contains(&self.mark) && self.vars.contains_key(ident) - || self.macros.contains_key(ident) - { - return; - } - ident.marks.remove(&self.mark); - self.up.strip_unused_marks(ident).await; - }) - } - */ - pub fn is_bound<'a>(&'a self, ident: &'a Identifier) -> BoxFuture<'a, bool> { Box::pin(async move { self.vars.contains_key(ident) @@ -153,18 +129,6 @@ impl ExpansionContext { } else { self.up.fetch_macro(ident).await } - /* - let ident = if ident.marks.contains(&self.mark) { - let stripped = self.strip(ident); - if let result @ Some(_) = self.macro_env.fetch_macro(&stripped).await { - return result; - } - stripped - } else { - Cow::Borrowed(ident) - }; - self.up.fetch_macro(&ident).await - */ }) } } @@ -187,16 +151,6 @@ pub enum Env { } impl Env { - /* - pub async fn strip_unused_marks(&self, ident: &mut Identifier) { - match self { - Self::Expansion(expansion) => expansion.read().await.strip_unused_marks(ident).await, - Self::LexicalContour(contour) => contour.read().await.strip_unused_marks(ident).await, - _ => (), - } - } - */ - pub async fn is_bound(&self, ident: &Identifier) -> bool { match self { Self::Top => false, diff --git a/src/error.rs b/src/error.rs index 6005b51..9ccb5bd 100644 --- a/src/error.rs +++ b/src/error.rs @@ -30,6 +30,10 @@ pub enum RuntimeErrorKind { expected: usize, provided: usize, }, + AssertEqFailed { + expected: String, + actual: String, + }, DivisionByZero, CompileError(CompileError), AbandonCurrentContinuation { @@ -88,6 +92,15 @@ impl RuntimeError { } } + pub fn assert_eq_failed(expected: &str, actual: &str) -> Self { + let expected = expected.to_string(); + let actual = actual.to_string(); + Self { + backtrace: Vec::new(), + kind: RuntimeErrorKind::AssertEqFailed { expected, actual }, + } + } + pub fn undefined_variable(ident: Identifier) -> Self { Self { backtrace: Vec::new(), diff --git a/src/lib.rs b/src/lib.rs index 3afe7ae..34d4a10 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ +extern crate self as scheme_rs; + pub mod ast; pub mod builtin; pub mod compile; @@ -16,3 +18,5 @@ pub mod proc; pub mod syntax; pub mod util; pub mod value; + +pub use proc_macros::*; diff --git a/src/stdlib.scm b/src/stdlib.scm index 836434c..4288830 100644 --- a/src/stdlib.scm +++ b/src/stdlib.scm @@ -18,6 +18,11 @@ (memp (lambda (x) (eq? x obj)) list)) (define call-with-current-continuation call/cc) +;; +;; WIP: All of the car/cdr combinations +(define caar (lambda (x) (car (car x)))) +(define cadr (lambda (x) (car (cdr x)))) + ;; ;; Complex definitions: ;; diff --git a/src/syntax.rs b/src/syntax.rs index 8ad3a6c..63a0b41 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -66,7 +66,6 @@ pub enum Syntax { span: Span, }, Identifier { - // #[debug(flatten = ".name")] ident: Identifier, #[debug(skip)] bound: bool, @@ -75,29 +74,6 @@ pub enum Syntax { }, } -impl fmt::Display for Syntax { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Null { .. } => write!(f, "()")?, - Self::List { list, .. } => { - write!(f, "(")?; - for item in list { - write!(f, "{} ", item)?; - } - write!(f, ")")?; - } - Self::Literal { literal, .. } => { - write!(f, "{:?}", literal)?; - } - Self::Identifier { ident, .. } => { - write!(f, "{}", ident.name)?; - } - _ => (), - } - Ok(()) - } -} - impl Syntax { pub fn mark(&mut self, mark: Mark) { match self { diff --git a/tests/r6rs.rs b/tests/r6rs.rs new file mode 100644 index 0000000..a1505ed --- /dev/null +++ b/tests/r6rs.rs @@ -0,0 +1,45 @@ +use std::sync::Arc; + +use scheme_rs::{ + builtin, + continuation::Continuation, + env::Env, + error::RuntimeError, + gc::Gc, + lex::Token, + syntax::ParsedSyntax, + value::{eqv, Value}, +}; + +#[builtin("assert-eq")] +pub async fn test_assert( + _cont: &Option>, + arg1: &Gc, + arg2: &Gc, +) -> Result>, RuntimeError> { + // + if !eqv(arg1, arg2).await { + let arg1 = arg1.read().await.fmt().await; + let arg2 = arg2.read().await.fmt().await; + Err(RuntimeError::assert_eq_failed(&arg1, &arg2)) + } else { + Ok(vec![]) + } +} + +#[tokio::test] +async fn r6rs() { + let top = Env::top().await; + + let r6rs_tok = Token::tokenize_file(include_str!("r6rs.scm"), "r6rs.scm").unwrap(); + let r6rs_sexprs = ParsedSyntax::parse(&r6rs_tok).unwrap(); + for sexpr in r6rs_sexprs { + sexpr + .compile(&top, &None) + .await + .unwrap() + .eval(&top, &None) + .await + .unwrap(); + } +} diff --git a/tests/r6rs.scm b/tests/r6rs.scm new file mode 100644 index 0000000..a6d7933 --- /dev/null +++ b/tests/r6rs.scm @@ -0,0 +1,203 @@ +;; r6rs.scm - Compatibility test for the R6RS implementation +;; +;; As of right now, this test simply takes all of the examples +;; given in the r6rs spec and runs them, asserting the values to +;; be the ones given in the spec. + +;; 1.2. Expressions + +;; The following are omitted because they don't really show anything: +;; (assert-eq #t #t) +;; (assert-eq 23 23) + +(assert-eq (+ 23 42) 65) +(assert-eq (+ 14 (* 23 42)) 980) + +;; 1.3. Variables and binding + +(assert-eq + (let ((x 23) + (y 42)) + (+ x y)) + 65) + +;; 1.4. Definitions + +(define x 23) +(define y 42) +(assert-eq (+ x y) 65) + +(define x 23) +(define y 42) +(assert-eq (let ((y 43)) + (+ x y)) + 66) + +(assert-eq (let ((y 43)) + (let ((y 44)) + (+ x y))) + 67) + +;; 1.6 Procedures + +(define (f x) + (+ x 42)) + +(assert-eq (f 23) 65) + +(define (f x) + (+ x 42)) + +(define (g p x) + (p x)) + +(assert-eq (g f 23) 65) + +(define (h op x y) + (op x y)) + +(assert-eq (h + 23 42) 65) +(assert-eq (h * 23 42) 966) + +(assert-eq ((lambda (x) (+ x 42)) 23) 65) + +;; 1.8 Assignments + +(assert-eq (let ((x 23)) + (set! x 42) + x) + 42) + +;; 1.11 Continuations + +(assert-eq (+ 1 (call-with-current-continuation + (lambda (escape) + (+ 2 (escape 3))))) + 4) + +;; 11.2.2. Syntax definitions + +(assert-eq (let () + (define even? + (lambda (x) + (or (= x 0) (odd? (- x 1))))) + (define-syntax odd? + (syntax-rules () + ((odd? x) (not (even? x))))) + (even? 10)) + #t) + +(assert-eq (let () + (define-syntax bind-to-zero + (syntax-rules () + ((bind-to-zero id) (define id 0)))) + (bind-to-zero x) + x) + 0) + +;; 11.3 Bodies + +(assert-eq (let ((x 5)) + (define foo (lambda (y) (bar x y))) + (define bar (lambda (a b) (+ (* a b) a))) + (foo (+ x 3))) + 45) + + +;; 11.4.2. Procedures + +;; (skipping a bunch of these because this stuff works) + +(assert-eq ((lambda (x) + (define (p y) + (+ y 1)) + (+ (p x) x)) + 5) + 11) + +;; 11.4.3 Conditionals + +(assert-eq (if (> 3 2) 'yes 'no) 'yes) +(assert-eq (if (> 2 3) 'yes 'no) 'no) +(assert-eq (if (> 3 2) + (- 3 2) + (+ 3 2)) + 1) + +;; 11.4.5 Derived conditionals + +(assert-eq (cond ((> 3 2) 'greater) + ((< 3 2) 'less)) + 'greater) +(assert-eq (cond ((> 3 3) 'greater) + ((< 3 3) 'less) + (else 'equal)) + 'equal) +(assert-eq (cond ('(1 2 3) => cadr) + (else #f)) + 2) + +(assert-eq (case (* 2 3) + ((2 3 5 7) 'prime) + ((1 4 6 8 9) 'composite)) + 'composite) +(assert-eq (case (car '(c d)) + ((a e i o u) 'vowel) + ((w y) 'semivowel) + (else 'consonant)) + 'consonant) + +;; 11.4.6. Binding constructs + +(assert-eq (let ((x 2) (y 3)) + (let* ((x 7) + (z (+ x y))) + (* z x))) + 70) + +(assert-eq (letrec ((even? + (lambda (n) + (if (zero? n) + #t + (odd? (- n 1))))) + (odd? + (lambda (n) + (if (zero? n) + #f + (even? (- n 1)))))) + (even? 88)) + #t) + +(assert-eq (letrec* ((p + (lambda (x) + (+ 1 (q (- x 1))))) + (q + (lambda (y) + (if (zero? y) + 0 + (+ 1 (p (- y 1)))))) + (x (p 5)) + (y x)) + y) + 5) + +(assert-eq (let-values (((a b) (values 1 2)) + ((c d) (values 3 4))) + (list a b c d)) + '(1 2 3 4)) + +(assert-eq (let-values (((a b . c) (values 1 2 3 4))) + (list a b c)) + '(1 2 (3 4))) + +(assert-eq (let ((a 'a) (b 'b) (x 'x) (y 'y)) + (let-values (((a b) (values x y)) + ((x y) (values a b))) + (list a b x y))) + '(x y a b)) + +;;(assert-eq (let ((a 'a) (b 'b) (x 'x) (y 'y)) +;; (let*-values (((a b) (values x y)) +;; ((x y) (values a b))) +;; (list a b x y))) +;; (x y x y))