;;;; syntax.test --- test suite for Guile's syntactic forms -*- scheme -*- ;;;; ;;;; Copyright (C) 2001, 2003, 2004, 2005, 2006, 2009, 2010, ;;;; 2011, 2012 Free Software Foundation, Inc. ;;;; ;;;; This library is free software; you can redistribute it and/or ;;;; modify it under the terms of the GNU Lesser General Public ;;;; License as published by the Free Software Foundation; either ;;;; version 3 of the License, or (at your option) any later version. ;;;; ;;;; This library is distributed in the hope that it will be useful, ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ;;;; Lesser General Public License for more details. ;;;; ;;;; You should have received a copy of the GNU Lesser General Public ;;;; License along with this library; if not, write to the Free Software ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA (define-module (test-suite test-syntax) #:use-module (ice-9 regex) #:use-module (test-suite lib)) (define exception:generic-syncase-error "source expression failed to match") (define exception:unexpected-syntax "unexpected syntax") (define exception:bad-expression "Bad expression") (define exception:missing/extra-expr "Missing or extra expression") (define exception:missing-expr "Missing expression") (define exception:missing-body-expr "no expressions in body") (define exception:extra-expr "Extra expression") (define exception:illegal-empty-combination "Illegal empty combination") (define exception:bad-lambda "bad lambda") (define exception:bad-let "bad let$") (define exception:bad-letrec "bad letrec$") (define exception:bad-letrec* "bad letrec\\*$") (define exception:bad-set! "bad set!") (define exception:bad-quote '(quote . "bad syntax")) (define exception:bad-bindings "Bad bindings") (define exception:bad-binding "Bad binding") (define exception:duplicate-binding "duplicate bound variable") (define exception:bad-body "^bad body") (define exception:bad-formals "invalid argument list") (define exception:bad-formal "Bad formal") (define exception:duplicate-formals "duplicate identifier in argument list") (define exception:missing-clauses "Missing clauses") (define exception:misplaced-else-clause "Misplaced else clause") (define exception:bad-case-clause "Bad case clause") (define exception:bad-case-labels "Bad case labels") (define exception:bad-cond-clause "Bad cond clause") (define exception:too-many-args "too many arguments") (define exception:zero-expression-sequence "sequence of zero expressions") ;; (put 'pass-if-syntax-error 'scheme-indent-function 1) (define-syntax pass-if-syntax-error (syntax-rules () ((_ name pat exp) (pass-if name (catch 'syntax-error (lambda () exp (error "expected syntax-error exception")) (lambda (k who what where form . maybe-subform) (if (if (pair? pat) (and (eq? who (car pat)) (string-match (cdr pat) what)) (string-match pat what)) #t (error "unexpected syntax-error exception" what pat)))))))) (with-test-prefix "expressions" (with-test-prefix "Bad argument list" (pass-if-syntax-error "improper argument list of length 1" exception:generic-syncase-error (eval '(let ((foo (lambda (x y) #t))) (foo . 1)) (interaction-environment))) (pass-if-syntax-error "improper argument list of length 2" exception:generic-syncase-error (eval '(let ((foo (lambda (x y) #t))) (foo 1 . 2)) (interaction-environment)))) (with-test-prefix "missing or extra expression" ;; R5RS says: ;; *Note:* In many dialects of Lisp, the empty combination, (), ;; is a legitimate expression. In Scheme, combinations must ;; have at least one subexpression, so () is not a syntactically ;; valid expression. ;; Fixed on 2001-3-3 (pass-if-syntax-error "empty parentheses \"()\"" exception:unexpected-syntax (eval '() (interaction-environment))))) (with-test-prefix "quote" #t) (with-test-prefix "quasiquote" (with-test-prefix "unquote" (pass-if "repeated execution" (let ((foo (let ((i 0)) (lambda () (set! i (+ i 1)) `(,i))))) (and (equal? (foo) '(1)) (equal? (foo) '(2)))))) (with-test-prefix "unquote-splicing" (pass-if "extra arguments" (equal? (eval '(quasiquote ((unquote-splicing (list 1 2) (list 3 4)))) (interaction-environment)) '(1 2 3 4))))) (with-test-prefix "begin" (pass-if "valid (begin)" (eval '(begin (begin) #t) (interaction-environment))) (if (not (include-deprecated-features)) (pass-if-syntax-error "invalid (begin)" exception:zero-expression-sequence (eval '(begin (if #t (begin)) #t) (interaction-environment))))) (define-syntax matches? (syntax-rules (<>) ((_ (op arg ...) pat) (let ((x (op arg ...))) (matches? x pat))) ((_ x ()) (null? x)) ((_ x (a . b)) (and (pair? x) (matches? (car x) a) (matches? (cdr x) b))) ((_ x <>) #t) ((_ x pat) (equal? x 'pat)))) (with-test-prefix "lambda" (with-test-prefix "bad formals" (pass-if-syntax-error "(lambda)" exception:bad-lambda (eval '(lambda) (interaction-environment))) (pass-if-syntax-error "(lambda . \"foo\")" exception:bad-lambda (eval '(lambda . "foo") (interaction-environment))) (pass-if-syntax-error "(lambda \"foo\")" exception:bad-lambda (eval '(lambda "foo") (interaction-environment))) (pass-if-syntax-error "(lambda \"foo\" #f)" exception:bad-formals (eval '(lambda "foo" #f) (interaction-environment))) (pass-if-syntax-error "(lambda (x 1) 2)" exception:bad-formals (eval '(lambda (x 1) 2) (interaction-environment))) (pass-if-syntax-error "(lambda (1 x) 2)" exception:bad-formals (eval '(lambda (1 x) 2) (interaction-environment))) (pass-if-syntax-error "(lambda (x \"a\") 2)" exception:bad-formals (eval '(lambda (x "a") 2) (interaction-environment))) (pass-if-syntax-error "(lambda (\"a\" x) 2)" exception:bad-formals (eval '(lambda ("a" x) 2) (interaction-environment)))) (with-test-prefix "duplicate formals" ;; Fixed on 2001-3-3 (pass-if-syntax-error "(lambda (x x) 1)" exception:duplicate-formals (eval '(lambda (x x) 1) (interaction-environment))) ;; Fixed on 2001-3-3 (pass-if-syntax-error "(lambda (x x x) 1)" exception:duplicate-formals (eval '(lambda (x x x) 1) (interaction-environment)))) (with-test-prefix "bad body" (pass-if-syntax-error "(lambda ())" exception:bad-lambda (eval '(lambda ()) (interaction-environment))))) (with-test-prefix "let" (with-test-prefix "bindings" (pass-if-exception "late binding" exception:unbound-var (let ((x 1) (y x)) y))) (with-test-prefix "bad bindings" (pass-if-syntax-error "(let)" exception:bad-let (eval '(let) (interaction-environment))) (pass-if-syntax-error "(let 1)" exception:bad-let (eval '(let 1) (interaction-environment))) (pass-if-syntax-error "(let (x))" exception:bad-let (eval '(let (x)) (interaction-environment))) (pass-if-syntax-error "(let ((x)))" exception:bad-let (eval '(let ((x))) (interaction-environment))) (pass-if-syntax-error "(let (x) 1)" exception:bad-let (eval '(let (x) 1) (interaction-environment))) (pass-if-syntax-error "(let ((x)) 3)" exception:bad-let (eval '(let ((x)) 3) (interaction-environment))) (pass-if-syntax-error "(let ((x 1) y) x)" exception:bad-let (eval '(let ((x 1) y) x) (interaction-environment))) (pass-if-syntax-error "(let ((1 2)) 3)" exception:bad-let (eval '(let ((1 2)) 3) (interaction-environment)))) (with-test-prefix "duplicate bindings" (pass-if-syntax-error "(let ((x 1) (x 2)) x)" exception:duplicate-binding (eval '(let ((x 1) (x 2)) x) (interaction-environment)))) (with-test-prefix "bad body" (pass-if-syntax-error "(let ())" exception:bad-let (eval '(let ()) (interaction-environment))) (pass-if-syntax-error "(let ((x 1)))" exception:bad-let (eval '(let ((x 1))) (interaction-environment))))) (with-test-prefix "named let" (with-test-prefix "initializers" (pass-if "evaluated in outer environment" (let ((f -)) (eqv? (let f ((n (f 1))) n) -1)))) (with-test-prefix "bad bindings" (pass-if-syntax-error "(let x (y))" exception:bad-let (eval '(let x (y)) (interaction-environment)))) (with-test-prefix "bad body" (pass-if-syntax-error "(let x ())" exception:bad-let (eval '(let x ()) (interaction-environment))) (pass-if-syntax-error "(let x ((y 1)))" exception:bad-let (eval '(let x ((y 1))) (interaction-environment))))) (with-test-prefix "let*" (with-test-prefix "bindings" (pass-if "(let* ((x 1) (x 2)) ...)" (let* ((x 1) (x 2)) (= x 2))) (pass-if "(let* ((x 1) (x x)) ...)" (let* ((x 1) (x x)) (= x 1))) (pass-if "(let ((x 1) (y 2)) (let* () ...))" (let ((x 1) (y 2)) (let* () (and (= x 1) (= y 2)))))) (with-test-prefix "bad bindings" (pass-if-syntax-error "(let*)" exception:generic-syncase-error (eval '(let*) (interaction-environment))) (pass-if-syntax-error "(let* 1)" exception:generic-syncase-error (eval '(let* 1) (interaction-environment))) (pass-if-syntax-error "(let* (x))" exception:generic-syncase-error (eval '(let* (x)) (interaction-environment))) (pass-if-syntax-error "(let* (x) 1)" exception:generic-syncase-error (eval '(let* (x) 1) (interaction-environment))) (pass-if-syntax-error "(let* ((x)) 3)" exception:generic-syncase-error (eval '(let* ((x)) 3) (interaction-environment))) (pass-if-syntax-error "(let* ((x 1) y) x)" exception:generic-syncase-error (eval '(let* ((x 1) y) x) (interaction-environment))) (pass-if-syntax-error "(let* x ())" exception:generic-syncase-error (eval '(let* x ()) (interaction-environment))) (pass-if-syntax-error "(let* x (y))" exception:generic-syncase-error (eval '(let* x (y)) (interaction-environment))) (pass-if-syntax-error "(let* ((1 2)) 3)" exception:generic-syncase-error (eval '(let* ((1 2)) 3) (interaction-environment)))) (with-test-prefix "bad body" (pass-if-syntax-error "(let* ())" exception:generic-syncase-error (eval '(let* ()) (interaction-environment))) (pass-if-syntax-error "(let* ((x 1)))" exception:generic-syncase-error (eval '(let* ((x 1))) (interaction-environment))))) (with-test-prefix "letrec" (with-test-prefix "bindings" (pass-if-syntax-error "initial bindings are undefined" exception:used-before-defined (let ((x 1)) ;; FIXME: the memoizer does initialize the var to undefined, but ;; the Scheme evaluator has no way of checking what's an ;; undefined value. Not sure how to do this. (throw 'unresolved) (letrec ((x 1) (y x)) y)))) (with-test-prefix "bad bindings" (pass-if-syntax-error "(letrec)" exception:bad-letrec (eval '(letrec) (interaction-environment))) (pass-if-syntax-error "(letrec 1)" exception:bad-letrec (eval '(letrec 1) (interaction-environment))) (pass-if-syntax-error "(letrec (x))" exception:bad-letrec (eval '(letrec (x)) (interaction-environment))) (pass-if-syntax-error "(letrec (x) 1)" exception:bad-letrec (eval '(letrec (x) 1) (interaction-environment))) (pass-if-syntax-error "(letrec ((x)) 3)" exception:bad-letrec (eval '(letrec ((x)) 3) (interaction-environment))) (pass-if-syntax-error "(letrec ((x 1) y) x)" exception:bad-letrec (eval '(letrec ((x 1) y) x) (interaction-environment))) (pass-if-syntax-error "(letrec x ())" exception:bad-letrec (eval '(letrec x ()) (interaction-environment))) (pass-if-syntax-error "(letrec x (y))" exception:bad-letrec (eval '(letrec x (y)) (interaction-environment))) (pass-if-syntax-error "(letrec ((1 2)) 3)" exception:bad-letrec (eval '(letrec ((1 2)) 3) (interaction-environment)))) (with-test-prefix "duplicate bindings" (pass-if-syntax-error "(letrec ((x 1) (x 2)) x)" exception:duplicate-binding (eval '(letrec ((x 1) (x 2)) x) (interaction-environment)))) (with-test-prefix "bad body" (pass-if-syntax-error "(letrec ())" exception:bad-letrec (eval '(letrec ()) (interaction-environment))) (pass-if-syntax-error "(letrec ((x 1)))" exception:bad-letrec (eval '(letrec ((x 1))) (interaction-environment))))) (with-test-prefix "letrec*" (with-test-prefix "bindings" (pass-if-syntax-error "initial bindings are undefined" exception:used-before-defined (begin ;; FIXME: the memoizer does initialize the var to undefined, but ;; the Scheme evaluator has no way of checking what's an ;; undefined value. Not sure how to do this. (throw 'unresolved) (letrec* ((x y) (y 1)) y)))) (with-test-prefix "bad bindings" (pass-if-syntax-error "(letrec*)" exception:bad-letrec* (eval '(letrec*) (interaction-environment))) (pass-if-syntax-error "(letrec* 1)" exception:bad-letrec* (eval '(letrec* 1) (interaction-environment))) (pass-if-syntax-error "(letrec* (x))" exception:bad-letrec* (eval '(letrec* (x)) (interaction-environment))) (pass-if-syntax-error "(letrec* (x) 1)" exception:bad-letrec* (eval '(letrec* (x) 1) (interaction-environment))) (pass-if-syntax-error "(letrec* ((x)) 3)" exception:bad-letrec* (eval '(letrec* ((x)) 3) (interaction-environment))) (pass-if-syntax-error "(letrec* ((x 1) y) x)" exception:bad-letrec* (eval '(letrec* ((x 1) y) x) (interaction-environment))) (pass-if-syntax-error "(letrec* x ())" exception:bad-letrec* (eval '(letrec* x ()) (interaction-environment))) (pass-if-syntax-error "(letrec* x (y))" exception:bad-letrec* (eval '(letrec* x (y)) (interaction-environment))) (pass-if-syntax-error "(letrec* ((1 2)) 3)" exception:bad-letrec* (eval '(letrec* ((1 2)) 3) (interaction-environment)))) (with-test-prefix "duplicate bindings" (pass-if-syntax-error "(letrec* ((x 1) (x 2)) x)" exception:duplicate-binding (eval '(letrec* ((x 1) (x 2)) x) (interaction-environment)))) (with-test-prefix "bad body" (pass-if-syntax-error "(letrec* ())" exception:bad-letrec* (eval '(letrec* ()) (interaction-environment))) (pass-if-syntax-error "(letrec* ((x 1)))" exception:bad-letrec* (eval '(letrec* ((x 1))) (interaction-environment)))) (with-test-prefix "referencing previous values" (pass-if (equal? (letrec ((a (cons 'foo 'bar)) (b a)) b) '(foo . bar))) (pass-if (equal? (let () (define a (cons 'foo 'bar)) (define b a) b) '(foo . bar))))) (with-test-prefix "if" (with-test-prefix "missing or extra expressions" (pass-if-syntax-error "(if)" exception:generic-syncase-error (eval '(if) (interaction-environment))) (pass-if-syntax-error "(if 1 2 3 4)" exception:generic-syncase-error (eval '(if 1 2 3 4) (interaction-environment))))) (with-test-prefix "cond" (with-test-prefix "cond is hygienic" (pass-if "bound 'else is handled correctly" (eq? (let ((else 'ok)) (cond (else))) 'ok)) (with-test-prefix "bound '=> is handled correctly" (pass-if "#t => 'ok" (let ((=> 'foo)) (eq? (cond (#t => 'ok)) 'ok))) (pass-if "else =>" (let ((=> 'foo)) (eq? (cond (else =>)) 'foo))) (pass-if "else => identity" (let ((=> 'foo)) (eq? (cond (else => identity)) identity))))) (with-test-prefix "SRFI-61" (pass-if "always available" (cond-expand (srfi-61 #t) (else #f))) (pass-if "single value consequent" (eq? 'ok (cond (#t identity => (lambda (x) 'ok)) (else #f)))) (pass-if "single value alternate" (eq? 'ok (cond (#t not => (lambda (x) #f)) (else 'ok)))) (pass-if-exception "doesn't affect standard =>" exception:wrong-num-args (cond ((values 1 2) => (lambda (x y) #t)))) (pass-if "multiple values consequent" (equal? '(2 1) (cond ((values 1 2) (lambda (one two) (and (= 1 one) (= 2 two))) => (lambda (one two) (list two one))) (else #f)))) (pass-if "multiple values alternate" (eq? 'ok (cond ((values 2 3 4) (lambda args (equal? '(1 2 3) args)) => (lambda (x y z) #f)) (else 'ok)))) (pass-if "zero values" (eq? 'ok (cond ((values) (lambda () #t) => (lambda () 'ok)) (else #f)))) (pass-if "bound => is handled correctly" (let ((=> 'ok)) (eq? 'ok (cond (#t identity =>) (else #f))))) (pass-if-syntax-error "missing recipient" '(cond . "wrong number of receiver expressions") (eval '(cond (#t identity =>)) (interaction-environment))) (pass-if-syntax-error "extra recipient" '(cond . "wrong number of receiver expressions") (eval '(cond (#t identity => identity identity)) (interaction-environment)))) (with-test-prefix "bad or missing clauses" (pass-if-syntax-error "(cond)" exception:generic-syncase-error (eval '(cond) (interaction-environment))) (pass-if-syntax-error "(cond #t)" '(cond . "invalid clause") (eval '(cond #t) (interaction-environment))) (pass-if-syntax-error "(cond 1)" '(cond . "invalid clause") (eval '(cond 1) (interaction-environment))) (pass-if-syntax-error "(cond 1 2)" '(cond . "invalid clause") (eval '(cond 1 2) (interaction-environment))) (pass-if-syntax-error "(cond 1 2 3)" '(cond . "invalid clause") (eval '(cond 1 2 3) (interaction-environment))) (pass-if-syntax-error "(cond 1 2 3 4)" '(cond . "invalid clause") (eval '(cond 1 2 3 4) (interaction-environment))) (pass-if-syntax-error "(cond ())" '(cond . "invalid clause") (eval '(cond ()) (interaction-environment))) (pass-if-syntax-error "(cond () 1)" '(cond . "invalid clause") (eval '(cond () 1) (interaction-environment))) (pass-if-syntax-error "(cond (1) 1)" '(cond . "invalid clause") (eval '(cond (1) 1) (interaction-environment))) (pass-if-syntax-error "(cond (else #f) (#t #t))" '(cond . "else must be the last clause") (eval '(cond (else #f) (#t #t)) (interaction-environment)))) (with-test-prefix "wrong number of arguments" (pass-if-exception "=> (lambda (x y) #t)" exception:wrong-num-args (cond (1 => (lambda (x y) #t)))))) (with-test-prefix "case" (pass-if "clause with empty labels list" (case 1 (() #f) (else #t))) (with-test-prefix "case handles '=> correctly" (pass-if "(1 2 3) => list" (equal? (case 1 ((1 2 3) => list)) '(1))) (pass-if "else => list" (equal? (case 6 ((1 2 3) 'wrong) (else => list)) '(6))) (with-test-prefix "bound '=> is handled correctly" (pass-if "(1) => 'ok" (let ((=> 'foo)) (eq? (case 1 ((1) => 'ok)) 'ok))) (pass-if "else =>" (let ((=> 'foo)) (eq? (case 1 (else =>)) 'foo))) (pass-if "else => list" (let ((=> 'foo)) (eq? (case 1 (else => identity)) identity)))) (pass-if-syntax-error "missing recipient" '(case . "wrong number of receiver expressions") (eval '(case 1 ((1) =>)) (interaction-environment))) (pass-if-syntax-error "extra recipient" '(case . "wrong number of receiver expressions") (eval '(case 1 ((1) => identity identity)) (interaction-environment)))) (with-test-prefix "case is hygienic" (pass-if-syntax-error "bound 'else is handled correctly" '(case . "invalid clause") (eval '(let ((else #f)) (case 1 (else #f))) (interaction-environment)))) (with-test-prefix "bad or missing clauses" (pass-if-syntax-error "(case)" exception:generic-syncase-error (eval '(case) (interaction-environment))) (pass-if-syntax-error "(case . \"foo\")" exception:generic-syncase-error (eval '(case . "foo") (interaction-environment))) (pass-if-syntax-error "(case 1)" exception:generic-syncase-error (eval '(case 1) (interaction-environment))) (pass-if-syntax-error "(case 1 . \"foo\")" exception:generic-syncase-error (eval '(case 1 . "foo") (interaction-environment))) (pass-if-syntax-error "(case 1 \"foo\")" '(case . "invalid clause") (eval '(case 1 "foo") (interaction-environment))) (pass-if-syntax-error "(case 1 ())" '(case . "invalid clause") (eval '(case 1 ()) (interaction-environment))) (pass-if-syntax-error "(case 1 (\"foo\"))" '(case . "invalid clause") (eval '(case 1 ("foo")) (interaction-environment))) (pass-if-syntax-error "(case 1 (\"foo\" \"bar\"))" '(case . "invalid clause") (eval '(case 1 ("foo" "bar")) (interaction-environment))) (pass-if-syntax-error "(case 1 ((2) \"bar\") . \"foo\")" exception:generic-syncase-error (eval '(case 1 ((2) "bar") . "foo") (interaction-environment))) (pass-if-syntax-error "(case 1 ((2) \"bar\") (else))" '(case . "invalid clause") (eval '(case 1 ((2) "bar") (else)) (interaction-environment))) (pass-if-syntax-error "(case 1 (else #f) . \"foo\")" exception:generic-syncase-error (eval '(case 1 (else #f) . "foo") (interaction-environment))) (pass-if-syntax-error "(case 1 (else #f) ((1) #t))" '(case . "else must be the last clause") (eval '(case 1 (else #f) ((1) #t)) (interaction-environment))))) (with-test-prefix "top-level define" (pass-if "redefinition" (let ((m (make-module))) (beautify-user-module! m) ;; The previous value of `round' must still be visible at the time the ;; new `round' is defined. According to R5RS (Section 5.2.1), `define' ;; should behave like `set!' in this case (except that in the case of ;; Guile, we respect module boundaries). (eval '(define round round) m) (eq? (module-ref m 'round) round))) (with-test-prefix "missing or extra expressions" (pass-if-syntax-error "(define)" exception:generic-syncase-error (eval '(define) (interaction-environment)))) (pass-if "module scoping" (equal? (eval '(begin (define-module (top-level-define/module-scoping-1) #:export (define-10)) (define-syntax-rule (define-10 name) (begin (define t 10) (define (name) t))) (define-module (top-level-define/module-scoping-2) #:use-module (top-level-define/module-scoping-1)) (define-10 foo) (foo)) (current-module)) 10)) (pass-if "module scoping, same symbolic name" (equal? (eval '(begin (define-module (top-level-define/module-scoping-3)) (define a 10) (define-module (top-level-define/module-scoping-4) #:use-module (top-level-define/module-scoping-3)) (define a (@@ (top-level-define/module-scoping-3) a)) a) (current-module)) 10)) (pass-if "module scoping, introduced names" (equal? (eval '(begin (define-module (top-level-define/module-scoping-5) #:export (define-constant)) (define-syntax-rule (define-constant name val) (begin (define t val) (define (name) t))) (define-module (top-level-define/module-scoping-6) #:use-module (top-level-define/module-scoping-5)) (define-constant foo 10) (define-constant bar 20) (foo)) (current-module)) 10)) (pass-if "module scoping, duplicate introduced name" (equal? (eval '(begin (define-module (top-level-define/module-scoping-7) #:export (define-constant)) (define-syntax-rule (define-constant name val) (begin (define t val) (define (name) t))) (define-module (top-level-define/module-scoping-8) #:use-module (top-level-define/module-scoping-7)) (define-constant foo 10) (define-constant foo 20) (foo)) (current-module)) 20))) (with-test-prefix "internal define" (pass-if "internal defines become letrec" (eval '(let ((a identity) (b identity) (c identity)) (define (a x) (if (= x 0) 'a (b (- x 1)))) (define (b x) (if (= x 0) 'b (c (- x 1)))) (define (c x) (if (= x 0) 'c (a (- x 1)))) (and (eq? 'a (a 0) (a 3)) (eq? 'b (a 1) (a 4)) (eq? 'c (a 2) (a 5)))) (interaction-environment))) (pass-if "binding is created before expression is evaluated" ;; Internal defines are equivalent to `letrec' (R5RS, Section 5.2.2). (= (eval '(let () (define foo (begin (set! foo 1) (+ foo 1))) foo) (interaction-environment)) 2)) (pass-if "internal defines with begin" (false-if-exception (eval '(let ((a identity) (b identity) (c identity)) (define (a x) (if (= x 0) 'a (b (- x 1)))) (begin (define (b x) (if (= x 0) 'b (c (- x 1))))) (define (c x) (if (= x 0) 'c (a (- x 1)))) (and (eq? 'a (a 0) (a 3)) (eq? 'b (a 1) (a 4)) (eq? 'c (a 2) (a 5)))) (interaction-environment)))) (pass-if "internal defines with empty begin" (false-if-exception (eval '(let ((a identity) (b identity) (c identity)) (define (a x) (if (= x 0) 'a (b (- x 1)))) (begin) (define (b x) (if (= x 0) 'b (c (- x 1)))) (define (c x) (if (= x 0) 'c (a (- x 1)))) (and (eq? 'a (a 0) (a 3)) (eq? 'b (a 1) (a 4)) (eq? 'c (a 2) (a 5)))) (interaction-environment)))) (pass-if "internal defines with macro application" (false-if-exception (eval '(begin (defmacro my-define forms (cons 'define forms)) (let ((a identity) (b identity) (c identity)) (define (a x) (if (= x 0) 'a (b (- x 1)))) (my-define (b x) (if (= x 0) 'b (c (- x 1)))) (define (c x) (if (= x 0) 'c (a (- x 1)))) (and (eq? 'a (a 0) (a 3)) (eq? 'b (a 1) (a 4)) (eq? 'c (a 2) (a 5))))) (interaction-environment)))) (pass-if-syntax-error "missing body expression" exception:missing-body-expr (eval '(let () (define x #t)) (interaction-environment)))) (with-test-prefix "set!" (with-test-prefix "missing or extra expressions" (pass-if-syntax-error "(set!)" exception:bad-set! (eval '(set!) (interaction-environment))) (pass-if-syntax-error "(set! 1)" exception:bad-set! (eval '(set! 1) (interaction-environment))) (pass-if-syntax-error "(set! 1 2 3)" exception:bad-set! (eval '(set! 1 2 3) (interaction-environment)))) (with-test-prefix "bad variable" (pass-if-syntax-error "(set! \"\" #t)" exception:bad-set! (eval '(set! "" #t) (interaction-environment))) (pass-if-syntax-error "(set! 1 #t)" exception:bad-set! (eval '(set! 1 #t) (interaction-environment))) (pass-if-syntax-error "(set! #t #f)" exception:bad-set! (eval '(set! #t #f) (interaction-environment))) (pass-if-syntax-error "(set! #f #t)" exception:bad-set! (eval '(set! #f #t) (interaction-environment))) (pass-if-syntax-error "(set! #\\space #f)" exception:bad-set! (eval '(set! #\space #f) (interaction-environment))))) (with-test-prefix "quote" (with-test-prefix "missing or extra expression" (pass-if-syntax-error "(quote)" exception:bad-quote (eval '(quote) (interaction-environment))) (pass-if-syntax-error "(quote a b)" exception:bad-quote (eval '(quote a b) (interaction-environment))))) (with-test-prefix "while" (define (unreachable) (error "unreachable code has been reached!")) ;; Return a new procedure COND which when called (COND) will return #t the ;; first N times, then #f, then any further call is an error. N=0 is ;; allowed, in which case #f is returned by the first call. (define (make-iterations-cond n) (lambda () (cond ((not n) (error "oops, condition re-tested after giving false")) ((= 0 n) (set! n #f) #f) (else (set! n (1- n)) #t)))) (pass-if-syntax-error "too few args" exception:generic-syncase-error (eval '(while) (interaction-environment))) (with-test-prefix "empty body" (do ((n 0 (1+ n))) ((> n 5)) (pass-if n (eval `(letrec ((make-iterations-cond (lambda (n) (lambda () (cond ((not n) (error "oops, condition re-tested after giving false")) ((= 0 n) (set! n #f) #f) (else (set! n (1- n)) #t)))))) (let ((cond (make-iterations-cond ,n))) (while (cond)) #t)) (interaction-environment))))) (pass-if "initially false" (while #f (unreachable)) #t) (with-test-prefix "iterations" (do ((n 0 (1+ n))) ((> n 5)) (pass-if n (let ((cond (make-iterations-cond n)) (i 0)) (while (cond) (set! i (1+ i))) (= i n))))) (with-test-prefix "break" (pass-if "normal return" (not (while #f (error "not reached")))) (pass-if "no args" (while #t (break))) (pass-if "multiple values" (equal? '(1 2 3) (call-with-values (lambda () (while #t (break 1 2 3))) list))) (with-test-prefix "from cond" (pass-if "first" (while (begin (break) (unreachable)) (unreachable)) #t) (do ((n 0 (1+ n))) ((> n 5)) (pass-if n (let ((cond (make-iterations-cond n)) (i 0)) (while (if (cond) #t (begin (break) (unreachable))) (set! i (1+ i))) (= i n))))) (with-test-prefix "from body" (pass-if "first" (while #t (break) (unreachable)) #t) (do ((n 0 (1+ n))) ((> n 5)) (pass-if n (let ((cond (make-iterations-cond n)) (i 0)) (while #t (if (not (cond)) (begin (break) (unreachable))) (set! i (1+ i))) (= i n))))) (pass-if "from nested" (while #t (let ((outer-break break)) (while #t (outer-break) (unreachable))) (unreachable)) #t) (pass-if "from recursive" (let ((outer-break #f)) (define (r n) (while #t (if (eq? n 'outer) (begin (set! outer-break break) (r 'inner)) (begin (outer-break) (unreachable)))) (if (eq? n 'inner) (error "broke only from inner loop"))) (r 'outer)) #t)) (with-test-prefix "continue" (pass-if-syntax-error "too many args" exception:too-many-args (eval '(while #t (continue 1)) (interaction-environment))) (with-test-prefix "from cond" (do ((n 0 (1+ n))) ((> n 5)) (pass-if n (let ((cond (make-iterations-cond n)) (i 0)) (while (if (cond) (begin (set! i (1+ i)) (continue) (unreachable)) #f) (unreachable)) (= i n))))) (with-test-prefix "from body" (do ((n 0 (1+ n))) ((> n 5)) (pass-if n (let ((cond (make-iterations-cond n)) (i 0)) (while (cond) (set! i (1+ i)) (continue) (unreachable)) (= i n))))) (pass-if "from nested" (let ((cond (make-iterations-cond 3))) (while (cond) (let ((outer-continue continue)) (while #t (outer-continue) (unreachable))))) #t) (pass-if "from recursive" (let ((outer-continue #f)) (define (r n) (let ((cond (make-iterations-cond 3)) (first #t)) (while (begin (if (and (not first) (eq? n 'inner)) (error "continued only to inner loop")) (cond)) (set! first #f) (if (eq? n 'outer) (begin (set! outer-continue continue) (r 'inner)) (begin (outer-continue) (unreachable)))))) (r 'outer)) #t)))