Scripting library

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

In order to demonstrating this, we provides a sample scripting library consisting of (mostly imperative) functions for

  • interfacing with operating system shell
  • basic IO, and
  • text formating

that allow you to write scripting code in an interactive session of SML#.

In our future release, we plan to provide a more complete and user-friendly scripting library.

This library is loaded and opened by the default prelude. If you use other prelude which does not load this library, you can load and open it as follows.

# use "Script.sml";
# open Script;
API
API document

Scripting examples

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

print (global_subst "<[^>]*>" "" (fgets stdIn NONE));

The next substitutes environment reference.

print
   (global_replace
        "\\${?([a-zA-Z_]+)}?"
        (fn [_, name] => env name)
        (fgets 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;
Last modified:2006/03/06 17:37:34
Keyword(s):
References:[Library]