This version of a shell program reads from a keyboard and executes `commands' typed by the user. Its own interface has the type of a Command module, and that is the type of the things it executes. In particular, it can call modules like the hello example at the beginning of the paper.
1 implement Command;
2 include "sys.m";
3 include "draw.m";
4 sys: Sys;
5 stdin: ref Sys->FD;
6 Command: module
7 {
8 init: fn(nil: ref Draw->Context, nil: list of string);
9 };
10 init(ctx: ref Draw->Context, nil: list of string)
11 {
12
13
14 buf := array[256] of byte;
15 sys = load Sys Sys->PATH;
16 stdin = sys->fildes(0);
17 for(;;) {
18 sys->print("$ ");
19 n := sys->read(stdin, buf, len buf);
20 if(n <= 0)
21 break;
22 (nw, arg) :=
sys->tokenize(string buf[0:n], " \t\n");
23 if(nw != 0)
24 exec(ctx, arg);
25 }
26 }
Local variables are declared on lines 12-14; line 15 loads the Sys module and stores a handle for it in the variable sys. Line 16 creates an FD for the standard input by calling the fildes function of the Sys module using the -> operator; the notation modhandle->func(...) specifies a call to the function named func in the module currently referred to by modhandle. (In general there can be several modules of the same type and name active, and there can also be unrelated modules containing identically named functions. The import declaration, described in §6.6 above, can be used to abbreviate the references when names do not clash.)
The loop on lines 17-25 prints a prompt (line 18), reads a line from the standard input (line 19), parses it into tokens (line 22), and executes the command.
The function call sys->tokenize is worth discussing as an example of style. It takes two strings as arguments. The characters in the second string are interpreted as separators of tokens in the first string. It returns a tuple whose first member is the number of tokens found, and whose second is a list of strings containing the tokens found: its declaration is
tokenize: fn (s: string, sep: string): (int, list of string);
The sys->read routine gathers an array of bytes into buf. Thus the expression for the first argument of sys->tokenize converts this array to a string by slicing the array with [0:n], using the actual number of bytes gathered by the read, and using a cast.
At lines 23-24, if there were any words found, exec is called:
27 exec(ctx: ref Draw->Context, args: list of string)
28 {
29 c: Command;
30 cmd, file: string;
31 cmd = hd args;
32 file = cmd + ".dis";
33 c = load Command file;
34 if(c == nil)
35 c = load Command "/dis/"+file;
36 if(c == nil) {
37 sys->print("%s: not found\n", cmd);
38 return;
39 }
40 c->init(ctx, args);
41 }
If either attempt to get a handle to the named module succeeds, c will contain a valid handle to it; line 40 calls its init function, passing it the whole argument list. When it returns, the exec function returns, and the main loop resumes.