home *** CD-ROM | disk | FTP | other *** search
- _THE MODULA-3 PROGRAMMING LANGUAGE_
- by Sam Harbison
-
- Listing One
-
- INTERFACE FieldList;
- (* Breaks text lines into a list of fields which can be treated
- as text or numbers. This interface is thread-safe. *)
- IMPORT Rd, Wr, Thread;
- EXCEPTION Error;
- CONST
- DefaultWS = SET OF CHAR{' ', '\t', '\n', '\f', ','};
- Zero: NumberType = 0.0D0;
- TYPE
- FieldNumber = [0..LAST(INTEGER)]; (* Fields are numbered 0, 1, ... *)
- NumberType = LONGREAL; (* Type of field as floating-point number *)
- T <: Public; (* A field list *)
- Public = MUTEX OBJECT (* The visible part of a field list *)
- METHODS
- init(ws := DefaultWS): T;
- (* Define whitespace characters. *)
- getLine(rd: Rd.T := NIL)
- RAISES {Rd.EndOfFile, Rd.Failure, Thread.Alerted};
- (* Reads a line and breaks it into fields that can be
- examined by other methods. Default reader is Stdio.stdin.
- numberOfFields(): CARDINAL;
- (* The number of fields in the last-read line. *)
- line(): TEXT;
- (* The entire line. *)
- isANumber(n: FieldNumber): BOOLEAN RAISES {Error};
- (* Is the field some number (either integer or real)? *)
- number(n: FieldNumber): NumberType RAISES {Error};
- (* The field's floating-poinnt value *)
- text(n: FieldNumber): TEXT RAISES {Error};
- (* The field's text value *)
- END;
- END FieldList.
-
-
- Listing Two
-
- MODULE Sum EXPORTS Main; (* Reads lines of numbers and prints their sums. *)
- IMPORT FieldList, Wr, Stdio, Fmt, Rd, Thread;
- CONST WhiteSpace = FieldList.DefaultWS + SET OF CHAR{','};
- VAR
- sum: FieldList.NumberType;
- fl := NEW(FieldList.T).init(ws := WhiteSpace);
- PROCEDURE Put(t: TEXT) =
- <*FATAL Wr.Failure, Thread.Alerted*>
- BEGIN
- Wr.PutText(Stdio.stdout, t);
- Wr.Flush (Stdio.stdout);
- END Put;
- BEGIN
- TRY
- LOOP Put("Type some numbers: ");
- fl.getLine();
- sum := FieldList.Zero;
- WITH nFields = fl.numberOfFields() DO
- FOR f := 0 TO nFields - 1 DO
- IF fl.isANumber(f) THEN
- sum := sum + fl.number(f);
- END;
- END;
- WITH sumText = Fmt.LongReal(FLOAT(sum, LONGREAL)) DO
- Put("The sum is " & sumText & ".\n");
- END(*WITH*);
- END(*WITH*);
- END(*LOOP*)
- EXCEPT
- Rd.EndOfFile =>
- Put("Done.\n");
- ELSE
- Put("Unknown exception; quit.\n");
- END(*TRY*);
- END Sum.
-
-
- Listing Three
-
- MODULE FieldList;
- (* Designed for ease of programming, not efficiency. We don't bother to reuse
- data structures; we allocate new ones each time a line is read. *)
- IMPORT Rd, Wr, Text, Stdio, Fmt, Thread, Scan;
- CONST DefaultFields = 20; (* How many fields we expect at first. *)
- TYPE
- DescriptorArray = REF ARRAY OF FieldDescriptor;
- FieldDescriptor = RECORD
- (* Description of a single field. The 'text' field and 'real'
- fields are invalid until field's value is first requested.
- (Invalid is signaled by 'text' being NIL. *)
- start : CARDINAL := 0; (* start of field in line *)
- len : CARDINAL := 0; (* length of field *)
- numeric: BOOLEAN := FALSE; (* Does field contain number? *)
- text : TEXT := NIL; (* The field text *)
- number : NumberType := 0.0D0; (* The field as a real. *)
- END;
- REVEAL
- T = Public BRANDED OBJECT
- originalLine: TEXT; (* the original input line *)
- chars : REF ARRAY OF CHAR := NIL; (* copy of input line *)
- nFields : CARDINAL := 0; (* number of fields found *)
- fds : DescriptorArray := NIL; (* descriptor for each field *)
- ws : SET OF CHAR := DefaultWS; (* our whitespace *)
- OVERRIDES (* supply real procedures for the methods *)
- init := init; getLine := getLine;
- numberOfFields := numberOfFields;
- line := line;
- isANumber := isANumber;
- number := number;
- text := text;
- END;
- PROCEDURE AddDescriptor(t: T; READONLY fd: FieldDescriptor) =
- (* Increment the number of fields, and store fd as the
- descriptor for the new field. Extend the fd array if necessary. *)
- BEGIN
- IF t.nFields >= NUMBER(t.fds^) THEN
- WITH
- n = NUMBER(t.fds^), (* current length; will double it *)
- new = NEW(DescriptorArray, 2 * n)
- DO
- SUBARRAY(new^, 0, n) := t.fds^; (* copy in old data *)
- t.fds := new;
- END;
- END;
- t.fds[t.nFields] := fd;
- INC(t.nFields);
- END AddDescriptor;
- PROCEDURE getLine(self: T; rd: Rd.T := NIL)
- RAISES {Rd.EndOfFile, Rd.Failure, Thread.Alerted} =
- (* Read an input line; store it in the object; finds all the
- whitespace-terminated fields. *)
- VAR
- next : CARDINAL; (* index of next char in line *)
- len : CARDINAL; (* # of characters in current field *)
- lineLength: CARDINAL; (* length of input line *)
- BEGIN
- IF rd = NIL THEN rd := Stdio.stdin; END; (* default reader *)
- LOCK self DO
- WITH text = Rd.GetLine(rd) DO
- lineLength := Text.Length(text);
- self.originalLine := text;
- self.fds := NEW(DescriptorArray, DefaultFields);
- self.nFields := 0;
- self.chars := NEW(REF ARRAY OF CHAR, lineLength);
- Text.SetChars(self.chars^, text);
- END;
- next := 0;
- WHILE next < lineLength DO (* for each field *)
- (* Skip whitespace characters *)
- WHILE next < lineLength AND (self.chars[next] IN
- self.ws) DO INC(next);
- END;
- (* Collect next field *)
- len := 0;
- WHILE next < lineLength
- AND NOT (self.chars[next] IN self.ws) DO
- INC(len); INC(next);
- END;
- (* Save information about the field *) IF len > 0 THEN
- AddDescriptor(self, FieldDescriptor{start:=
- next - len, len := len});
- END;
- END(*WHILE*);
- END(*LOCK*);
- END getLine;
- PROCEDURE GetDescriptor(t: T; n: FieldNumber): FieldDescriptor RAISES {Error}
- (* Return the descriptor for field n. Depending on user's wishes,
- treat too-large field numbers as empty fields or as an error. *)
- BEGIN
- (* Handle bad field number first. *)
- IF n >= t.nFields THEN
- RAISE Error;
- END;
- (* Be sure text and numeric values are set. *)
- WITH fd = t.fds[n] DO
- IF fd.text # NIL THEN RETURN fd; END; (* Already done this *)
- fd.text := Text.FromChars(SUBARRAY(t.chars^, fd.start,
- fd.len)
- TRY (* to interpret field as floating-point number *)
- fd.number := FLOAT(Scan.LongReal(fd.text), NumberType);
- fd.numeric := TRUE;
- EXCEPT
- Scan.BadFormat =>
- TRY (* to interpret field as integer *)
- fd.number := FLOAT(Scan.Int(fd.text),
- NumberType
- fd.numeric := TRUE;
- EXCEPT
- Scan.BadFormat => (* not a number *)
- fd.number := Zero;
- fd.numeric := FALSE;
- END;
- END;
- RETURN fd;
- END(*WITH*);
- END GetDescriptor;
- PROCEDURE numberOfFields(self: T): CARDINAL =
- BEGIN
- LOCK self DO RETURN self.nFields; END;
- END numberOfFields;
- PROCEDURE isANumber(self: T; n: FieldNumber): BOOLEAN RAISES {Error} =
- BEGIN
- LOCK self DO
- WITH fd = GetDescriptor(self, n) DO RETURN fd.numeric; END;
- END;
- END isANumber;
- PROCEDURE number(self: T; n: FieldNumber): NumberType RAISES {Error} =
- BEGIN
- LOCK self DO
- WITH fd = GetDescriptor(self, n) DO RETURN fd.number; END;
- END;
- END number;
- PROCEDURE line(self: T): TEXT = BEGIN
- LOCK self DO RETURN self.originalLine; END;
- END line;
- PROCEDURE text(self: T; n: FieldNumber): TEXT RAISES {Error} =
- BEGIN
- LOCK self DO
- WITH fd = GetDescriptor(self, n) DO
- RETURN self.fds[n].text;
- END;
- END(*LOCK*);
- END text;
- PROCEDURE init(self: T; ws := DefaultWS): T =
- BEGIN
- LOCK self DO
- self.ws := ws;
- END;
- RETURN self;
- END init;
- BEGIN
- (* No module initialization code needed *)
- END FieldList.
-
-
-
- Figure 1: Modula_3 version of the classic "Hello, World!" program
-
-
- MODULE Hello EXPORTS Main;
- IMPORT Wr, Stdio;
- BEGIN
- Wr.PutText(Stdio.stdout, "Hello, World!\n");
- Wr.Close(Stdio.stdout);
- END Hello.
-
-
-
-
- Figure 2. Signatures for isANumber.
-
- Method Procedure
- isANumber(n: FieldNumber): BOOLEAN
- RAISES {Error} isANumber(self: T; n: FieldNumber): BOOLEAN
- RAISES {Error}
-
-
- Figure 3. Procedure to accept a pointer of any type and return as
- a floating-point number the value pointed to.
-
-
- PROCEDURE GetReal(ptr: REFANY): REAL = (* Return ptr^ as a REAL *)
- VAR realPtr := NARROW(ptr, REF REAL);
- BEGIN
- RETURN realPtr^;
- END GetReal;
-
- Figure 4. Making explicit run-time type testing in the GetReal
- procedure
-
- PROCEDURE GetReal2(ptr: REFANY): REAL = (* Return ptr^, or 0.0 *)
- BEGIN
- IF ptr # NIL AND ISTYPE(ptr, REF REAL) THEN
- RETURN NARROW(ptr, REF REAL)^;
- ELSE
- RETURN 0.0; (* ptr is not what we expected *)
- END;
- END GetReal2;
-
-
-
- Figure 1:
-
- MODULE Hello EXPORTS Main; IMPORT Wr, Stdio;
- BEGIN
- Wr. PutText(Stdio.stdout, "Hello, World!\n");
- Wr. Close(Stdio.stdout);
- END Hello.
-
-
-
- Figure 2:
-
- Method:
- isANumber (n: FieldNumber) : BOOLEAN RAISES {Error}
- Procedure:
- isANumber (self: T; n: FieldNumber) : BOOLEAN RAISES {Error}
-
-
-
- Figure 3:
-
-
- PROCEDURE GetReal(ptr: REFANY) : REAL = (* Return ptr^ as a REAL *)
- VAR realPtr:= NARROW(ptr, REF REAL);
- BEGIN
- RETURN realPtr^;
- END GetReal;
-
-
-
-
- Figure 4:
-
- PROCEDURE GetReal2(ptr: REFANY) : REAL = (* Return prt^, or 0.0*)
- BEGIN
- IF ptr # NIL AND ISTYPE(ptr, REF REAL) THEN
- RETURN NARROW(ptr, REF REAL)^;
- ELSE
- RETURN 0.0; (* ptr is not what we expected *)
- END;
- END GetReal2;
-
-
-