diff options
-rw-r--r-- | module/language/elisp/README | 9 | ||||
-rw-r--r-- | module/language/elisp/runtime.scm | 29 | ||||
-rw-r--r-- | module/language/elisp/runtime/macro-slot.scm | 39 | ||||
-rw-r--r-- | test-suite/tests/elisp-compiler.test | 35 |
4 files changed, 92 insertions, 20 deletions
diff --git a/module/language/elisp/README b/module/language/elisp/README index 684677bf4..8068351e7 100644 --- a/module/language/elisp/README +++ b/module/language/elisp/README @@ -6,10 +6,10 @@ as status information. Already implemented: * progn - * if, cond - * and, or + * if, cond, when, unless + * not, and, or * referencing and setting (setq) variables - * while + * while, dotimes * let, let* * lambda expressions, function calls using list notation * some built-ins (mainly numbers/arithmetic) @@ -19,8 +19,7 @@ Already implemented: Especially still missing: * other progX forms, will be done in macros - * where, unless, will be done in macros - * dolist, dotimes using macros + * dolist using macros * catch/throw, unwind-protect * real elisp reader instead of Scheme's * set, makunbound, boundp functions diff --git a/module/language/elisp/runtime.scm b/module/language/elisp/runtime.scm index afdf59152..2bdadf9b4 100644 --- a/module/language/elisp/runtime.scm +++ b/module/language/elisp/runtime.scm @@ -20,8 +20,8 @@ ;;; Code: (define-module (language elisp runtime) - #:export (void nil-value t-value elisp-bool) - #:export-syntax (built-in-func)) + #:export (void nil-value t-value elisp-bool macro-error) + #:export-syntax (built-in-func built-in-macro)) ; This module provides runtime support for the Elisp front-end. @@ -38,6 +38,13 @@ (define t-value #t) +; Report an error during macro compilation, that means some special compilation +; (syntax) error. + +(define (macro-error msg . args) + (apply error msg args)) + + ; Convert a scheme boolean to Elisp. (define (elisp-bool b) @@ -46,9 +53,17 @@ nil-value)) -; Define a predefined function; convenient macro for this task. +; Define a predefined function or predefined macro for use in the function-slot +; and macro-slot modules, respectively. + +(define-syntax built-in-func + (syntax-rules () + ((_ name value) + (begin + (define-public name (make-fluid)) + (fluid-set! name value))))) -(define-macro (built-in-func name value) - `(begin - (define-public ,name (make-fluid)) - (fluid-set! ,name ,value))) +(define-syntax built-in-macro + (syntax-rules () + ((_ name value) + (define-public name value)))) diff --git a/module/language/elisp/runtime/macro-slot.scm b/module/language/elisp/runtime/macro-slot.scm index d17b6f414..9d0477be9 100644 --- a/module/language/elisp/runtime/macro-slot.scm +++ b/module/language/elisp/runtime/macro-slot.scm @@ -19,9 +19,46 @@ ;;; Code: -(define-module (language elisp runtime macro-slot)) +(define-module (language elisp runtime macro-slot) + #:use-module (language elisp runtime)) ; This module contains the macro definitions of elisp symbols. In contrast to ; the other runtime modules, those are used directly during compilation, of ; course, so not really in runtime. But I think it fits well to the others ; here. + + +; Define the conditionals when and unless as macros. + +(built-in-macro when + (lambda (condition . thens) + `(if ,condition (progn ,@thens) nil))) + +(built-in-macro unless + (lambda (condition . elses) + `(if ,condition nil (progn ,@elses)))) + + +; Define the dotimes and dolist iteration macros. +; As the variable has to be bound locally for elisp, this needs to go through +; the dynamic scoping fluid system. So we can't speed these forms up by +; implementing them directly in the compiler with just a lexical variable +; anyways. + +(built-in-macro dotimes + (lambda (args . body) + (if (or (not (list? args)) + (< (length args) 2) + (> (length args) 3)) + (macro-error "invalid dotimes arguments" args) + (let ((var (car args)) + (count (cadr args))) + (if (not (symbol? var)) + (macro-error "expected symbol as dotimes variable")) + `(let ((,var 0)) + (while (< ,var ,count) + ,@body + (setq ,var (1+ ,var))) + ,@(if (= (length args) 3) + (list (caddr args)) + '())))))) diff --git a/test-suite/tests/elisp-compiler.test b/test-suite/tests/elisp-compiler.test index 1705a971e..d36f47c77 100644 --- a/test-suite/tests/elisp-compiler.test +++ b/test-suite/tests/elisp-compiler.test @@ -56,12 +56,24 @@ (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)) @@ -101,7 +113,16 @@ (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))))) ; Test handling of variables. |