HerculesExpr.mesa

Last Edited by: Stolfi, April 9, 1984 7:20:16 pm PST

Modified pieces of JunoAlgebra and other modules
written July, 1982 by Donna M. Auguste and Greg Nelson
Last Edited by: Gnelson, October 11, 1983 4:15 pm

Format and manipulation of Symbolic Expressions.

HerculesExpr: DEFINITIONS =

BEGIN

- - - - SYMBOLIC EXPRESSIONS

Se: TYPE = REF ANY;

Symbolic Expression. One of the following:
NIL, or
atom, or
Cell pointer: REF NumCell or REF RopeCell, or
Functional Value: REF FunRec, or
Application: LIST OF Se, of the form (<zerop>), (<unop> <se>), or (<binop> <se> <se>)

where <zerop>, <unop>, and <binop> are the "operators" and the <se>s are their "arguments". The only <zerop>s are <leftParen> and <leftBracket>; they occur as <zerops> in symbolic expressions resulting from the parsing of the Hercules external expressions () and []. In all other cases, <leftParen> and <leftBracket> are always <unop>s. For example, the external expression f(a, b) should be parsed into ($f (<leftParen> (<comma> $a $b))).

- - - - New Se's (April 9, 1984 7:18:46 pm PST)

Se: TYPE = REF ANY; -- Symbolic Expression. Either:

ATOM (variable, operator, or function name)
Cell pointer: REF NumCell or REF RopeCell, or
Functional Value: REF FunRec, or
OpPtr

OpPtr: TYPE = REF OpRec;

OpRec: TYPE = RECORD [
op: ATOM, -- operation (usually atom, a function or operator name)
larg, rarg: Se -- Unevaluated arguments. larg is NIL for prefix operators.
<unparser stuff>
];

- - - - SIMPLE VALUE CELLS

CellPtr: TYPE = REF;

A simple value cell is a record that can hold a single value of type Int, Real, or Rope. Value cells are created (1) when a new atom is declared in an if or do operation, or (2) by operations such as +, -, etc., to hold the resulting value. In the first case, the "value" of the atom is the CellPtr, not the contents of the cell.

NumPtr: TYPE = REF NumCell;

NumCell: TYPE = RECORD
[val: REAL, -- current value
const: BOOLTRUE, -- if TRUE, val is frozen (e.g., result of '+', or coord of frozen point)
int: BOOL FALSE, -- if TRUE, val is constrained to be integral
col: INTEGER ← 0 -- (used by solver) column index in the partial derivative matrix
];

RopePtr: TYPE = REF RopeCell;

RopeCell: TYPE = RECORD
[rope: Rope.ROPE, -- rope (obviously)
const: BOOLTRUE -- if FALSE, rope can be changed by assignment
];

- - - - FUNCTIONAL VALUES

A "functional value" is equivalent to LISP's FUNARG, essentially a lambda-expression plus an a-list that binds all free varaibles (if any) in its body.

However, any free variable that is not bound by the given a-list is looked up in the global a-list. Changes in the latter (brought upon, for example, by re-parsing the Procs Viewer) may change the meaning of a FunVal; in this sense, FunVals are not perfectly "closed", as theory would demand. Also, even when a variable is solidly bound to a CellPtr, it may be possible to change the contents of the referenced cell by assignment.

FunPtr: TYPE = REF FunRec;

FunRec: TYPE = RECORD
[body: Se, -- Body of lambda.
parms: LIST OF ATOM, -- formal parameters of the lambda.
alist: Alist -- bindings for free variables in body of lambda, other than the parms
];

- - - - MOLDS AND CASTING

A
Mold describes a set of Symbolic Expressions. The possible classes of Molds, and the set of Se's that they describe, as listed below:

NIL --> just the NIL Se

$ANY --> any Se

$ATOM --> an atom

$LIST --> a list of zero or more arbitrary Se s

$NUM, $ROPE --> a non-NIL pointer to a cell of the specified type

$FUN --> a non-NIL pointer to a FunRec,

$INT --> a pointer to a NumCell with the int flag set,

a UnionMold, --> a Se satisfying one of the alternatives listed within it

a VectorMold, --> a list of [min...max] Se s all fitting the given elm mold

a LIST OF Mold of the form (mold1, mold2, ..., moldn),
--> a list of exactly n Se s, each fitting the corresponding mold.

