stamp .go with timestamp of .scm; a fresh go has same mtime of .scm
[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
KN
4
5;; This program is free software; you can redistribute it and/or modify
6;; it under the terms of the GNU General Public License as published by
7;; the Free Software Foundation; either version 2, or (at your option)
8;; any later version.
9;;
10;; This program 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
13;; GNU General Public License for more details.
14;;
15;; You should have received a copy of the GNU General Public License
16;; along with this program; see the file COPYING. If not, write to
17;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18;; Boston, MA 02111-1307, USA.
19
20;;; Code:
21
22(define-module (system base compile)
b0b180d5 23 #:use-module (system base syntax)
1a1a10d3 24 #:use-module (system base language)
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
AW
133 (let ((f (string-append
134 %compile-fallback-path "/" file (compiled-extension))))
5ea401bf
AW
135 (and (false-if-exception (ensure-writable-dir (dirname f)))
136 f))))
f3130a2e 137
b8076ec6
AW
138(define* (compile-file file #:key
139 (output-file #f)
140 (env #f)
141 (from (current-language))
142 (to 'objcode)
143 (opts '()))
73f4d8d1 144 (let ((comp (or output-file (compiled-file-name file)))
b8076ec6 145 (in (open-input-file file)))
f3130a2e 146 (ensure-writable-dir (dirname comp))
b8076ec6
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)
535fb833
AW
151 port))
152 file)
b8076ec6 153 comp))
8f5cfc81 154
34f3d47d
AW
155(define* (compile-and-load file #:key (from 'scheme) (to 'value) (opts '()))
156 (read-and-compile (open-input-file file)
157 #:from from #:to to #:opts opts))
8f5cfc81 158
8f5cfc81
KN
159\f
160;;;
b0b180d5 161;;; Compiler interface
8f5cfc81
KN
162;;;
163
b0b180d5 164(define (compile-passes from to opts)
5d6fb8bb
AW
165 (map cdr
166 (or (lookup-compilation-order from to)
167 (error "no way to compile" from "to" to))))
8f5cfc81 168
b0b180d5 169(define (compile-fold passes exp env opts)
b8076ec6
AW
170 (let lp ((passes passes) (x exp) (e env) (cenv env) (first? #t))
171 (if (null? passes)
172 (values x e cenv)
173 (receive (x e new-cenv) ((car passes) x e opts)
174 (lp (cdr passes) x e (if first? new-cenv cenv) #f)))))
8f5cfc81 175
b8076ec6
AW
176(define (find-language-joint from to)
177 (let lp ((in (reverse (or (lookup-compilation-order from to)
178 (error "no way to compile" from "to" to))))
179 (lang to))
180 (cond ((null? in)
181 (error "don't know how to join expressions" from to))
182 ((language-joiner lang) lang)
183 (else
184 (lp (cdr in) (caar in))))))
185
186(define* (read-and-compile port #:key
187 (env #f)
188 (from (current-language))
189 (to 'objcode)
190 (opts '()))
191 (let ((from (ensure-language from))
192 (to (ensure-language to)))
193 (let ((joint (find-language-joint from to)))
194 (with-fluids ((*current-language* from))
195 (let lp ((exps '()) (env #f) (cenv env))
196 (let ((x ((language-reader (current-language)) port)))
197 (cond
198 ((eof-object? x)
199 (compile ((language-joiner joint) (reverse exps) env)
200 #:from joint #:to to #:env env #:opts opts))
201 (else
202 ;; compile-fold instead of compile so we get the env too
203 (receive (jexp jenv jcenv)
204 (compile-fold (compile-passes (current-language) joint opts)
205 x cenv opts)
206 (lp (cons jexp exps) jenv jcenv))))))))))
207
b0b180d5
AW
208(define* (compile x #:key
209 (env #f)
210 (from (current-language))
7b107cce 211 (to 'value)
b0b180d5 212 (opts '()))
b8076ec6
AW
213 (receive (exp env cenv)
214 (compile-fold (compile-passes from to opts) x env opts)
215 exp))
7b107cce
AW
216
217\f
218;;;
219;;; Decompiler interface
220;;;
221
222(define (decompile-passes from to opts)
223 (map cdr
224 (or (lookup-decompilation-order from to)
225 (error "no way to decompile" from "to" to))))
226
d7236899
AW
227(define (decompile-fold passes exp env opts)
228 (if (null? passes)
229 (values exp env)
230 (receive (exp env) ((car passes) exp env opts)
231 (decompile-fold (cdr passes) exp env opts))))
232
7b107cce
AW
233(define* (decompile x #:key
234 (env #f)
235 (from 'value)
236 (to 'assembly)
237 (opts '()))
d7236899
AW
238 (decompile-fold (decompile-passes from to opts)
239 x
240 env
241 opts))