fix brainfuck for new tree-il, and add tests
[bpt/guile.git] / module / system / base / compile.scm
CommitLineData
cb4cca12
KN
1;;; High-level compiler interface
2
b96dac4d 3;; Copyright (C) 2001, 2009 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
AW
28 #:use-module (ice-9 receive)
29 #:export (syntax-error
3de80ed5 30 *current-language*
8753fd53
AW
31 compiled-file-name
32 compile-file
33 compile-and-load
34 read-and-compile
68623e8e 35 compile
7b107cce 36 decompile)
3de80ed5 37 #:export-syntax (call-with-compile-error-catch))
8f5cfc81
KN
38
39;;;
40;;; Compiler environment
41;;;
42
77046be3 43(define (syntax-error loc msg exp)
1e6ebf54 44 (throw 'syntax-error-compile-time loc msg exp))
8f5cfc81 45
48302624 46(define-macro (call-with-compile-error-catch thunk)
1e6ebf54 47 `(catch 'syntax-error-compile-time
48302624 48 ,thunk
d8eeb67c 49 (lambda (key loc msg exp)
2335fb97 50 (if (pair? loc)
237f96e7
AW
51 (let ((file (or (assq-ref loc 'filename) "unknown file"))
52 (line (assq-ref loc 'line))
53 (col (assq-ref loc 'column)))
54 (format (current-error-port)
55 "~A:~A:~A: ~A: ~A~%" file line col msg exp))
56 (format (current-error-port)
98922879 57 "unknown location: ~A: ~S~%" msg exp)))))
48302624 58
8f5cfc81
KN
59\f
60;;;
61;;; Compiler
62;;;
cb4cca12 63
3de80ed5 64(define *current-language* (make-fluid))
7b107cce 65(fluid-set! *current-language* 'scheme)
b0b180d5 66(define (current-language)
7b107cce 67 (fluid-ref *current-language*))
cb4cca12 68
b0b180d5
AW
69(define (call-once thunk)
70 (let ((entered #f))
03fa04df
AW
71 (dynamic-wind
72 (lambda ()
73 (if entered
74 (error "thunk may only be entered once: ~a" thunk))
75 (set! entered #t))
b0b180d5
AW
76 thunk
77 (lambda () #t))))
78
535fb833 79(define* (call-with-output-file/atomic filename proc #:optional reference)
e6d4e05c
AW
80 (let* ((template (string-append filename ".XXXXXX"))
81 (tmp (mkstemp! template)))
b0b180d5 82 (call-once
03fa04df 83 (lambda ()
b0b180d5
AW
84 (with-throw-handler #t
85 (lambda ()
98922879 86 (proc tmp)
a56db0f6 87 (chmod tmp (logand #o0666 (lognot (umask))))
5a0df7be 88 (close-port tmp)
535fb833
AW
89 (if reference
90 (let ((st (stat reference)))
91 (utime template (stat:atime st) (stat:mtime st))))
b0b180d5
AW
92 (rename-file template filename))
93 (lambda args
94 (delete-file template)))))))
e6d4e05c 95
7b107cce
AW
96(define (ensure-language x)
97 (if (language? x)
98 x
99 (lookup-language x)))
100
f3130a2e
AW
101;; Throws an exception if `dir' is not writable. The double-stat is OK,
102;; as this is only used during compilation.
103(define (ensure-writable-dir dir)
104 (if (file-exists? dir)
105 (if (access? dir W_OK)
106 #t
107 (error "directory not writable" dir))
4c9c9b9b 108 (begin
f3130a2e 109 (ensure-writable-dir (dirname dir))
4c9c9b9b
AW
110 (mkdir dir))))
111
f3130a2e
AW
112(define (dsu-sort list key less)
113 (map cdr
114 (stable-sort (map (lambda (x) (cons (key x) x)) list)
115 (lambda (x y) (less (car x) (car y))))))
116
5ea401bf
AW
117;;; This function is among the trickiest I've ever written. I tried many
118;;; variants. In the end, simple is best, of course.
119;;;
120;;; After turning this around a number of times, it seems that the the
121;;; desired behavior is that .go files should exist in a path, for
122;;; searching. That is orthogonal to this function. For writing .go
3c997c4b
AW
123;;; files, either you know where they should go, in which case you tell
124;;; compile-file explicitly, as in the srcdir != builddir case; or you
125;;; don't know, in which case this function is called, and we just put
126;;; them in your own ccache dir in ~/.guile-ccache.
f3130a2e 127(define (compiled-file-name file)
5ea401bf
AW
128 (define (compiled-extension)
129 (cond ((or (null? %load-compiled-extensions)
130 (string-null? (car %load-compiled-extensions)))
131 (warn "invalid %load-compiled-extensions"
132 %load-compiled-extensions)
133 ".go")
134 (else (car %load-compiled-extensions))))
135 (and %compile-fallback-path
3c997c4b 136 (let ((f (string-append
179fe336
AW
137 %compile-fallback-path
138 ;; no need for '/' separator here, canonicalize-path
139 ;; will give us an absolute path
140 (canonicalize-path file)
25b82b34 141 (compiled-extension))))
5ea401bf
AW
142 (and (false-if-exception (ensure-writable-dir (dirname f)))
143 f))))
f3130a2e 144
b8076ec6
AW
145(define* (compile-file file #:key
146 (output-file #f)
b8076ec6
AW
147 (from (current-language))
148 (to 'objcode)
f95f82f8 149 (env (default-environment from))
b8076ec6 150 (opts '()))
889975e5
MG
151 (let* ((comp (or output-file (compiled-file-name file)))
152 (in (open-input-file file))
153 (enc (file-encoding in)))
154 (if enc
155 (set-port-encoding! in enc))
f3130a2e 156 (ensure-writable-dir (dirname comp))
b8076ec6
AW
157 (call-with-output-file/atomic comp
158 (lambda (port)
159 ((language-printer (ensure-language to))
160 (read-and-compile in #:env env #:from from #:to to #:opts opts)
535fb833
AW
161 port))
162 file)
b8076ec6 163 comp))
8f5cfc81 164
f95f82f8
AW
165(define* (compile-and-load file #:key (from 'scheme) (to 'value)
166 (env (current-module)) (opts '()))
34f3d47d 167 (read-and-compile (open-input-file file)
87c595c7 168 #:from from #:to to #:opts opts
f95f82f8 169 #:env env))
8f5cfc81 170
8f5cfc81
KN
171\f
172;;;
b0b180d5 173;;; Compiler interface
8f5cfc81
KN
174;;;
175
b0b180d5 176(define (compile-passes from to opts)
5d6fb8bb
AW
177 (map cdr
178 (or (lookup-compilation-order from to)
179 (error "no way to compile" from "to" to))))
8f5cfc81 180
b0b180d5 181(define (compile-fold passes exp env opts)
b8076ec6
AW
182 (let lp ((passes passes) (x exp) (e env) (cenv env) (first? #t))
183 (if (null? passes)
184 (values x e cenv)
185 (receive (x e new-cenv) ((car passes) x e opts)
186 (lp (cdr passes) x e (if first? new-cenv cenv) #f)))))
8f5cfc81 187
b8076ec6
AW
188(define (find-language-joint from to)
189 (let lp ((in (reverse (or (lookup-compilation-order from to)
190 (error "no way to compile" from "to" to))))
191 (lang to))
192 (cond ((null? in)
193 (error "don't know how to join expressions" from to))
194 ((language-joiner lang) lang)
195 (else
196 (lp (cdr in) (caar in))))))
197
198(define* (read-and-compile port #:key
b8076ec6
AW
199 (from (current-language))
200 (to 'objcode)
f95f82f8 201 (env (default-environment from))
b8076ec6
AW
202 (opts '()))
203 (let ((from (ensure-language from))
204 (to (ensure-language to)))
205 (let ((joint (find-language-joint from to)))
40867c97
AW
206 (with-fluids ((*current-language* from))
207 (let lp ((exps '()) (env #f) (cenv env))
208 (let ((x ((language-reader (current-language)) port cenv)))
b8076ec6
AW
209 (cond
210 ((eof-object? x)
211 (compile ((language-joiner joint) (reverse exps) env)
f95f82f8
AW
212 #:from joint #:to to
213 ;; env can be false if no expressions were read.
214 #:env (or env (default-environment joint))
215 #:opts opts))
b8076ec6
AW
216 (else
217 ;; compile-fold instead of compile so we get the env too
218 (receive (jexp jenv jcenv)
219 (compile-fold (compile-passes (current-language) joint opts)
220 x cenv opts)
221 (lp (cons jexp exps) jenv jcenv))))))))))
222
b0b180d5 223(define* (compile x #:key
b0b180d5 224 (from (current-language))
7b107cce 225 (to 'value)
f95f82f8 226 (env (default-environment from))
b0b180d5 227 (opts '()))
2e4c3227
LC
228
229 (let ((warnings (memq #:warnings opts)))
230 (if (pair? warnings)
231 (let ((warnings (cadr warnings)))
232 ;; Sanity-check the requested warnings.
233 (for-each (lambda (w)
234 (or (lookup-warning-type w)
235 (warning 'unsupported-warning #f w)))
236 warnings))))
237
b8076ec6 238 (receive (exp env cenv)
40867c97 239 (compile-fold (compile-passes from to opts) x env opts)
b8076ec6 240 exp))
7b107cce
AW
241
242\f
243;;;
244;;; Decompiler interface
245;;;
246
247(define (decompile-passes from to opts)
248 (map cdr
249 (or (lookup-decompilation-order from to)
250 (error "no way to decompile" from "to" to))))
251
d7236899
AW
252(define (decompile-fold passes exp env opts)
253 (if (null? passes)
254 (values exp env)
255 (receive (exp env) ((car passes) exp env opts)
256 (decompile-fold (cdr passes) exp env opts))))
257
7b107cce
AW
258(define* (decompile x #:key
259 (env #f)
260 (from 'value)
261 (to 'assembly)
262 (opts '()))
d7236899
AW
263 (decompile-fold (decompile-passes from to opts)
264 x
265 env
266 opts))