(define-module (lang elisp primitives syntax) #:use-syntax (lang elisp expand) #:use-module (lang elisp internals evaluation) #:use-module (lang elisp internals fset) #:use-module (lang elisp internals lambda) #:use-module (lang elisp internals set) #:use-module (lang elisp internals trace) #:use-module (lang elisp transform)) ;;; Define Emacs Lisp special forms as macros. This is more flexible ;;; than handling them specially in the translator: allows them to be ;;; redefined, and hopefully allows better source location tracking. ;;; {Variables} (define (setq exp env) (cons begin (let loop ((sets (cdr exp))) (if (null? sets) '() (cons `(,set (,quote ,(car sets)) ,(transformer (cadr sets))) (loop (cddr sets))))))) (fset 'setq (procedure->memoizing-macro setq)) (fset 'defvar (procedure->memoizing-macro (lambda (exp env) (trc 'defvar (cadr exp)) (if (null? (cddr exp)) `(,quote ,(cadr exp)) `(,begin (,if (,not (,defined? (,quote ,(cadr exp)))) ,(setq (list (car exp) (cadr exp) (caddr exp)) env)) (,quote ,(cadr exp))))))) (fset 'defconst (procedure->memoizing-macro (lambda (exp env) (trc 'defconst (cadr exp)) `(,begin ,(setq (list (car exp) (cadr exp) (caddr exp)) env) (,quote ,(cadr exp)))))) ;;; {lambda, function and macro definitions} (fset 'lambda (procedure->memoizing-macro (lambda (exp env) (transform-lambda/interactive exp ')))) (fset 'defun (procedure->memoizing-macro (lambda (exp env) (trc 'defun (cadr exp)) `(,begin (,fset (,quote ,(cadr exp)) ,(transform-lambda/interactive (cdr exp) (symbol-append '))) (,quote ,(cadr exp)))))) (fset 'interactive (procedure->memoizing-macro (lambda (exp env) (fluid-set! interactive-spec exp) #f))) (fset 'defmacro (procedure->memoizing-macro (lambda (exp env) (trc 'defmacro (cadr exp)) (call-with-values (lambda () (parse-formals (caddr exp))) (lambda (required optional rest) (let ((num-required (length required)) (num-optional (length optional))) `(,begin (,fset (,quote ,(cadr exp)) (,procedure->memoizing-macro (,lambda (exp1 env1) (,trc (,quote using) (,quote ,(cadr exp))) (,let* ((%--args (,cdr exp1)) (%--num-args (,length %--args))) (,cond ((,< %--num-args ,num-required) (,error "Wrong number of args (not enough required args)")) ,@(if rest '() `(((,> %--num-args ,(+ num-required num-optional)) (,error "Wrong number of args (too many args)")))) (else (,transformer (, @bind ,(append (map (lambda (i) (list (list-ref required i) `(,list-ref %--args ,i))) (iota num-required)) (map (lambda (i) (let ((i+nr (+ i num-required))) (list (list-ref optional i) `(,if (,> %--num-args ,i+nr) (,list-ref %--args ,i+nr) ,%nil)))) (iota num-optional)) (if rest (list (list rest `(,if (,> %--num-args ,(+ num-required num-optional)) (,list-tail %--args ,(+ num-required num-optional)) ,%nil))) '())) ,@(map transformer (cdddr exp))))))))))))))))) ;;; {Sequencing} (fset 'progn (procedure->memoizing-macro (lambda (exp env) `(,begin ,@(map transformer (cdr exp)))))) (fset 'prog1 (procedure->memoizing-macro (lambda (exp env) `(,let ((%--res1 ,(transformer (cadr exp)))) ,@(map transformer (cddr exp)) %--res1)))) (fset 'prog2 (procedure->memoizing-macro (lambda (exp env) `(,begin ,(transformer (cadr exp)) (,let ((%--res2 ,(transformer (caddr exp)))) ,@(map transformer (cdddr exp)) %--res2))))) ;;; {Conditionals} (fset 'if (procedure->memoizing-macro (lambda (exp env) (let ((else-case (cdddr exp))) (cond ((null? else-case) `(,nil-cond ,(transformer (cadr exp)) ,(transformer (caddr exp)) ,%nil)) ((null? (cdr else-case)) `(,nil-cond ,(transformer (cadr exp)) ,(transformer (caddr exp)) ,(transformer (car else-case)))) (else `(,nil-cond ,(transformer (cadr exp)) ,(transformer (caddr exp)) (,begin ,@(map transformer else-case))))))))) (fset 'and (procedure->memoizing-macro (lambda (exp env) (cond ((null? (cdr exp)) #t) ((null? (cddr exp)) (transformer (cadr exp))) (else (cons nil-cond (let loop ((args (cdr exp))) (if (null? (cdr args)) (list (transformer (car args))) (cons (list not (transformer (car args))) (cons %nil (loop (cdr args)))))))))))) ;;; NIL-COND expressions have the form: ;;; ;;; (nil-cond COND VAL COND VAL ... ELSEVAL) ;;; ;;; The CONDs are evaluated in order until one of them returns true ;;; (in the Elisp sense, so not including empty lists). If a COND ;;; returns true, its corresponding VAL is evaluated and returned, ;;; except if that VAL is the unspecified value, in which case the ;;; result of evaluating the COND is returned. If none of the COND's ;;; returns true, ELSEVAL is evaluated and its value returned. (define <-- *unspecified*) (fset 'or (procedure->memoizing-macro (lambda (exp env) (cond ((null? (cdr exp)) %nil) ((null? (cddr exp)) (transformer (cadr exp))) (else (cons nil-cond (let loop ((args (cdr exp))) (if (null? (cdr args)) (list (transformer (car args))) (cons (transformer (car args)) (cons <-- (loop (cdr args)))))))))))) (fset 'cond (procedure->memoizing-macro (lambda (exp env) (if (null? (cdr exp)) %nil (cons nil-cond (let loop ((clauses (cdr exp))) (if (null? clauses) (list %nil) (let ((clause (car clauses))) (if (eq? (car clause) #t) (cond ((null? (cdr clause)) (list #t)) ((null? (cddr clause)) (list (transformer (cadr clause)))) (else `((,begin ,@(map transformer (cdr clause)))))) (cons (transformer (car clause)) (cons (cond ((null? (cdr clause)) <--) ((null? (cddr clause)) (transformer (cadr clause))) (else `(,begin ,@(map transformer (cdr clause))))) (loop (cdr clauses))))))))))))) (fset 'while (procedure->memoizing-macro (lambda (exp env) `((,letrec ((%--while (,lambda () (,nil-cond ,(transformer (cadr exp)) (,begin ,@(map transformer (cddr exp)) (%--while)) ,%nil)))) %--while))))) ;;; {Local binding} (fset 'let (procedure->memoizing-macro (lambda (exp env) `(, @bind ,(map (lambda (binding) (trc 'let binding) (if (pair? binding) `(,(car binding) ,(transformer (cadr binding))) `(,binding ,%nil))) (cadr exp)) ,@(map transformer (cddr exp)))))) (fset 'let* (procedure->memoizing-macro (lambda (exp env) (if (null? (cadr exp)) `(,begin ,@(map transformer (cddr exp))) (car (let loop ((bindings (cadr exp))) (if (null? bindings) (map transformer (cddr exp)) `((, @bind (,(let ((binding (car bindings))) (if (pair? binding) `(,(car binding) ,(transformer (cadr binding))) `(,binding ,%nil)))) ,@(loop (cdr bindings))))))))))) ;;; {Exception handling} (fset 'unwind-protect (procedure->memoizing-macro (lambda (exp env) (trc 'unwind-protect (cadr exp)) `(,let ((%--throw-args #f)) (,catch #t (,lambda () ,(transformer (cadr exp))) (,lambda args (,set! %--throw-args args))) ,@(map transformer (cddr exp)) (,if %--throw-args (,apply ,throw %--throw-args))))))