add fluid->parameter
[bpt/guile.git] / module / system / base / compile.scm
CommitLineData
cb4cca12
KN
1;;; High-level compiler interface
2
b3219085 3;; Copyright (C) 2001, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
cb4cca12 4
e1203ea0
LC
5;;; This library is free software; you can redistribute it and/or
6;;; modify it under the terms of the GNU Lesser General Public
7;;; License as published by the Free Software Foundation; either
8;;; version 3 of the License, or (at your option) any later version.
9;;;
10;;; This library is distributed in the hope that it will be useful,
11;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
12;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13;;; Lesser General Public License for more details.
14;;;
15;;; You should have received a copy of the GNU Lesser General Public
16;;; License along with this library; if not, write to the Free Software
17;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
cb4cca12
KN
18
19;;; Code:
20
21(define-module (system base compile)
b0b180d5 22 #:use-module (system base syntax)
1a1a10d3 23 #:use-module (system base language)
2e4c3227 24 #:use-module (system base message)
b0b180d5 25 #:use-module (system vm vm) ;; FIXME: there's a reason for this, can't remember why tho
1a1a10d3 26 #:use-module (ice-9 regex)
3de80ed5 27 #:use-module (ice-9 optargs)
b0b180d5 28 #:use-module (ice-9 receive)
6cb42361 29 #:export (compiled-file-name
8753fd53
AW
30 compile-file
31 compile-and-load
32 read-and-compile
68623e8e 33 compile
6cb42361 34 decompile))
8f5cfc81 35
48302624 36
8f5cfc81
KN
37;;;
38;;; Compiler
39;;;
cb4cca12 40
b0b180d5
AW
41(define (call-once thunk)
42 (let ((entered #f))
03fa04df
AW
43 (dynamic-wind
44 (lambda ()
45 (if entered
46 (error "thunk may only be entered once: ~a" thunk))
47 (set! entered #t))
b0b180d5
AW
48 thunk
49 (lambda () #t))))
50
b9e67767 51;; (put 'call-with-output-file/atomic 'scheme-indent-function 1)
535fb833 52(define* (call-with-output-file/atomic filename proc #:optional reference)
e6d4e05c
AW
53 (let* ((template (string-append filename ".XXXXXX"))
54 (tmp (mkstemp! template)))
b0b180d5 55 (call-once
03fa04df 56 (lambda ()
b0b180d5
AW
57 (with-throw-handler #t
58 (lambda ()
98922879 59 (proc tmp)
a56db0f6 60 (chmod tmp (logand #o0666 (lognot (umask))))
5a0df7be 61 (close-port tmp)
b0b180d5
AW
62 (rename-file template filename))
63 (lambda args
64 (delete-file template)))))))
e6d4e05c 65
7b107cce
AW
66(define (ensure-language x)
67 (if (language? x)
68 x
69 (lookup-language x)))
70
56dbc8a8
AW
71;; Throws an exception if `dir' is not writable. The mkdir occurs
72;; before the check, so that we avoid races (possibly due to parallel
73;; compilation).
74;;
b6aedd68 75(define (ensure-directory dir)
56dbc8a8
AW
76 (catch 'system-error
77 (lambda ()
78 (mkdir dir))
79 (lambda (k subr fmt args rest)
80 (let ((errno (and (pair? rest) (car rest))))
81 (cond
82 ((eqv? errno EEXIST)
b6aedd68
LC
83 ;; Assume it's a writable directory, to avoid TOCTOU errors,
84 ;; as well as UID/EUID mismatches that occur with access(2).
85 #t)
56dbc8a8 86 ((eqv? errno ENOENT)
b6aedd68
LC
87 (ensure-directory (dirname dir))
88 (ensure-directory dir))
56dbc8a8
AW
89 (else
90 (throw k subr fmt args rest)))))))
4c9c9b9b 91
5ea401bf
AW
92;;; This function is among the trickiest I've ever written. I tried many
93;;; variants. In the end, simple is best, of course.
94;;;
b3da54d1 95;;; After turning this around a number of times, it seems that the
5ea401bf
AW
96;;; desired behavior is that .go files should exist in a path, for
97;;; searching. That is orthogonal to this function. For writing .go
3c997c4b
AW
98;;; files, either you know where they should go, in which case you tell
99;;; compile-file explicitly, as in the srcdir != builddir case; or you
100;;; don't know, in which case this function is called, and we just put
b72ab481 101;;; them in your own ccache dir in ~/.cache/guile/ccache.
04af4c4c
AW
102;;;
103;;; See also boot-9.scm:load.
f3130a2e 104(define (compiled-file-name file)
6934d9e7
AW
105 ;; FIXME: would probably be better just to append SHA1(canon-path)
106 ;; to the %compile-fallback-path, to avoid deep directory stats.
107 (define (canonical->suffix canon)
108 (cond
109 ((string-prefix? "/" canon) canon)
110 ((and (> (string-length canon) 2)
111 (eqv? (string-ref canon 1) #\:))
112 ;; Paths like C:... transform to /C...
113 (string-append "/" (substring canon 0 1) (substring canon 2)))
114 (else canon)))
5ea401bf
AW
115 (define (compiled-extension)
116 (cond ((or (null? %load-compiled-extensions)
117 (string-null? (car %load-compiled-extensions)))
118 (warn "invalid %load-compiled-extensions"
119 %load-compiled-extensions)
120 ".go")
121 (else (car %load-compiled-extensions))))
122 (and %compile-fallback-path
3c997c4b 123 (let ((f (string-append
179fe336 124 %compile-fallback-path
6934d9e7 125 (canonical->suffix (canonicalize-path file))
25b82b34 126 (compiled-extension))))
b6aedd68 127 (and (false-if-exception (ensure-directory (dirname f)))
5ea401bf 128 f))))
f3130a2e 129
b8076ec6
AW
130(define* (compile-file file #:key
131 (output-file #f)
b8076ec6
AW
132 (from (current-language))
133 (to 'objcode)
f95f82f8 134 (env (default-environment from))
b9e67767
AW
135 (opts '())
136 (canonicalization 'relative))
137 (with-fluids ((%file-port-name-canonicalization canonicalization))
2183d66e 138 (let* ((comp (or output-file (compiled-file-name file)
6f06e8d3 139 (error "failed to create path for auto-compiled file"
2183d66e 140 file)))
b9e67767
AW
141 (in (open-input-file file))
142 (enc (file-encoding in)))
eda06220
LC
143 ;; Choose the input encoding deterministically.
144 (set-port-encoding! in (or enc "UTF-8"))
145
b6aedd68 146 (ensure-directory (dirname comp))
b9e67767
AW
147 (call-with-output-file/atomic comp
148 (lambda (port)
149 ((language-printer (ensure-language to))
150 (read-and-compile in #:env env #:from from #:to to #:opts opts)
151 port))
152 file)
153 comp)))
8f5cfc81 154
f95f82f8 155(define* (compile-and-load file #:key (from 'scheme) (to 'value)
b9e67767
AW
156 (env (current-module)) (opts '())
157 (canonicalization 'relative))
158 (with-fluids ((%file-port-name-canonicalization canonicalization))
159 (read-and-compile (open-input-file file)
160 #:from from #:to to #:opts opts
161 #:env env)))
8f5cfc81 162
8f5cfc81
KN
163\f
164;;;
b0b180d5 165;;; Compiler interface
8f5cfc81
KN
166;;;
167
b0b180d5 168(define (compile-passes from to opts)
5d6fb8bb
AW
169 (map cdr
170 (or (lookup-compilation-order from to)
171 (error "no way to compile" from "to" to))))
8f5cfc81 172
b0b180d5 173(define (compile-fold passes exp env opts)
b8076ec6
AW
174 (let lp ((passes passes) (x exp) (e env) (cenv env) (first? #t))
175 (if (null? passes)
176 (values x e cenv)
177 (receive (x e new-cenv) ((car passes) x e opts)
178 (lp (cdr passes) x e (if first? new-cenv cenv) #f)))))
8f5cfc81 179
b8076ec6
AW
180(define (find-language-joint from to)
181 (let lp ((in (reverse (or (lookup-compilation-order from to)
182 (error "no way to compile" from "to" to))))
183 (lang to))
776491ca 184 (cond ((null? in) to)
b8076ec6
AW
185 ((language-joiner lang) lang)
186 (else
187 (lp (cdr in) (caar in))))))
188
776491ca
AW
189(define (default-language-joiner lang)
190 (lambda (exps env)
191 (if (and (pair? exps) (null? (cdr exps)))
192 (car exps)
193 (error
194 "Multiple expressions read and compiled, but language has no joiner"
195 lang))))
196
197(define (read-and-parse lang port cenv)
198 (let ((exp ((language-reader lang) port cenv)))
199 (cond
200 ((eof-object? exp) exp)
201 ((language-parser lang) => (lambda (parse) (parse exp)))
202 (else exp))))
203
b8076ec6 204(define* (read-and-compile port #:key
b8076ec6
AW
205 (from (current-language))
206 (to 'objcode)
f95f82f8 207 (env (default-environment from))
b8076ec6
AW
208 (opts '()))
209 (let ((from (ensure-language from))
210 (to (ensure-language to)))
211 (let ((joint (find-language-joint from to)))
40867c97
AW
212 (with-fluids ((*current-language* from))
213 (let lp ((exps '()) (env #f) (cenv env))
776491ca 214 (let ((x (read-and-parse (current-language) port cenv)))
b8076ec6
AW
215 (cond
216 ((eof-object? x)
b3219085 217 (close-port port)
776491ca
AW
218 (compile ((or (language-joiner joint)
219 (default-language-joiner joint))
220 (reverse exps)
221 env)
f95f82f8
AW
222 #:from joint #:to to
223 ;; env can be false if no expressions were read.
224 #:env (or env (default-environment joint))
225 #:opts opts))
b8076ec6
AW
226 (else
227 ;; compile-fold instead of compile so we get the env too
228 (receive (jexp jenv jcenv)
229 (compile-fold (compile-passes (current-language) joint opts)
230 x cenv opts)
231 (lp (cons jexp exps) jenv jcenv))))))))))
232
b0b180d5 233(define* (compile x #:key
b0b180d5 234 (from (current-language))
7b107cce 235 (to 'value)
f95f82f8 236 (env (default-environment from))
b0b180d5 237 (opts '()))
2e4c3227
LC
238
239 (let ((warnings (memq #:warnings opts)))
240 (if (pair? warnings)
241 (let ((warnings (cadr warnings)))
242 ;; Sanity-check the requested warnings.
243 (for-each (lambda (w)
244 (or (lookup-warning-type w)
245 (warning 'unsupported-warning #f w)))
246 warnings))))
247
b8076ec6 248 (receive (exp env cenv)
40867c97 249 (compile-fold (compile-passes from to opts) x env opts)
b8076ec6 250 exp))
7b107cce
AW
251
252\f
253;;;
254;;; Decompiler interface
255;;;
256
257(define (decompile-passes from to opts)
258 (map cdr
259 (or (lookup-decompilation-order from to)
260 (error "no way to decompile" from "to" to))))
261
d7236899
AW
262(define (decompile-fold passes exp env opts)
263 (if (null? passes)
264 (values exp env)
265 (receive (exp env) ((car passes) exp env opts)
266 (decompile-fold (cdr passes) exp env opts))))
267
7b107cce
AW
268(define* (decompile x #:key
269 (env #f)
270 (from 'value)
271 (to 'assembly)
272 (opts '()))
d7236899
AW
273 (decompile-fold (decompile-passes from to opts)
274 x
275 env
276 opts))