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: BOOL ← TRUE, -- 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: BOOL ← TRUE -- 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];
VectorMold: TYPE = REF VectorMoldRec;
VectorMoldRec: TYPE = RECORD
[min: INTEGER,
max: INTEGER ← LAST[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: INTEGER ← LAST[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: INTEGER ← LAST[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: BOOL ← FALSE] 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.