## libm3/src/sx/Sx.i3

Distributed only by permission.
See the file COPYRIGHT for a full description.
modified on Wed Apr 28 21:37:34 PDT 1993 by meehan
modified on Wed Feb 17 21:46:45 PST 1993 by mjordan
modified on Tue Jan 26 14:38:00 PST 1993 by gnelson

An Sx.T is a symbolic expression represented as a recursive linked list structure, as in Lisp. This interface provides routines for reading and printing symbolic expressions, as well as some convenience procedures for manipulating them. The syntax of an Sx.T is as follows:

Sx = Char | Text | Int | Real | Longreal | Extended
| Atom | Boolean | "(" List ")".

List =  {Sx}.
\index{symbolic expression}

A Char is a Modula-3 character literal; the corresponding Sx.T is of type REF CHAR.

A Text is a Modula-3 text literal. The corresponding Sx.T is a TEXT.

An Int is a Modula-3 integer literal, possibly preceded by a plus sign (+) or minus sign (-). The corresponding Sx.T is of type REF INTEGER.

A Real, Longreal, or Extended is a floating-decimal number parsed using the grammar for Float specified in the Lex interface. The corresponding Sx.T is of type REF REAL, REF LONGREAL or REF EXTENDED, depending on whether the letter introducing the exponent is 'e', 'd', or 'x'. If there is no exponent, the result will be of type REF REAL.

An Atom is either (1) a Modula-3 identifier, or (2) a non-empty sequence of characters from the set

! # \$ % & * + - . / : < = > ? @ [ ] ^ _ { } {\tt ~}
or (3) a sequence of characters and escape sequences surrounded by vertical bars (|s). The escape sequences are the same as those allowed in Modula-3 text literals, with the addition of {\def\ttSlashBackslash{{\tt \|}} \ttSlashBackslash} to allow an atom to contain |. In all three cases, the corresponding Sx.T is an Atom.T.

For example, the following are valid atoms:

A1
+=
|1\||
A Boolean is either TRUE or FALSE; the corresponding Sx.T is of type Atom.T; in other words, this is not a distinct type.

The Sx.T corresponding to a List is a RefList.T containing the items of the list in order.

The tokens of an Sx.T can be separated by arbitrary sequences of blanks, tabs, newlines, carriage returns, form feeds, and vertical tabs, which are ignored. (These are the same whitespace characters that are ignored between tokens of a Modula-3 program.) They can also be separated by comments, which begin with a semicolon and end with newline.

The syntax of tokens can be extended with the SetReadMacro procedure.

INTERFACE Sx;

IMPORT Atom, Rd, RefList, Thread, Wr;

TYPE T = REFANY;

EXCEPTION
PrintError(TEXT);

PROCEDURE FromChar(c: CHAR): REF CHAR;
Return a Char with value c.

PROCEDURE FromInt(i: INTEGER): REF INTEGER;
Return an Int with value i.

PROCEDURE FromReal(r: REAL): REF REAL;
Return a Real with value r.

PROCEDURE FromLongReal(r: LONGREAL): REF LONGREAL;
Return a Longreal with value r.

PROCEDURE FromExtended(r: EXTENDED): REF EXTENDED;
Return an Extended with value r.

PROCEDURE FromBool(b: BOOLEAN): Atom.T;
Return a Boolean. If b is TRUE, return Sx.True. Otherwise, return Sx.False.
The From... procedures do not necessarily perform an allocation:
if the same value is passed to two calls, the same reference may be
returned.  As a consequence, clients should not modify the referent
of a reference returned by any of these procedures.

Each REF CHAR, REF INTEGER, REF REAL, REF LONGREAL, REF EXTENDED, TEXT, or Atom.T, no matter how constructed, is an Sx.T.

VAR (*CONST*) True, False: Atom.T;
{\tt True = Atom.FromText("TRUE")}, {\tt False = Atom.FromText("FALSE")}.

PROCEDURE Read(rd: Rd.T; syntax: Syntax := NIL): T
Read and return a symbolic expression from rd, ignoring whitespace and comments. If syntax is NIL, use the syntax described above; otherwise use any read macros that have been registered in syntax.

rd: Rd.T; delim : CHAR; syntax: Syntax := NIL): RefList.T
Repeatedly read symbolic expressions from rd, ignoring whitespace and comments, until the next character is delim; consume the delimiter and return the list of symbolic expressions that were read. Raise ReadError if there is a syntax error, including unexpected end of file.

PROCEDURE Print(
wr: Wr.T;
sx: T;
maxDepth: CARDINAL := LAST(CARDINAL);
maxLength: CARDINAL := LAST(CARDINAL))
Print the symbolic expression sx on the writer wr, assuming the standard syntax.
Each sublist will contain no more than maxLength elements; extra
elements are replaced by an ellipsis (three dots).  Any sublist
nested at a depth greater than maxDepth is also replaced by an
ellipsis.  Print inserts | around atoms if necessary to ensure
that they are readable.  Print does not insert line-breaks or
indentation to produce a human-readable (pretty-printed'') format
for large symbolic expressions.

Print will raise PrintError if it tries to print something that is not printable'' (as defined below). If a list contains an unprintable element that is beyond the limits established by maxDepth and maxLength, PrintError may or may not be raised.

An object is said to be printable'' if it satisfies the following hypothetical predicate:

PROCEDURE Printable(x: REFANY): BOOLEAN =
BEGIN
TYPECASE x OF
| NULL, REF CHAR, TEXT, REF INTEGER, REF REAL,
REF LONGREAL, REF EXTENDED, Atom.T =>
RETURN TRUE
| RefList.T (list) => RETURN Printable(list.head) AND
Printable(list.tail)
ELSE
RETURN FALSE
END
END Printable;

Read(rd,NIL) is guaranteed to return a printable value unless it raises an exception. Assuming the defaults for syntax, maxDepth, and maxLength, and assuming no exceptions are raised, Read and Print are inverses''.

TYPE Syntax <: REFANY;
A Syntax is a partial map from characters to read macros.

PROCEDURE CopySyntax(s: Syntax := NIL): Syntax;
Allocate and return a new syntax table whose contents are the same as s or, if s = NIL, the same as the standard syntax table. The standard syntax table has no read macros.

Set s[ch] := m. It is a checked runtime error if s = NIL, if ch is a whitespace character, or if ch = ';'. It is allowed for m to be NIL; this has the effect of removing the mapping, if any, from ch to a readmacro.

END;

For example, the following program fragment constructs a syntax table that extends the standard syntax in two ways. First, additional comments are supported by ignoring all characters between { and }. Second, an expression of the form [e1~...~en] is turned into the list (ARRAY e1~...~en):

VAR syn := CopySyntax(); BEGIN
...

self: ReadMacro; rd: Rd.T; <* UNUSED *> s: Syntax)
: RefList.T =
BEGIN
WHILE NOT Rd.EOF() AND Rd.GetChar(rd) # '}' DO
(* SKIP

|     END;
|     RETURN NIL
|
| VAR (*CONST*) arrayAtm := Atom.FromText("ARRAY");
|
|   : RefList.T =
|   VAR elements := ReadDelimitedList(rd, ']', s);
|   BEGIN
|     RETURN RefList.List1(RefList.Cons(arrayAtm, elements))

The call to "RefList.List1" in "ReadArray" is important.  If it were
omitted, then the text

| (a b [c d])