(import (scheme base) (scheme char) (scheme file) (scheme lazy) (scheme read) (scheme write) (scheme eval) ;(srfi 9) ) (define *num-passed* 0) (define (assert:equal msg actual expected) (if (not (equal? actual expected)) (error "Unit test failed [" msg "] actual [" actual "] expected [" expected "]") (set! *num-passed* (+ *num-passed* 1)))) (define (assert:not-equal msg x y) (assert:equal msg (not (equal? x y)) #t)) (define (assert msg val) (assert:equal msg (not val) #f)) (assert "Testing assert function" #t) (assert "Testing assert function" 1) ;; Quotation / Quasi-quotes (assert:equal "quasi-quote #1" `(read ,(+ 1 2 3)) '(read 6)) (assert:equal "quasi-quote #2" `(read ,(list 1 2 3)) '(read (1 2 3))) (assert:equal "quasi-quote splicing" `(read ,@(list 1 2 3)) '(read 1 2 3)) ;; Lists (define l (list 'a 'b 'c)) ; TODO: seems to break eval below, is there a GC problem with a circular list? ;(set-cdr! l l) (set-cdr! l '(c b)) ; Above seems to break if it replaces this line (assert:equal "list? on circular list" (list? l) #t) ;; Circular data structures (define v1 (vector #f)) (define v2 (vector v1)) (vector-set! v1 0 v2) (cond-expand (memory-streams (let ((fp (open-output-string))) (display v1 fp) (assert:equal "display circular vector" (> (string-length (get-output-string fp)) 0) #t)))) (assert:equal "equality on circular vectors" (equal? v1 v2) #t) (newline) (define v1 (vector 1 2 3)) (define v2 (vector 1 v1 3)) (vector-set! v1 1 v2) (cond-expand (memory-streams (let ((fp (open-output-string))) (write v1 fp) (assert:equal "display circular vector" (> (string-length (get-output-string fp)) 0) #t)))) (assert:equal "equality on circular vectors, test 2" (equal? v1 v2) #t) (newline) (define l1 (list #f)) (define l2 (list l1)) (set-cdr! l1 l2) (cond-expand (memory-streams (let ((fp (open-output-string))) (display l1 fp) (assert:equal "display circular list" (> (string-length (get-output-string fp)) 0) #t)))) (assert:equal "equality on circular lists" (equal? l1 l2) #f) (define l1 (list 1 2 3)) (define l2 (list 1 l1 3)) (set-cdr! (cdr l1) l2) (cond-expand (memory-streams (let ((fp (open-output-string))) (write l1 fp) (assert:equal "display circular list" (> (string-length (get-output-string fp)) 0) #t)))) (assert:equal "equality on circular lists, test 2" (equal? l1 l2) #f) ;; Adder example (define (make-adder x) (lambda (y) (+ x y))) (define increment (make-adder +1)) (assert:equal "Adder #1" (increment 41) 42) (define decrement (make-adder -1)) (assert:equal "Adder #2" (decrement 42) 41) (assert:equal "Application example" ((lambda (x) x) (+ 41 1)) 42) ;; Apply section (assert:equal "" (apply length '((#t #f))) 2) (assert:equal "" (apply cons '(#t #f)) '(#t . #f)) (assert:equal "" (apply cadr (list (list 1 2 3 4))) 2) (assert:equal "" (apply null? (list '())) #t) ;; Varargs (define (list2 a b . objs) objs) (assert:equal "apply varargs" (list 42 1) '(42 1)) (assert:equal "apply varargs" (list 42 1 2) '(42 1 2)) (assert:equal "apply varargs" (list2 42 1) '()) (assert:equal "apply varargs" (list2 42 1 2) '(2)) (assert:equal "begin" (begin 1 2 (+ 1 2) (+ 3 4)) 7) ;; Continuation section (assert:equal "simple call/cc" (call/cc (lambda (k) (k 2))) 2) (assert:equal "escape continuation" (call/cc (lambda (return) (begin (return 'return)))) 'return) ;; Closure section (assert:equal "simple closure" (((lambda (x.1) (lambda (y.2) (cons x.1 y.2))) #t) #f) '(#t . #f)) (assert:equal "closure #2" ((lambda (x y) ((lambda () (- x y)))) 5 4) 1) ;; Anonymous lambda's (assert:equal "execute anonymous lambda" ((lambda (a . Y) Y) 'x) '()) (assert:equal "execute anonymous lambda" ((lambda (a . Y) Y) 'x 'y) '(y)) (assert:equal "execute anonymous lambda" ((lambda (a . Y) Y) 'x 'y 'z) '(y z)) (assert:equal "execute anonymous lambda" ((lambda (a b . Y) Y) 'x 'y 'z) '(z)) (assert:equal "execute anonymous lambda" ((lambda Y Y) 'x 'y 'z) '(x y z)) (assert:equal "execute anonymous lambda" ((lambda Y Y) ) '()) ;; Factorial (define (fac n) (if (= n 0) 1 (* n (fac (- n 1))))) (assert:equal "Factorial example" (fac 10) 3628800) ;; If section (assert:equal "if example" (if #t 1 2) 1) (assert:equal "if example" (if #f 1 2) 2) (assert:equal "if example" (if (+ 1 2) (+ 3 4) (* 3 4)) 7) (assert:equal "if" (if ((lambda (x) (+ x 1)) 0) (+ 1 1) (* 0 0)) 2) (assert:equal "no else clause" (if #t 'no-else-clause) 'no-else-clause) (assert:equal "" (+ (+ 1 1) (* 3 4)) 14) ;; Set section ((lambda (x) (set! x #t) ; (+ 2 (* 3 4))) (assert:equal "set local x" x #t)) #f) (define a (list #f #f)) (define b (cons #f #f)) (set-car! a 1) (set-cdr! a '(2)) (assert:equal "set car/cdr a" a '(1 2)) (set-cdr! a 2) (set-car! b '(#t)) (set-cdr! b '#t) (assert:equal "set a" a '(1 . 2)) (assert:equal "set b" b '((#t) . #t)) ;; Scoping example (define scope #f) (assert:equal "outer scope" scope #f) ((lambda (scope) (assert:equal "inner scope" scope #t) ) #t) ;; Square example (let ((x 10) (y 20) (square (lambda (x) (* x x)))) (begin (assert:equal "square x" (square x) 100) (assert:equal "square y" (square y) 400))) ;; String section (define a "a0123456789") (assert:equal "string eq" a "a0123456789") (assert:not-equal "string eq" a 'a0123456789) (define b "abcdefghijklmnopqrstuvwxyz") (define c "hello, world!") (define d (list->string '(#\( #\" #\a #\b #\c #\" #\)))) (assert:equal "strings" d "(\"abc\")") (assert:equal "strings" d "(\"abc\")") ;; Test GC (assert:equal "strings" d "(\"abc\")") ;; Test GC (set! a "hello 2") (assert:equal "strings" a "hello 2") (define (f) (make-string 3 #\*)) (define (g) "***") (assert:equal "string-set!" (string-set! (f) 0 #\?) "?**") (define a "12345") (define b (string-copy "abcde")) (assert:equal "string-copy!" (string-copy! b 1 a 0 2) "a12de") (let ((v '())) (string-for-each (lambda (c) (set! v (cons (char->integer c) v))) "abcde") (assert:equal "string-for-each" v '(101 100 99 98 97))) (assert:equal "interline whitespace" "1 2 \ 3 4" "1 2 3 4") (assert:equal "interline whitespace" "1 2 \ 3 4" "1 2 \n\n3 4") ;; UTF-8 / Strings (assert:equal "UTF8 string length" (string-length (make-string 1 (integer->char 128))) 1) (assert:equal "UTF8 bv length" (bytevector-length (string->utf8 (make-string 1 (integer->char 128)))) 2) (assert:equal "UTF8 char" (string-ref (make-string 1 (integer->char 128)) 0) #\x80) ;; Recursion example: (letrec ((fnc (lambda (i) (begin ;(display i) (if (> i 0) (fnc (- i 1)) 0))))) (fnc 10)) (assert:equal "numeric small reverse" (reverse '(1 2)) '(2 1)) (assert:equal "small reverse" (reverse '(a b c)) '(c b a)) (assert:equal "larger reverse" (reverse '(1 2 3 4 5 6 7 8 9 10)) '(10 9 8 7 6 5 4 3 2 1)) ;; ;TODO: improper list, this is an error: (reverse '(1 . 2)) (assert:equal "char whitespace" (char-whitespace? #\space) #t) (assert:equal "char whitespace" (char-whitespace? #\a) #f) (assert:equal "char numeric" (char-numeric? #\1) #t) (assert:equal "char numeric" (char-numeric? #\newline) #f) (assert:equal "" (and 1 2 3) 3) (assert:equal "" (and #t #f 'a 'b 'c) #f) (assert:equal "" (or 1 2 3) 1) (assert:equal "" (or #f 'a 'b 'c) 'a) (assert:equal "" (string-append "") "") ;error - (string-append 1) (assert:equal "" (string-append "test") "test") (assert:equal "" (string-append "ab" "cdefgh ij" "klmno" "p" "q" "rs " "tuv" "w" " x " "yz") "abcdefgh ijklmnopqrs tuvw x yz") (assert:equal "" (string->number "0") 0) (assert:equal "" (string->number "42") 42) ;(assert:equal "" (string->number "343243243232") ;; Note no bignum support (assert:equal "" (string->number "3.14159") 3.14159) (assert:equal "" (list->string (list #\A #\B #\C)) "ABC") (assert:equal "" (list->string (list #\A)) "A") (assert:equal "" (list->string (list)) "") (assert:equal "" (integer->char 65) #\A) (assert:equal "" (char->integer #\a) 97) (assert:equal "" (number->string (+ 1 2)) "3") (assert:equal "" (string->list "test") '(#\t #\e #\s #\t)) (assert:equal "" (string->symbol "a-b-c-d") 'a-b-c-d) (assert:equal "" (symbol->string 'a/test-01) "a/test-01") (assert:equal "" (eq? 'a-1 'a-1) #t) (assert:equal "" (eq? (string->symbol "aa") 'aa) #t) (assert:equal "" (eq? 0.0 0.0) #f) (assert:equal "" (eq? 33333333333333 33333333333333) #f) (assert:equal "" (eqv? 'a-1 'a-1) #t) (assert:equal "" (eqv? (string->symbol "aa") 'aa) #t) (assert:equal "" (eqv? 0.0 0.0) #t) (assert:equal "" (eqv? 33333333333333 33333333333333) #t) (assert:equal "" (equal? (string->symbol "aa") 'aa) #t) ;; Map (assert:equal "map 1" (map (lambda (x) (car x)) '((a . b) (1 . 2) (#\h #\w))) '(a 1 #\h)) (assert:equal "map 2" (map car '((a . b) (1 . 2) (#\h #\w))) '(a 1 #\h)) (assert:equal "map 3" (map cdr '((a . b) (1 . 2) (#\h #\w))) '(b 2 (#\w))) (assert:equal "map length" (map length '(() (1) (1 2) (1 2 3) (1 2 3 4))) '(0 1 2 3 4)) ;; Prove internal defines are compiled properly ;; ;; Illustrates an old problem with compiling parser. ;; how to handle the internal define p? ;; trans was trying to wrap p with a lambda, which is not going to ;; work because callers want to pass a,b,c directly. (define (glob a b c) (define (p d) (list a b c d)) (p 4)) (assert:equal "internal defs for global funcs" (glob 1 2 3) '(1 2 3 4)) ;; Global shadowing issue ;; Do not allow global define to shadow local ones (define x 'global) ((lambda () (define x 1) ((lambda () (define x 2) (assert:equal "local define of x" x 2))) (assert:equal "another local define of x" x 1))) (assert:equal "global define of x" x 'global) ; TODO: could add parser tests for these ;( ;123(list) ;1'b ;(write ; (list ; 1;2 ; )) ;1;2 ;3"four five" ;#\space ;) ;; EVAL section (define x 1) (define y 2) (define *z* 3) ;(write (eval '(Cyc-global-vars))) (assert:equal "eval compiled - x" (eval 'x) x) (eval '(set! x 'mutated-x)) (assert:equal "Access var with a mangled name" (eval '*z*) *z*) (assert:equal "Access compiled var mutated by eval" x 'mutated-x) ;; END eval ;; Vectors (assert:equal "vector-ref" (vector-ref #(1 1 2 3 5 8 13 21) 5) 8) (assert:equal "vector-set!" (vector-set! #(0 '(2 2 2 2) "anna") 1 "test") #(0 "test" "anna")) (assert:equal "vector->list" (vector->list #(dah dah didah)) '(dah dah didah)) ;(vector->list .#(dah dah didah) 1 2) ;=. (dah) (assert:equal "list->vector" (list->vector '(dididit dah)) #(dididit dah)) (assert:equal "string->vector" (string->vector "ABC") #(#\A #\B #\C)) (assert:equal "vector->string" (vector->string #(#\1 #\2 #\3)) "123") (define a (vector 1 2 3 4 5)) (define b (vector 10 20 30 40 50)) (vector-copy! b 1 a 0 2) (assert "vector-copy!" (equal? b #(10 1 2 40 50))) (define a (vector 1 2 3 4 5)) (vector-fill! a 'smash 2 4) (assert:equal "vector-fill!" a #(1 2 smash smash 5)) ;; END vectors ;; I/O (with-output-to-file "test.out" (lambda () (write 'hello-world))) ;(write "done with output") (with-input-from-file "test.out" (lambda () (assert:equal "I/O with-*-file test" (read) 'hello-world))) (call-with-output-file "test.txt" (lambda (port) (write 'ok port))) ;; Note: was a good test, did not always work in icyc (call-with-input-file "test.txt" (lambda (port) (assert:equal "read input" (read port) 'ok))) ;(write "done with input") ;; TODO: (delete-file "test.out") ;; TODO: (delete-file "test.txt") ;; END I/O ;; Macros (define-syntax test (er-macro-transformer (lambda (expr rename compare) (cond ((null? (cdr expr)) #t) ((null? (cddr expr)) (cadr expr)) (else (list (rename 'if) (cadr expr) (cons (rename 'and) (cddr expr)) #f)))))) (define-syntax test2 (er-macro-transformer (lambda (expr rename compare) (test 1 2) (test 1 2 3) (and ''test ''test2)))) (define-syntax indirect-def (syntax-rules () ( (indirect-def x y) (begin (define z y) (define x z) ) ) ) ) (define x 42) (assert:equal "macro: test2" (test2 1 2 3) 'test2) (assert:equal "macro: test" (test 1 2 3) 3) (assert:equal "macro: eval test" (eval '(test 1 2 x)) x) (assert:equal "macro: define syntax indirect-def" (indirect-def a 3) '(3)) (assert:equal "macro: let syntax indirect-def" (let-syntax ( (indirect-def (syntax-rules () ( (indirect-def x y) (begin (define z y) (define x z) ) ) ) ) ) (indirect-def a 3) a) '3) ;; END macros ;; Record types (define-record-type (kons x y) pare? (x kar set-kar!) (y kdr)) (define-record-type (point x y) point? (x get-x set-x!) (y get-y set-y!) (z get-z set-z!)) (assert:equal "Records predicate (t)" (pare? (kons 1 2)) #t) (assert:equal "Records predicate (f)" (pare? (cons 1 2)) #f) (assert:equal "Records kar" (kar (kons 1 2)) 1) (assert:equal "Records kdr" (kdr (kons 1 2)) 2) (assert:equal "Records setter" (let ((k (kons 1 2))) (set-kar! k 3) (kar k)) 3) (assert:equal "Record type predicate (t)" (record? (kons 1 2)) #t) (assert:equal "Record type predicate (f)" (record? (cons 1 2)) #f) (assert:equal "Record type field not in constructor (f)" (get-z (point 1 2)) #f) (let ((p (point 1 2))) (set-z! p 99) (assert:equal "Record type get field not in constructor" (get-z p) 99)) ;; END records ;; Lazy evaluation (assert:equal "Basic lazy" (force (delay (+ 1 2))) 3) (assert:equal "Lazy test #2" (let ((p (delay (+ 1 2)))) (list (force p) (force p))) '(3 3)) ((lambda () (define integers (letrec ((next (lambda (n) (delay (cons n (next (+ n 1))))))) (next 0))) (define head (lambda (stream) (car (force stream)))) (define tail (lambda (stream) (cdr (force stream)))) (assert:equal "Lazy #3" (head (tail (tail integers))) 2))) ; TODO: use display, output without surrounding quotes (display (list *num-passed* "tests passed with no errors")) (newline) ;;