| 1 | val root = valOf (OS.Process.getEnv "REMY_ROOT"); |
| 2 | val _ = use (root ^ "/.build/Remy.Export/code/remy.ML"); |
| 3 | |
| 4 | fun chars s = map (Remy.ascii o IntInf.fromInt o Char.ord) (String.explode s); |
| 5 | fun text cs = String.implode (map (Char.chr o IntInf.toInt o Remy.ordinal) cs); |
| 6 | |
| 7 | fun read path = |
| 8 | let |
| 9 | val stream = TextIO.openIn path |
| 10 | val input = TextIO.inputN (stream, 65537) |
| 11 | handle exn => (TextIO.closeIn stream; raise exn) |
| 12 | val _ = TextIO.closeIn stream |
| 13 | in |
| 14 | if size input > 65536 then raise Fail "source exceeds 64 KiB" else chars input |
| 15 | end; |
| 16 | |
| 17 | fun write path data = |
| 18 | let |
| 19 | val bytes = Byte.stringToBytes (text data) |
| 20 | val pid = SysWord.toString (Posix.Process.pidToWord (Posix.ProcEnv.getpid ())) |
| 21 | val prefix = OS.Path.concat (OS.Path.dir path, "." ^ OS.Path.file path ^ "." ^ pid ^ ".") |
| 22 | val defaultMode = Posix.FileSys.S.flags [Posix.FileSys.S.irusr, Posix.FileSys.S.iwusr, |
| 23 | Posix.FileSys.S.irgrp, Posix.FileSys.S.iwgrp, Posix.FileSys.S.iroth, Posix.FileSys.S.iwoth] |
| 24 | val mode = Posix.FileSys.S.intersect [Posix.FileSys.ST.mode (Posix.FileSys.stat path), |
| 25 | Posix.FileSys.S.flags [Posix.FileSys.S.irwxu, Posix.FileSys.S.irwxg, Posix.FileSys.S.irwxo]] |
| 26 | handle exn as OS.SysErr (_, SOME error) => |
| 27 | if error = Posix.Error.noent then defaultMode else raise exn |
| 28 | fun reserve n = |
| 29 | if n = 128 then raise Fail "cannot reserve output file" |
| 30 | else |
| 31 | let |
| 32 | val temporary = prefix ^ Int.toString n |
| 33 | in |
| 34 | (temporary, Posix.FileSys.createf |
| 35 | (temporary, Posix.FileSys.O_WRONLY, Posix.FileSys.O.excl, mode)) |
| 36 | end handle exn as OS.SysErr (_, SOME error) => |
| 37 | if error = Posix.Error.exist then reserve (n + 1) else raise exn |
| 38 | val (temporary, fd) = reserve 0 |
| 39 | val closed = ref false |
| 40 | fun close () = (Posix.IO.close fd; closed := true) |
| 41 | fun discard () = |
| 42 | ((if !closed then () else close ()) handle _ => (); |
| 43 | OS.FileSys.remove temporary handle _ => ()) |
| 44 | fun loop offset = |
| 45 | if offset = Word8Vector.length bytes then () |
| 46 | else |
| 47 | let |
| 48 | val count = Posix.IO.writeVec (fd, Word8VectorSlice.slice (bytes, offset, NONE)) |
| 49 | in |
| 50 | if count = 0 then raise Fail "output write made no progress" else loop (offset + count) |
| 51 | end |
| 52 | in |
| 53 | (loop 0; close (); OS.FileSys.rename {old = temporary, new = path}) |
| 54 | handle exn => (discard (); raise exn) |
| 55 | end; |
| 56 | |
| 57 | fun output (ok, message) = |
| 58 | (TextIO.output (if ok then TextIO.stdOut else TextIO.stdErr, text message); |
| 59 | if ok then () else OS.Process.exit OS.Process.failure); |
| 60 | |
| 61 | fun emit destination ((ok, bytes), report) = |
| 62 | if ok then (write destination bytes; output (true,report)) else output (false,report); |
| 63 | |
| 64 | fun main () = |
| 65 | (case CommandLine.arguments () of |
| 66 | ["vectors", source, destination] => |
| 67 | (case Remy.vectors (read source) of (true, bytes) => write destination bytes | result => output result) |
| 68 | | ["eval", source, x, y, t] => output (Remy.reference (read source) (chars x) (chars y) (chars t)) |
| 69 | | ["limits", depth, nodes, stack, source, destination] => |
| 70 | emit destination (Remy.configured (chars depth) (chars nodes) (chars stack) (read source)) |
| 71 | | ["check", binary] => output (Remy.validate (read binary)) |
| 72 | | ["disassemble", binary] => output (Remy.disassemble (read binary)) |
| 73 | | [source, destination] => emit destination (Remy.artifact (read source)) |
| 74 | | _ => raise Fail "usage: remy check binary | remy disassemble binary | remy limits depth nodes stack source binary | remy source binary | remy vectors source data | remy eval source x y t") |
| 75 | handle exn => |
| 76 | (TextIO.output (TextIO.stdErr, "remy: " ^ General.exnMessage exn ^ "\n"); |
| 77 | OS.Process.exit OS.Process.failure); |