SML# - Library/Scripting Diff

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

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|smlsharp:lib/Script/doc/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;