Commit | Line | Data |
---|---|---|
c0a3b488 AC |
1 | (* |
2 | * Dynamic web page generation with Standard ML | |
3 | * Copyright (C) 2003 Adam Chlipala | |
4 | * | |
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 2.1 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 | *) | |
19 | ||
20 | (* Translation of templates into SML source *) | |
21 | ||
22 | structure Mlt :> MLT = | |
23 | struct | |
24 | open Tree | |
25 | ||
26 | exception Skip | |
27 | ||
28 | fun error (pos, msg) = (ErrorMsg.error pos msg; | |
29 | raise Skip) | |
30 | ||
31 | type varid = int | |
32 | ||
33 | datatype var = VAR of Types.ty | REF of Types.ty | |
34 | ||
35 | val errorTy = Types.WILDCARDty | |
36 | ||
37 | val ppstream = PrettyPrint.mk_ppstream {consumer = TextIO.print, flush = fn () => TextIO.flushOut TextIO.stdOut, | |
38 | linewidth = 80} | |
39 | ||
40 | (* States to thread throughout translation *) | |
41 | local | |
42 | datatype state = STATE of {env: StaticEnv.staticEnv, | |
43 | vars : var StringMap.map, | |
44 | config : Config.config, | |
45 | templates : StringSet.set} | |
46 | ||
47 | datatype strct = STRCT of {elements: Modules.elements, | |
48 | eenv: EntityEnv.entityEnv} | |
49 | ||
50 | fun getElements (Modules.SIG {elements, ...}) = elements | |
51 | | getElements _ = raise Fail "Unexpected Signature in getElements" | |
52 | ||
53 | val bogusStamp = Stamps.special "<bogus>" | |
54 | val errorStrct = STRCT {elements = [], eenv = EntityEnv.empty} | |
55 | in | |
56 | fun mkState (config, env, templates) = | |
57 | STATE {config = config, | |
58 | env = env, | |
59 | vars = StringMap.empty, | |
60 | templates = templates} | |
61 | ||
62 | fun addVar (STATE {config, env, vars, templates}, v, ty) = | |
63 | STATE {config = config, env = env, vars = StringMap.insert (vars, v, ty), templates = templates} | |
64 | fun addVars (state, vars) = StringMap.foldli (fn (v, ty, state) => addVar (state, v, ty)) state vars | |
65 | ||
66 | fun getVar (STATE {vars, ...}, v, pos) = StringMap.find (vars, v) | |
67 | fun lookVal (STATE {env, ...}, v, pos) = | |
68 | (case StaticEnv.look (env, Symbol.varSymbol v) of | |
69 | Bindings.VALbind var => | |
70 | (case var of | |
71 | VarCon.VALvar {typ, ...} => #1 (TypesUtil.instantiatePoly (!typ)) | |
72 | | _ => raise Fail "Unexpected var in lookVal") | |
73 | | Bindings.CONbind (Types.DATACON {typ, ...}) => #1 (TypesUtil.instantiatePoly typ) | |
74 | | _ => raise Fail "Unexpected binding in lookVal") | |
75 | handle StaticEnv.Unbound => (error (SOME pos, "Unbound variable " ^ v); | |
76 | errorTy) | |
77 | ||
78 | fun lookCon' (STATE {env, ...}, v) = | |
79 | (case StaticEnv.look (env, Symbol.varSymbol v) of | |
80 | Bindings.CONbind (Types.DATACON {typ, ...}) => #1 (TypesUtil.instantiatePoly typ) | |
81 | | _ => raise Fail "Unexpected binding in lookVal") | |
82 | fun lookCon (env, v, pos) = (lookCon' (env, v)) | |
83 | handle ModuleUtil.Unbound _ => (error (SOME pos, "Unbound constructor " ^ v); | |
84 | errorTy) | |
85 | fun lookStr (STATE {env, ...}, v, pos) = | |
86 | (case StaticEnv.look (env, Symbol.strSymbol v) of | |
87 | Bindings.STRbind modl => | |
88 | (case modl of | |
89 | Modules.STR {rlzn = {entities, ...}, sign, ...} => STRCT {elements = getElements sign, | |
90 | eenv = entities} | |
91 | | _=> raise Fail "Unexpected module in lookStr") | |
92 | | _ => raise Fail "Unexpected binding in lookStr") | |
93 | handle StaticEnv.Unbound => (error (SOME pos, "Unbound structure " ^ v); | |
94 | errorStrct) | |
95 | ||
96 | fun getStr (STRCT {elements, eenv, ...}, v, pos) = | |
97 | (case ModuleUtil.getStr (elements, eenv, Symbol.strSymbol v, Access.nullAcc, II.Null) of | |
98 | (Modules.STR {rlzn = {entities, ...}, sign = Modules.SIG {elements, ...}, ...}, _) => | |
99 | STRCT {elements = elements, eenv = entities} | |
100 | | _ => raise Fail "Unexpected spec in getStr") | |
101 | handle ModuleUtil.Unbound _ => (error (SOME pos, "Unbound structure " ^ v); | |
102 | errorStrct) | |
103 | fun getVal (STRCT {elements, ...}, v, pos) = | |
104 | (case ModuleUtil.getSpec (elements, Symbol.varSymbol v) of | |
105 | Modules.VALspec {spec, ...} => #1 (TypesUtil.instantiatePoly spec) | |
106 | | _ => raise Fail "Unexpected spec in getVal") | |
107 | handle ModuleUtil.Unbound _ => (case ModuleUtil.getSpec (elements, Symbol.tycSymbol v) of | |
108 | Modules.CONspec {spec = Types.DATACON {typ, ...}, ...} => #1 (TypesUtil.instantiatePoly typ) | |
109 | | _ => raise Fail "Unexpected spec in getVal") | |
110 | handle ModuleUtil.Unbound _ => (error (SOME pos, "Unbound variable " ^ v); | |
111 | errorTy) | |
112 | fun getCon (STRCT {elements, ...}, v, pos) = | |
113 | (case ModuleUtil.getSpec (elements, Symbol.varSymbol v) of | |
114 | Modules.CONspec {spec = Types.DATACON {typ, ...}, ...} => #1 (TypesUtil.instantiatePoly typ) | |
115 | | _ => raise Fail "Unexpected spec in getVal") | |
116 | handle ModuleUtil.Unbound _ => (error (SOME pos, "Unbound constructor " ^ v); | |
117 | errorTy) | |
118 | ||
119 | fun unify (STATE {env, ...}) (pos, t1, t2) = | |
120 | (*let | |
121 | val t1 = ModuleUtil.transType eenv t1 | |
122 | val t2 = ModuleUtil.transType eenv t2 | |
123 | in*) | |
124 | Unify.unifyTy (t1, t2) | |
125 | (*end*) | |
126 | handle Unify.Unify msg => | |
127 | (PrettyPrint.begin_block ppstream PrettyPrint.CONSISTENT 0; | |
8291a2b9 AC |
128 | PrettyPrint.add_string ppstream "Error unifying"; |
129 | PrettyPrint.add_break ppstream (1, 0); | |
c0a3b488 AC |
130 | PrettyPrint.begin_block ppstream PrettyPrint.CONSISTENT 5; |
131 | PPType.ppType env ppstream t1; | |
132 | PrettyPrint.end_block ppstream; | |
8291a2b9 AC |
133 | PrettyPrint.add_break ppstream (1, 0); |
134 | PrettyPrint.add_string ppstream "and"; | |
135 | PrettyPrint.add_break ppstream (1, 0); | |
c0a3b488 AC |
136 | PrettyPrint.begin_block ppstream PrettyPrint.CONSISTENT 5; |
137 | PPType.ppType env ppstream t2; | |
138 | PrettyPrint.end_block ppstream; | |
c0a3b488 | 139 | PrettyPrint.end_block ppstream; |
8291a2b9 | 140 | PrettyPrint.add_break ppstream (1, 0); |
c0a3b488 AC |
141 | PrettyPrint.flush_ppstream ppstream; |
142 | error (SOME pos, Unify.failMessage msg)) | |
143 | ||
144 | fun resolvePath (getter, transer) (pos, state, path) = | |
145 | let | |
146 | fun traverse ([], _, _) = raise Fail "Impossible empty variable path in pat traverse" | |
147 | | traverse ([v], str as STRCT {eenv, ...}, path) = | |
148 | let | |
149 | val ty = getter (str, v, pos) | |
150 | val ty = transer eenv ty | |
151 | fun folder (STRCT {eenv, ...}, ty) = transer eenv ty | |
152 | in | |
153 | foldl folder ty path | |
154 | end | |
155 | | traverse (s::rest, str, path) = traverse (rest, getStr (str, s, pos), str::path) | |
156 | in | |
157 | case path of | |
158 | [] => raise Fail "Empty path to resolvePath" | |
159 | | [_] => raise Fail "Singleton path to resolvePath" | |
160 | | (first::rest) => traverse (rest, lookStr (state, first, pos), []) | |
161 | end | |
162 | ||
163 | fun resolveStructure (pos, state, path) = | |
164 | let | |
165 | fun look (STATE {env, ...}, v, pos) = | |
166 | (case StaticEnv.look (env, Symbol.strSymbol v) of | |
167 | Bindings.STRbind modl => | |
168 | (case modl of | |
169 | Modules.STR {rlzn = {entities, ...}, sign, ...} => (getElements sign, entities, modl) | |
170 | | _=> raise Fail "Unexpected module in lookStr") | |
171 | | _ => raise Fail "Unexpected binding in lookStr") | |
172 | handle ModuleUtil.Unbound _ => (error (SOME pos, "Unbound structure " ^ v); | |
173 | ([], EntityEnv.empty, Modules.ERRORstr)) | |
174 | ||
175 | fun get (elements, eenv, v) = | |
176 | let | |
177 | val sym = Symbol.strSymbol v | |
178 | in | |
179 | (case ModuleUtil.getStr (elements, eenv, sym, Access.nullAcc, II.Null) of | |
180 | (str as Modules.STR {rlzn = {entities, ...}, sign = Modules.SIG {elements, ...}, ...}, _) => | |
181 | (elements, entities, str) | |
182 | | _ => raise Fail "Unexpected spec in resolveStructure") | |
183 | handle ModuleUtil.Unbound _ => (error (SOME pos, "Unbound structure " ^ v); | |
184 | ([], EntityEnv.empty, Modules.ERRORstr)) | |
185 | end | |
186 | ||
187 | fun traverse ([], (_, _, str)) = str | |
188 | | traverse ([v], (elements, eenv, _)) = #3 (get (elements, eenv, v)) | |
189 | | traverse (s::rest, (elements, eenv, _)) = traverse (rest, get (elements, eenv, s)) | |
190 | in | |
191 | case path of | |
192 | [] => raise Fail "Empty path to resolveStructure" | |
193 | | (first::rest) => traverse (rest, look (state, first, pos)) | |
194 | end | |
195 | ||
196 | fun openStructure (pos, state as STATE {config, env, vars, templates}, path) = | |
197 | let | |
198 | val str = resolveStructure (pos, state, path) | |
199 | val env = ModuleUtil.openStructure (env, str) | |
200 | in | |
201 | STATE {config = config, env = env, vars = vars, templates = templates} | |
202 | end | |
203 | ||
204 | fun tyToString (STATE {env, ...}) ty = | |
205 | PrettyPrint.pp_to_string 65535 (PPType.ppType env) ty | |
206 | ||
207 | fun printFn (state as STATE {config, env, ...}) ty = | |
208 | let | |
209 | val tyname = tyToString state ty | |
210 | in | |
211 | Config.printFn config tyname | |
212 | end | |
213 | ||
214 | fun isTemplate (STATE {templates, ...}) s = StringSet.member (templates, s) | |
215 | end | |
216 | ||
217 | fun twiddleType f ty = | |
218 | (case TypesUtil.headReduceType ty of | |
219 | Types.WILDCARDty => ty | |
220 | | _ => f ty) | |
221 | ||
222 | val domain = twiddleType BasicTypes.domain | |
223 | val range = twiddleType BasicTypes.range | |
224 | ||
225 | (*val _ = (Unify.debugging := true; | |
226 | EntityEnv.debugging := true; | |
227 | ModuleUtil.debugging := true)*) | |
228 | ||
229 | fun newTyvar eq = Types.VARty (Types.mkTyvar (Types.OPEN {depth = 0, eq = eq, kind = Types.META})) | |
230 | fun newFlex elms = Types.VARty (Types.mkTyvar (Types.OPEN {depth = 0, eq = false, kind = Types.FLEX elms})) | |
231 | ||
232 | val resolveVal = resolvePath (getVal, ModuleUtil.transType) | |
233 | val resolveCon = resolvePath (getCon, ModuleUtil.transType) | |
234 | ||
235 | fun escapeString s = | |
236 | let | |
237 | val chars = String.explode s | |
238 | val escd = map (fn #"\"" => "\\\"" | |
239 | | #"\n" => "\\n" | |
240 | | #"\r" => "" | |
241 | | #"\t" => "\\t" | |
242 | | x => str x) chars | |
243 | in | |
244 | String.concat escd | |
245 | end | |
246 | ||
247 | val mkTuple = BasicTypes.tupleTy | |
248 | ||
249 | val templateTy = BasicTypes.--> (Types.CONty (BasicTypes.listTycon, | |
250 | [mkTuple [BasicTypes.stringTy, | |
251 | BasicTypes.stringTy]]), BasicTypes.unitTy) | |
252 | ||
253 | fun xexp state (EXP (e, pos)) = | |
254 | (case e of | |
255 | Int_e n => | |
256 | (BasicTypes.intTy, Int.toString n) | |
8291a2b9 AC |
257 | | Real_e n => |
258 | (BasicTypes.realTy, Real.toString n) | |
c0a3b488 AC |
259 | | String_e s => |
260 | (BasicTypes.stringTy, "\"" ^ s ^ "\"") | |
261 | | Char_e s => | |
262 | (BasicTypes.charTy, "#\"" ^ s ^ "\"") | |
263 | | Cons_e (e1, e2) => | |
264 | let | |
265 | val (ty1, es1) = xexp state e1 | |
266 | val (ty2, es2) = xexp state e2 | |
267 | ||
268 | val parm = newTyvar false | |
269 | val ran = Types.CONty (BasicTypes.listTycon, [parm]) | |
270 | val dom = mkTuple [parm, ran] | |
271 | ||
272 | val xt = mkTuple [ty1, ty2] | |
273 | in | |
274 | unify state (pos, dom, xt); | |
275 | (ran, "(" ^ es1 ^ ") :: (" ^ es2 ^ ")") | |
276 | end | |
277 | | StrCat_e (e1, e2) => | |
278 | let | |
279 | val (ty1, es1) = xexp state e1 | |
280 | val (ty2, es2) = xexp state e2 | |
281 | in | |
282 | unify state (pos, ty1, BasicTypes.stringTy); | |
283 | unify state (pos, ty2, BasicTypes.stringTy); | |
284 | (BasicTypes.stringTy, "(" ^ es1 ^ ") ^ (" ^ es2 ^ ")") | |
285 | end | |
286 | | Orelse_e (e1, e2) => | |
287 | let | |
288 | val (ty1, es1) = xexp state e1 | |
289 | val (ty2, es2) = xexp state e2 | |
290 | in | |
291 | unify state (pos, ty1, BasicTypes.boolTy); | |
292 | unify state (pos, ty2, BasicTypes.boolTy); | |
293 | (BasicTypes.boolTy, "(" ^ es1 ^ ") orelse (" ^ es2 ^ ")") | |
294 | end | |
295 | | Andalso_e (e1, e2) => | |
296 | let | |
297 | val (ty1, es1) = xexp state e1 | |
298 | val (ty2, es2) = xexp state e2 | |
299 | in | |
300 | unify state (pos, ty1, BasicTypes.boolTy); | |
301 | unify state (pos, ty2, BasicTypes.boolTy); | |
302 | (BasicTypes.boolTy, "(" ^ es1 ^ ") andalso (" ^ es2 ^ ")") | |
303 | end | |
304 | | Plus_e (e1, e2) => | |
305 | let | |
306 | val (ty1, es1) = xexp state e1 | |
307 | val (ty2, es2) = xexp state e2 | |
308 | in | |
309 | unify state (pos, ty1, BasicTypes.intTy); | |
310 | unify state (pos, ty2, BasicTypes.intTy); | |
311 | (BasicTypes.intTy, "(" ^ es1 ^ ") + (" ^ es2 ^ ")") | |
312 | end | |
313 | | Minus_e (e1, e2) => | |
314 | let | |
315 | val (ty1, es1) = xexp state e1 | |
316 | val (ty2, es2) = xexp state e2 | |
317 | in | |
318 | unify state (pos, ty1, BasicTypes.intTy); | |
319 | unify state (pos, ty2, BasicTypes.intTy); | |
320 | (BasicTypes.intTy, "(" ^ es1 ^ ") - (" ^ es2 ^ ")") | |
321 | end | |
322 | | Times_e (e1, e2) => | |
323 | let | |
324 | val (ty1, es1) = xexp state e1 | |
325 | val (ty2, es2) = xexp state e2 | |
326 | in | |
327 | unify state (pos, ty1, BasicTypes.intTy); | |
328 | unify state (pos, ty2, BasicTypes.intTy); | |
329 | (BasicTypes.intTy, "(" ^ es1 ^ ") * (" ^ es2 ^ ")") | |
330 | end | |
331 | | Divide_e (e1, e2) => | |
332 | let | |
333 | val (ty1, es1) = xexp state e1 | |
334 | val (ty2, es2) = xexp state e2 | |
335 | in | |
336 | unify state (pos, ty1, BasicTypes.intTy); | |
337 | unify state (pos, ty2, BasicTypes.intTy); | |
338 | (BasicTypes.intTy, "(" ^ es1 ^ ") div (" ^ es2 ^ ")") | |
339 | end | |
340 | | Mod_e (e1, e2) => | |
341 | let | |
342 | val (ty1, es1) = xexp state e1 | |
343 | val (ty2, es2) = xexp state e2 | |
344 | in | |
345 | unify state (pos, ty1, BasicTypes.intTy); | |
346 | unify state (pos, ty2, BasicTypes.intTy); | |
347 | (BasicTypes.intTy, "(" ^ es1 ^ ") mod (" ^ es2 ^ ")") | |
348 | end | |
349 | | Lt_e (e1, e2) => | |
350 | let | |
351 | val (ty1, es1) = xexp state e1 | |
352 | val (ty2, es2) = xexp state e2 | |
353 | in | |
354 | unify state (pos, ty1, BasicTypes.intTy); | |
355 | unify state (pos, ty2, BasicTypes.intTy); | |
356 | (BasicTypes.boolTy, "(" ^ es1 ^ ") < (" ^ es2 ^ ")") | |
357 | end | |
358 | | Lte_e (e1, e2) => | |
359 | let | |
360 | val (ty1, es1) = xexp state e1 | |
361 | val (ty2, es2) = xexp state e2 | |
362 | in | |
363 | unify state (pos, ty1, BasicTypes.intTy); | |
364 | unify state (pos, ty2, BasicTypes.intTy); | |
365 | (BasicTypes.boolTy, "(" ^ es1 ^ ") <= (" ^ es2 ^ ")") | |
366 | end | |
367 | | Gt_e (e1, e2) => | |
368 | let | |
369 | val (ty1, es1) = xexp state e1 | |
370 | val (ty2, es2) = xexp state e2 | |
371 | in | |
372 | unify state (pos, ty1, BasicTypes.intTy); | |
373 | unify state (pos, ty2, BasicTypes.intTy); | |
374 | (BasicTypes.boolTy, "(" ^ es1 ^ ") > (" ^ es2 ^ ")") | |
375 | end | |
376 | | Gte_e (e1, e2) => | |
377 | let | |
378 | val (ty1, es1) = xexp state e1 | |
379 | val (ty2, es2) = xexp state e2 | |
380 | in | |
381 | unify state (pos, ty1, BasicTypes.intTy); | |
382 | unify state (pos, ty2, BasicTypes.intTy); | |
383 | (BasicTypes.boolTy, "(" ^ es1 ^ ") >= (" ^ es2 ^ ")") | |
384 | end | |
385 | | Param_e => (BasicTypes.--> (BasicTypes.stringTy, BasicTypes.stringTy), "Web.getParam") | |
386 | | Neg_e => (BasicTypes.--> (BasicTypes.intTy, BasicTypes.intTy), "~") | |
387 | | Template_e name => | |
388 | if isTemplate state name then | |
389 | let | |
390 | fun toUpper ch = chr (ord ch + ord #"A" - ord #"a") | |
391 | val name = str (toUpper (String.sub (name, 0))) ^ String.extract (name, 1, NONE) | |
392 | in | |
393 | (templateTy, "(Web.withParams " ^ name ^ ".exec)") | |
394 | end | |
395 | else | |
396 | (error (SOME pos, "Unknown template " ^ name); | |
397 | (errorTy, "<errorTemplate>")) | |
398 | | Proj_e field => | |
399 | let | |
400 | val carried = newTyvar false | |
401 | in | |
402 | (BasicTypes.--> (newFlex [(Symbol.labSymbol field, carried)], carried), "#" ^ field) | |
403 | end | |
404 | | Eq_e (e1, e2) => | |
405 | let | |
406 | val (ty1, s1) = xexp state e1 | |
407 | val (ty2, s2) = xexp state e2 | |
408 | in | |
409 | unify state (pos, ty1, ty2); | |
410 | unify state (pos, ty1, newTyvar true); | |
411 | (BasicTypes.boolTy, "(" ^ s1 ^ ") = (" ^ s2 ^ ")") | |
412 | end | |
413 | | Neq_e (e1, e2) => | |
414 | let | |
415 | val (ty1, s1) = xexp state e1 | |
416 | val (ty2, s2) = xexp state e2 | |
417 | in | |
418 | unify state (pos, ty1, ty2); | |
419 | unify state (pos, ty1, newTyvar true); | |
420 | (BasicTypes.boolTy, "(" ^ s1 ^ ") <> (" ^ s2 ^ ")") | |
421 | end | |
422 | | Ident_e [] => raise Fail "Impossible empty variable path" | |
423 | | Ident_e [id] => | |
424 | (case getVar (state, id, SOME pos) of | |
425 | NONE => (lookVal (state, id, pos), id) | |
426 | | SOME (VAR ty) => (ty, id) | |
427 | | SOME (REF ty) => (ty, "!" ^ id)) | |
428 | | Ident_e (path as (s::rest)) => | |
429 | (resolveVal (pos, state, path), foldl (fn (v, st) => st ^ "." ^ v) s rest) | |
430 | | App_e (f, x) => | |
431 | let | |
432 | val (ft, fs) = xexp state f | |
433 | val (xt, xs) = xexp state x | |
434 | ||
435 | (*val (ft, _) = TypesUtil.instantiatePoly ft*) | |
436 | val dom = domain ft | |
437 | val ran = range ft | |
438 | in | |
439 | unify state (pos, dom, xt); | |
440 | (ran, "(" ^ fs ^ ") (" ^ xs ^ ")") | |
441 | end | |
8291a2b9 AC |
442 | | Case_e (e, matches) => |
443 | let | |
444 | val (ty, s) = xexp state e | |
445 | ||
446 | fun folder ((p, e'), (first, str, bodyTy)) = | |
447 | let | |
448 | val (pty, vars', ps) = xpat state p | |
449 | ||
450 | val _ = unify state (pos, ty, pty) | |
451 | ||
452 | val (ty', str') = xexp (addVars (state, vars')) e' | |
453 | in | |
454 | unify state (pos, ty', bodyTy); | |
455 | (false, | |
456 | str ^ (if first then " " else " | ") ^ "(" ^ ps ^ ") => " ^ | |
457 | str' ^ "\n", | |
458 | bodyTy) | |
459 | end | |
460 | val bodyTy = newTyvar false | |
461 | val (_, str, _) = | |
462 | foldl folder (true, "(case (" ^ s ^ ") of\n", bodyTy) matches | |
463 | val str = str ^ ")\n" | |
464 | in | |
465 | (bodyTy, str) | |
466 | end | |
c0a3b488 AC |
467 | | Record_e (ist, cs) => |
468 | let | |
469 | val (cs, str) = foldl (fn ((id, e), (cs, str)) => | |
470 | let | |
471 | val idSym = Symbol.labSymbol id | |
472 | val _ = List.all (fn (id', _) => idSym <> id') cs | |
473 | orelse error (SOME pos, "Duplicate label " ^ id ^ " in record") | |
474 | val (ty, s) = xexp state e | |
475 | in | |
476 | ((idSym, ty) :: cs, str ^ ", " ^ id ^ " = " ^ s) | |
477 | end) ([], "") cs | |
478 | val cs = rev cs | |
479 | val str = | |
480 | case str of | |
481 | "" => str | |
482 | | _ => String.extract(str, 2, NONE) | |
483 | val str = "{" ^ str ^ "}" | |
484 | in | |
485 | (BasicTypes.recordTy cs, str) | |
486 | end | |
8291a2b9 AC |
487 | | Fn_e matches => |
488 | let | |
489 | val dom = newTyvar false | |
490 | val ran = newTyvar false | |
491 | ||
492 | fun folder ((p, e'), (first, str)) = | |
493 | let | |
494 | val (pty, vars', ps) = xpat state p | |
495 | ||
496 | val _ = unify state (pos, dom, pty) | |
497 | ||
498 | val (ty', str') = xexp (addVars (state, vars')) e' | |
499 | in | |
500 | unify state (pos, ty', ran); | |
501 | (false, | |
502 | str ^ (if first then " " else " | ") ^ "(" ^ ps ^ ") => " ^ | |
503 | str' ^ "\n") | |
504 | end | |
505 | val (_, str) = | |
506 | foldl folder (true, "(fn \n") matches | |
507 | val str = str ^ ")\n" | |
508 | in | |
509 | (BasicTypes.--> (dom, ran), str) | |
510 | end | |
511 | | Raise_e e => | |
512 | let | |
513 | val (ty, es) = xexp state e | |
514 | in | |
515 | unify state (pos, ty, BasicTypes.exnTy); | |
516 | (newTyvar false, "(raise (" ^ es ^ "))") | |
517 | end | |
c0a3b488 AC |
518 | | RecordUpd_e (e, cs) => |
519 | let | |
520 | val (ty, es) = xexp state e | |
521 | ||
522 | val cs' = | |
523 | case TypesUtil.headReduceType ty of | |
524 | Types.CONty (Types.RECORDtyc labs, tys) => ListPair.zip (labs, tys) | |
525 | | _ => error (SOME pos, "Record update on non-record") | |
526 | ||
527 | val (n, str) = foldl (fn ((id, ty), (n, str)) => | |
528 | case List.find (fn (id', _) => id = Symbol.labSymbol id') cs of | |
529 | NONE => (n, str ^ ", " ^ Symbol.name id ^ " = #" ^ | |
530 | Symbol.name id ^ " " ^ "UPD'") | |
531 | | SOME (_, e) => | |
532 | let | |
533 | val (ty', s) = xexp state e | |
534 | in | |
535 | unify state (pos, ty, ty'); | |
536 | (n + 1, str ^ ", " ^ Symbol.name id ^ " = " ^ s) | |
537 | end) (0, "") cs' | |
538 | ||
539 | val _ = n = length cs | |
540 | orelse error (SOME pos, "Updated fields in record update not found in starting expression") | |
541 | ||
542 | val str = | |
543 | case str of | |
544 | "" => str | |
545 | | _ => String.extract(str, 2, NONE) | |
546 | val str = "let val UPD' = " ^ es ^ " in {" ^ str ^ "} end" | |
547 | in | |
548 | (ty, str) | |
549 | end) | |
550 | handle Skip => (errorTy, "<error>") | |
551 | ||
8291a2b9 | 552 | and mergePatVars pos (vars1, vars2) = |
c0a3b488 AC |
553 | StringMap.foldli (fn (v, ty, vars) => |
554 | (case StringMap.find (vars, v) of | |
555 | NONE => StringMap.insert (vars, v, ty) | |
556 | | SOME _ => error (SOME pos, "Duplicate variable " ^ v ^ " in pattern"))) vars1 vars2 | |
557 | ||
8291a2b9 | 558 | and xpat state (PAT (p, pos)) = |
c0a3b488 AC |
559 | (case p of |
560 | Ident_p [] => raise Fail "Impossible empty Ident_p" | |
561 | | Ident_p [id] => | |
562 | ((lookCon' (state, id), StringMap.empty, id) | |
563 | handle StaticEnv.Unbound => | |
564 | let | |
565 | val ty = newTyvar false | |
566 | in | |
567 | (ty, StringMap.insert (StringMap.empty, id, VAR ty), id) | |
568 | end) | |
569 | | Ident_p (path as (s::rest)) => | |
570 | (resolveCon (pos, state, path), StringMap.empty, foldl (fn (v, st) => st ^ "." ^ v) s rest) | |
571 | | Wild_p => (newTyvar false, StringMap.empty, "_") | |
572 | | Int_p n => (BasicTypes.intTy, StringMap.empty, Int.toString n) | |
8291a2b9 | 573 | | Real_p n => (BasicTypes.realTy, StringMap.empty, Real.toString n) |
c0a3b488 | 574 | | String_p s => (BasicTypes.stringTy, StringMap.empty, "\"" ^ s ^ "\"") |
8291a2b9 | 575 | | Char_p s => (BasicTypes.charTy, StringMap.empty, "#\"" ^ s ^ "\"") |
c0a3b488 AC |
576 | | App_p ([], _) => raise Fail "Impossible App_p" |
577 | | App_p ([id], p) => | |
578 | let | |
579 | val (ty, vars, s) = xpat state p | |
580 | val tyc = lookCon (state, id, pos) | |
581 | val dom = domain tyc | |
582 | in | |
583 | unify state (pos, dom, ty); | |
584 | (range tyc, vars, id ^ " (" ^ s ^ ")") | |
585 | end | |
586 | | App_p (path as (fst::rest), p) => | |
587 | let | |
588 | val (ty, vars, s) = xpat state p | |
589 | val tyc = resolveCon (pos, state, path) | |
590 | val dom = domain tyc | |
591 | in | |
592 | unify state (pos, dom, ty); | |
593 | (range tyc, vars, foldl (fn (n, st) => st ^ "." ^ n) fst rest ^ " (" ^ s ^ ")") | |
594 | end | |
595 | | Cons_p (p1, p2) => | |
596 | let | |
597 | val (ty1, vars', s1) = xpat state p1 | |
598 | val (ty2, vars'', s2) = xpat state p2 | |
599 | ||
600 | val resty = Types.CONty (BasicTypes.listTycon, [ty1]) | |
601 | in | |
602 | unify state (pos, ty2, resty); | |
603 | (resty, mergePatVars pos (vars', vars''), "(" ^ s1 ^ ")::(" ^ s2 ^ ")") | |
604 | end | |
605 | | As_p (id, p) => | |
606 | let | |
607 | val (ty, vars, s) = xpat state p | |
608 | in | |
609 | not (Option.isSome (StringMap.find(vars, id))) | |
610 | orelse error (SOME pos, "Duplicate variable " ^ id ^ " in pattern"); | |
611 | (ty, StringMap.insert (vars, id, VAR ty), id ^ " as (" ^ s ^ ")") | |
612 | end | |
613 | | Record_p (ist, cs) => | |
614 | let | |
615 | val (cs, vars, str) = foldl (fn ((id, p), (cs, vars, str)) => | |
616 | let | |
617 | val (ty, vars', s) = xpat state p | |
618 | in | |
619 | ((Symbol.labSymbol id, ty)::cs, mergePatVars pos (vars, vars'), | |
620 | str ^ ", " ^ id ^ " = " ^ s) | |
621 | end) ([], StringMap.empty, "") cs | |
622 | val cs = rev cs | |
623 | val str = | |
624 | if String.size str >= 2 then | |
625 | String.extract(str, 2, NONE) | |
626 | else | |
627 | str | |
628 | val str = "{" ^ str ^ "}" | |
629 | in | |
630 | (BasicTypes.recordTy cs, vars, str) | |
631 | end | |
632 | | FlexRecord_p cs => | |
633 | let | |
634 | val (cs, vars, str) = foldl (fn ((id, p), (cs, vars, str)) => | |
635 | let | |
636 | val (ty, vars', s) = xpat state p | |
637 | in | |
638 | ((Symbol.labSymbol id, ty)::cs, mergePatVars pos (vars, vars'), | |
639 | str ^ ", " ^ id ^ " = " ^ s) | |
640 | end) ([], StringMap.empty, "") cs | |
641 | val cs = rev cs | |
642 | val str = | |
643 | if String.size str >= 2 then | |
644 | String.extract(str, 2, NONE) | |
645 | else | |
646 | str | |
647 | val str = "{" ^ str ^ ", ...}" | |
648 | in | |
649 | (newFlex cs, vars, str) | |
650 | end | |
651 | (*| _ => | |
652 | error (SOME pos, "Not done yet!!!")*)) | |
653 | handle Skip => (errorTy, StringMap.empty, "<error>") | |
654 | ||
655 | fun xblock state (BLOCK (blocks, pos)) = | |
656 | let | |
657 | fun folder (BITEM (bi, pos), (state, str)) = | |
658 | (case bi of | |
659 | Html_i s => | |
660 | (state, str ^ "val _ = Web.print (\"" ^ escapeString s ^ "\")\n") | |
661 | | Ref_i rs => | |
662 | let | |
663 | fun folder ((id, e), (state, str)) = | |
664 | let | |
665 | val (ty, es) = xexp state e | |
666 | ||
667 | val state = addVar (state, id, REF ty) | |
668 | in | |
669 | (state, str ^ "val " ^ id ^ " = ref (" ^ es ^ ")\n") | |
670 | end | |
671 | in | |
672 | foldl folder (state, str) rs | |
673 | end | |
674 | | Assn_i (id, e) => | |
675 | let | |
676 | val vty = | |
677 | case getVar (state, id, SOME pos) of | |
678 | NONE => error (SOME pos, "Unbound variable " ^ id) | |
679 | | SOME (REF vty) => vty | |
680 | | _ => error (SOME pos, "Can't assign to non-ref variable " ^ id) | |
681 | ||
682 | val (ty, es) = xexp state e | |
683 | in | |
684 | unify state (pos, ty, vty); | |
685 | (state, str ^ "val _ = " ^ id ^ " := (" ^ es ^ ")\n") | |
686 | end | |
687 | | Val_i (p, e) => | |
688 | let | |
689 | val (pty, vars, ps) = xpat state p | |
690 | val state' = addVars (state, vars) | |
691 | val (ty, es) = xexp state e | |
692 | in | |
693 | unify state (pos, pty, ty); | |
694 | (state', str ^ "val " ^ ps ^ " = (" ^ es ^ ")\n") | |
695 | end | |
696 | | Exp_i e => | |
697 | let | |
698 | val (ty, s) = xexp state e | |
699 | val ty = TypesUtil.headReduceType ty | |
700 | val printFn = | |
701 | case printFn state ty of | |
702 | NONE => (if tyToString state ty = "_" then | |
703 | () | |
704 | else | |
705 | error (SOME pos, "Unable to convert value of type " ^ | |
706 | tyToString state ty ^ " to string"); | |
707 | "<errorPrint>") | |
708 | | SOME v => v | |
709 | in | |
710 | (state, str ^ "val _ = " ^ printFn ^ " (" ^ s ^ ")\n") | |
711 | end | |
8291a2b9 | 712 | | Ifthenelse_i (e, b, els) => |
c0a3b488 AC |
713 | let |
714 | val str = str ^ "val _ = " | |
8291a2b9 AC |
715 | val (ty, s) = xexp state e |
716 | val (_, str') = xblock state b | |
717 | val _ = unify state (pos, ty, BasicTypes.boolTy) | |
718 | val str = str ^ "if (" ^ s ^ ") then let\n" ^ | |
719 | str' ^ | |
720 | "in () end\n" | |
c0a3b488 AC |
721 | val str = |
722 | case els of | |
723 | NONE => | |
724 | str ^ "else ()\n" | |
725 | | SOME els => | |
726 | let | |
727 | val (_, str') = xblock state els | |
728 | in | |
729 | str ^ "else let\n" ^ | |
730 | str' ^ | |
731 | "in () end\n" | |
732 | end | |
733 | in | |
734 | (state, str) | |
735 | end | |
736 | | Foreach_i (id, e, b) => | |
737 | let | |
738 | val parm = newTyvar false | |
739 | ||
740 | val (ty, es) = xexp state e | |
741 | ||
742 | val _ = unify state (pos, ty, Types.CONty (BasicTypes.listTycon, [parm])) | |
743 | ||
744 | (*val _ = print ("... to " ^ tyToString (context, ivmap, pty) ^ "\n")*) | |
745 | ||
746 | val state = addVar (state, id, VAR parm) | |
747 | val (_, bs) = xblock state b | |
748 | in | |
8291a2b9 AC |
749 | (state, str ^ "fun foreach (" ^ id ^ " : " ^ |
750 | tyToString state parm ^ ") = let\n" ^ | |
c0a3b488 AC |
751 | bs ^ |
752 | "in () end\n" ^ | |
753 | "val _ = app foreach (" ^ es ^ ")\n") | |
754 | end | |
755 | | For_i (id, eFrom, eTo, b) => | |
756 | let | |
757 | val (ty1, es1) = xexp state eFrom | |
758 | val _ = unify state (pos, ty1, BasicTypes.intTy) | |
759 | ||
760 | val (ty2, es2) = xexp state eTo | |
761 | val _ = unify state (pos, ty2, BasicTypes.intTy) | |
762 | ||
763 | val state = addVar (state, id, VAR BasicTypes.intTy) | |
764 | val (_, bs) = xblock state b | |
765 | in | |
766 | (state, str ^ "fun forFunc " ^ id ^ " = let\n" ^ | |
767 | bs ^ | |
768 | "in () end\n" ^ | |
8291a2b9 | 769 | "val _ = Web.for forFunc (" ^ es1 ^ ", " ^ es2 ^ ")\n") |
c0a3b488 AC |
770 | end |
771 | | Case_i (e, matches) => | |
772 | let | |
773 | val (ty, s) = xexp state e | |
774 | ||
775 | fun folder ((p, b), (first, str)) = | |
776 | let | |
777 | val (pty, vars', ps) = xpat state p | |
778 | ||
779 | val _ = unify state (pos, ty, pty) | |
780 | ||
781 | val (_, str') = xblock (addVars (state, vars')) b | |
782 | ||
783 | (*val _ = print ("Pattern type: " ^ tyToString (context, ivmap, pty) ^ " vs. " ^ tyToString (context, ivmap, ty) ^ "\n")*) | |
784 | in | |
785 | (false, | |
786 | str ^ (if first then " " else " | ") ^ "(" ^ ps ^ ") => let\n" ^ | |
787 | str' ^ | |
788 | "in () end\n") | |
789 | end | |
790 | val (_, str) = | |
791 | foldl folder (true, str ^ "val _ = (case (" ^ s ^ ") of\n") matches | |
792 | val str = str ^ ") handle Match => ()\n" | |
793 | in | |
794 | (state, str) | |
795 | end | |
796 | | TryCatch_i (b, matches) => | |
797 | let | |
798 | val (_, bs) = xblock state b | |
799 | ||
800 | fun folder ((p, b), (first, str)) = | |
801 | let | |
802 | val (pty, vars, ps) = xpat state p | |
803 | val state = addVars (state, vars) | |
804 | val (_, str') = xblock state b | |
805 | in | |
806 | unify state (pos, BasicTypes.exnTy, pty); | |
807 | (false, | |
808 | str ^ (if first then " " else " | ") ^ "(" ^ ps ^ ") => let\n" ^ | |
809 | str' ^ | |
810 | "in () end\n") | |
811 | end | |
812 | val (_, str) = | |
813 | foldl folder (true, | |
814 | str ^ "val _ = (let\n" ^ | |
815 | bs ^ | |
816 | "in () end handle\n") matches | |
817 | val str = str ^ ")\n" | |
818 | in | |
819 | (state, str) | |
820 | end | |
821 | | Open_i paths => | |
822 | let | |
823 | fun folder (path, state) = openStructure (pos, state, path) | |
824 | ||
825 | val str = foldl (fn (path, str) => str ^ " " ^ Tree.pathString path) (str ^ "open") paths | |
826 | val str = str ^ "\n" | |
827 | in | |
828 | (foldl folder state paths, str) | |
829 | end) | |
830 | handle Skip => (state, str) | |
831 | in | |
832 | foldl folder (state, "") blocks | |
833 | end | |
834 | ||
835 | fun trans (config, env, templates, name, block) = | |
836 | let | |
837 | val state = mkState (config, env, templates) | |
838 | val (_, str) = xblock state block | |
839 | in | |
840 | "(* This file generated automatically by something or other *)\n" ^ | |
841 | "\n" ^ | |
842 | "structure " ^ name ^ " :> TEMPLATE =\n" ^ | |
843 | "struct\n" ^ | |
844 | "fun exec () = let\n" ^ | |
845 | str ^ | |
846 | "in () end\n" ^ | |
847 | "end\n" | |
848 | end | |
849 | end | |
850 | ||
851 |