adamc@1
|
1 (* Copyright (c) 2008, Adam Chlipala
|
adamc@1
|
2 * All rights reserved.
|
adamc@1
|
3 *
|
adamc@1
|
4 * Redistribution and use in source and binary forms, with or without
|
adamc@1
|
5 * modification, are permitted provided that the following conditions are met:
|
adamc@1
|
6 *
|
adamc@1
|
7 * - Redistributions of source code must retain the above copyright notice,
|
adamc@1
|
8 * this list of conditions and the following disclaimer.
|
adamc@1
|
9 * - Redistributions in binary form must reproduce the above copyright notice,
|
adamc@1
|
10 * this list of conditions and the following disclaimer in the documentation
|
adamc@1
|
11 * and/or other materials provided with the distribution.
|
adamc@1
|
12 * - The names of contributors may not be used to endorse or promote products
|
adamc@1
|
13 * derived from this software without specific prior written permission.
|
adamc@1
|
14 *
|
adamc@1
|
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
adamc@1
|
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
adamc@1
|
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
adamc@1
|
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
adamc@1
|
19 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
adamc@1
|
20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
adamc@1
|
21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
adamc@1
|
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
adamc@1
|
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
adamc@1
|
24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
adamc@1
|
25 * POSSIBILITY OF SUCH DAMAGE.
|
adamc@1
|
26 *)
|
adamc@1
|
27
|
adamc@244
|
28 (* Ur/Web language parser *)
|
adamc@1
|
29
|
adamc@1
|
30 structure Compiler :> COMPILER = struct
|
adamc@1
|
31
|
adamc@244
|
32 structure UrwebLrVals = UrwebLrValsFn(structure Token = LrParser.Token)
|
adamc@244
|
33 structure Lex = UrwebLexFn(structure Tokens = UrwebLrVals.Tokens)
|
adamc@244
|
34 structure UrwebP = Join(structure ParserData = UrwebLrVals.ParserData
|
adamc@1
|
35 structure Lex = Lex
|
adamc@1
|
36 structure LrParser = LrParser)
|
adamc@1
|
37
|
adamc@201
|
38 type job = string list
|
adamc@201
|
39
|
adamc@201
|
40 type ('src, 'dst) phase = {
|
adamc@201
|
41 func : 'src -> 'dst,
|
adamc@201
|
42 print : 'dst -> Print.PD.pp_desc
|
adamc@201
|
43 }
|
adamc@201
|
44
|
adamc@201
|
45 type pmap = (string * Time.time) list
|
adamc@201
|
46
|
adamc@201
|
47 type ('src, 'dst) transform = {
|
adamc@201
|
48 func : 'src -> 'dst option,
|
adamc@201
|
49 print : 'dst -> Print.PD.pp_desc,
|
adamc@201
|
50 time : 'src * pmap -> 'dst option * pmap
|
adamc@201
|
51 }
|
adamc@201
|
52
|
adamc@201
|
53 fun transform (ph : ('src, 'dst) phase) name = {
|
adamc@201
|
54 func = fn input => let
|
adamc@201
|
55 val v = #func ph input
|
adamc@201
|
56 in
|
adamc@201
|
57 if ErrorMsg.anyErrors () then
|
adamc@201
|
58 NONE
|
adamc@201
|
59 else
|
adamc@201
|
60 SOME v
|
adamc@201
|
61 end,
|
adamc@201
|
62 print = #print ph,
|
adamc@201
|
63 time = fn (input, pmap) => let
|
adamc@201
|
64 val befor = Time.now ()
|
adamc@201
|
65 val v = #func ph input
|
adamc@201
|
66 val elapsed = Time.- (Time.now (), befor)
|
adamc@201
|
67 in
|
adamc@201
|
68 (if ErrorMsg.anyErrors () then
|
adamc@201
|
69 NONE
|
adamc@201
|
70 else
|
adamc@201
|
71 SOME v,
|
adamc@201
|
72 (name, elapsed) :: pmap)
|
adamc@201
|
73 end
|
adamc@201
|
74 }
|
adamc@201
|
75
|
adamc@201
|
76 fun op o (tr1 : ('a, 'b) transform, tr2 : ('b, 'c) transform) = {
|
adamc@201
|
77 func = fn input => case #func tr1 input of
|
adamc@201
|
78 NONE => NONE
|
adamc@201
|
79 | SOME v => #func tr2 v,
|
adamc@201
|
80 print = #print tr2,
|
adamc@201
|
81 time = fn (input, pmap) => let
|
adamc@201
|
82 val (ro, pmap) = #time tr1 (input, pmap)
|
adamc@201
|
83 in
|
adamc@201
|
84 case ro of
|
adamc@201
|
85 NONE => (NONE, pmap)
|
adamc@201
|
86 | SOME v => #time tr2 (v, pmap)
|
adamc@201
|
87 end
|
adamc@201
|
88 }
|
adamc@201
|
89
|
adamc@201
|
90 fun run (tr : ('src, 'dst) transform) = #func tr
|
adamc@201
|
91
|
adamc@201
|
92 fun runPrint (tr : ('src, 'dst) transform) input =
|
adamc@201
|
93 case #func tr input of
|
adamc@201
|
94 NONE => print "Failure\n"
|
adamc@201
|
95 | SOME v =>
|
adamc@201
|
96 (print "Success\n";
|
adamc@201
|
97 Print.print (#print tr v);
|
adamc@201
|
98 print "\n")
|
adamc@201
|
99
|
adamc@201
|
100 fun time (tr : ('src, 'dst) transform) input =
|
adamc@55
|
101 let
|
adamc@201
|
102 val (_, pmap) = #time tr (input, [])
|
adamc@201
|
103 in
|
adamc@201
|
104 app (fn (name, time) =>
|
adamc@201
|
105 print (name ^ ": " ^ LargeReal.toString (Time.toReal time) ^ "\n")) (rev pmap);
|
adamc@201
|
106 print ("TOTAL: " ^ LargeReal.toString (Time.toReal (foldl Time.+ Time.zeroTime (map #2 pmap))) ^ "\n");
|
adamc@201
|
107 print "\n"
|
adamc@201
|
108 end
|
adamc@55
|
109
|
adamc@201
|
110 fun timePrint (tr : ('src, 'dst) transform) input =
|
adamc@201
|
111 let
|
adamc@201
|
112 val (ro, pmap) = #time tr (input, [])
|
adamc@55
|
113 in
|
adamc@201
|
114 app (fn (name, time) =>
|
adamc@201
|
115 print (name ^ ": " ^ LargeReal.toString (Time.toReal time) ^ "\n")) (rev pmap);
|
adamc@201
|
116 print ("TOTAL: " ^ LargeReal.toString (Time.toReal (foldl Time.+ Time.zeroTime (map #2 pmap))) ^ "\n");
|
adamc@201
|
117 print "\n";
|
adamc@201
|
118 case ro of
|
adamc@201
|
119 NONE => print "Failure\n"
|
adamc@201
|
120 | SOME v =>
|
adamc@201
|
121 (print "Success\n";
|
adamc@201
|
122 Print.print (#print tr v);
|
adamc@201
|
123 print "\n")
|
adamc@55
|
124 end
|
adamc@55
|
125
|
adamc@244
|
126 val parseUrs =
|
adamc@201
|
127 {func = fn filename => let
|
adamc@201
|
128 val fname = OS.FileSys.tmpName ()
|
adamc@201
|
129 val outf = TextIO.openOut fname
|
adamc@201
|
130 val () = TextIO.output (outf, "sig\n")
|
adamc@201
|
131 val inf = TextIO.openIn filename
|
adamc@201
|
132 fun loop () =
|
adamc@201
|
133 case TextIO.inputLine inf of
|
adamc@201
|
134 NONE => ()
|
adamc@201
|
135 | SOME line => (TextIO.output (outf, line);
|
adamc@201
|
136 loop ())
|
adamc@201
|
137 val () = loop ()
|
adamc@201
|
138 val () = TextIO.closeIn inf
|
adamc@201
|
139 val () = TextIO.closeOut outf
|
adamc@201
|
140
|
adamc@201
|
141 val () = (ErrorMsg.resetErrors ();
|
adamc@201
|
142 ErrorMsg.resetPositioning filename;
|
adamc@201
|
143 Lex.UserDeclarations.initialize ())
|
adamc@201
|
144 val file = TextIO.openIn fname
|
adamc@201
|
145 fun get _ = TextIO.input file
|
adamc@201
|
146 fun parseerror (s, p1, p2) = ErrorMsg.errorAt' (p1, p2) s
|
adamc@201
|
147 val lexer = LrParser.Stream.streamify (Lex.makeLexer get)
|
adamc@244
|
148 val (absyn, _) = UrwebP.parse (30, lexer, parseerror, ())
|
adamc@201
|
149 in
|
adamc@201
|
150 TextIO.closeIn file;
|
adamc@201
|
151 case absyn of
|
adamc@201
|
152 [(Source.DSgn ("?", (Source.SgnConst sgis, _)), _)] => sgis
|
adamc@201
|
153 | _ => (ErrorMsg.errorAt {file = filename,
|
adamc@201
|
154 first = {line = 0,
|
adamc@201
|
155 char = 0},
|
adamc@201
|
156 last = {line = 0,
|
adamc@201
|
157 char = 0}} "Not a signature";
|
adamc@201
|
158 [])
|
adamc@201
|
159 end
|
adamc@201
|
160 handle LrParser.ParseError => [],
|
adamc@201
|
161 print = Print.p_list_sep Print.PD.newline SourcePrint.p_sgn_item}
|
adamc@55
|
162
|
adamc@1
|
163 (* The main parsing routine *)
|
adamc@244
|
164 val parseUr = {
|
adamc@201
|
165 func = fn filename =>
|
adamc@201
|
166 let
|
adamc@201
|
167 val () = (ErrorMsg.resetErrors ();
|
adamc@201
|
168 ErrorMsg.resetPositioning filename;
|
adamc@201
|
169 Lex.UserDeclarations.initialize ())
|
adamc@201
|
170 val file = TextIO.openIn filename
|
adamc@201
|
171 fun get _ = TextIO.input file
|
adamc@201
|
172 fun parseerror (s, p1, p2) = ErrorMsg.errorAt' (p1, p2) s
|
adamc@201
|
173 val lexer = LrParser.Stream.streamify (Lex.makeLexer get)
|
adamc@244
|
174 val (absyn, _) = UrwebP.parse (30, lexer, parseerror, ())
|
adamc@201
|
175 in
|
adamc@201
|
176 TextIO.closeIn file;
|
adamc@201
|
177 case absyn of
|
adamc@201
|
178 [(Source.DSgn ("?", _), _)] =>
|
adamc@201
|
179 (ErrorMsg.errorAt {file = filename,
|
adamc@201
|
180 first = {line = 0,
|
adamc@201
|
181 char = 0},
|
adamc@201
|
182 last = {line = 0,
|
adamc@201
|
183 char = 0}} "File starts with 'sig'";
|
adamc@201
|
184 [])
|
adamc@201
|
185 | _ => absyn
|
adamc@201
|
186 end
|
adamc@201
|
187 handle LrParser.ParseError => [],
|
adamc@201
|
188 print = SourcePrint.p_file}
|
adamc@56
|
189
|
adamc@56
|
190 fun capitalize "" = ""
|
adamc@56
|
191 | capitalize s = str (Char.toUpper (String.sub (s, 0))) ^ String.extract (s, 1, NONE)
|
adamc@56
|
192
|
adamc@201
|
193 val parse = {
|
adamc@201
|
194 func = fn fnames =>
|
adamc@201
|
195 let
|
adamc@201
|
196 fun nameOf fname = capitalize (OS.Path.file fname)
|
adamc@109
|
197
|
adamc@201
|
198 fun parseOne fname =
|
adamc@201
|
199 let
|
adamc@201
|
200 val mname = nameOf fname
|
adamc@244
|
201 val ur = OS.Path.joinBaseExt {base = fname, ext = SOME "ur"}
|
adamc@244
|
202 val urs = OS.Path.joinBaseExt {base = fname, ext = SOME "urs"}
|
adamc@56
|
203
|
adamc@201
|
204 val sgnO =
|
adamc@244
|
205 if Posix.FileSys.access (urs, []) then
|
adamc@244
|
206 SOME (Source.SgnConst (#func parseUrs urs),
|
adamc@244
|
207 {file = urs,
|
adamc@201
|
208 first = ErrorMsg.dummyPos,
|
adamc@201
|
209 last = ErrorMsg.dummyPos})
|
adamc@201
|
210 else
|
adamc@201
|
211 NONE
|
adamc@56
|
212
|
adamc@244
|
213 val loc = {file = ur,
|
adamc@201
|
214 first = ErrorMsg.dummyPos,
|
adamc@201
|
215 last = ErrorMsg.dummyPos}
|
adamc@56
|
216
|
adamc@244
|
217 val ds = #func parseUr ur
|
adamc@201
|
218 in
|
adamc@201
|
219 (Source.DStr (mname, sgnO, (Source.StrConst ds, loc)), loc)
|
adamc@201
|
220 end
|
adamc@56
|
221
|
adamc@201
|
222 val ds = map parseOne fnames
|
adamc@201
|
223 in
|
adamc@201
|
224 let
|
adamc@201
|
225 val final = nameOf (List.last fnames)
|
adamc@201
|
226 in
|
adamc@201
|
227 ds @ [(Source.DExport (Source.StrVar final, ErrorMsg.dummySpan), ErrorMsg.dummySpan)]
|
adamc@201
|
228 end handle Empty => ds
|
adamc@201
|
229 end,
|
adamc@201
|
230 print = SourcePrint.p_file
|
adamc@201
|
231 }
|
adamc@56
|
232
|
adamc@201
|
233 val toParse = transform parse "parse"
|
adamc@38
|
234
|
adamc@201
|
235 val elaborate = {
|
adamc@201
|
236 func = fn file => let
|
adamc@244
|
237 val basis = #func parseUrs "lib/basis.urs"
|
adamc@201
|
238 in
|
adamc@201
|
239 Elaborate.elabFile basis ElabEnv.empty file
|
adamc@201
|
240 end,
|
adamc@201
|
241 print = ElabPrint.p_file ElabEnv.empty
|
adamc@201
|
242 }
|
adamc@5
|
243
|
adamc@201
|
244 val toElaborate = toParse o transform elaborate "elaborate"
|
adamc@201
|
245
|
adamc@201
|
246 val explify = {
|
adamc@201
|
247 func = Explify.explify,
|
adamc@201
|
248 print = ExplPrint.p_file ExplEnv.empty
|
adamc@201
|
249 }
|
adamc@201
|
250
|
adamc@201
|
251 val toExplify = toElaborate o transform explify "explify"
|
adamc@201
|
252
|
adamc@201
|
253 val corify = {
|
adamc@201
|
254 func = Corify.corify,
|
adamc@201
|
255 print = CorePrint.p_file CoreEnv.empty
|
adamc@201
|
256 }
|
adamc@201
|
257
|
adamc@201
|
258 val toCorify = toExplify o transform corify "corify"
|
adamc@201
|
259
|
adamc@202
|
260 val shake = {
|
adamc@202
|
261 func = Shake.shake,
|
adamc@202
|
262 print = CorePrint.p_file CoreEnv.empty
|
adamc@202
|
263 }
|
adamc@39
|
264
|
adamc@202
|
265 val toShake1 = toCorify o transform shake "shake1"
|
adamc@110
|
266
|
adamc@202
|
267 val tag = {
|
adamc@202
|
268 func = Tag.tag,
|
adamc@202
|
269 print = CorePrint.p_file CoreEnv.empty
|
adamc@202
|
270 }
|
adamc@193
|
271
|
adamc@202
|
272 val toTag = toShake1 o transform tag "tag"
|
adamc@20
|
273
|
adamc@202
|
274 val reduce = {
|
adamc@202
|
275 func = Reduce.reduce,
|
adamc@202
|
276 print = CorePrint.p_file CoreEnv.empty
|
adamc@202
|
277 }
|
adamc@25
|
278
|
adamc@202
|
279 val toReduce = toTag o transform reduce "reduce"
|
adamc@23
|
280
|
adamc@202
|
281 val specialize = {
|
adamc@202
|
282 func = Specialize.specialize,
|
adamc@202
|
283 print = CorePrint.p_file CoreEnv.empty
|
adamc@202
|
284 }
|
adamc@132
|
285
|
adamc@202
|
286 val toSpecialize = toReduce o transform specialize "specialize"
|
adamc@131
|
287
|
adamc@202
|
288 val toShake2 = toSpecialize o transform shake "shake2"
|
adamc@133
|
289
|
adamc@202
|
290 val monoize = {
|
adamc@202
|
291 func = Monoize.monoize CoreEnv.empty,
|
adamc@202
|
292 print = MonoPrint.p_file MonoEnv.empty
|
adamc@202
|
293 }
|
adamc@134
|
294
|
adamc@202
|
295 val toMonoize = toShake2 o transform monoize "monoize"
|
adamc@96
|
296
|
adamc@202
|
297 val mono_opt = {
|
adamc@202
|
298 func = MonoOpt.optimize,
|
adamc@202
|
299 print = MonoPrint.p_file MonoEnv.empty
|
adamc@202
|
300 }
|
adamc@29
|
301
|
adamc@202
|
302 val toMono_opt1 = toMonoize o transform mono_opt "mono_opt1"
|
adamc@5
|
303
|
adamc@202
|
304 val untangle = {
|
adamc@202
|
305 func = Untangle.untangle,
|
adamc@202
|
306 print = MonoPrint.p_file MonoEnv.empty
|
adamc@202
|
307 }
|
adamc@1
|
308
|
adamc@202
|
309 val toUntangle = toMono_opt1 o transform untangle "untangle"
|
adamc@38
|
310
|
adamc@202
|
311 val mono_reduce = {
|
adamc@202
|
312 func = MonoReduce.reduce,
|
adamc@202
|
313 print = MonoPrint.p_file MonoEnv.empty
|
adamc@202
|
314 }
|
adamc@16
|
315
|
adamc@268
|
316 val toMono_reduce = toUntangle o transform mono_reduce "mono_reduce"
|
adamc@39
|
317
|
adamc@202
|
318 val mono_shake = {
|
adamc@202
|
319 func = MonoShake.shake,
|
adamc@202
|
320 print = MonoPrint.p_file MonoEnv.empty
|
adamc@202
|
321 }
|
adamc@110
|
322
|
adamc@268
|
323 val toMono_shake = toMono_reduce o transform mono_shake "mono_shake1"
|
adamc@193
|
324
|
adamc@268
|
325 val toMono_opt2 = toMono_shake o transform mono_opt "mono_opt2"
|
adamc@20
|
326
|
adamc@202
|
327 val cjrize = {
|
adamc@202
|
328 func = Cjrize.cjrize,
|
adamc@202
|
329 print = CjrPrint.p_file CjrEnv.empty
|
adamc@202
|
330 }
|
adamc@23
|
331
|
adamc@268
|
332 val toCjrize = toMono_opt2 o transform cjrize "cjrize"
|
adamc@29
|
333
|
adamc@183
|
334 fun compileC {cname, oname, ename} =
|
adamc@183
|
335 let
|
adamc@183
|
336 val compile = "gcc -O3 -I include -c " ^ cname ^ " -o " ^ oname
|
adamc@244
|
337 val link = "gcc -pthread -O3 clib/urweb.o " ^ oname ^ " clib/driver.o -o " ^ ename
|
adamc@183
|
338 in
|
adamc@183
|
339 if not (OS.Process.isSuccess (OS.Process.system compile)) then
|
adamc@183
|
340 print "C compilation failed\n"
|
adamc@183
|
341 else if not (OS.Process.isSuccess (OS.Process.system link)) then
|
adamc@186
|
342 print "C linking failed\n"
|
adamc@183
|
343 else
|
adamc@183
|
344 print "Success\n"
|
adamc@183
|
345 end
|
adamc@183
|
346
|
adamc@202
|
347 fun compile job =
|
adamc@202
|
348 case run toCjrize job of
|
adamc@244
|
349 NONE => print "Ur compilation failed\n"
|
adamc@29
|
350 | SOME file =>
|
adamc@202
|
351 let
|
adamc@244
|
352 val cname = "/tmp/urweb.c"
|
adamc@244
|
353 val oname = "/tmp/urweb.o"
|
adamc@202
|
354 val ename = "/tmp/webapp"
|
adamc@102
|
355
|
adamc@202
|
356 val outf = TextIO.openOut cname
|
adamc@202
|
357 val s = TextIOPP.openOut {dst = outf, wid = 80}
|
adamc@202
|
358 in
|
adamc@202
|
359 Print.fprint s (CjrPrint.p_file CjrEnv.empty file);
|
adamc@202
|
360 TextIO.closeOut outf;
|
adamc@102
|
361
|
adamc@202
|
362 compileC {cname = cname, oname = oname, ename = ename}
|
adamc@202
|
363 end
|
adamc@29
|
364
|
adamc@1
|
365 end
|