comparison src/compiler.sml @ 767:d27ed5ddeb52

Add 'library' directive
author Adam Chlipala <adamc@hcoop.net>
date Sat, 02 May 2009 12:50:52 -0400
parents df09c95085f8
children 3b7e46790fa7
comparison
equal deleted inserted replaced
766:df09c95085f8 767:d27ed5ddeb52
262 val (s, _) = Substring.splitr Char.isSpace s 262 val (s, _) = Substring.splitr Char.isSpace s
263 in 263 in
264 s 264 s
265 end 265 end
266 266
267 fun parseUrp' filename =
268 let
269 val dir = OS.Path.dir filename
270 val inf = TextIO.openIn (OS.Path.joinBaseExt {base = filename, ext = SOME "urp"})
271
272 fun relify fname =
273 OS.Path.concat (dir, fname)
274 handle OS.Path.Path => fname
275
276 val absDir = OS.Path.mkAbsolute {path = dir, relativeTo = OS.FileSys.getDir ()}
277
278 fun relifyA fname = OS.Path.mkAbsolute {path = fname, relativeTo = absDir}
279
280 fun readSources acc =
281 case TextIO.inputLine inf of
282 NONE => rev acc
283 | SOME line =>
284 let
285 val acc = if CharVector.all Char.isSpace line then
286 acc
287 else
288 let
289 val fname = String.implode (List.filter (fn x => not (Char.isSpace x))
290 (String.explode line))
291 val fname = relify fname
292 in
293 fname :: acc
294 end
295 in
296 readSources acc
297 end
298
299 val prefix = ref NONE
300 val database = ref NONE
301 val exe = ref NONE
302 val sql = ref NONE
303 val debug = ref false
304 val profile = ref false
305 val timeout = ref NONE
306 val ffi = ref []
307 val link = ref []
308 val headers = ref []
309 val scripts = ref []
310 val clientToServer = ref []
311 val effectful = ref []
312 val clientOnly = ref []
313 val serverOnly = ref []
314 val jsFuncs = ref []
315 val libs = ref []
316
317 fun finish sources =
318 let
319 val job = {
320 prefix = Option.getOpt (!prefix, "/"),
321 database = !database,
322 exe = Option.getOpt (!exe, OS.Path.joinBaseExt {base = OS.Path.base filename,
323 ext = SOME "exe"}),
324 sql = !sql,
325 debug = !debug,
326 profile = !profile,
327 timeout = Option.getOpt (!timeout, 60),
328 ffi = rev (!ffi),
329 link = rev (!link),
330 headers = rev (!headers),
331 scripts = rev (!scripts),
332 clientToServer = rev (!clientToServer),
333 effectful = rev (!effectful),
334 clientOnly = rev (!clientOnly),
335 serverOnly = rev (!serverOnly),
336 jsFuncs = rev (!jsFuncs),
337 sources = sources
338 }
339
340 fun mergeO f (old, new) =
341 case (old, new) of
342 (NONE, _) => new
343 | (_, NONE) => old
344 | (SOME v1, SOME v2) => SOME (f (v1, v2))
345
346 fun same desc = mergeO (fn (x : string, y) =>
347 (if x = y then
348 ()
349 else
350 ErrorMsg.error ("Multiple "
351 ^ desc ^ " values that don't agree");
352 x))
353
354 fun merge (old : job, new : job) = {
355 prefix = #prefix old,
356 database = #database old,
357 exe = #exe old,
358 sql = #sql old,
359 debug = #debug old orelse #debug new,
360 profile = #profile old orelse #profile new,
361 timeout = #timeout old,
362 ffi = #ffi old @ #ffi new,
363 link = #link old @ #link new,
364 headers = #headers old @ #headers new,
365 scripts = #scripts old @ #scripts new,
366 clientToServer = #clientToServer old @ #clientToServer new,
367 effectful = #effectful old @ #effectful new,
368 clientOnly = #clientOnly old @ #clientOnly new,
369 serverOnly = #serverOnly old @ #serverOnly new,
370 jsFuncs = #jsFuncs old @ #jsFuncs new,
371 sources = #sources old @ #sources new
372 }
373 in
374 foldr (fn (fname, job) => merge (job, parseUrp' fname)) job (!libs)
375 end
376
377 fun read () =
378 case TextIO.inputLine inf of
379 NONE => finish []
380 | SOME "\n" => finish (readSources [])
381 | SOME line =>
382 let
383 val (cmd, arg) = Substring.splitl (fn x => not (Char.isSpace x)) (Substring.full line)
384 val cmd = Substring.string (trim cmd)
385 val arg = Substring.string (trim arg)
386
387 fun ffiS () =
388 case String.fields (fn ch => ch = #".") arg of
389 [m, x] => (m, x)
390 | _ => (ErrorMsg.error (cmd ^ " argument not of the form Module.func");
391 ("", ""))
392
393 fun ffiM () =
394 case String.fields (fn ch => ch = #"=") arg of
395 [f, s] =>
396 (case String.fields (fn ch => ch = #".") f of
397 [m, x] => ((m, x), s)
398 | _ => (ErrorMsg.error (cmd ^ " argument not of the form Module.func=func'");
399 (("", ""), "")))
400 | _ => (ErrorMsg.error (cmd ^ " argument not of the form Module.func=func'");
401 (("", ""), ""))
402 in
403 case cmd of
404 "prefix" =>
405 (case !prefix of
406 NONE => ()
407 | SOME _ => ErrorMsg.error "Duplicate 'prefix' directive";
408 prefix := SOME arg)
409 | "database" =>
410 (case !database of
411 NONE => ()
412 | SOME _ => ErrorMsg.error "Duplicate 'database' directive";
413 database := SOME arg)
414 | "exe" =>
415 (case !exe of
416 NONE => ()
417 | SOME _ => ErrorMsg.error "Duplicate 'exe' directive";
418 exe := SOME (relify arg))
419 | "sql" =>
420 (case !sql of
421 NONE => ()
422 | SOME _ => ErrorMsg.error "Duplicate 'sql' directive";
423 sql := SOME (relify arg))
424 | "debug" => debug := true
425 | "profile" => profile := true
426 | "timeout" =>
427 (case !timeout of
428 NONE => ()
429 | SOME _ => ErrorMsg.error "Duplicate 'timeout' directive";
430 timeout := SOME (valOf (Int.fromString arg)))
431 | "ffi" => ffi := relify arg :: !ffi
432 | "link" => link := relifyA arg :: !link
433 | "include" => headers := relifyA arg :: !headers
434 | "script" => scripts := arg :: !scripts
435 | "clientToServer" => clientToServer := ffiS () :: !clientToServer
436 | "effectful" => effectful := ffiS () :: !effectful
437 | "clientOnly" => clientOnly := ffiS () :: !clientOnly
438 | "serverOnly" => serverOnly := ffiS () :: !serverOnly
439 | "jsFunc" => jsFuncs := ffiM () :: !jsFuncs
440 | "library" => libs := relify arg :: !libs
441 | _ => ErrorMsg.error ("Unrecognized command '" ^ cmd ^ "'");
442 read ()
443 end
444
445 val job = read ()
446 in
447 TextIO.closeIn inf;
448 Settings.setUrlPrefix (#prefix job);
449 Settings.setTimeout (#timeout job);
450 Settings.setHeaders (#headers job);
451 Settings.setScripts (#scripts job);
452 Settings.setClientToServer (#clientToServer job);
453 Settings.setEffectful (#effectful job);
454 Settings.setClientOnly (#clientOnly job);
455 Settings.setServerOnly (#serverOnly job);
456 Settings.setJsFuncs (#jsFuncs job);
457 job
458 end
459
267 val parseUrp = { 460 val parseUrp = {
268 func = fn filename => 461 func = parseUrp',
269 let
270 val dir = OS.Path.dir filename
271 val inf = TextIO.openIn (OS.Path.joinBaseExt {base = filename, ext = SOME "urp"})
272
273 fun relify fname =
274 OS.Path.concat (dir, fname)
275 handle OS.Path.Path => fname
276
277 val absDir = OS.Path.mkAbsolute {path = dir, relativeTo = OS.FileSys.getDir ()}
278
279 fun relifyA fname = OS.Path.mkAbsolute {path = fname, relativeTo = absDir}
280
281 fun readSources acc =
282 case TextIO.inputLine inf of
283 NONE => rev acc
284 | SOME line =>
285 let
286 val acc = if CharVector.all Char.isSpace line then
287 acc
288 else
289 let
290 val fname = String.implode (List.filter (fn x => not (Char.isSpace x))
291 (String.explode line))
292 val fname = relify fname
293 in
294 fname :: acc
295 end
296 in
297 readSources acc
298 end
299
300 val prefix = ref NONE
301 val database = ref NONE
302 val exe = ref NONE
303 val sql = ref NONE
304 val debug = ref false
305 val profile = ref false
306 val timeout = ref NONE
307 val ffi = ref []
308 val link = ref []
309 val headers = ref []
310 val scripts = ref []
311 val clientToServer = ref []
312 val effectful = ref []
313 val clientOnly = ref []
314 val serverOnly = ref []
315 val jsFuncs = ref []
316
317 fun finish sources =
318 {prefix = Option.getOpt (!prefix, "/"),
319 database = !database,
320 exe = Option.getOpt (!exe, OS.Path.joinBaseExt {base = OS.Path.base filename,
321 ext = SOME "exe"}),
322 sql = !sql,
323 debug = !debug,
324 profile = !profile,
325 timeout = Option.getOpt (!timeout, 60),
326 ffi = rev (!ffi),
327 link = rev (!link),
328 headers = rev (!headers),
329 scripts = rev (!scripts),
330 clientToServer = rev (!clientToServer),
331 effectful = rev (!effectful),
332 clientOnly = rev (!clientOnly),
333 serverOnly = rev (!serverOnly),
334 jsFuncs = rev (!jsFuncs),
335 sources = sources}
336
337 fun read () =
338 case TextIO.inputLine inf of
339 NONE => finish []
340 | SOME "\n" => finish (readSources [])
341 | SOME line =>
342 let
343 val (cmd, arg) = Substring.splitl (fn x => not (Char.isSpace x)) (Substring.full line)
344 val cmd = Substring.string (trim cmd)
345 val arg = Substring.string (trim arg)
346
347 fun ffiS () =
348 case String.fields (fn ch => ch = #".") arg of
349 [m, x] => (m, x)
350 | _ => (ErrorMsg.error (cmd ^ " argument not of the form Module.func");
351 ("", ""))
352
353 fun ffiM () =
354 case String.fields (fn ch => ch = #"=") arg of
355 [f, s] =>
356 (case String.fields (fn ch => ch = #".") f of
357 [m, x] => ((m, x), s)
358 | _ => (ErrorMsg.error (cmd ^ " argument not of the form Module.func=func'");
359 (("", ""), "")))
360 | _ => (ErrorMsg.error (cmd ^ " argument not of the form Module.func=func'");
361 (("", ""), ""))
362 in
363 case cmd of
364 "prefix" =>
365 (case !prefix of
366 NONE => ()
367 | SOME _ => ErrorMsg.error "Duplicate 'prefix' directive";
368 prefix := SOME arg)
369 | "database" =>
370 (case !database of
371 NONE => ()
372 | SOME _ => ErrorMsg.error "Duplicate 'database' directive";
373 database := SOME arg)
374 | "exe" =>
375 (case !exe of
376 NONE => ()
377 | SOME _ => ErrorMsg.error "Duplicate 'exe' directive";
378 exe := SOME (relify arg))
379 | "sql" =>
380 (case !sql of
381 NONE => ()
382 | SOME _ => ErrorMsg.error "Duplicate 'sql' directive";
383 sql := SOME (relify arg))
384 | "debug" => debug := true
385 | "profile" => profile := true
386 | "timeout" =>
387 (case !timeout of
388 NONE => ()
389 | SOME _ => ErrorMsg.error "Duplicate 'timeout' directive";
390 timeout := SOME (valOf (Int.fromString arg)))
391 | "ffi" => ffi := relify arg :: !ffi
392 | "link" => link := relifyA arg :: !link
393 | "include" => headers := relifyA arg :: !headers
394 | "script" => scripts := arg :: !scripts
395 | "clientToServer" => clientToServer := ffiS () :: !clientToServer
396 | "effectful" => effectful := ffiS () :: !effectful
397 | "clientOnly" => clientOnly := ffiS () :: !clientOnly
398 | "serverOnly" => serverOnly := ffiS () :: !serverOnly
399 | "jsFunc" => jsFuncs := ffiM () :: !jsFuncs
400 | _ => ErrorMsg.error ("Unrecognized command '" ^ cmd ^ "'");
401 read ()
402 end
403
404 val job = read ()
405 in
406 TextIO.closeIn inf;
407 Settings.setUrlPrefix (#prefix job);
408 Settings.setTimeout (#timeout job);
409 Settings.setHeaders (#headers job);
410 Settings.setScripts (#scripts job);
411 Settings.setClientToServer (#clientToServer job);
412 Settings.setEffectful (#effectful job);
413 Settings.setClientOnly (#clientOnly job);
414 Settings.setServerOnly (#serverOnly job);
415 Settings.setJsFuncs (#jsFuncs job);
416 job
417 end,
418 print = p_job 462 print = p_job
419 } 463 }
420 464
421 val toParseJob = transform parseUrp "parseJob" 465 val toParseJob = transform parseUrp "parseJob"
422 466