comparison src/jscomp.sml @ 589:102e81d975e3

Included a recursive function in JavaScript
author Adam Chlipala <adamc@hcoop.net>
date Thu, 01 Jan 2009 11:58:00 -0500
parents 5803b4f041cb
children 57f476c934da
comparison
equal deleted inserted replaced
588:5803b4f041cb 589:102e81d975e3
31 31
32 structure EM = ErrorMsg 32 structure EM = ErrorMsg
33 structure E = MonoEnv 33 structure E = MonoEnv
34 structure U = MonoUtil 34 structure U = MonoUtil
35 35
36 structure IS = IntBinarySet
37 structure IM = IntBinaryMap
38
36 val funcs = [(("Basis", "alert"), "alert"), 39 val funcs = [(("Basis", "alert"), "alert"),
37 (("Basis", "htmlifyBool"), "bs"), 40 (("Basis", "htmlifyBool"), "bs"),
38 (("Basis", "htmlifyFloat"), "ts"), 41 (("Basis", "htmlifyFloat"), "ts"),
39 (("Basis", "htmlifyInt"), "ts"), 42 (("Basis", "htmlifyInt"), "ts"),
40 (("Basis", "htmlifyString"), "escape"), 43 (("Basis", "htmlifyString"), "escape"),
52 55
53 fun ffi k = FM.find (funcs, k) 56 fun ffi k = FM.find (funcs, k)
54 57
55 type state = { 58 type state = {
56 decls : decl list, 59 decls : decl list,
57 script : string 60 script : string list,
61 included : IS.set
58 } 62 }
59 63
60 fun varDepth (e, _) = 64 fun varDepth (e, _) =
61 case e of 65 case e of
62 EPrim _ => 0 66 EPrim _ => 0
96 case es of 100 case es of
97 [] => (EPrim (Prim.String ""), loc) 101 [] => (EPrim (Prim.String ""), loc)
98 | [x] => x 102 | [x] => x
99 | x :: es' => (EStrcat (x, strcat loc es'), loc) 103 | x :: es' => (EStrcat (x, strcat loc es'), loc)
100 104
101 fun jsExp mode skip outer =
102 let
103 val len = length outer
104
105 fun jsE inner (e as (_, loc), st) =
106 let
107 fun str s = (EPrim (Prim.String s), loc)
108
109 fun var n = Int.toString (len + inner - n - 1)
110
111 fun patCon pc =
112 case pc of
113 PConVar n => str (Int.toString n)
114 | PConFfi {mod = "Basis", con = "True", ...} => str "true"
115 | PConFfi {mod = "Basis", con = "False", ...} => str "false"
116 | PConFfi {con, ...} => str ("\"_" ^ con ^ "\"")
117
118 fun isNullable (t, _) =
119 case t of
120 TOption _ => true
121 | TRecord [] => true
122 | _ => false
123
124 fun unsupported s =
125 (EM.errorAt loc (s ^ " in code to be compiled to JavaScript");
126 (str "ERROR", st))
127
128 val strcat = strcat loc
129
130 fun quoteExp (t : typ) e =
131 case #1 t of
132 TSource => strcat [str "s",
133 (EFfiApp ("Basis", "htmlifyInt", [e]), loc)]
134 | TRecord [] => str "null"
135 | TFfi ("Basis", "string") => e
136 | _ => (EM.errorAt loc "Don't know how to embed type in JavaScript";
137 Print.prefaces "Can't embed" [("t", MonoPrint.p_typ MonoEnv.empty t)];
138 str "ERROR")
139
140 fun jsPrim p =
141 case p of
142 Prim.String s =>
143 str ("\""
144 ^ String.translate (fn #"'" =>
145 if mode = Attribute then
146 "\\047"
147 else
148 "'"
149 | #"\"" => "\\\""
150 | #"<" =>
151 if mode = Script then
152 "<"
153 else
154 "\\074"
155 | #"\\" => "\\\\"
156 | ch => String.str ch) s
157 ^ "\"")
158 | _ => str (Prim.toString p)
159
160 fun jsPat depth inner (p, _) succ fail =
161 case p of
162 PWild => succ
163 | PVar _ => strcat [str ("(_" ^ Int.toString (len + inner) ^ "=d" ^ Int.toString depth ^ ","),
164 succ,
165 str ")"]
166 | PPrim p => strcat [str ("(d" ^ Int.toString depth ^ "=="),
167 jsPrim p,
168 str "?",
169 succ,
170 str ":",
171 fail,
172 str ")"]
173 | PCon (_, PConFfi {mod = "Basis", con = "True", ...}, NONE) =>
174 strcat [str ("(d" ^ Int.toString depth ^ "?"),
175 succ,
176 str ":",
177 fail,
178 str ")"]
179 | PCon (_, PConFfi {mod = "Basis", con = "False", ...}, NONE) =>
180 strcat [str ("(d" ^ Int.toString depth ^ "?"),
181 fail,
182 str ":",
183 succ,
184 str ")"]
185 | PCon (_, pc, NONE) =>
186 strcat [str ("(d" ^ Int.toString depth ^ "=="),
187 patCon pc,
188 str "?",
189 succ,
190 str ":",
191 fail,
192 str ")"]
193 | PCon (_, pc, SOME p) =>
194 strcat [str ("(d" ^ Int.toString depth ^ ".n=="),
195 patCon pc,
196 str ("?(d" ^ Int.toString depth ^ "=d" ^ Int.toString depth ^ ".v,"),
197 succ,
198 str "):",
199 fail,
200 str ")"]
201 | PRecord xps =>
202 let
203 val (_, succ) = foldl
204 (fn ((x, p, _), (inner, succ)) =>
205 (inner + E.patBindsN p,
206 strcat [str ("(d" ^ Int.toString (depth+1) ^ "=d"
207 ^ Int.toString depth ^ "._" ^ x ^ ","),
208 jsPat (depth+1) inner p succ fail,
209 str ")"]))
210 (inner, succ) xps
211 in
212 succ
213 end
214 | PNone _ => strcat [str ("(d" ^ Int.toString depth ^ "?"),
215 fail,
216 str ":",
217 succ,
218 str ")"]
219 | PSome (_, p) => strcat [str ("(d" ^ Int.toString depth ^ "?"),
220 jsPat depth inner p succ fail,
221 str ":",
222 fail,
223 str ")"]
224 in
225 case #1 e of
226 EPrim p => (jsPrim p, st)
227 | ERel n =>
228 if n < inner then
229 (str ("_" ^ var n), st)
230 else
231 let
232 val n = n - inner
233 in
234 (quoteExp (List.nth (outer, n)) (ERel (n - skip), loc), st)
235 end
236 | ENamed _ => raise Fail "Named"
237 | ECon (_, pc, NONE) => (patCon pc, st)
238 | ECon (_, pc, SOME e) =>
239 let
240 val (s, st) = jsE inner (e, st)
241 in
242 (strcat [str "{n:",
243 patCon pc,
244 str ",v:",
245 s,
246 str "}"], st)
247 end
248 | ENone _ => (str "null", st)
249 | ESome (t, e) =>
250 let
251 val (e, st) = jsE inner (e, st)
252 in
253 (if isNullable t then
254 strcat [str "{v:", e, str "}"]
255 else
256 e, st)
257 end
258
259 | EFfi k =>
260 let
261 val name = case ffi k of
262 NONE => (EM.errorAt loc ("Unsupported FFI identifier " ^ #2 k ^ " in JavaScript");
263 "ERROR")
264 | SOME s => s
265 in
266 (str name, st)
267 end
268 | EFfiApp (m, x, args) =>
269 let
270 val args =
271 case (m, x, args) of
272 ("Basis", "new_client_source", [(EJavaScript (_, e, _), _)]) => [e]
273 | ("Basis", "set_client_source", [e1, (EJavaScript (_, e2, _), _)]) => [e1, e2]
274 | _ => args
275
276 val name = case ffi (m, x) of
277 NONE => (EM.errorAt loc ("Unsupported FFI function " ^ x ^ " in JavaScript");
278 "ERROR")
279 | SOME s => s
280 in
281 case args of
282 [] => (str (name ^ "()"), st)
283 | [e] =>
284 let
285 val (e, st) = jsE inner (e, st)
286 in
287 (strcat [str (name ^ "("),
288 e,
289 str ")"], st)
290 end
291 | e :: es =>
292 let
293 val (e, st) = jsE inner (e, st)
294 val (es, st) = ListUtil.foldlMapConcat
295 (fn (e, st) =>
296 let
297 val (e, st) = jsE inner (e, st)
298 in
299 ([str ",", e], st)
300 end)
301 st es
302 in
303 (strcat (str (name ^ "(")
304 :: e
305 :: es
306 @ [str ")"]), st)
307 end
308 end
309
310 | EApp (e1, e2) =>
311 let
312 val (e1, st) = jsE inner (e1, st)
313 val (e2, st) = jsE inner (e2, st)
314 in
315 (strcat [e1, str "(", e2, str ")"], st)
316 end
317 | EAbs (_, _, _, e) =>
318 let
319 val locals = List.tabulate
320 (varDepth e,
321 fn i => str ("var _" ^ Int.toString (len + inner + i + 1) ^ ";"))
322 val (e, st) = jsE (inner + 1) (e, st)
323 in
324 (strcat (str ("function(_"
325 ^ Int.toString (len + inner)
326 ^ "){")
327 :: locals
328 @ [str "return ",
329 e,
330 str "}"]),
331 st)
332 end
333
334 | EUnop (s, e) =>
335 let
336 val (e, st) = jsE inner (e, st)
337 in
338 (strcat [str ("(" ^ s),
339 e,
340 str ")"],
341 st)
342 end
343 | EBinop (s, e1, e2) =>
344 let
345 val (e1, st) = jsE inner (e1, st)
346 val (e2, st) = jsE inner (e2, st)
347 in
348 (strcat [str "(",
349 e1,
350 str s,
351 e2,
352 str ")"],
353 st)
354 end
355
356 | ERecord [] => (str "null", st)
357 | ERecord [(x, e, _)] =>
358 let
359 val (e, st) = jsE inner (e, st)
360 in
361 (strcat [str "{_x:", e, str "}"], st)
362 end
363 | ERecord ((x, e, _) :: xes) =>
364 let
365 val (e, st) = jsE inner (e, st)
366
367 val (es, st) =
368 foldr (fn ((x, e, _), (es, st)) =>
369 let
370 val (e, st) = jsE inner (e, st)
371 in
372 (str (",_" ^ x ^ ":")
373 :: e
374 :: es,
375 st)
376 end)
377 ([str "}"], st) xes
378 in
379 (strcat (str ("{_" ^ x ^ ":")
380 :: e
381 :: es),
382 st)
383 end
384 | EField (e, x) =>
385 let
386 val (e, st) = jsE inner (e, st)
387 in
388 (strcat [e,
389 str ("._" ^ x)], st)
390 end
391
392 | ECase (e, pes, _) =>
393 let
394 val plen = length pes
395
396 val (cases, st) = ListUtil.foldliMap
397 (fn (i, (p, e), st) =>
398 let
399 val (e, st) = jsE (inner + E.patBindsN p) (e, st)
400 val fail =
401 if i = plen - 1 then
402 str "pf()"
403 else
404 str ("c" ^ Int.toString (i+1) ^ "()")
405 val c = jsPat 0 inner p e fail
406 in
407 (strcat [str ("c" ^ Int.toString i ^ "=function(){return "),
408 c,
409 str "},"],
410 st)
411 end)
412 st pes
413
414 val (e, st) = jsE inner (e, st)
415 in
416 (strcat (str "("
417 :: List.revAppend (cases,
418 [str "d0=",
419 e,
420 str ",c0())"])), st)
421 end
422
423 | EStrcat (e1, e2) =>
424 let
425 val (e1, st) = jsE inner (e1, st)
426 val (e2, st) = jsE inner (e2, st)
427 in
428 (strcat [str "(", e1, str "+", e2, str ")"], st)
429 end
430
431 | EError (e, _) =>
432 let
433 val (e, st) = jsE inner (e, st)
434 in
435 (strcat [str "alert(\"ERROR: \"+", e, str ")"],
436 st)
437 end
438
439 | EWrite e =>
440 let
441 val (e, st) = jsE inner (e, st)
442 in
443 (strcat [str "document.write(",
444 e,
445 str ".v)"], st)
446 end
447
448 | ESeq (e1, e2) =>
449 let
450 val (e1, st) = jsE inner (e1, st)
451 val (e2, st) = jsE inner (e2, st)
452 in
453 (strcat [str "(", e1, str ",", e2, str ")"], st)
454 end
455 | ELet (_, _, e1, e2) =>
456 let
457 val (e1, st) = jsE inner (e1, st)
458 val (e2, st) = jsE (inner + 1) (e2, st)
459 in
460 (strcat [str ("(_" ^ Int.toString (len + inner) ^ "="),
461 e1,
462 str ",",
463 e2,
464 str ")"], st)
465 end
466
467 | EClosure _ => unsupported "EClosure"
468 | EQuery _ => unsupported "Query"
469 | EDml _ => unsupported "DML"
470 | ENextval _ => unsupported "Nextval"
471 | EUnurlify _ => unsupported "EUnurlify"
472 | EJavaScript _ => unsupported "Nested JavaScript"
473 | ESignalReturn e =>
474 let
475 val (e, st) = jsE inner (e, st)
476 in
477 (strcat [str "sr(",
478 e,
479 str ")"],
480 st)
481 end
482 | ESignalBind (e1, e2) =>
483 let
484 val (e1, st) = jsE inner (e1, st)
485 val (e2, st) = jsE inner (e2, st)
486 in
487 (strcat [str "sb(",
488 e1,
489 str ",",
490 e2,
491 str ")"],
492 st)
493 end
494 | ESignalSource e =>
495 let
496 val (e, st) = jsE inner (e, st)
497 in
498 (strcat [str "ss(",
499 e,
500 str ")"],
501 st)
502 end
503 end
504 in
505 jsE
506 end
507
508 val decl : state -> decl -> decl * state =
509 U.Decl.foldMapB {typ = fn x => x,
510 exp = fn (env, e, st) =>
511 let
512 fun doCode m skip env orig e =
513 let
514 val len = length env
515 fun str s = (EPrim (Prim.String s), #2 e)
516
517 val locals = List.tabulate
518 (varDepth e,
519 fn i => str ("var _" ^ Int.toString (len + i) ^ ";"))
520 val (e, st) = jsExp m skip env 0 (e, st)
521 in
522 (EJavaScript (m, orig, SOME (strcat (#2 e) (locals @ [e]))), st)
523 end
524 in
525 case e of
526 EJavaScript (m, orig as (EAbs (_, t, _, e), _), _) => doCode m 1 (t :: env) orig e
527 | EJavaScript (m, e, _) => doCode m 0 env e e
528 | _ => (e, st)
529 end,
530 decl = fn (_, e, st) => (e, st),
531 bind = fn (env, U.Decl.RelE (_, t)) => t :: env
532 | (env, _) => env}
533 []
534
535 fun process file = 105 fun process file =
536 let 106 let
107 val nameds = foldl (fn ((DVal (_, n, t, e, _), _), nameds) => IM.insert (nameds, n, e)
108 | ((DValRec vis, _), nameds) =>
109 foldl (fn ((_, n, _, e, _), nameds) => IM.insert (nameds, n, e))
110 nameds vis
111 | (_, nameds) => nameds)
112 IM.empty file
113
114 fun jsExp mode skip outer =
115 let
116 val len = length outer
117
118 fun jsE inner (e as (_, loc), st) =
119 let
120 fun str s = (EPrim (Prim.String s), loc)
121
122 fun var n = Int.toString (len + inner - n - 1)
123
124 fun patCon pc =
125 case pc of
126 PConVar n => str (Int.toString n)
127 | PConFfi {mod = "Basis", con = "True", ...} => str "true"
128 | PConFfi {mod = "Basis", con = "False", ...} => str "false"
129 | PConFfi {con, ...} => str ("\"_" ^ con ^ "\"")
130
131 fun isNullable (t, _) =
132 case t of
133 TOption _ => true
134 | TRecord [] => true
135 | _ => false
136
137 fun unsupported s =
138 (EM.errorAt loc (s ^ " in code to be compiled to JavaScript");
139 (str "ERROR", st))
140
141 val strcat = strcat loc
142
143 fun quoteExp (t : typ) e =
144 case #1 t of
145 TSource => strcat [str "s",
146 (EFfiApp ("Basis", "htmlifyInt", [e]), loc)]
147 | TRecord [] => str "null"
148 | TFfi ("Basis", "string") => e
149 | _ => (EM.errorAt loc "Don't know how to embed type in JavaScript";
150 Print.prefaces "Can't embed" [("t", MonoPrint.p_typ MonoEnv.empty t)];
151 str "ERROR")
152
153 fun jsPrim p =
154 case p of
155 Prim.String s =>
156 str ("\""
157 ^ String.translate (fn #"'" =>
158 if mode = Attribute then
159 "\\047"
160 else
161 "'"
162 | #"\"" => "\\\""
163 | #"<" =>
164 if mode = Script then
165 "<"
166 else
167 "\\074"
168 | #"\\" => "\\\\"
169 | ch => String.str ch) s
170 ^ "\"")
171 | _ => str (Prim.toString p)
172
173 fun jsPat depth inner (p, _) succ fail =
174 case p of
175 PWild => succ
176 | PVar _ => strcat [str ("(_" ^ Int.toString (len + inner) ^ "=d"
177 ^ Int.toString depth ^ ","),
178 succ,
179 str ")"]
180 | PPrim p => strcat [str ("(d" ^ Int.toString depth ^ "=="),
181 jsPrim p,
182 str "?",
183 succ,
184 str ":",
185 fail,
186 str ")"]
187 | PCon (_, PConFfi {mod = "Basis", con = "True", ...}, NONE) =>
188 strcat [str ("(d" ^ Int.toString depth ^ "?"),
189 succ,
190 str ":",
191 fail,
192 str ")"]
193 | PCon (_, PConFfi {mod = "Basis", con = "False", ...}, NONE) =>
194 strcat [str ("(d" ^ Int.toString depth ^ "?"),
195 fail,
196 str ":",
197 succ,
198 str ")"]
199 | PCon (_, pc, NONE) =>
200 strcat [str ("(d" ^ Int.toString depth ^ "=="),
201 patCon pc,
202 str "?",
203 succ,
204 str ":",
205 fail,
206 str ")"]
207 | PCon (_, pc, SOME p) =>
208 strcat [str ("(d" ^ Int.toString depth ^ ".n=="),
209 patCon pc,
210 str ("?(d" ^ Int.toString depth ^ "=d" ^ Int.toString depth ^ ".v,"),
211 jsPat depth inner p succ fail,
212 str "):",
213 fail,
214 str ")"]
215 | PRecord xps =>
216 let
217 val (_, succ) = foldl
218 (fn ((x, p, _), (inner, succ)) =>
219 (inner + E.patBindsN p,
220 strcat [str ("(d" ^ Int.toString (depth+1) ^ "=d"
221 ^ Int.toString depth ^ "._" ^ x ^ ","),
222 jsPat (depth+1) inner p succ fail,
223 str ")"]))
224 (inner, succ) xps
225 in
226 succ
227 end
228 | PNone _ => strcat [str ("(d" ^ Int.toString depth ^ "?"),
229 fail,
230 str ":",
231 succ,
232 str ")"]
233 | PSome (_, p) => strcat [str ("(d" ^ Int.toString depth ^ "?"),
234 jsPat depth inner p succ fail,
235 str ":",
236 fail,
237 str ")"]
238
239 fun deStrcat (e, _) =
240 case e of
241 EPrim (Prim.String s) => s
242 | EStrcat (e1, e2) => deStrcat e1 ^ deStrcat e2
243 | _ => raise Fail "Jscomp: deStrcat"
244 in
245 case #1 e of
246 EPrim p => (jsPrim p, st)
247 | ERel n =>
248 if n < inner then
249 (str ("_" ^ var n), st)
250 else
251 let
252 val n = n - inner
253 in
254 (quoteExp (List.nth (outer, n)) (ERel (n - skip), loc), st)
255 end
256
257 | ENamed n =>
258 let
259 val st =
260 if IS.member (#included st, n) then
261 st
262 else
263 case IM.find (nameds, n) of
264 NONE => raise Fail "Jscomp: Unbound ENamed"
265 | SOME e =>
266 let
267 val st = {decls = #decls st,
268 script = #script st,
269 included = IS.add (#included st, n)}
270
271 val (e, st) = jsExp mode skip [] 0 (e, st)
272 val e = deStrcat e
273
274 val sc = "_n" ^ Int.toString n ^ "=" ^ e ^ ";\n"
275 in
276 {decls = #decls st,
277 script = sc :: #script st,
278 included = #included st}
279 end
280 in
281 (str ("_n" ^ Int.toString n), st)
282 end
283
284 | ECon (_, pc, NONE) => (patCon pc, st)
285 | ECon (_, pc, SOME e) =>
286 let
287 val (s, st) = jsE inner (e, st)
288 in
289 (strcat [str "{n:",
290 patCon pc,
291 str ",v:",
292 s,
293 str "}"], st)
294 end
295 | ENone _ => (str "null", st)
296 | ESome (t, e) =>
297 let
298 val (e, st) = jsE inner (e, st)
299 in
300 (if isNullable t then
301 strcat [str "{v:", e, str "}"]
302 else
303 e, st)
304 end
305
306 | EFfi k =>
307 let
308 val name = case ffi k of
309 NONE => (EM.errorAt loc ("Unsupported FFI identifier " ^ #2 k
310 ^ " in JavaScript");
311 "ERROR")
312 | SOME s => s
313 in
314 (str name, st)
315 end
316 | EFfiApp (m, x, args) =>
317 let
318 val args =
319 case (m, x, args) of
320 ("Basis", "new_client_source", [(EJavaScript (_, e, _), _)]) => [e]
321 | ("Basis", "set_client_source", [e1, (EJavaScript (_, e2, _), _)]) => [e1, e2]
322 | _ => args
323
324 val name = case ffi (m, x) of
325 NONE => (EM.errorAt loc ("Unsupported FFI function "
326 ^ x ^ " in JavaScript");
327 "ERROR")
328 | SOME s => s
329 in
330 case args of
331 [] => (str (name ^ "()"), st)
332 | [e] =>
333 let
334 val (e, st) = jsE inner (e, st)
335 in
336 (strcat [str (name ^ "("),
337 e,
338 str ")"], st)
339 end
340 | e :: es =>
341 let
342 val (e, st) = jsE inner (e, st)
343 val (es, st) = ListUtil.foldlMapConcat
344 (fn (e, st) =>
345 let
346 val (e, st) = jsE inner (e, st)
347 in
348 ([str ",", e], st)
349 end)
350 st es
351 in
352 (strcat (str (name ^ "(")
353 :: e
354 :: es
355 @ [str ")"]), st)
356 end
357 end
358
359 | EApp (e1, e2) =>
360 let
361 val (e1, st) = jsE inner (e1, st)
362 val (e2, st) = jsE inner (e2, st)
363 in
364 (strcat [e1, str "(", e2, str ")"], st)
365 end
366 | EAbs (_, _, _, e) =>
367 let
368 val locals = List.tabulate
369 (varDepth e,
370 fn i => str ("var _" ^ Int.toString (len + inner + i + 1) ^ ";"))
371 val (e, st) = jsE (inner + 1) (e, st)
372 in
373 (strcat (str ("function(_"
374 ^ Int.toString (len + inner)
375 ^ "){")
376 :: locals
377 @ [str "return ",
378 e,
379 str "}"]),
380 st)
381 end
382
383 | EUnop (s, e) =>
384 let
385 val (e, st) = jsE inner (e, st)
386 in
387 (strcat [str ("(" ^ s),
388 e,
389 str ")"],
390 st)
391 end
392 | EBinop (s, e1, e2) =>
393 let
394 val (e1, st) = jsE inner (e1, st)
395 val (e2, st) = jsE inner (e2, st)
396 in
397 (strcat [str "(",
398 e1,
399 str s,
400 e2,
401 str ")"],
402 st)
403 end
404
405 | ERecord [] => (str "null", st)
406 | ERecord [(x, e, _)] =>
407 let
408 val (e, st) = jsE inner (e, st)
409 in
410 (strcat [str "{_x:", e, str "}"], st)
411 end
412 | ERecord ((x, e, _) :: xes) =>
413 let
414 val (e, st) = jsE inner (e, st)
415
416 val (es, st) =
417 foldr (fn ((x, e, _), (es, st)) =>
418 let
419 val (e, st) = jsE inner (e, st)
420 in
421 (str (",_" ^ x ^ ":")
422 :: e
423 :: es,
424 st)
425 end)
426 ([str "}"], st) xes
427 in
428 (strcat (str ("{_" ^ x ^ ":")
429 :: e
430 :: es),
431 st)
432 end
433 | EField (e, x) =>
434 let
435 val (e, st) = jsE inner (e, st)
436 in
437 (strcat [e,
438 str ("._" ^ x)], st)
439 end
440
441 | ECase (e, pes, _) =>
442 let
443 val plen = length pes
444
445 val (cases, st) = ListUtil.foldliMap
446 (fn (i, (p, e), st) =>
447 let
448 val (e, st) = jsE (inner + E.patBindsN p) (e, st)
449 val fail =
450 if i = plen - 1 then
451 str "pf()"
452 else
453 str ("c" ^ Int.toString (i+1) ^ "()")
454 val c = jsPat 0 inner p e fail
455 in
456 (strcat [str ("c" ^ Int.toString i ^ "=function(){return "),
457 c,
458 str "},"],
459 st)
460 end)
461 st pes
462
463 val (e, st) = jsE inner (e, st)
464 in
465 (strcat (str "("
466 :: List.revAppend (cases,
467 [str "d0=",
468 e,
469 str ",c0())"])), st)
470 end
471
472 | EStrcat (e1, e2) =>
473 let
474 val (e1, st) = jsE inner (e1, st)
475 val (e2, st) = jsE inner (e2, st)
476 in
477 (strcat [str "(", e1, str "+", e2, str ")"], st)
478 end
479
480 | EError (e, _) =>
481 let
482 val (e, st) = jsE inner (e, st)
483 in
484 (strcat [str "alert(\"ERROR: \"+", e, str ")"],
485 st)
486 end
487
488 | EWrite e =>
489 let
490 val (e, st) = jsE inner (e, st)
491 in
492 (strcat [str "document.write(",
493 e,
494 str ".v)"], st)
495 end
496
497 | ESeq (e1, e2) =>
498 let
499 val (e1, st) = jsE inner (e1, st)
500 val (e2, st) = jsE inner (e2, st)
501 in
502 (strcat [str "(", e1, str ",", e2, str ")"], st)
503 end
504 | ELet (_, _, e1, e2) =>
505 let
506 val (e1, st) = jsE inner (e1, st)
507 val (e2, st) = jsE (inner + 1) (e2, st)
508 in
509 (strcat [str ("(_" ^ Int.toString (len + inner) ^ "="),
510 e1,
511 str ",",
512 e2,
513 str ")"], st)
514 end
515
516 | EClosure _ => unsupported "EClosure"
517 | EQuery _ => unsupported "Query"
518 | EDml _ => unsupported "DML"
519 | ENextval _ => unsupported "Nextval"
520 | EUnurlify _ => unsupported "EUnurlify"
521 | EJavaScript _ => unsupported "Nested JavaScript"
522 | ESignalReturn e =>
523 let
524 val (e, st) = jsE inner (e, st)
525 in
526 (strcat [str "sr(",
527 e,
528 str ")"],
529 st)
530 end
531 | ESignalBind (e1, e2) =>
532 let
533 val (e1, st) = jsE inner (e1, st)
534 val (e2, st) = jsE inner (e2, st)
535 in
536 (strcat [str "sb(",
537 e1,
538 str ",",
539 e2,
540 str ")"],
541 st)
542 end
543 | ESignalSource e =>
544 let
545 val (e, st) = jsE inner (e, st)
546 in
547 (strcat [str "ss(",
548 e,
549 str ")"],
550 st)
551 end
552 end
553 in
554 jsE
555 end
556
557 val decl : state -> decl -> decl * state =
558 U.Decl.foldMapB {typ = fn x => x,
559 exp = fn (env, e, st) =>
560 let
561 fun doCode m skip env orig e =
562 let
563 val len = length env
564 fun str s = (EPrim (Prim.String s), #2 e)
565
566 val locals = List.tabulate
567 (varDepth e,
568 fn i => str ("var _" ^ Int.toString (len + i) ^ ";"))
569 val (e, st) = jsExp m skip env 0 (e, st)
570 in
571 (EJavaScript (m, orig, SOME (strcat (#2 e) (locals @ [e]))), st)
572 end
573 in
574 case e of
575 EJavaScript (m, orig as (EAbs (_, t, _, e), _), _) =>
576 doCode m 1 (t :: env) orig e
577 | EJavaScript (m, e, _) => doCode m 0 env e e
578 | _ => (e, st)
579 end,
580 decl = fn (_, e, st) => (e, st),
581 bind = fn (env, U.Decl.RelE (_, t)) => t :: env
582 | (env, _) => env}
583 []
584
537 fun doDecl (d, st) = 585 fun doDecl (d, st) =
538 let 586 let
539 val (d, st) = decl st d 587 val (d, st) = decl st d
540 in 588 in
541 (List.revAppend (#decls st, [d]), 589 (List.revAppend (#decls st, [d]),
542 {decls = [], 590 {decls = [],
543 script = #script st}) 591 script = #script st,
592 included = #included st})
544 end 593 end
545 594
546 val (ds, st) = ListUtil.foldlMapConcat doDecl 595 val (ds, st) = ListUtil.foldlMapConcat doDecl
547 {decls = [], 596 {decls = [],
548 script = ""} 597 script = [],
598 included = IS.empty}
549 file 599 file
550 600
551 val inf = TextIO.openIn (OS.Path.joinDirFile {dir = Config.libJs, file = "urweb.js"}) 601 val inf = TextIO.openIn (OS.Path.joinDirFile {dir = Config.libJs, file = "urweb.js"})
552 fun lines acc = 602 fun lines acc =
553 case TextIO.inputLine inf of 603 case TextIO.inputLine inf of
554 NONE => String.concat (rev acc) 604 NONE => String.concat (rev acc)
555 | SOME line => lines (line :: acc) 605 | SOME line => lines (line :: acc)
556 val lines = lines [] 606 val lines = lines []
557 in 607 in
558 TextIO.closeIn inf; 608 TextIO.closeIn inf;
559 (DJavaScript lines, ErrorMsg.dummySpan) :: ds 609 (DJavaScript (lines ^ String.concat (rev (#script st))), ErrorMsg.dummySpan) :: ds
560 end 610 end
561 611
562 end 612 end