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.
INTERFACESx ; IMPORT Atom, Rd, RefList, Thread, Wr; TYPE T = REFANY; EXCEPTION ReadError(TEXT); PrintError(TEXT); PROCEDURE FromChar(c: CHAR): REF CHAR;
Return aChar
with valuec
.
PROCEDURE FromInt(i: INTEGER): REF INTEGER;
Return anInt
with valuei
.
PROCEDURE FromReal(r: REAL): REF REAL;
Return aReal
with valuer
.
PROCEDURE FromLongReal(r: LONGREAL): REF LONGREAL;
Return aLongreal
with valuer
.
PROCEDURE FromExtended(r: EXTENDED): REF EXTENDED;
Return anExtended
with valuer
.
PROCEDURE FromBool(b: BOOLEAN): Atom.T;
Return aBoolean
. Ifb
isTRUE
, returnSx.True
. Otherwise, returnSx.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 RAISES {ReadError, Rd.EndOfFile, Thread.Alerted};
Read and return a symbolic expression fromrd
, ignoring whitespace and comments. Ifsyntax
isNIL
, use the syntax described above; otherwise use any read macros that have been registered insyntax
.
PROCEDURE ReadDelimitedList( rd: Rd.T; delim : CHAR; syntax: Syntax := NIL): RefList.T RAISES {ReadError, Thread.Alerted};
Repeatedly read symbolic expressions fromrd
, ignoring whitespace and comments, until the next character isdelim
; consume the delimiter and return the list of symbolic expressions that were read. RaiseReadError
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)) RAISES {PrintError, Wr.Failure, Thread.Alerted};
Print the symbolic expressionsx
on the writerwr
, 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 ass
or, ifs = NIL
, the same as the standard syntax table. The standard syntax table has no read macros.
PROCEDURE SetReadMacro(s: Syntax; ch: CHAR; m: ReadMacro);
Sets[ch] := m
. It is a checked runtime error ifs = NIL
, ifch
is a whitespace character, or ifch = ';'
. It is allowed form
to beNIL
; this has the effect of removing the mapping, if any, fromch
to a readmacro.
TYPE ReadMacro = OBJECT METHODS read(rd: Rd.T; s: Syntax): RefList.T RAISES {ReadError, Thread.Alerted} END;If you pass a
Syntax
s
to Read
or ReadDelimitedList
, then
the reading algorithm is modified as follows. After skipping
whitespace and comments, and before reading a token, the next
character in the input stream is consumed and examined. If s
defines a read macro for this character, then this read macro is
called with the same arguments that were passed to Read
or
ReadDelimitedList
. The resulting list is spliced into the
current list being built. In particular, if the macro returns
NIL
, then everything it read is ignored; if the macro returns a
single-element list, then that single element is inserted into the
list being built. ReadError
is raised if the macro returns a
non-list or if it returns a multi-element list in a context where
no list is being built, such as at the top level of Read
.
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 SetReadMacro(syn, '{', NEW(ReadMacro, read := ReadComment)); SetReadMacro(syn, '[', NEW(ReadMacro, read := ReadArray)); ... PROCEDURE ReadComment( self: ReadMacro; rd: Rd.T; <* UNUSED *> s: Syntax) : RefList.T = BEGIN WHILE NOT Rd.EOF() AND Rd.GetChar(rd) # '}' DO (* SKIP| END; | RETURN NIL | END ReadComment; | | VAR (*CONST*) arrayAtm := Atom.FromText("ARRAY"); | | PROCEDURE ReadArray(self: ReadMacro; rd: Rd.T; s: Syntax) | : RefList.T = | VAR elements := ReadDelimitedList(rd, ']', s); | BEGIN | RETURN RefList.List1(RefList.Cons(arrayAtm, elements)) | END ReadArray; The call to "RefList.List1" in "ReadArray" is important. If it were omitted, then the text | (a b [c d]) would be read as | (a b ARRAY c d) instead of the intended | (a b (ARRAY c d)). *) END Sx.