[Contents]
[Prev] [Next] [Limbo Basics] [Limbo Programming] [Language Definition]

A Simple Command Interpreter Module

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 this chapter.

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	};

After the boilerplate on lines 1-3, the variables sys and stdin are declared on lines 4 and 5. The I/O operations of the Sys module use the ref FD type to refer to open files.

10	init(ctx : ref Draw->Context, nil: list of string)
11	{
12		n, nw : int;
13		arg : list of string;
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	}

Line 10: conventionally, stand-alone modules are started by calling their init functions. The Command module follows this convention. The arguments are presented as a list of strings. In this simple example, the command interpreter itself ignores its second argument, so it need not be given a name.

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 also can be unrelated modules containing identically named functions. The import declaration, described in Declarations with Import, 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 (c : string, sep : string) :
		(int, list of string);

In the example, the second argument is " \t\n", so that the routine returns the number of, and a list of, "words" separated by blanks, tabs, and new-lines. The free use of strings, lists, and tuple-returning functions is common in Limbo.

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	}

On lines 31 and 32 of exec, cmd is set to the first of the words in the argument list, and the string .dis is concatenated to it (to account for the fact that Limbo object program files are conventionally named using this suffix). On line 33 an attempt is made to load the named module from the derived file name; it will fail if the file does not exist. The attempt will succeed, and a non-nil handle assigned to c, if the file is found, and if the module stored in that file does in fact implement the Command module type. In case this fails, lines 34-35 make another attempt, after prefixing
/dis/ to the file name.

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.



[Contents]
[Prev] [Next] [Limbo Basics] [Limbo Programming] [Language Definition]

Copyright © 1998, Lucent Technologies, Inc. All rights reserved.