change remaining %nil -> #nil
[bpt/guile.git] / test-suite / tests / elisp-compiler.test
index fdb6771..61f0acd 100644 (file)
@@ -1,6 +1,6 @@
-;;;; elisp-compiler.test --- Test the compiler for Elisp.
+;;;; elisp-compiler.test --- Test the compiler for Elisp.  -*- scheme -*-
 ;;;;
-;;;; Copyright (C) 2009 Free Software Foundation, Inc.
+;;;; Copyright (C) 2009, 2010 Free Software Foundation, Inc.
 ;;;; Daniel Kraft
 ;;;;
 ;;;; This library is free software; you can redistribute it and/or
@@ -29,6 +29,8 @@
   (syntax-rules (pass-if pass-if-exception)
     ((_ (pass-if test-name exp))
      (pass-if test-name (compile 'exp #:from 'elisp #:to 'value)))
+    ((_ (pass-if test-name exp #:opts opts))
+     (pass-if test-name (compile 'exp #:from 'elisp #:to 'value #:opts opts)))
     ((_ (pass-if-equal test-name result exp))
      (pass-if test-name (equal? result
                                 (compile 'exp #:from 'elisp #:to 'value))))
   (pass-if-equal "progn" 1
     (progn (setq a 0)
            (setq a (1+ a))
-           a)))
+           a))
+
+  (pass-if "prog1"
+    (progn (setq a 0)
+           (setq b (prog1 a (setq a (1+ a))))
+           (and (= a 1) (= b 0))))
+
+  (pass-if "prog2"
+    (progn (setq a 0)
+           (setq b (prog2 (setq a (1+ a))
+                          (setq a (1+ a))
+                          (setq a (1+ a))))
+           (and (= a 3) (= b 2)))))
 
 (with-test-prefix/compile "Conditionals"
 
   (pass-if-equal "succeeding if" 1
     (if t 1 2))
-  (pass-if-equal "failing if" 3
-    (if nil
-      1
-      (setq a 2)
-      (setq a (1+ a))
-      a))
+  (pass-if "failing if"
+    (and (= (if nil
+              1
+              (setq a 2) (setq a (1+ a)) a)
+            3)
+         (equal (if nil 1) nil)))
+
+  (pass-if-equal "failing when" nil-value
+    (when nil 1 2 3))
+  (pass-if-equal "succeeding when" 42
+    (progn (setq a 0)
+           (when t (setq a 42) a)))
+
+  (pass-if-equal "failing unless" nil-value
+    (unless t 1 2 3))
+  (pass-if-equal "succeeding unless" 42
+    (progn (setq a 0)
+           (unless nil (setq a 42) a)))
 
   (pass-if-equal "empty cond" nil-value
     (cond))
            (while (<= i 5)
              (setq prod (* i prod))
              (setq i (1+ i)))
-           prod)))
+           prod))
+
+  (pass-if "dotimes"
+    (progn (setq a 0)
+           (setq count 100)
+           (setq b (dotimes (i count)
+                     (setq j (1+ i))
+                     (setq a (+ a j))))
+           (setq c (dotimes (i 10 42) nil))
+           (and (= a 5050) (equal b nil) (= c 42))))
+
+  (pass-if "dolist"
+    (let ((mylist '(7 2 5)))
+      (setq sum 0)
+      (setq a (dolist (i mylist)
+                (setq sum (+ sum i))))
+      (setq b (dolist (i mylist 5) 0))
+      (and (= sum (+ 7 2 5))
+           (equal a nil)
+           (equal mylist '(7 2 5))
+           (equal b 5)))))
+
+(with-test-prefix/compile "Exceptions"
+
+  (pass-if "catch without exception"
+    (and (setq a 0)
+         (= (catch 'foobar
+                   (setq a (1+ a))
+                   (setq a (1+ a))
+                   a)
+            2)
+         (= (catch (+ 1 2) a) 2)))
+
+  ; FIXME: Figure out how to do this...
+  ;(pass-if-exception "uncaught exception" 'elisp-exception
+  ;  (throw 'abc 1))
+
+  (pass-if "catch and throw"
+    (and (setq mylist '(1 2))
+         (= (catch 'abc (throw 'abc 2) 1) 2)
+         (= (catch 'abc (catch 'def (throw 'abc (1+ 0)) 2) 3) 1)
+         (= (catch 'abc (catch 'def (throw 'def 1) 2) 3) 3)
+         (= (catch mylist (catch '(1 2) (throw mylist 1) 2) 3) 1)))
+
+  (pass-if "unwind-protect"
+    (progn (setq a 0 b 1 c 1)
+           (catch 'exc
+                  (unwind-protect (progn (setq a 1)
+                                         (throw 'exc 0))
+                                  (setq a 0)
+                                  (setq b 0)))
+           (unwind-protect nil (setq c 0))
+           (and (= a 0) (= b 0) (= c 0)
+                (= (unwind-protect 42 1 2 3) 42)))))
+
+(with-test-prefix/compile "Eval"
+
+  (pass-if-equal "basic eval" 3
+    (progn (setq code '(+ 1 2))
+           (eval code)))
+
+  (pass-if "real dynamic code"
+    (and (setq a 1 b 1 c 1)
+         (defun set-code (var val)
+           (list 'setq var val))
+         (= a 1) (= b 1) (= c 1)
+         (eval (set-code 'a '(+ 2 3)))
+         (eval (set-code 'c 42))
+         (= a 5) (= b 1) (= c 42)))
+
+  ; Build code that recursively again and again calls eval.  What we want is
+  ; something like:
+  ; (eval '(1+ (eval '(1+ (eval 1)))))
+  (pass-if "recursive eval"
+    (progn (setq depth 10 i depth)
+           (setq code '(eval 0))
+           (while (not (zerop i))
+             (setq code (\` (eval (quote (1+ (\, code))))))
+             (setq i (1- i)))
+           (= (eval code) depth))))
 
 
 ; Test handling of variables.
   ; TODO: Check for variable-void error
 
   (pass-if-equal "setq and reference" 6
-    (progn (setq a 1
-                 b 2
-                 c 3)
-           (+ a b c))))
+    (progn (setq a 1 b 2 c 3)
+           (+ a b c)))
+  (pass-if-equal "setq evaluation order" 1
+    (progn (setq a 0 b 0)
+           (setq a 1 b a)))
+  (pass-if-equal "setq value" 2
+    (progn (setq a 1 b 2)))
+
+  (pass-if "set and symbol-value"
+    (progn (setq myvar 'a)
+           (and (= (set myvar 42) 42)
+                (= a 42)
+                (= (symbol-value myvar) 42))))
+  (pass-if "void variables"
+    (progn (setq a 1 b 2)
+           (and (eq (makunbound 'b) 'b)
+                (boundp 'a)
+                (not (boundp 'b)))))
+
+  (pass-if "disabled void check (all)"
+    (progn (makunbound 'a) a t)
+    #:opts '(#:disable-void-check all))
+  (pass-if "disabled void check (symbol list)"
+    (progn (makunbound 'a) a t)
+    #:opts '(#:disable-void-check (x y a b)))
+  (pass-if "without-void-checks"
+    (progn (makunbound 'a)
+           (= (without-void-checks (a) a 5) 5))))
 
 (with-test-prefix/compile "Let and Let*"
 
            (let ((a 1)
                  (b a))
              b)))
-  (pass-if-equal "let*" 1
+
+  (pass-if "let*"
     (progn (setq a 0)
-           (let* ((a 1)
-                  (b a))
-             b)))
+           (and (let* ((a 1)
+                       (b a))
+                  (= b 1))
+                (let* (a b)
+                  (setq a 1 b 2)
+                  (and (= a 1) (= b 2)))
+                (= a 0)
+                (not (boundp 'b)))))
 
   (pass-if "local scope"
     (progn (setq a 0)
            (and (= a 0)
                 (= b 1)))))
 
+(with-test-prefix/compile "Lexical Scoping"
+
+  (pass-if "basic let semantics"
+    (and (setq a 1)
+         (lexical-let ((a 2) (b a))
+           (and (= a 2) (= b 1)))
+         (lexical-let* ((a 2) (b a))
+           (and (= a 2) (= b 2) (setq a 42) (= a 42)))
+         (= a 1)))
+
+  (pass-if "lexical scope with lexical-let's"
+    (and (setq a 1)
+         (defun dyna () a)
+         (lexical-let (a)
+           (setq a 2)
+           (and (= a 2) (= (dyna) 1)))
+         (= a 1)
+         (lexical-let* (a)
+           (setq a 2)
+           (and (= a 2) (= (dyna) 1)))
+         (= a 1)))
+
+  (pass-if "lexical scoping vs. symbol-value / set"
+    (and (setq a 1)
+         (lexical-let ((a 2))
+           (and (= a 2)
+                (= (symbol-value 'a) 1)
+                (set 'a 3)
+                (= a 2)
+                (= (symbol-value 'a) 3)))
+         (= a 3)))
+
+  (pass-if "let inside lexical-let"
+    (and (setq a 1 b 1)
+         (defun dynvals () (cons a b))
+         (lexical-let ((a 2))
+           (and (= a 2) (equal (dynvals) '(1 . 1))
+                (let ((a 3) (b a))
+                  (and (= a 3) (= b 2)
+                       (equal (dynvals) '(1 . 2))))
+                (let* ((a 4) (b a))
+                  (and (= a 4) (= b 4)
+                       (equal (dynvals) '(1 . 4))))
+                (= a 2)))
+         (= a 1)))
+
+  (pass-if "lambda args inside lexical-let"
+    (and (setq a 1)
+         (defun dyna () a)
+         (lexical-let ((a 2) (b 42))
+           (and (= a 2) (= (dyna) 1)
+                ((lambda (a) (and (= a 3) (= b 42) (= (dyna) 3))) 3)
+                ((lambda () (let ((a 3))
+                              (and (= a 3) (= (dyna) 1)))))
+                (= a 2) (= (dyna) 1)))
+         (= a 1)))
+
+  (pass-if "closures"
+    (and (defun make-counter ()
+           (lexical-let ((cnt 0))
+             (lambda ()
+               (setq cnt (1+ cnt)))))
+         (setq c1 (make-counter) c2 (make-counter))
+         (= (funcall c1) 1)
+         (= (funcall c1) 2)
+         (= (funcall c1) 3)
+         (= (funcall c2) 1)
+         (= (funcall c2) 2)
+         (= (funcall c1) 4)
+         (= (funcall c2) 3)))
+
+  (pass-if "always lexical option (all)"
+    (progn (setq a 0)
+           (defun dyna () a)
+           (let ((a 1))
+             (and (= a 1) (= (dyna) 0))))
+    #:opts '(#:always-lexical all))
+  (pass-if "always lexical option (list)"
+    (progn (setq a 0 b 0)
+           (defun dyna () a)
+           (defun dynb () b)
+           (let ((a 1)
+                 (b 1))
+             (and (= a 1) (= (dyna) 0)
+                  (= b 1) (= (dynb) 1))))
+    #:opts '(#:always-lexical (a)))
+  (pass-if "with-always-lexical"
+    (progn (setq a 0)
+           (defun dyna () a)
+           (with-always-lexical (a)
+             (let ((a 1))
+               (and (= a 1) (= (dyna) 0))))))
+
+  (pass-if "lexical lambda args"
+    (progn (setq a 1 b 1)
+           (defun dyna () a)
+           (defun dynb () b)
+           (with-always-lexical (a c)
+             ((lambda (a b &optional c)
+                (and (= a 3) (= (dyna) 1)
+                     (= b 2) (= (dynb) 2)
+                     (= c 1)))
+              3 2 1))))
+
+  ; Check if a lambda without dynamically bound arguments
+  ; is tail-optimized by doing a deep recursion that would otherwise overflow
+  ; the stack.
+  (pass-if "lexical lambda tail-recursion"
+    (with-always-lexical (i)
+      (setq to 1000000)
+      (defun iteration-1 (i)
+        (if (< i to)
+          (iteration-1 (1+ i))))
+      (iteration-1 0)
+      (setq x 0)
+      (defun iteration-2 ()
+        (if (< x to)
+          (setq x (1+ x))
+          (iteration-2)))
+      (iteration-2)
+      t)))
+
+
 (with-test-prefix/compile "defconst and defvar"
 
   (pass-if-equal "defconst without docstring" 3.141
     (progn (setq a 42)
            (defvar a 1 "Some docstring is also ok")
            a))
-  ; FIXME: makunbound a!
   (pass-if-equal "defvar on undefined variable" 1
-    (progn (defvar a 1)
+    (progn (makunbound 'a)
+           (defvar a 1)
            a))
   (pass-if-equal "defvar value" 'a
     (defvar a)))
     (progn (defun test (a b) (+ a b))
            (test 1 2)))
   (pass-if-equal "defun value" 'test
-    (defun test (a b) (+ a b))))
+    (defun test (a b) (+ a b)))
+
+  (pass-if "fset and symbol-function"
+    (progn (setq myfunc 'x x 5)
+           (and (= (fset myfunc 42) 42)
+                (= (symbol-function myfunc) 42)
+                (= x 5))))
+  (pass-if "void function values"
+    (progn (setq a 1)
+           (defun test (a b) (+ a b))
+           (fmakunbound 'a)
+           (fset 'b 5)
+           (and (fboundp 'b) (fboundp 'test)
+                (not (fboundp 'a))
+                (= a 1))))
+
+  (pass-if "flet and flet*"
+    (progn (defun foobar () 42)
+           (defun test () (foobar))
+           (and (= (test) 42)
+                (flet ((foobar (lambda () 0))
+                       (myfoo (symbol-function 'foobar)))
+                  (and (= (myfoo) 42)
+                       (= (test) 0)))
+                (flet* ((foobar (lambda () 0))
+                        (myfoo (symbol-function 'foobar)))
+                  (= (myfoo) 0))
+                (flet (foobar)
+                  (defun foobar () 0)
+                  (= (test) 0))
+                (= (test) 42)))))
 
 (with-test-prefix/compile "Calling Functions"
 
            (defun bar (a)
              (foo))
            (and (= 43 (bar 42))
-                (zerop a)))))
+                (zerop a))))
+
+  (pass-if "funcall and apply argument handling"
+    (and (defun allid (&rest args) args)
+         (setq allid-var (symbol-function 'allid))
+         (equal (funcall allid-var 1 2 3) '(1 2 3))
+         (equal (funcall allid-var) nil)
+         (equal (funcall allid-var 1 2 '(3 4)) '(1 2 (3 4)))
+         (equal (funcall allid-var '()) '(()))
+         (equal (apply allid-var 1 2 '(3 4)) '(1 2 3 4))
+         (equal (apply allid-var '(1 2)) '(1 2))
+         (equal (apply allid-var '()) nil)))
+
+  (pass-if "raw functions with funcall"
+    (and (= (funcall '+ 1 2) 3)
+         (= (funcall (lambda (a b) (+ a b)) 1 2) 3)
+         (= (funcall '(lambda (a b) (+ a b)) 1 2) 3))))
+
+
+; Quoting and Backquotation.
+; ==========================
+
+(with-test-prefix/compile "Quotation"
+
+  (pass-if "quote"
+    (and (equal '42 42) (equal '"abc" "abc")
+         (equal '(1 2 (3 (4) x)) '(1 2 (3 (4) x)))
+         (not (equal '(1 2 (3 4 (x))) '(1 2 3 4 x)))
+         (equal '(1 2 . 3) '(1 2 . 3))))
+
+  (pass-if "simple backquote"
+    (and (equal (\` 42) 42)
+         (equal (\` (1 (a))) '(1 (a)))
+         (equal (\` (1 . 2)) '(1 . 2))))
+  (pass-if "unquote"
+    (progn (setq a 42 l '(18 12))
+           (and (equal (\` (\, a)) 42)
+                (equal (\` (1 a ((\, l)) . (\, a))) '(1 a ((18 12)) . 42)))))
+  (pass-if "unquote splicing"
+    (progn (setq l '(18 12) empty '())
+           (and (equal (\` (\,@ l)) '(18 12))
+                (equal (\` (l 2 (3 (\,@ l)) ((\,@ l)) (\,@ l)))
+                       '(l 2 (3 18 12) (18 12) 18 12))
+                (equal (\` (1 2 (\,@ empty) 3)) '(1 2 3))))))
+      
+
+
+; Macros.
+; =======
+
+(with-test-prefix/compile "Macros"
+
+  (pass-if-equal "defmacro value" 'magic-number
+    (defmacro magic-number () 42))
+
+  (pass-if-equal "macro expansion" 1
+    (progn (defmacro take-first (a b) a)
+           (take-first 1 (/ 1 0)))))
 
 
 ; Test the built-ins.
 ; ===================
 
+(with-test-prefix/compile "Equivalence Predicates"
+
+  (pass-if "equal"
+    (and (equal 2 2) (not (equal 1 2))
+         (equal "abc" "abc") (not (equal "abc" "ABC"))
+         (equal 'abc 'abc) (not (equal 'abc 'def))
+         (equal '(1 2 (3 4) 5) '(1 2 (3 4) 5))
+         (not (equal '(1 2 3 4 5) '(1 2 (3 4) 5)))))
+
+  (pass-if "eq"
+    (progn (setq some-list '(1 2))
+           (setq some-string "abc")
+           (and (eq 2 2) (not (eq 1 2))
+                (eq 'abc 'abc) (not (eq 'abc 'def))
+                (eq some-string some-string) (not (eq some-string "abc"))
+                (eq some-list some-list) (not (eq some-list '(1 2)))))))
+
 (with-test-prefix/compile "Number Built-Ins"
 
   (pass-if "floatp"
          (= (fceiling 1.2) 2.0) (= (fceiling -1.7) -1.0) (= (fceiling 1.0) 1.0)
          (= (ftruncate 1.6) 1.0) (= (ftruncate -1.7) -1.0)
          (= (fround 1.2) 1.0) (= (fround 1.7) 2.0) (= (fround -1.7) -2.0))))
+
+(with-test-prefix/compile "List Built-Ins"
+
+  (pass-if "consp and atomp"
+    (and (consp '(1 2 3)) (consp '(1 2 . 3)) (consp '(a . b))
+         (not (consp '())) (not (consp 1)) (not (consp "abc"))
+         (atomp 'a) (atomp '()) (atomp -1.5) (atomp "abc")
+         (not (atomp '(1 . 2))) (not (atomp '(1)))))
+  (pass-if "listp and nlistp"
+    (and (listp '(1 2 3)) (listp '(1)) (listp '()) (listp '(1 . 2))
+         (not (listp 'a)) (not (listp 42)) (nlistp 42)
+         (not (nlistp '())) (not (nlistp '(1 2 3))) (not (nlistp '(1 . 2)))))
+  (pass-if "null"
+    (and (null '()) (not (null 1)) (not (null '(1 2))) (not (null '(1 . 2)))))
+
+  (pass-if "car and cdr"
+    (and (equal (car '(1 2 3)) 1) (equal (cdr '(1 2 3)) '(2 3))
+         (equal (car '()) nil) (equal (cdr '()) nil)
+         (equal (car '(1 . 2)) 1) (equal (cdr '(1 . 2)) 2)
+         (null (cdr '(1)))))
+  (pass-if "car-safe and cdr-safe"
+    (and (equal (car-safe '(1 2)) 1) (equal (cdr-safe '(1 2)) '(2))
+         (equal (car-safe 5) nil) (equal (cdr-safe 5) nil)))
+
+  (pass-if "pop"
+    (progn (setq mylist '(a b c))
+           (setq value (pop mylist))
+           (and (equal value 'a)
+                (equal mylist '(b c)))))
+  (pass-if-equal "push" '(a b c)
+    (progn (setq mylist '(b c))
+           (push 'a mylist)))
+
+  (pass-if "nth and nthcdr"
+    (and (equal (nth -5 '(1 2 3)) 1) (equal (nth 3 '(1 2 3)) nil)
+         (equal (nth 0 '(1 2 3)) 1) (equal (nth 2 '(1 2 3)) 3)
+         (equal (nthcdr -5 '(1 2 3)) '(1 2 3))
+         (equal (nthcdr 4 '(1 2 3)) nil)
+         (equal (nthcdr 1 '(1 2 3)) '(2 3))
+         (equal (nthcdr 2 '(1 2 3)) '(3))))
+
+  (pass-if "length"
+    (and (= (length '()) 0)
+         (= (length '(1 2 3 4 5)) 5)
+         (= (length '(1 2 (3 4 (5)) 6)) 4)))
+
+  (pass-if "cons, list and make-list"
+    (and (equal (cons 1 2) '(1 . 2)) (equal (cons 1 '(2 3)) '(1 2 3))
+         (equal (cons 1 '()) '(1))
+         (equal (list 'a) '(a)) (equal (list) '()) (equal (list 1 2) '(1 2))
+         (equal (make-list 3 42) '(42 42 42))
+         (equal (make-list 0 1) '())))
+  (pass-if "append"
+    (and (equal (append '(1 2) '(3 4) '(5)) '(1 2 3 4 5))
+         (equal (append '(1 2) 3) '(1 2 . 3))))
+  (pass-if "reverse"
+    (and (equal (reverse '(5 4 3 2 1)) '(1 2 3 4 5))
+         (equal (reverse '()) '())))
+  (pass-if "copy-tree"
+    (progn (setq mylist '(1 2 (3 4)))
+           (and (not (eq mylist (copy-tree mylist)))
+                (equal mylist (copy-tree mylist)))))
+
+  (pass-if "number-sequence"
+    (and (equal (number-sequence 5) '(5))
+         (equal (number-sequence 5 9) '(5 6 7 8 9))
+         (equal (number-sequence 5 9 3) '(5 8))
+         (equal (number-sequence 5 1 -2) '(5 3 1))
+         (equal (number-sequence 5 8 -1) '())
+         (equal (number-sequence 5 1) '())
+         (equal (number-sequence 5 5 0) '(5))))
+
+  (pass-if "setcar and setcdr"
+    (progn (setq pair '(1 . 2))
+           (setq copy pair)
+           (setq a (setcar copy 3))
+           (setq b (setcdr copy 4))
+           (and (= a 3) (= b 4)
+                (equal pair '(3 . 4))))))