-;;;; 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
(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))))
(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.
; ===========================
(pass-if-equal "setq and reference" 6
(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))))
+ (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.
(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))