UnionMold: TYPE = REF UnionRec;

UnionMoldRec: TYPE = RECORD [alts: LIST OF Mold];

Vector
Mold: TYPE = REF VectorMoldRec;

Vector
MoldRec: TYPE = RECORD
[min: INTEGER,
max: INTEGERLAST[INTEGER], -- LAST[INTEGER] means infinity
elm: Mold];

Mold: TYPE = REF;

Fits
: PROC [e: Se, mold: Mold] RETURNS [fits: BOOL, culprit: Se];
-- Returns fits=true if the expression e is in the set of expressions described by the mold.
-- If fits = FALSE, the culprit is the first sub-expression of e that
-- does not fit the corresponding
-- piece of the mold.
-- More precisely, if the mold is not list or vector, then culprit will be always
-- e; if the mold is a vector
-- or list, culprit will be e if e is not a list, or is of the wrong length; otherwise
-- culprit will be the culprit in the first element of e that fails to fit.

IsList: PROC [e: Se, min: INTEGER ← 0, max: INTEGERLAST[INTEGER]]
RETURNS [fits: BOOL, list: LIST OF Se];
-- If e is LIST OF Se, returns [Length[Se] IN [min..max], NARROW[e]];
-- otherwise returns [FALSE, NIL]

IsUnAppl: PROC [e: Se, op: Se] RETURNS [fits: BOOL, arg: Se];
-- If e is (op <expr1>), returns [TRUE, <expr1>]; otherwise returns [FALSE, NIL]

IsBinAppl: PROC [e: Se, op: Se] RETURNS [fits: BOOL, larg, rarg: Se];
-- If e is (op <expr1> <expr2>), returns [TRUE, <expr1>, <expr2>];
-- otherwise returns [FALSE, NIL, NIL]

The following procedures narrow the argument to a specific type and format, and return its components in a more convenient form. They will raise the InvalidSe error if the argument is of the wrong type. Note: they DO NOT evaluate their arguments in any way.

InvalidSe: ERROR [e: Se];
-- Raised by the various procedures here when the argument is of the wrong format.

ToList: PROC [e: Se, min: INTEGER ← 0, max: INTEGERLAST[INTEGER]]
RETURNS [list: LIST OF Se];
-- Narrows e to LIST OF Se with [min..max] elements, and returns it.

- - - - LIST MANIPULATION PROCS

The following procedures narrow their argument(s) to LIST OF Se:

Car
: PROC [r: Se] RETURNS [Se];

Cdr
: PROC [r: Se] RETURNS [Se];

Cadr
: PROC [r: Se] RETURNS [Se];

Caddr
: PROC [r: Se] RETURNS [Se];

Unnest: PROC[e: Se, op, zero: Se, tail: LIST OF Se ← NIL] RETURNS [list: LIST OF Se];
-- Recursively unbundles an expression of the form (op <expr> <expr>),
-- and returns the list (<x1> <x2> ... <xn-1> <xn>)*<tail> of all the arguments of <op>
-- that are not themselves of the above form, prepended to the given tail.
-- Any <xi>s that are = zero will be ignored.

- - - - ASSOCIATION LISTS

Alist: TYPE = LIST OF Se; -- of the form (<atom> <value> <atom> <value> ...)

InsertDef
: PROC [name: ATOM, value: Se, alist: Alist] RETURNS [new: Alist];
-- Inserts the definition of name in the front of the alist.

GetDef
: PROC [name: ATOM, alist: Alist, backup: Alist ← NIL] RETURNS [value: Se];
-- Gets most recently defined value of name from alist. If not found, looks it up in backup.
-- Returns NIL if name is not defined (or if value is NIL)

BindArgs
: PROC [parms: LIST OF Se, args: LIST OF Se, alist: Alist]
RETURNS [new: Alist];
-- Prepends to the given alist a segment (<parm1> <arg1> <parm2> <arg2> ... <parmn> <argn>)
-- Raises the error ParmNumberError if one of the lists is longer than the other.

ParmNumberError: ERROR;

- - - - MISCELLANEOUS

MakeNum
: PROC [val: REAL, int, const: BOOLFALSE] RETURNS [n: NumPtr];
-- Makes a new NumCell with given contents.
-- If
int=TRUE, round the number to the nearest integer; if the
-- result can be represented as a REAL with no error, sets the int flag, otherwise
-- leaves it off.

END
.