| 1 |
62 |
tbourke |
(* $Id: urpal.sml 65 2008-08-25 22:47:20Z tbourke $
|
| 2 |
|
|
*
|
| 3 |
|
|
* Copyright (c) 2008 Timothy Bourke (University of NSW and NICTA)
|
| 4 |
|
|
* All rights reserved.
|
| 5 |
|
|
*
|
| 6 |
|
|
* This program is free software; you can redistribute it and/or modify it
|
| 7 |
|
|
* under the terms of the "BSD License" which is distributed with the
|
| 8 |
|
|
* software in the file LICENSE.
|
| 9 |
|
|
*
|
| 10 |
|
|
* This program is distributed in the hope that it will be useful, but
|
| 11 |
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
| 12 |
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the BSD
|
| 13 |
|
|
* License for more details.
|
| 14 |
|
|
*)
|
| 15 |
4 |
tbourke |
|
| 16 |
|
|
structure Urpal :
|
| 17 |
|
|
sig
|
| 18 |
|
|
val main : string * string list -> OS.Process.status
|
| 19 |
|
|
val run: string -> OS.Process.status
|
| 20 |
|
|
end
|
| 21 |
|
|
=
|
| 22 |
|
|
let structure UP = UppaalParse
|
| 23 |
|
|
and UXML = UppaalXML
|
| 24 |
|
|
and TIO = TextIO
|
| 25 |
|
|
and CMD = Commands
|
| 26 |
|
|
in
|
| 27 |
|
|
struct
|
| 28 |
|
|
exception FailedCommand
|
| 29 |
|
|
and NoInputFile
|
| 30 |
|
|
|
| 31 |
|
|
fun abort ss = let in
|
| 32 |
|
|
TIO.output (TIO.stdErr, String.concat (Settings.progName::":"::ss));
|
| 33 |
|
|
TIO.output (TIO.stdErr, "\n");
|
| 34 |
|
|
raise FailedCommand
|
| 35 |
|
|
end
|
| 36 |
|
|
|
| 37 |
|
|
fun readNta filename = let
|
| 38 |
|
|
val uri = Uri.String2Uri filename
|
| 39 |
|
|
in case UXML.parse uri
|
| 40 |
|
|
of NONE => abort [filename,
|
| 41 |
|
|
" is not a valid XML file or the dtd is invalid."]
|
| 42 |
|
|
| SOME xnta => UP.parse (xnta, filename)
|
| 43 |
|
|
end
|
| 44 |
|
|
|
| 45 |
|
|
fun writeNta (filename, nta) = let
|
| 46 |
|
|
fun makeStream (s, c) = (TIO.getOutstream s, c)
|
| 47 |
|
|
val (strm, close) = makeStream (case filename of
|
| 48 |
|
|
NONE => (TIO.stdOut, fn _=>())
|
| 49 |
|
|
| SOME n => (TIO.openOut n, TIO.StreamIO.closeOut))
|
| 50 |
|
|
in ParsedNta.output strm nta; close strm end
|
| 51 |
|
|
handle IO.Io {cause,...} => (Util.warn ["failed to write nta (",
|
| 52 |
|
|
General.exnMessage cause, ")"])
|
| 53 |
|
|
before (raise FailedCommand)
|
| 54 |
|
|
|
| 55 |
|
|
fun tryConfigFiles () = let
|
| 56 |
|
|
fun tryDir (NONE, _, _) = NONE
|
| 57 |
|
|
| tryDir (SOME d, subs, f) = let
|
| 58 |
|
|
val dir=foldl (fn(f,d)=>OS.Path.joinDirFile{dir=d,file=f}) d subs
|
| 59 |
|
|
in SOME (OS.Path.joinDirFile {dir=dir, file=f}) end
|
| 60 |
|
|
|
| 61 |
|
|
fun loadFile NONE = ()
|
| 62 |
|
|
| loadFile (SOME path) = let
|
| 63 |
|
|
val ()= Util.debugOutline (fn()=>["seeking config file ",path,"..."])
|
| 64 |
|
|
|
| 65 |
|
|
val inso = SOME (TextIO.getInstream (TextIO.openIn path))
|
| 66 |
|
|
before Util.debugOutline (fn()=>["found"])
|
| 67 |
|
|
handle IO.Io {cause, ...} => NONE before
|
| 68 |
|
|
Util.debugOutline (fn()=>["not found (",
|
| 69 |
|
|
General.exnMessage cause,")"])
|
| 70 |
|
|
in
|
| 71 |
|
|
ignore (Option.map
|
| 72 |
|
|
(SettingsRW.loadConfigFile TextIO.StreamIO.inputLine) inso)
|
| 73 |
|
|
end
|
| 74 |
|
|
|
| 75 |
|
|
in
|
| 76 |
|
|
app loadFile [tryDir (Settings.prefix (), ["etc"], "urpalrc"),
|
| 77 |
|
|
tryDir (OS.Process.getEnv "HOME", [], ".urpalrc"),
|
| 78 |
|
|
SOME "urpalrc"]
|
| 79 |
|
|
end
|
| 80 |
|
|
|
| 81 |
|
|
fun testFlip (NONE, _) = abort ["no input file specified"]
|
| 82 |
|
|
| testFlip (SOME inf,NONE) = TestTransFlip.runTest {input=inf,
|
| 83 |
|
|
output=TextIO.stdOut}
|
| 84 |
|
|
| testFlip (SOME inf,SOME outf) = (let
|
| 85 |
|
|
val ostrm = TextIO.openOut outf
|
| 86 |
|
|
in
|
| 87 |
|
|
TestTransFlip.runTest {input=inf, output=ostrm};
|
| 88 |
|
|
TextIO.closeOut ostrm;
|
| 89 |
|
|
OS.Process.success
|
| 90 |
|
|
end
|
| 91 |
|
|
handle IO.Io {cause, ...} => (Util.warn
|
| 92 |
|
|
["cannot write to '", outf, "': ",
|
| 93 |
|
|
General.exnMessage cause]; OS.Process.failure))
|
| 94 |
|
|
|
| 95 |
|
|
fun main (name, args) = let
|
| 96 |
65 |
tbourke |
val _ = Util.debugIndent (Settings.Detailed,fn()=>["try config files..."])
|
| 97 |
4 |
tbourke |
val _ = tryConfigFiles ()
|
| 98 |
65 |
tbourke |
val _ = Util.debugOutdent (Settings.Detailed, fn()=>["done."])
|
| 99 |
4 |
tbourke |
val (cmds, {inputfile, outputfile}) = CMD.processCommands args
|
| 100 |
|
|
val _ = Settings.validate ()
|
| 101 |
18 |
tbourke |
val freshEnv = SOME (CmdEnv.fromNta ParsedNta.emptyNta)
|
| 102 |
4 |
tbourke |
|
| 103 |
18 |
tbourke |
fun doCmd (a as CMD.ScriptFile _, NONE) = doCmd (a, freshEnv)
|
| 104 |
4 |
tbourke |
| doCmd (CMD.ScriptFile filename, SOME env) = let
|
| 105 |
|
|
val ins = TextIO.openIn filename
|
| 106 |
|
|
in
|
| 107 |
|
|
CmdLang.parse TextIO.StreamIO.inputLine
|
| 108 |
|
|
(env, TextIO.getInstream ins)
|
| 109 |
|
|
end
|
| 110 |
|
|
|
| 111 |
18 |
tbourke |
| doCmd (a as CMD.ScriptText _, NONE) = doCmd (a, freshEnv)
|
| 112 |
4 |
tbourke |
| doCmd (CMD.ScriptText script, SOME env) =
|
| 113 |
|
|
CmdLang.parse (fn""=>NONE|s=>SOME(s,"")) (env, script)
|
| 114 |
|
|
|
| 115 |
18 |
tbourke |
| doCmd (a as CMD.ScriptTerminal, NONE) = doCmd (a, freshEnv)
|
| 116 |
4 |
tbourke |
| doCmd (CMD.ScriptTerminal, SOME env) = let
|
| 117 |
|
|
fun getLine ins = (TextIO.print "> ";
|
| 118 |
|
|
TextIO.StreamIO.inputLine ins)
|
| 119 |
|
|
in
|
| 120 |
|
|
CmdLang.parse getLine (env, TextIO.getInstream TextIO.stdIn)
|
| 121 |
|
|
end
|
| 122 |
|
|
|
| 123 |
|
|
| doCmd (CMD.ConfigFile p, envo) = (let
|
| 124 |
|
|
val ins = TextIO.getInstream (TextIO.openIn p)
|
| 125 |
|
|
in SettingsRW.loadConfigFile TextIO.StreamIO.inputLine ins;
|
| 126 |
|
|
TextIO.StreamIO.closeIn ins;
|
| 127 |
|
|
envo
|
| 128 |
|
|
end
|
| 129 |
|
|
handle IO.Io {cause, ...} => (Util.warn ["reading ",p," failed (",
|
| 130 |
|
|
General.exnMessage cause, ")"];
|
| 131 |
|
|
raise FailedCommand))
|
| 132 |
|
|
|
| 133 |
|
|
| doCmd (CMD.ConfigText t, envo) = envo before
|
| 134 |
|
|
SettingsRW.loadConfigFile (fn""=>NONE|s=>SOME(s,"")) t
|
| 135 |
|
|
|
| 136 |
|
|
| doCmd (CMD.ShowConfig, envo) = envo before
|
| 137 |
|
|
Settings.saveConfigFile TextIO.stdOut
|
| 138 |
|
|
| doCmd (CMD.TestFlip, _) = (Util.warn
|
| 139 |
|
|
["The test flip command must be used by itself."];
|
| 140 |
|
|
raise FailedCommand)
|
| 141 |
|
|
|
| 142 |
|
|
in
|
| 143 |
|
|
case cmds of
|
| 144 |
18 |
tbourke |
[CMD.TestFlip] => testFlip (inputfile, outputfile)
|
| 145 |
4 |
tbourke |
|
| 146 |
|
|
| _ => let
|
| 147 |
|
|
val ntao = Option.mapPartial readNta inputfile
|
| 148 |
|
|
val inputEnvo = Option.map CmdEnv.fromNta ntao
|
| 149 |
|
|
val outputfile= if isSome outputfile then outputfile
|
| 150 |
|
|
else inputfile
|
| 151 |
|
|
in
|
| 152 |
|
|
case foldl doCmd inputEnvo cmds of
|
| 153 |
|
|
NONE => OS.Process.success
|
| 154 |
|
|
| SOME env => (writeNta (outputfile, CmdEnv.toNta env);
|
| 155 |
|
|
OS.Process.success)
|
| 156 |
|
|
end
|
| 157 |
|
|
end
|
| 158 |
|
|
handle FailedCommand => OS.Process.failure
|
| 159 |
55 |
tbourke |
| CmdLang.Failure => OS.Process.failure
|
| 160 |
4 |
tbourke |
| e => Util.abort ["uncaught exception:", General.exnMessage e]
|
| 161 |
|
|
|
| 162 |
|
|
fun run args = main (Settings.progName, String.tokens Char.isSpace args)
|
| 163 |
|
|
|
| 164 |
|
|
end
|
| 165 |
|
|
end
|
| 166 |
|
|
|