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