mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-05-01 20:30:28 +02:00
* module/language/elisp/compile-tree-il.scm: Update for changes to tree-il (lambda-case, mainly). * module/language/elisp/spec.scm: Update GPL version to 3. Update reader for new taking a port and environment argument. * libguile/_scm.h: Bump objcode version. * libguile/vm-i-system.c: Fix conflicts. * module/Makefile.am: Fix conflicts, and add elisp modules to the build.
890 lines
36 KiB
Scheme
890 lines
36 KiB
Scheme
;;; Guile Emacs Lisp
|
|
|
|
;; Copyright (C) 2009 Free Software Foundation, Inc.
|
|
|
|
;; This program is free software; you can redistribute it and/or modify
|
|
;; it under the terms of the GNU General Public License as published by
|
|
;; the Free Software Foundation; either version 3, or (at your option)
|
|
;; any later version.
|
|
;;
|
|
;; This program is distributed in the hope that it will be useful,
|
|
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
;; GNU General Public License for more details.
|
|
;;
|
|
;; You should have received a copy of the GNU General Public License
|
|
;; along with this program; see the file COPYING. If not, write to
|
|
;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
;; Boston, MA 02111-1307, USA.
|
|
|
|
;;; Code:
|
|
|
|
(define-module (language elisp compile-tree-il)
|
|
#:use-module (language elisp bindings)
|
|
#:use-module (language tree-il)
|
|
#:use-module (system base pmatch)
|
|
#:use-module (system base compile)
|
|
#:use-module (srfi srfi-1)
|
|
#:export (compile-tree-il))
|
|
|
|
|
|
; Certain common parameters (like the bindings data structure or compiler
|
|
; options) are not always passed around but accessed using fluids to simulate
|
|
; dynamic binding (hey, this is about elisp).
|
|
|
|
; The bindings data structure to keep track of symbol binding related data.
|
|
(define bindings-data (make-fluid))
|
|
|
|
; Store for which symbols (or all/none) void checks are disabled.
|
|
(define disable-void-check (make-fluid))
|
|
|
|
; Store which symbols (or all/none) should always be bound lexically, even
|
|
; with ordinary let and as lambda arguments.
|
|
(define always-lexical (make-fluid))
|
|
|
|
|
|
; Find the source properties of some parsed expression if there are any
|
|
; associated with it.
|
|
|
|
(define (location x)
|
|
(and (pair? x)
|
|
(let ((props (source-properties x)))
|
|
(and (not (null? props))
|
|
props))))
|
|
|
|
|
|
; Values to use for Elisp's nil and t.
|
|
|
|
(define (nil-value loc) (make-const loc (@ (language elisp runtime) nil-value)))
|
|
(define (t-value loc) (make-const loc (@ (language elisp runtime) t-value)))
|
|
|
|
|
|
; Modules that contain the value and function slot bindings.
|
|
|
|
(define runtime '(language elisp runtime))
|
|
(define macro-slot '(language elisp runtime macro-slot))
|
|
(define value-slot (@ (language elisp runtime) value-slot-module))
|
|
(define function-slot (@ (language elisp runtime) function-slot-module))
|
|
|
|
|
|
; The backquoting works the same as quasiquotes in Scheme, but the forms are
|
|
; named differently; to make easy adaptions, we define these predicates checking
|
|
; for a symbol being the car of an unquote/unquote-splicing/backquote form.
|
|
|
|
(define (backquote? sym)
|
|
(and (symbol? sym) (eq? sym '\`)))
|
|
|
|
(define (unquote? sym)
|
|
(and (symbol? sym) (eq? sym '\,)))
|
|
|
|
(define (unquote-splicing? sym)
|
|
(and (symbol? sym) (eq? sym '\,@)))
|
|
|
|
|
|
; Build a call to a primitive procedure nicely.
|
|
|
|
(define (call-primitive loc sym . args)
|
|
(make-application loc (make-primitive-ref loc sym) args))
|
|
|
|
|
|
; Error reporting routine for syntax/compilation problems or build code for
|
|
; a runtime-error output.
|
|
|
|
(define (report-error loc . args)
|
|
(apply error args))
|
|
|
|
(define (runtime-error loc msg . args)
|
|
(make-application loc (make-primitive-ref loc 'error)
|
|
(cons (make-const loc msg) args)))
|
|
|
|
|
|
; Generate code to ensure a global symbol is there for further use of a given
|
|
; symbol. In general during the compilation, those needed are only tracked with
|
|
; the bindings data structure. Afterwards, however, for all those needed
|
|
; symbols the globals are really generated with this routine.
|
|
|
|
(define (generate-ensure-global loc sym module)
|
|
(make-application loc (make-module-ref loc runtime 'ensure-fluid! #t)
|
|
(list (make-const loc module)
|
|
(make-const loc sym))))
|
|
|
|
|
|
; See if we should do a void-check for a given variable. That means, check
|
|
; that this check is not disabled via the compiler options for this symbol.
|
|
; Disabling of void check is only done for the value-slot module!
|
|
|
|
(define (want-void-check? sym module)
|
|
(let ((disabled (fluid-ref disable-void-check)))
|
|
(or (not (equal? module value-slot))
|
|
(and (not (eq? disabled 'all))
|
|
(not (memq sym disabled))))))
|
|
|
|
|
|
; Build a construct that establishes dynamic bindings for certain variables.
|
|
; We may want to choose between binding with fluids and with-fluids* and
|
|
; using just ordinary module symbols and setting/reverting their values with
|
|
; a dynamic-wind.
|
|
|
|
(define (let-dynamic loc syms module vals body)
|
|
(call-primitive loc 'with-fluids*
|
|
(make-application loc (make-primitive-ref loc 'list)
|
|
(map (lambda (sym)
|
|
(make-module-ref loc module sym #t))
|
|
syms))
|
|
(make-application loc (make-primitive-ref loc 'list) vals)
|
|
(make-lambda loc '()
|
|
(make-lambda-case #f '() #f #f #f '() '() body #f))))
|
|
|
|
|
|
; Handle access to a variable (reference/setting) correctly depending on
|
|
; whether it is currently lexically or dynamically bound.
|
|
; lexical access is done only for references to the value-slot module!
|
|
|
|
(define (access-variable loc sym module handle-lexical handle-dynamic)
|
|
(let ((lexical (get-lexical-binding (fluid-ref bindings-data) sym)))
|
|
(if (and lexical (equal? module value-slot))
|
|
(handle-lexical lexical)
|
|
(handle-dynamic))))
|
|
|
|
|
|
; Generate code to reference a variable.
|
|
; For references in the value-slot module, we may want to generate a lexical
|
|
; reference instead if the variable has a lexical binding.
|
|
|
|
(define (reference-variable loc sym module)
|
|
(access-variable loc sym module
|
|
(lambda (lexical)
|
|
(make-lexical-ref loc lexical lexical))
|
|
(lambda ()
|
|
(mark-global-needed! (fluid-ref bindings-data) sym module)
|
|
(call-primitive loc 'fluid-ref
|
|
(make-module-ref loc module sym #t)))))
|
|
|
|
|
|
; Reference a variable and error if the value is void.
|
|
|
|
(define (reference-with-check loc sym module)
|
|
(if (want-void-check? sym module)
|
|
(let ((var (gensym)))
|
|
(make-let loc '(value) `(,var) `(,(reference-variable loc sym module))
|
|
(make-conditional loc
|
|
(call-primitive loc 'eq?
|
|
(make-module-ref loc runtime 'void #t)
|
|
(make-lexical-ref loc 'value var))
|
|
(runtime-error loc "variable is void:" (make-const loc sym))
|
|
(make-lexical-ref loc 'value var))))
|
|
(reference-variable loc sym module)))
|
|
|
|
|
|
; Generate code to set a variable.
|
|
; Just as with reference-variable, in case of a reference to value-slot,
|
|
; we want to generate a lexical set when the variable has a lexical binding.
|
|
|
|
(define (set-variable! loc sym module value)
|
|
(access-variable loc sym module
|
|
(lambda (lexical)
|
|
(make-lexical-set loc lexical lexical value))
|
|
(lambda ()
|
|
(mark-global-needed! (fluid-ref bindings-data) sym module)
|
|
(call-primitive loc 'fluid-set!
|
|
(make-module-ref loc module sym #t)
|
|
value))))
|
|
|
|
|
|
; Process the bindings part of a let or let* expression; that is, check for
|
|
; correctness and bring it to the form ((sym1 . val1) (sym2 . val2) ...).
|
|
|
|
(define (process-let-bindings loc bindings)
|
|
(map (lambda (b)
|
|
(if (symbol? b)
|
|
(cons b 'nil)
|
|
(if (or (not (list? b))
|
|
(not (= (length b) 2)))
|
|
(report-error loc "expected symbol or list of 2 elements in let")
|
|
(if (not (symbol? (car b)))
|
|
(report-error loc "expected symbol in let")
|
|
(cons (car b) (cadr b))))))
|
|
bindings))
|
|
|
|
|
|
; Split the let bindings into a list to be done lexically and one dynamically.
|
|
; A symbol will be bound lexically if and only if:
|
|
; We're processing a lexical-let (i.e. module is 'lexical), OR
|
|
; we're processing a value-slot binding AND
|
|
; the symbol is already lexically bound or it is always lexical.
|
|
|
|
(define (bind-lexically? sym module)
|
|
(or (eq? module 'lexical)
|
|
(and (equal? module value-slot)
|
|
(let ((always (fluid-ref always-lexical)))
|
|
(or (eq? always 'all)
|
|
(memq sym always)
|
|
(get-lexical-binding (fluid-ref bindings-data) sym))))))
|
|
|
|
(define (split-let-bindings bindings module)
|
|
(let iterate ((tail bindings)
|
|
(lexical '())
|
|
(dynamic '()))
|
|
(if (null? tail)
|
|
(values (reverse lexical) (reverse dynamic))
|
|
(if (bind-lexically? (caar tail) module)
|
|
(iterate (cdr tail) (cons (car tail) lexical) dynamic)
|
|
(iterate (cdr tail) lexical (cons (car tail) dynamic))))))
|
|
|
|
|
|
; Compile let and let* expressions. The code here is used both for let/let*
|
|
; and flet/flet*, just with a different bindings module.
|
|
;
|
|
; A special module value 'lexical means that we're doing a lexical-let instead
|
|
; and the bindings should not be saved to globals at all but be done with the
|
|
; lexical framework instead.
|
|
|
|
; Let is done with a single call to let-dynamic binding them locally to new
|
|
; values all "at once". If there is at least one variable to bind lexically
|
|
; among the bindings, we first do a let for all of them to evaluate all
|
|
; values before any bindings take place, and then call let-dynamic for the
|
|
; variables to bind dynamically.
|
|
(define (generate-let loc module bindings body)
|
|
(let ((bind (process-let-bindings loc bindings)))
|
|
(call-with-values
|
|
(lambda ()
|
|
(split-let-bindings bind module))
|
|
(lambda (lexical dynamic)
|
|
(for-each (lambda (sym)
|
|
(mark-global-needed! (fluid-ref bindings-data) sym module))
|
|
(map car dynamic))
|
|
(let ((make-values (lambda (for)
|
|
(map (lambda (el)
|
|
(compile-expr (cdr el)))
|
|
for)))
|
|
(make-body (lambda ()
|
|
(make-sequence loc (map compile-expr body)))))
|
|
(if (null? lexical)
|
|
(let-dynamic loc (map car dynamic) module
|
|
(make-values dynamic) (make-body))
|
|
(let* ((lexical-syms (map (lambda (el) (gensym)) lexical))
|
|
(dynamic-syms (map (lambda (el) (gensym)) dynamic))
|
|
(all-syms (append lexical-syms dynamic-syms))
|
|
(vals (append (make-values lexical) (make-values dynamic))))
|
|
(make-let loc all-syms all-syms vals
|
|
(with-lexical-bindings (fluid-ref bindings-data)
|
|
(map car lexical) lexical-syms
|
|
(lambda ()
|
|
(if (null? dynamic)
|
|
(make-body)
|
|
(let-dynamic loc (map car dynamic) module
|
|
(map (lambda (sym)
|
|
(make-lexical-ref loc sym sym))
|
|
dynamic-syms)
|
|
(make-body)))))))))))))
|
|
|
|
|
|
; Let* is compiled to a cascaded set of "small lets" for each binding in turn
|
|
; so that each one already sees the preceding bindings.
|
|
(define (generate-let* loc module bindings body)
|
|
(let ((bind (process-let-bindings loc bindings)))
|
|
(begin
|
|
(for-each (lambda (sym)
|
|
(if (not (bind-lexically? sym module))
|
|
(mark-global-needed! (fluid-ref bindings-data) sym module)))
|
|
(map car bind))
|
|
(let iterate ((tail bind))
|
|
(if (null? tail)
|
|
(make-sequence loc (map compile-expr body))
|
|
(let ((sym (caar tail))
|
|
(value (compile-expr (cdar tail))))
|
|
(if (bind-lexically? sym module)
|
|
(let ((target (gensym)))
|
|
(make-let loc `(,target) `(,target) `(,value)
|
|
(with-lexical-bindings (fluid-ref bindings-data)
|
|
`(,sym) `(,target)
|
|
(lambda ()
|
|
(iterate (cdr tail))))))
|
|
(let-dynamic loc
|
|
`(,(caar tail)) module `(,value)
|
|
(iterate (cdr tail))))))))))
|
|
|
|
|
|
; Split the argument list of a lambda expression into required, optional and
|
|
; rest arguments and also check it is actually valid.
|
|
; Additionally, we create a list of all "local variables" (that is, required,
|
|
; optional and rest arguments together) and also this one split into those to
|
|
; be bound lexically and dynamically.
|
|
; Returned is as multiple values: required optional rest lexical dynamic
|
|
|
|
(define (bind-arg-lexical? arg)
|
|
(let ((always (fluid-ref always-lexical)))
|
|
(or (eq? always 'all)
|
|
(memq arg always))))
|
|
|
|
(define (split-lambda-arguments loc args)
|
|
(let iterate ((tail args)
|
|
(mode 'required)
|
|
(required '())
|
|
(optional '())
|
|
(lexical '())
|
|
(dynamic '()))
|
|
(cond
|
|
|
|
((null? tail)
|
|
(let ((final-required (reverse required))
|
|
(final-optional (reverse optional))
|
|
(final-lexical (reverse lexical))
|
|
(final-dynamic (reverse dynamic)))
|
|
(values final-required final-optional #f
|
|
final-lexical final-dynamic)))
|
|
|
|
((and (eq? mode 'required)
|
|
(eq? (car tail) '&optional))
|
|
(iterate (cdr tail) 'optional required optional lexical dynamic))
|
|
|
|
((eq? (car tail) '&rest)
|
|
(if (or (null? (cdr tail))
|
|
(not (null? (cddr tail))))
|
|
(report-error loc "expected exactly one symbol after &rest")
|
|
(let* ((rest (cadr tail))
|
|
(rest-lexical (bind-arg-lexical? rest))
|
|
(final-required (reverse required))
|
|
(final-optional (reverse optional))
|
|
(final-lexical (reverse (if rest-lexical
|
|
(cons rest lexical)
|
|
lexical)))
|
|
(final-dynamic (reverse (if rest-lexical
|
|
dynamic
|
|
(cons rest dynamic)))))
|
|
(values final-required final-optional rest
|
|
final-lexical final-dynamic))))
|
|
|
|
(else
|
|
(if (not (symbol? (car tail)))
|
|
(report-error loc "expected symbol in argument list, got" (car tail))
|
|
(let* ((arg (car tail))
|
|
(bind-lexical (bind-arg-lexical? arg))
|
|
(new-lexical (if bind-lexical
|
|
(cons arg lexical)
|
|
lexical))
|
|
(new-dynamic (if bind-lexical
|
|
dynamic
|
|
(cons arg dynamic))))
|
|
(case mode
|
|
((required) (iterate (cdr tail) mode
|
|
(cons arg required) optional
|
|
new-lexical new-dynamic))
|
|
((optional) (iterate (cdr tail) mode
|
|
required (cons arg optional)
|
|
new-lexical new-dynamic))
|
|
(else
|
|
(error "invalid mode in split-lambda-arguments" mode)))))))))
|
|
|
|
|
|
; Compile a lambda expression. Things get a little complicated because TreeIL
|
|
; does not allow optional arguments but only one rest argument, and also the
|
|
; rest argument should be nil instead of '() for no values given. Because of
|
|
; this, we have to do a little preprocessing to get everything done before the
|
|
; real body is called.
|
|
;
|
|
; (lambda (a &optional b &rest c) body) should become:
|
|
; (lambda (a_ . rest_)
|
|
; (with-fluids* (list a b c) (list a_ nil nil)
|
|
; (lambda ()
|
|
; (if (not (null? rest_))
|
|
; (begin
|
|
; (fluid-set! b (car rest_))
|
|
; (set! rest_ (cdr rest_))
|
|
; (if (not (null? rest_))
|
|
; (fluid-set! c rest_))))
|
|
; body)))
|
|
;
|
|
; This is formulated very imperatively, but I think in this case that is quite
|
|
; clear and better than creating a lot of nested let's.
|
|
;
|
|
; Another thing we have to be aware of is that lambda arguments are usually
|
|
; dynamically bound, even when a lexical binding is in tact for a symbol.
|
|
; For symbols that are marked as 'always lexical' however, we bind them here
|
|
; lexically, too -- and thus we get them out of the let-dynamic call and
|
|
; register a lexical binding for them (the lexical target variable is already
|
|
; there, namely the real lambda argument from TreeIL).
|
|
; For optional arguments that are lexically bound we need to create the lexical
|
|
; bindings though with an additional let, as those arguments are not part of the
|
|
; ordinary argument list.
|
|
|
|
(define (compile-lambda loc args body)
|
|
(if (not (list? args))
|
|
(report-error loc "expected list for argument-list" args))
|
|
(if (null? body)
|
|
(report-error loc "function body might not be empty"))
|
|
(call-with-values
|
|
(lambda ()
|
|
(split-lambda-arguments loc args))
|
|
(lambda (required optional rest lexical dynamic)
|
|
(let* ((make-sym (lambda (sym) (gensym)))
|
|
(required-sym (map make-sym required))
|
|
(required-pairs (map cons required required-sym))
|
|
(have-real-rest (or rest (not (null? optional))))
|
|
(rest-sym (if have-real-rest (gensym) '()))
|
|
(rest-name (if rest rest rest-sym))
|
|
(rest-lexical (and rest (memq rest lexical)))
|
|
(rest-dynamic (and rest (not rest-lexical)))
|
|
(real-args (append required-sym rest-sym))
|
|
(arg-names (append required rest-name))
|
|
(lex-optionals (lset-intersection eq? optional lexical))
|
|
(dyn-optionals (lset-intersection eq? optional dynamic))
|
|
(optional-sym (map make-sym lex-optionals))
|
|
(optional-lex-pairs (map cons lex-optionals optional-sym))
|
|
(find-required-pairs (lambda (filter)
|
|
(lset-intersection (lambda (name-sym el)
|
|
(eq? (car name-sym)
|
|
el))
|
|
required-pairs filter)))
|
|
(required-lex-pairs (find-required-pairs lexical))
|
|
(rest-pair (if rest-lexical `((,rest . ,rest-sym)) '()))
|
|
(all-lex-pairs (append required-lex-pairs optional-lex-pairs
|
|
rest-pair)))
|
|
(for-each (lambda (sym)
|
|
(mark-global-needed! (fluid-ref bindings-data)
|
|
sym value-slot))
|
|
dynamic)
|
|
(with-dynamic-bindings (fluid-ref bindings-data) dynamic
|
|
(lambda ()
|
|
(with-lexical-bindings (fluid-ref bindings-data)
|
|
(map car all-lex-pairs)
|
|
(map cdr all-lex-pairs)
|
|
(lambda ()
|
|
(make-lambda loc '()
|
|
(make-lambda-case
|
|
#f required #f
|
|
(if have-real-rest rest-name #f)
|
|
#f '()
|
|
(if have-real-rest
|
|
(append required-sym (list rest-sym))
|
|
required-sym)
|
|
(let* ((init-req (map (lambda (name-sym)
|
|
(make-lexical-ref loc (car name-sym)
|
|
(cdr name-sym)))
|
|
(find-required-pairs dynamic)))
|
|
(init-nils (map (lambda (sym) (nil-value loc))
|
|
(if rest-dynamic
|
|
`(,@dyn-optionals ,rest-sym)
|
|
dyn-optionals)))
|
|
(init (append init-req init-nils))
|
|
(func-body (make-sequence loc
|
|
`(,(process-optionals loc optional
|
|
rest-name rest-sym)
|
|
,(process-rest loc rest
|
|
rest-name rest-sym)
|
|
,@(map compile-expr body))))
|
|
(dynlet (let-dynamic loc dynamic value-slot
|
|
init func-body))
|
|
(full-body (if (null? dynamic) func-body dynlet)))
|
|
(if (null? optional-sym)
|
|
full-body
|
|
(make-let loc
|
|
optional-sym optional-sym
|
|
(map (lambda (sym) (nil-value loc)) optional-sym)
|
|
full-body)))
|
|
#f))))))))))
|
|
|
|
; Build the code to handle setting of optional arguments that are present
|
|
; and updating the rest list.
|
|
(define (process-optionals loc optional rest-name rest-sym)
|
|
(let iterate ((tail optional))
|
|
(if (null? tail)
|
|
(make-void loc)
|
|
(make-conditional loc
|
|
(call-primitive loc 'null? (make-lexical-ref loc rest-name rest-sym))
|
|
(make-void loc)
|
|
(make-sequence loc
|
|
(list (set-variable! loc (car tail) value-slot
|
|
(call-primitive loc 'car
|
|
(make-lexical-ref loc rest-name rest-sym)))
|
|
(make-lexical-set loc rest-name rest-sym
|
|
(call-primitive loc 'cdr
|
|
(make-lexical-ref loc rest-name rest-sym)))
|
|
(iterate (cdr tail))))))))
|
|
|
|
; This builds the code to set the rest variable to nil if it is empty.
|
|
(define (process-rest loc rest rest-name rest-sym)
|
|
(let ((rest-empty (call-primitive loc 'null?
|
|
(make-lexical-ref loc rest-name rest-sym))))
|
|
(cond
|
|
(rest
|
|
(make-conditional loc rest-empty
|
|
(make-void loc)
|
|
(set-variable! loc rest value-slot
|
|
(make-lexical-ref loc rest-name rest-sym))))
|
|
((not (null? rest-sym))
|
|
(make-conditional loc rest-empty
|
|
(make-void loc)
|
|
(runtime-error loc "too many arguments and no rest argument")))
|
|
(else (make-void loc)))))
|
|
|
|
|
|
; Handle the common part of defconst and defvar, that is, checking for a correct
|
|
; doc string and arguments as well as maybe in the future handling the docstring
|
|
; somehow.
|
|
|
|
(define (handle-var-def loc sym doc)
|
|
(cond
|
|
((not (symbol? sym)) (report-error loc "expected symbol, got" sym))
|
|
((> (length doc) 1) (report-error loc "too many arguments to defvar"))
|
|
((and (not (null? doc)) (not (string? (car doc))))
|
|
(report-error loc "expected string as third argument of defvar, got"
|
|
(car doc)))
|
|
; TODO: Handle doc string if present.
|
|
(else #t)))
|
|
|
|
|
|
; Handle macro bindings.
|
|
|
|
(define (is-macro? sym)
|
|
(module-defined? (resolve-interface macro-slot) sym))
|
|
|
|
(define (define-macro! loc sym definition)
|
|
(let ((resolved (resolve-module macro-slot)))
|
|
(if (is-macro? sym)
|
|
(report-error loc "macro is already defined" sym)
|
|
(begin
|
|
(module-define! resolved sym definition)
|
|
(module-export! resolved (list sym))))))
|
|
|
|
(define (get-macro sym)
|
|
(module-ref (resolve-module macro-slot) sym))
|
|
|
|
|
|
; See if a (backquoted) expression contains any unquotes.
|
|
|
|
(define (contains-unquotes? expr)
|
|
(if (pair? expr)
|
|
(if (or (unquote? (car expr)) (unquote-splicing? (car expr)))
|
|
#t
|
|
(or (contains-unquotes? (car expr))
|
|
(contains-unquotes? (cdr expr))))
|
|
#f))
|
|
|
|
|
|
; Process a backquoted expression by building up the needed cons/append calls.
|
|
; For splicing, it is assumed that the expression spliced in evaluates to a
|
|
; list. The emacs manual does not really state either it has to or what to do
|
|
; if it does not, but Scheme explicitly forbids it and this seems reasonable
|
|
; also for elisp.
|
|
|
|
(define (unquote-cell? expr)
|
|
(and (list? expr) (= (length expr) 2) (unquote? (car expr))))
|
|
(define (unquote-splicing-cell? expr)
|
|
(and (list? expr) (= (length expr) 2) (unquote-splicing? (car expr))))
|
|
|
|
(define (process-backquote loc expr)
|
|
(if (contains-unquotes? expr)
|
|
(if (pair? expr)
|
|
(if (or (unquote-cell? expr) (unquote-splicing-cell? expr))
|
|
(compile-expr (cadr expr))
|
|
(let* ((head (car expr))
|
|
(processed-tail (process-backquote loc (cdr expr)))
|
|
(head-is-list-2 (and (list? head) (= (length head) 2)))
|
|
(head-unquote (and head-is-list-2 (unquote? (car head))))
|
|
(head-unquote-splicing (and head-is-list-2
|
|
(unquote-splicing? (car head)))))
|
|
(if head-unquote-splicing
|
|
(call-primitive loc 'append
|
|
(compile-expr (cadr head)) processed-tail)
|
|
(call-primitive loc 'cons
|
|
(if head-unquote
|
|
(compile-expr (cadr head))
|
|
(process-backquote loc head))
|
|
processed-tail))))
|
|
(report-error loc "non-pair expression contains unquotes" expr))
|
|
(make-const loc expr)))
|
|
|
|
|
|
; Temporarily update a list of symbols that are handled specially (disabled
|
|
; void check or always lexical) for compiling body.
|
|
; We need to handle special cases for already all / set to all and the like.
|
|
|
|
(define (with-added-symbols loc fluid syms body)
|
|
(if (null? body)
|
|
(report-error loc "symbol-list construct has empty body"))
|
|
(if (not (or (eq? syms 'all)
|
|
(and (list? syms) (and-map symbol? syms))))
|
|
(report-error loc "invalid symbol list" syms))
|
|
(let ((old (fluid-ref fluid))
|
|
(make-body (lambda ()
|
|
(make-sequence loc (map compile-expr body)))))
|
|
(if (eq? old 'all)
|
|
(make-body)
|
|
(let ((new (if (eq? syms 'all)
|
|
'all
|
|
(append syms old))))
|
|
(with-fluids ((fluid new))
|
|
(make-body))))))
|
|
|
|
|
|
; Compile a symbol expression. This is a variable reference or maybe some
|
|
; special value like nil.
|
|
|
|
(define (compile-symbol loc sym)
|
|
(case sym
|
|
((nil) (nil-value loc))
|
|
((t) (t-value loc))
|
|
(else (reference-with-check loc sym value-slot))))
|
|
|
|
|
|
; Compile a pair-expression (that is, any structure-like construct).
|
|
|
|
(define (compile-pair loc expr)
|
|
(pmatch expr
|
|
|
|
((progn . ,forms)
|
|
(make-sequence loc (map compile-expr forms)))
|
|
|
|
((if ,condition ,ifclause)
|
|
(make-conditional loc (compile-expr condition)
|
|
(compile-expr ifclause)
|
|
(nil-value loc)))
|
|
((if ,condition ,ifclause ,elseclause)
|
|
(make-conditional loc (compile-expr condition)
|
|
(compile-expr ifclause)
|
|
(compile-expr elseclause)))
|
|
((if ,condition ,ifclause . ,elses)
|
|
(make-conditional loc (compile-expr condition)
|
|
(compile-expr ifclause)
|
|
(make-sequence loc (map compile-expr elses))))
|
|
|
|
; defconst and defvar are kept here in the compiler (rather than doing them
|
|
; as macros) for if we may want to handle the docstring somehow.
|
|
|
|
((defconst ,sym ,value . ,doc)
|
|
(if (handle-var-def loc sym doc)
|
|
(make-sequence loc
|
|
(list (set-variable! loc sym value-slot (compile-expr value))
|
|
(make-const loc sym)))))
|
|
|
|
((defvar ,sym) (make-const loc sym))
|
|
((defvar ,sym ,value . ,doc)
|
|
(if (handle-var-def loc sym doc)
|
|
(make-sequence loc
|
|
(list (make-conditional loc
|
|
(call-primitive loc 'eq?
|
|
(make-module-ref loc runtime 'void #t)
|
|
(reference-variable loc sym value-slot))
|
|
(set-variable! loc sym value-slot
|
|
(compile-expr value))
|
|
(make-void loc))
|
|
(make-const loc sym)))))
|
|
|
|
; Build a set form for possibly multiple values. The code is not formulated
|
|
; tail recursive because it is clearer this way and large lists of symbol
|
|
; expression pairs are very unlikely.
|
|
((setq . ,args) (guard (not (null? args)))
|
|
(make-sequence loc
|
|
(let iterate ((tail args))
|
|
(let ((sym (car tail))
|
|
(tailtail (cdr tail)))
|
|
(if (not (symbol? sym))
|
|
(report-error loc "expected symbol in setq")
|
|
(if (null? tailtail)
|
|
(report-error loc "missing value for symbol in setq" sym)
|
|
(let* ((val (compile-expr (car tailtail)))
|
|
(op (set-variable! loc sym value-slot val)))
|
|
(if (null? (cdr tailtail))
|
|
(let* ((temp (gensym))
|
|
(ref (make-lexical-ref loc temp temp)))
|
|
(list (make-let loc `(,temp) `(,temp) `(,val)
|
|
(make-sequence loc
|
|
(list (set-variable! loc sym value-slot ref)
|
|
ref)))))
|
|
(cons (set-variable! loc sym value-slot val)
|
|
(iterate (cdr tailtail)))))))))))
|
|
|
|
; All lets (let, flet, lexical-let and let* forms) are done using the
|
|
; generate-let/generate-let* methods.
|
|
|
|
((let ,bindings . ,body) (guard (and (list? bindings)
|
|
(not (null? bindings))
|
|
(not (null? body))))
|
|
(generate-let loc value-slot bindings body))
|
|
((lexical-let ,bindings . ,body) (guard (and (list? bindings)
|
|
(not (null? bindings))
|
|
(not (null? body))))
|
|
(generate-let loc 'lexical bindings body))
|
|
((flet ,bindings . ,body) (guard (and (list? bindings)
|
|
(not (null? bindings))
|
|
(not (null? body))))
|
|
(generate-let loc function-slot bindings body))
|
|
|
|
((let* ,bindings . ,body) (guard (and (list? bindings)
|
|
(not (null? bindings))
|
|
(not (null? body))))
|
|
(generate-let* loc value-slot bindings body))
|
|
((lexical-let* ,bindings . ,body) (guard (and (list? bindings)
|
|
(not (null? bindings))
|
|
(not (null? body))))
|
|
(generate-let* loc 'lexical bindings body))
|
|
((flet* ,bindings . ,body) (guard (and (list? bindings)
|
|
(not (null? bindings))
|
|
(not (null? body))))
|
|
(generate-let* loc function-slot bindings body))
|
|
|
|
; Temporarily disable void checks or set symbols as always lexical only
|
|
; for the lexical scope of a construct.
|
|
|
|
((without-void-checks ,syms . ,body)
|
|
(with-added-symbols loc disable-void-check syms body))
|
|
|
|
((with-always-lexical ,syms . ,body)
|
|
(with-added-symbols loc always-lexical syms body))
|
|
|
|
; guile-ref allows building TreeIL's module references from within
|
|
; elisp as a way to access data within
|
|
; the Guile universe. The module and symbol referenced are static values,
|
|
; just like (@ module symbol) does!
|
|
((guile-ref ,module ,sym) (guard (and (list? module) (symbol? sym)))
|
|
(make-module-ref loc module sym #t))
|
|
|
|
; guile-primitive allows to create primitive references, which are still
|
|
; a little faster.
|
|
((guile-primitive ,sym) (guard (symbol? sym))
|
|
(make-primitive-ref loc sym))
|
|
|
|
; A while construct is transformed into a tail-recursive loop like this:
|
|
; (letrec ((iterate (lambda ()
|
|
; (if condition
|
|
; (begin body
|
|
; (iterate))
|
|
; %nil))))
|
|
; (iterate))
|
|
;
|
|
; As letrec is not directly accessible from elisp, while is implemented here
|
|
; instead of with a macro.
|
|
((while ,condition . ,body)
|
|
(let* ((itersym (gensym))
|
|
(compiled-body (map compile-expr body))
|
|
(iter-call (make-application loc
|
|
(make-lexical-ref loc 'iterate itersym)
|
|
(list)))
|
|
(full-body (make-sequence loc
|
|
`(,@compiled-body ,iter-call)))
|
|
(lambda-body (make-conditional loc
|
|
(compile-expr condition)
|
|
full-body
|
|
(nil-value loc)))
|
|
(iter-thunk (make-lambda loc '()
|
|
(make-lambda-case #f '() #f #f #f '() '()
|
|
lambda-body #f))))
|
|
(make-letrec loc '(iterate) (list itersym) (list iter-thunk)
|
|
iter-call)))
|
|
|
|
; Either (lambda ...) or (function (lambda ...)) denotes a lambda-expression
|
|
; that should be compiled.
|
|
((lambda ,args . ,body)
|
|
(compile-lambda loc args body))
|
|
((function (lambda ,args . ,body))
|
|
(compile-lambda loc args body))
|
|
|
|
; Build a lambda and also assign it to the function cell of some symbol.
|
|
; This is no macro as we might want to honour the docstring at some time;
|
|
; just as with defvar/defconst.
|
|
((defun ,name ,args . ,body)
|
|
(if (not (symbol? name))
|
|
(report-error loc "expected symbol as function name" name)
|
|
(make-sequence loc
|
|
(list (set-variable! loc name function-slot
|
|
(compile-lambda loc args body))
|
|
(make-const loc name)))))
|
|
|
|
; Define a macro (this is done directly at compile-time!).
|
|
; FIXME: Recursive macros don't work!
|
|
((defmacro ,name ,args . ,body)
|
|
(if (not (symbol? name))
|
|
(report-error loc "expected symbol as macro name" name)
|
|
(let* ((tree-il (with-fluids ((bindings-data (make-bindings)))
|
|
(compile-lambda loc args body)))
|
|
(object (compile tree-il #:from 'tree-il #:to 'value)))
|
|
(define-macro! loc name object)
|
|
(make-const loc name))))
|
|
|
|
; XXX: Maybe we could implement backquotes in macros, too.
|
|
((,backq ,val) (guard (backquote? backq))
|
|
(process-backquote loc val))
|
|
|
|
; XXX: Why do we need 'quote here instead of quote?
|
|
(('quote ,val)
|
|
(make-const loc val))
|
|
|
|
; Macro calls are simply expanded and recursively compiled.
|
|
((,macro . ,args) (guard (and (symbol? macro) (is-macro? macro)))
|
|
(let ((expander (get-macro macro)))
|
|
(compile-expr (apply expander args))))
|
|
|
|
; Function calls using (function args) standard notation; here, we have to
|
|
; take the function value of a symbol if it is one. It seems that functions
|
|
; in form of uncompiled lists are not supported in this syntax, so we don't
|
|
; have to care for them.
|
|
((,func . ,args)
|
|
(make-application loc
|
|
(if (symbol? func)
|
|
(reference-with-check loc func function-slot)
|
|
(compile-expr func))
|
|
(map compile-expr args)))
|
|
|
|
(else
|
|
(report-error loc "unrecognized elisp" expr))))
|
|
|
|
|
|
; Compile a single expression to TreeIL.
|
|
|
|
(define (compile-expr expr)
|
|
(let ((loc (location expr)))
|
|
(cond
|
|
((symbol? expr)
|
|
(compile-symbol loc expr))
|
|
((pair? expr)
|
|
(compile-pair loc expr))
|
|
(else (make-const loc expr)))))
|
|
|
|
|
|
; Process the compiler options.
|
|
; FIXME: Why is '(()) passed as options by the REPL?
|
|
|
|
(define (valid-symbol-list-arg? value)
|
|
(or (eq? value 'all)
|
|
(and (list? value) (and-map symbol? value))))
|
|
|
|
(define (process-options! opt)
|
|
(if (and (not (null? opt))
|
|
(not (equal? opt '(()))))
|
|
(if (null? (cdr opt))
|
|
(report-error #f "Invalid compiler options" opt)
|
|
(let ((key (car opt))
|
|
(value (cadr opt)))
|
|
(case key
|
|
((#:disable-void-check)
|
|
(if (valid-symbol-list-arg? value)
|
|
(fluid-set! disable-void-check value)
|
|
(report-error #f "Invalid value for #:disable-void-check" value)))
|
|
((#:always-lexical)
|
|
(if (valid-symbol-list-arg? value)
|
|
(fluid-set! always-lexical value)
|
|
(report-error #f "Invalid value for #:always-lexical" value)))
|
|
(else (report-error #f "Invalid compiler option" key)))))))
|
|
|
|
|
|
; Entry point for compilation to TreeIL.
|
|
; This creates the bindings data structure, and after compiling the main
|
|
; expression we need to make sure all globals for symbols used during the
|
|
; compilation are created using the generate-ensure-global function.
|
|
|
|
(define (compile-tree-il expr env opts)
|
|
(values
|
|
(with-fluids ((bindings-data (make-bindings))
|
|
(disable-void-check '())
|
|
(always-lexical '()))
|
|
(process-options! opts)
|
|
(let ((loc (location expr))
|
|
(compiled (compile-expr expr)))
|
|
(make-sequence loc
|
|
`(,@(map-globals-needed (fluid-ref bindings-data)
|
|
(lambda (mod sym)
|
|
(generate-ensure-global loc sym mod)))
|
|
,compiled))))
|
|
env
|
|
env))
|