SML# - Library/Scripting Diff

  • Added parts are displayed like this.
  • Deleted parts are displayed like this.

ML is a superb tool for scripting.
In order to demonstrant this, SML#
provides a sample scripting library
consisting of (mostly imperative) functions
for
* interfacing with opetating system shell
* basic IO, and
* text formating
that allow you to write scripting code
in an interactive session of SML#.

The features of SML#, including
higher-order functions, pattern matching,
and record polymorphism,
makes it a particularly cool scripting language.

To use this library, simply open the library in the interactive session as:
# open Script;
and write an SML# code using the functions desribed in
[[this API|smlsharp:lib/Script/doc/api/]].


!! Scripting examples

The first is a one-liner that strips HTML tags off
from a text read from the standard input.

app (print o (global_subst "<[^>]*>" "")) (readlines stdIn (SOME "\n"));

The next substitutes environment reference.

app
    (print
     o (global_replace "\\${?([a-zA-Z_]+)}?" (fn [_, name] => env name)))
    (readlines stdIn NONE)

Using this library, Unix commands can be implemented as follows.

''grep'' command

val pattern = hd argv;
app
   (fn line => (if line =~ pattern then print line else ()))
   (readlines stdIn (SOME "\n"));

''wc'' command.

val (ls, ws, cs) =
    foldl
        (fn (line, (ls, ws, cs)) =>
            (
              ls + 1,
              ws + length (tokens "[ \t]" line),
              cs + (size line)
            ))
        (0, 0, 0)
        (readlines stdIn (SOME "\n"));
print (itoa ls ^ " " ^ itoa ws ^ " " ^ itoa cs ^ "\n");

''id'' command.

val user = case argv of [] => env "USER" | name :: _ => name;
case
   List.filter
       (fn entry => hd entry = user)
       (map (fields ":") (readlines (fopen "/etc/passwd" "r") (SOME "\n")))
  of [] => print ("not found: " ^ user)
   | [_ :: _ :: id :: gid :: _] => print ("uid=" ^ id ^ " gid=" ^ gid);

''split'' command.

val (src, dest) = case argv of src :: dest :: _ => (src, dest);
val lines = 9; (* split into 10-line pieces. (not 9) *)
val count = ref 0;
fun destName () = dest ^ "." ^ itoa (!count) before count := !count + 1;
val (_, file) =
    foldl
    (fn (line, (ls, file)) =>
        (
          fputs file line;
          if 0 = ls
          then (fclose file; (lines, fopen (destName()) "w"))
          else (ls - 1, file)
        ))
    (lines, fopen (destName()) "w")
    (readlines (fopen src "r") (SOME "\n"));
fclose file;

''which'' command.

structure P = OS.Path;
structure F = OS.FileSys;
exception Found of string
val cmd = case argv of name :: _ => name;
(
   app
       (fn dir =>
           let val path = P.concat (dir, cmd)
           in
             if F.access (path, [F.A_EXEC])
             then raise Found path
             else ()
           end handle SysErr _ => ())
       (fields ":" (env "PATH"));
   print "not found\n"
)
handle Found name => print name;