MathDisplayExprImpl.mesa
Carl Waldspurger, August 17, 1986 7:26:47 pm PDT
DIRECTORY
MathDisplayExpr,
MathExpr,
MathBox,
MathTypes,
MathRules,
Convert USING [RopeFromAtom],
Rope USING [ROPE, Length, Find, Replace, Cat],
Imager USING [Context, black, SetColor, MaskRectangle];
MathDisplayExprImpl: CEDAR PROGRAM
IMPORTS MathBox, MathExpr, Imager, MathRules, Rope, Convert
EXPORTS MathDisplayExpr ~
BEGIN
Type Abbreviations from Imported Types
ROPE: TYPE ~ Rope.ROPE;
BOX: TYPE ~ MathBox.BOX;
EXPR: TYPE ~ MathExpr.EXPR;
AtomEXPR: TYPE ~ MathExpr.AtomEXPR;
CompoundEXPR: TYPE ~ MathExpr.CompoundEXPR;
MatrixEXPR: TYPE ~ MathExpr.MatrixEXPR;
Style: TYPE ~ MathTypes.Style;
Argument: TYPE ~ MathExpr.Argument;
Symbol: TYPE ~ MathExpr.Symbol;
AtomClass: TYPE ~ MathExpr.AtomClass;
AtomFlavor: TYPE ~ MathExpr.AtomFlavor;
CompoundClass: TYPE ~ MathExpr.CompoundClass;
AtomValue: TYPE ~ MathTypes.AtomValue;
Size: TYPE ~ MathRules.Size;
Selection: TYPE ~ MathDisplayExpr.Selection;
MatrixFlavor: TYPE ~ MathExpr.MatrixFlavor;
Private Type Rep
A DisplayExpr is a MUTABLE math expression which is mapped to a viewer.
DisplayExprRep: PUBLIC TYPE ~
RECORD [
tag: ATOM, -- identifying tag such as $integrand
expression: DisplayObj, -- atom or compound expr
relativeBox: BOX, -- bounding box in relative units
absoluteBox: BOX, -- bounding box in absolute units
iconic: BOOLFALSE, -- TRUE iff expression is currently iconic
parent: DisplayExpr ← NIL -- enclosing parent expression
];
DisplayObj: TYPE ~ REF DisplayObjRep;
DisplayObjRep: TYPE ~ RECORD [
SELECT type:* FROM
atom => [class: AtomClass, value: AtomValue],
compound => [class: CompoundClass, subExprs: LIST OF DisplayExpr ← NIL],
matrix => [
flavor: MatrixFlavor, -- type of matrix op, e.g. matrix, determinant, etc.
nRows, nCols: INT, -- matrix size, rows x cols
elements: LIST OF DisplayExpr ← NIL, -- matrix elements
openSym: DisplayExpr, -- left bracket
closeSym: DisplayExpr, -- right bracket
space: DisplayExpr -- inter-element spacing
]
ENDCASE
];
useful type shorthands
AtomDisplayObj: TYPE ~ REF AtomDisplayObjRep;
AtomDisplayObjRep: TYPE ~ atom DisplayObjRep;
CompoundDisplayObj: TYPE ~ REF CompoundDisplayObjRep;
CompoundDisplayObjRep: TYPE ~ compound DisplayObjRep;
MatrixDisplayObj: TYPE ~ REF MatrixDisplayObjRep;
MatrixDisplayObjRep: TYPE ~ matrix DisplayObjRep;
DisplayExpr: TYPE ~ REF DisplayExprRep; -- inside impl module, use rep
Operations on DisplayExpr
Display Expression Constructors
MakeAtomicDisplayExpr: PUBLIC PROC[tag: ATOM, class: AtomClass, value: AtomValue, relBox, absBox: BOX, parent: DisplayExpr ← NIL] RETURNS[DisplayExpr] ~ {
effects: Constructs and returns a new atomic display expression.
RETURN[NEW[DisplayExprRep ← [tag: tag, relativeBox: relBox, absoluteBox: absBox, parent: parent, expression: NEW[AtomDisplayObjRep ← [atom[class: class, value: value]]]]]];
};
MakeCompoundDisplayExpr: PUBLIC PROC[tag: ATOM, class: CompoundClass, subExprs: LIST OF DisplayExpr, relBox, absBox: BOX, parent: DisplayExpr ← NIL] RETURNS[DisplayExpr] ~ {
effects: Constructs and returns a new compound display expression.
RETURN[NEW[DisplayExprRep ← [tag: tag, relativeBox: relBox, absoluteBox: absBox, parent: parent, expression: NEW[CompoundDisplayObjRep ← [compound[class: class, subExprs: subExprs]]]]]];
};
MakeMatrixDisplayExpr: PUBLIC PROC[tag: ATOM, flavor: MatrixFlavor, nRows, nCols: INT, elements: LIST OF DisplayExpr, relBox, absBox: BOX, openSym, closeSym, space: DisplayExpr, parent: DisplayExpr ← NIL]
RETURNS[DisplayExpr] ~ {
effects: Constructs and returns a new matrix display expression.
RETURN[NEW[DisplayExprRep ← [tag: tag, relativeBox: relBox, absoluteBox: absBox, parent: parent, expression: NEW[MatrixDisplayObjRep ← [matrix[flavor: flavor, nRows: nRows, nCols: nCols, elements: elements, openSym: openSym, closeSym: closeSym, space: space]]]]]];
};
ASRopeFromDisplayExpr: PUBLIC PROC[expr: DisplayExpr] RETURNS[ROPE] ~ {
effects: Returns a ROPE in Dennis Arnon's AlgebraStructures format
from expr.
WITH expr.expression SELECT FROM
a: AtomDisplayObj => {
RETURN[a.class.cvtRope[a.value]];
};
c: CompoundDisplayObj => {
local declarations
valueAS: ROPE ← c.class.cvtAS; -- template for return value
argName: ROPENIL;
position: INT ← -1;
IF Rope.Length[valueAS] < 1 THEN RETURN[""];
recursively copy subexpressions
FOR l: LIST OF Argument ← c.class.arguments, l.rest UNTIL l = NIL DO
argName ← Convert.RopeFromAtom[l.first.name];
position ← Rope.Find[valueAS, argName];
IF position # -1 THEN valueAS ← Rope.Replace[base: valueAS, start: position, len: Rope.Length[argName], with: ASRopeFromDisplayExpr[GetDisplayExpr[l.first.name, c.subExprs]]];
ENDLOOP;
RETURN[valueAS];
};
m: MatrixDisplayObj => {
type declarations
Element: TYPE ~ RECORD [
expr: DisplayExpr ← NIL
];
Row: TYPE ~ REF RowRec;
RowRec: TYPE ~ RECORD [
elements: SEQUENCE numberOfCols: NAT OF Element
];
Matrix: TYPE ~ REF MatrixRec;
MatrixRec: TYPE ~ RECORD[
rows: SEQUENCE numberOfRows: NAT OF Row
];
local variable declarations
a: Matrix;
currentRow, currentCol: NAT;
result: ROPENIL; -- return value
convert boxes into a SEQUENCE (ugh!) matrix type
instantiate matrix "a" (isn't CLU's array[]$create() much nicer, really?)
a ← NEW[MatrixRec[m.nRows]];
FOR row:INT IN [0..m.nRows) DO
a[row] ← NEW[RowRec[m.nCols]];
ENDLOOP;
FOR l: LIST OF DisplayExpr ← m.elements, l.rest UNTIL l = NIL DO
[currentRow, currentCol] ← MathRules.RowColFromAtom[l.first.tag];
a[currentRow - 1][currentCol - 1].expr ← l.first;
ENDLOOP;
construct rope from matrix in algebrastructures format:
result ← "[ "; -- initialize to matrix start char
FOR r:INT IN [0..m.nRows) DO
result ← Rope.Cat[result, "[ "]; -- row start char
FOR c:INT IN [0..m.nCols) DO
result ← Rope.Cat[result, ASRopeFromDisplayExpr[a[r][c].expr]];
add a comma if not last col in row
IF c # (m.nCols - 1) THEN result ← Rope.Cat[result, ", "]; -- col separator
ENDLOOP;
result ← Rope.Cat[result, " ]"]; -- row end char
add a comma if not last row in matrix
IF r # (m.nRows - 1) THEN result ← Rope.Cat[result, ", "]; -- row separator
ENDLOOP;
result ← Rope.Cat[result, " ]"]; -- end with matrix end char
RETURN[result];
};
ENDCASE => ERROR;
};
DisplayExprFromExpr: PUBLIC PROC[expr: EXPR] RETURNS[DisplayExpr] ~ {
effects: Constructs and returns a new display expression from expr.
All Display formatting information is set to default values.
local declarations
myDisplayExpr, tempDisplayExpr: DisplayExpr;
emptyAtom: ATOM ← $expr; -- generic identifier
emptyRelBox: BOX ← MathBox.MakeBox[emptyAtom, NIL, other, relative];
emptyAbsBox: BOX ← MathBox.MakeBox[emptyAtom, NIL, other, absolute];
SELECT expr.GetType[] FROM
atom => {
a: AtomEXPR ← expr.GetAtomExpr[];
should really combine passed in style with local atomic style
RETURN[MakeAtomicDisplayExpr[emptyAtom, MathExpr.GetAtomClass[a], MathExpr.GetValue[a], emptyRelBox, emptyAbsBox]];
};
compound => {
c: CompoundEXPR ← expr.GetCompoundExpr[];
compClass: CompoundClass ← MathExpr.GetCompoundClass[c];
recursively generate displayexprs for all subexpressions (arguments & symbols)
displaySubExprs: LIST OF DisplayExpr ← NIL; -- cons up list of display expressions
generate all argument subexpressions
FOR l: LIST OF Argument ← compClass.arguments, l.rest UNTIL l = NIL DO
tempDisplayExpr ← DisplayExprFromExpr[MathExpr.GetTaggedExpr[l.first.name, MathExpr.GetSubExprs[c] ! exprNotFound => {ERROR unable[$missingArg]}].expression];
tempDisplayExpr.tag ← l.first.name; -- set name tag for display expr
displaySubExprs ← CONS[tempDisplayExpr, displaySubExprs];
ENDLOOP;
generate all symbol subexpressions
FOR l: LIST OF Symbol ← compClass.symbols, l.rest UNTIL l = NIL DO
tempDisplayExpr ← DisplayExprFromExpr[MathExpr.GetTaggedExpr[l.first.name, MathExpr.GetSubExprs[c] ! exprNotFound => {ERROR unable[$missingSym]}].expression];
tempDisplayExpr.tag ← l.first.name; -- set name tag for display expr
displaySubExprs ← CONS[tempDisplayExpr, displaySubExprs];
ENDLOOP;
construct return value now so can set "parent" component of subexpressions
myDisplayExpr ← MakeCompoundDisplayExpr[emptyAtom, MathExpr.GetCompoundClass[c], displaySubExprs, emptyRelBox, emptyAbsBox];
set parents of display subexpressions
FOR l: LIST OF DisplayExpr ← displaySubExprs, l.rest UNTIL l = NIL DO
l.first.parent ← myDisplayExpr; -- set parent
ENDLOOP;
RETURN[myDisplayExpr];
};
matrix => {
m: MatrixEXPR ← expr.GetMatrixExpr[];
elts: LIST OF MathExpr.TaggedMathExpr ← MathExpr.GetMatrixElements[m];
flavor: MatrixFlavor ← MathExpr.GetMatrixFlavor[m];
displayElts: LIST OF DisplayExpr ← NIL;
nRows, nCols: NAT;
openSym, closeSym, space: DisplayExpr;
[nRows, nCols] ← MathExpr.GetMatrixSize[m];
convert elements from expr to displayexpr
FOR l: LIST OF MathExpr.TaggedMathExpr ← elts, l.rest UNTIL l = NIL DO
tempDisplayExpr ← DisplayExprFromExpr[l.first.expression];
tempDisplayExpr.tag ← l.first.id; -- set name tag for display expr
displayElts ← CONS[tempDisplayExpr, displayElts];
ENDLOOP;
openSym ← DisplayExprFromExpr[MathExpr.GetMatrixOpenSym[m]];
closeSym ← DisplayExprFromExpr[MathExpr.GetMatrixCloseSym[m]];
space ← DisplayExprFromExpr[MathExpr.GetMatrixSpace[m]];
construct return value now so can set "parent" component of subexpressions
myDisplayExpr ← MakeMatrixDisplayExpr[emptyAtom, flavor, nRows, nCols, displayElts, emptyRelBox, emptyAbsBox, openSym, closeSym, space];
FOR l: LIST OF DisplayExpr ← displayElts, l.rest UNTIL l = NIL DO
l.first.parent ← myDisplayExpr; -- set parent
ENDLOOP;
RETURN[myDisplayExpr];
};
ENDCASE => ERROR;
};
ExprFromDisplayExpr: PUBLIC PROC[expr: DisplayExpr] RETURNS[EXPR] ~ {
effects: Constructs and returns a new EXPR from expr.
WITH expr.expression SELECT FROM
a: AtomDisplayObj => {
RETURN[MathExpr.MakeAtomicExpr[a.class.name, a.value]];
};
c: CompoundDisplayObj => {
local declarations
args: LIST OF MathExpr.TaggedMathExpr ← NIL;
recursively build a list of tagged arg exprs
FOR l: LIST OF DisplayExpr ← c.subExprs, l.rest UNTIL l = NIL DO
args ← CONS[[l.first.tag, ExprFromDisplayExpr[l.first]], args];
ENDLOOP;
RETURN[MathExpr.MakeCompoundExpr[c.class.name, args]];
};
m: MatrixDisplayObj => {
local declarations
elts: LIST OF MathExpr.TaggedMathExpr;
openSym, closeSym, space: EXPR;
FOR l: LIST OF DisplayExpr ← m.elements, l.rest UNTIL l = NIL DO
elts ← CONS[[l.first.tag, ExprFromDisplayExpr[l.first]], elts];
ENDLOOP;
openSym ← ExprFromDisplayExpr[m.openSym];
closeSym ← ExprFromDisplayExpr[m.closeSym];
space ← ExprFromDisplayExpr[m.space];
RETURN[MathExpr.MakeMatrixExpr[m.flavor, m.nRows, m.nCols, elts, openSym, closeSym, space]];
};
ENDCASE => ERROR;
};
Copy: PUBLIC PROC[expr: DisplayExpr] RETURNS[DisplayExpr] ~ {
effects: Returns a new, distinct copy of expr.
WITH expr.expression SELECT FROM
a: AtomDisplayObj => {
RETURN[MakeAtomicDisplayExpr[expr.tag, a.class, a.value, expr.relativeBox, expr.absoluteBox]];
};
c: CompoundDisplayObj => {
local declarations
exprCopy: DisplayExpr; -- return value
copiedSubExprs: LIST OF DisplayExpr ← NIL; -- cons up new list of copies
recursively copy subexpressions
FOR l: LIST OF DisplayExpr ← c.subExprs, l.rest UNTIL l = NIL DO
copiedSubExprs ← CONS[Copy[l.first], copiedSubExprs];
ENDLOOP;
construct return value now so we can set parent pointers for subexprs
exprCopy ← MakeCompoundDisplayExpr[expr.tag, c.class, copiedSubExprs, expr.relativeBox, expr.absoluteBox];
FOR l: LIST OF DisplayExpr ← copiedSubExprs, l.rest UNTIL l = NIL DO
l.first.parent ← exprCopy; -- set parent pointers appropriately
ENDLOOP;
RETURN[exprCopy];
};
m: MatrixDisplayObj => {
local declarations
exprCopy: DisplayExpr; -- return value
copiedElts: LIST OF DisplayExpr ← NIL; -- cons up a new list of copied elements
recursively copy elements
FOR l: LIST OF DisplayExpr ← m.elements, l.rest UNTIL l = NIL DO
copiedElts ← CONS[Copy[l.first], copiedElts];
ENDLOOP;
construct return value now so we can set parent pointers for subexprs
exprCopy ← MakeMatrixDisplayExpr[expr.tag, m.flavor, m.nRows, m.nCols, copiedElts, expr.relativeBox, expr.absoluteBox, Copy[m.openSym], Copy[m.closeSym], Copy[m.space]];
FOR l: LIST OF DisplayExpr ← copiedElts, l.rest UNTIL l = NIL DO
l.first.parent ← exprCopy; -- set parent pointers appropriately
ENDLOOP;
RETURN[exprCopy];
};
ENDCASE => ERROR;
};
Selectors
Tag: PUBLIC PROC[expr: DisplayExpr] RETURNS[ATOM] ~ {
effects: Returns tag associated with expr.
RETURN[expr.tag];
};
Class: PUBLIC PROC[expr: DisplayExpr] RETURNS[ATOM] ~ {
effects: Returns class of expr.
WITH expr.expression SELECT FROM
a: AtomDisplayObj => RETURN[a.class.name];
c: CompoundDisplayObj => RETURN[c.class.name];
m: MatrixDisplayObj => RETURN[m.flavor.class];
ENDCASE => ERROR;
};
GetSubExprs: PUBLIC PROC[expr: DisplayExpr] RETURNS[LIST OF DisplayExpr] ~ {
effects: Returns subexpressions for expr.
Returns NIL if no subexpressions exist
WITH expr.expression SELECT FROM
a: AtomDisplayObj => RETURN[NIL]; -- atoms don't have any children
c: CompoundDisplayObj => RETURN[c.subExprs];
m: MatrixDisplayObj => RETURN[m.elements];
ENDCASE => ERROR;
};
Display & Formatting
Paint: PUBLIC PROC[expr: DisplayExpr, context: Imager.Context, absBox: BOX,
selections: LIST OF Selection] ~ {
requires: absBox is an absolute BOX
modifies: expr
effects: Displays expr in context, bounded by absBox.
Updates absolute bounding boxes (viewer coords).
SIGNALS unable[reason: ATOM] if Painting cannot be completed.
local declarations
currentDisplayExpr: DisplayExpr;
update absolute coordinates
IF absBox.Type # absolute THEN ERROR unable[$wrongBoxType];
expr.absoluteBox ← absBox;
WITH expr.expression SELECT FROM
a: AtomDisplayObj => {
a.class.paintRule[a.value, a.class.style, context, absBox];
};
c: CompoundDisplayObj => {
paint all subexpressions
paint arguments
FOR l: LIST OF Argument ← c.class.arguments, l.rest UNTIL l = NIL DO
currentDisplayExpr ← GetDisplayExpr[l.first.name, c.subExprs ! exprNotFound => {ERROR unable[$missingArg]}];
Paint[currentDisplayExpr, context, MathBox.RelToAbsBox[currentDisplayExpr.relativeBox, absBox], selections];
ENDLOOP;
paint symbols
FOR l: LIST OF Symbol ← c.class.symbols, l.rest UNTIL l = NIL DO
currentDisplayExpr ← GetDisplayExpr[l.first.name, c.subExprs ! exprNotFound => {ERROR unable[$missingArg]}];
Paint[currentDisplayExpr, context, MathBox.RelToAbsBox[currentDisplayExpr.relativeBox, absBox], selections];
ENDLOOP;
};
m: MatrixDisplayObj => {
paint all matrix elements
FOR l: LIST OF DisplayExpr ← m.elements, l.rest UNTIL l = NIL DO
Paint[l.first, context, MathBox.RelToAbsBox[l.first.relativeBox, absBox], selections];
ENDLOOP;
paint open & close symbols
Paint[m.openSym, context, MathBox.RelToAbsBox[m.openSym.relativeBox, absBox], selections];
Paint[m.closeSym, context, MathBox.RelToAbsBox[m.closeSym.relativeBox, absBox], selections];
};
ENDCASE => ERROR;
if selected, highlight region appropriately, using a one pixel border
FOR l: LIST OF Selection ← selections, l.rest UNTIL l = NIL DO
IF (expr = l.first.expr) THEN {
set proper color depending on selection
context.SetColor[l.first.color];
highlight region
Imager.MaskRectangle[context, [x: absBox.Offset[].x - (absBox.Extents[].leftExtent + 1), y: absBox.Offset[].y - (absBox.Extents[].descent + 1), w: absBox.Width[] + 1, h: absBox.Height[] + 2]];
reset color to black
context.SetColor[Imager.black];
};
ENDLOOP;
};
Format: PUBLIC PROC[expr: DisplayExpr, size: Size] RETURNS[BOX] ~ {
modifies: expr
effects: expr is updated into a "paintable" form.
Returns a bounding box for expr.
SIGNALS unable[reason: ATOM] if Formatting can not be completed.
local declarations
subExprBoxes: LIST OF BOX;
myBox, tempBox: BOX;
WITH expr.expression SELECT FROM
a: AtomDisplayObj => {
RETURN[MathBox.Scale[MathBox.MakeBox[tag: a.class.name, aliases: NIL, formatClass: a.class.formatClass, type: absolute, extents: a.class.boxRule[a.value, a.class.style]], MathRules.VecFromSize[size]]];
};
c: CompoundDisplayObj => {
recursively format all subexpressions (arguments & symbols)
subExprBoxes ← NIL; -- cons up list of subexpression boxes
format all argument subexpressions
FOR l: LIST OF Argument ← c.class.arguments, l.rest UNTIL l = NIL DO
tempBox ← Format[GetDisplayExpr[l.first.name, c.subExprs ! exprNotFound => {ERROR unable[$missingArg]}], MathRules.ComputeSize[base: size, adjustment: l.first.size]];
subExprBoxes ← CONS[MathBox.MakeBox[l.first.name, l.first.aliases, tempBox.FormatClass[], tempBox.Type[], tempBox.Extents[]], subExprBoxes];
ENDLOOP;
format all symbol subexpressions
FOR l: LIST OF Symbol ← c.class.symbols, l.rest UNTIL l = NIL DO
tempBox ← Format[GetDisplayExpr[l.first.name, c.subExprs ! exprNotFound => {ERROR unable[$missingSym]}], MathRules.ComputeSize[base: size, adjustment: l.first.size]];
subExprBoxes ← CONS[MathBox.MakeBox[l.first.name, l.first.aliases, tempBox.FormatClass[], tempBox.Type[], tempBox.Extents[]], subExprBoxes];
ENDLOOP;
compute corrected boxes using sizing constraints
subExprBoxes ← c.class.boxRule[subExprBoxes];
compose layout for boxes using alignment constraints
[myBox, subExprBoxes] ← c.class.compBox[subExprBoxes];
set format class for mybox
myBox ← MathBox.ChangeFormatClass[myBox, c.class.formatClass];
set relative boxes of display subexpressions
FOR l: LIST OF DisplayExpr ← c.subExprs, l.rest UNTIL l = NIL DO
IF l.first.relativeBox.Type # relative THEN ERROR unable[$wrongBoxType];
l.first.relativeBox ← MathBox.GetBox[l.first.tag, subExprBoxes ! MathBox.boxNotFound => {ERROR unable[$boxNotFound]}]; -- set box
ENDLOOP;
RETURN[myBox];
};
m: MatrixDisplayObj => {
local declarations
elementBoxes: LIST OF BOXNIL;
tempBox, spaceBox, openSymBox, closeSymBox: BOX;
recursively format all matrix elements
FOR l: LIST OF DisplayExpr ← m.elements, l.rest UNTIL l = NIL DO
tempBox ← Format[l.first, MathRules.ComputeSize[base: size, adjustment: script]];
elementBoxes ← CONS[MathBox.MakeBox[l.first.tag, NIL, tempBox.FormatClass[], tempBox.Type[], tempBox.Extents[]], elementBoxes];
ENDLOOP;
format openSym, closeSym, and space
spaceBox ← Format[m.space, MathRules.ComputeSize[base: size, adjustment: script]];
openSymBox ← Format[m.openSym, normal];
closeSymBox ← Format[m.closeSym, normal];
compose layout for matrix by using alignment constraints
[myBox, elementBoxes, m.openSym.relativeBox, m.closeSym.relativeBox] ← MathRules.ComposeMatrix[m.nRows, m.nCols, elementBoxes, spaceBox, openSymBox, closeSymBox];
set format class for mybox
myBox ← MathBox.ChangeFormatClass[myBox, m.flavor.formatClass];
set relative boxes of matrix elements
FOR l: LIST OF DisplayExpr ← m.elements, l.rest UNTIL l = NIL DO
IF l.first.relativeBox.Type # relative THEN ERROR unable[$wrongBoxType];
l.first.relativeBox ← MathBox.GetBox[l.first.tag, elementBoxes ! MathBox.boxNotFound => {ERROR unable[$boxNotFound]}]; -- set box
ENDLOOP;
RETURN[myBox];
};
ENDCASE => ERROR;
};
List Operations
GetDisplayExpr: PUBLIC PROC[tag: ATOM, exprs: LIST OF DisplayExpr] RETURNS[DisplayExpr] ~ {
effects: Returns the DisplayExpr in exprs associated with tag.
SIGNALS exprNotFound if no association exists.
cdr down list looking for tag
FOR l: LIST OF DisplayExpr ← exprs, l.rest UNTIL l = NIL DO
IF l.first.tag = tag THEN RETURN[l.first];
ENDLOOP;
not found, so signal error
ERROR exprNotFound;
};
Selection Operations
DisplayExprFromCoords: PUBLIC PROC[expr: DisplayExpr, x, y: REAL] RETURNS[DisplayExpr] ~ {
effects: Returns the subexpression associated with coordinates [x, y] in expression expr.
SIGNALS noSelection if no association exists.
IF expr = NIL THEN ERROR noSelection;
IF ~MathBox.Inside[expr.absoluteBox, x, y] THEN ERROR noSelection;
WITH expr.expression SELECT FROM
a: AtomDisplayObj => {
IF Selectable[expr] THEN RETURN[expr] ELSE ERROR noSelection;
};
c: CompoundDisplayObj => {
see if selection can be further narrowed
FOR l: LIST OF DisplayExpr ← c.subExprs, l.rest UNTIL l = NIL DO
IF MathBox.Inside[l.first.absoluteBox, x, y] THEN
RETURN[DisplayExprFromCoords[l.first, x, y ! noSelection => CONTINUE]];
ENDLOOP;
if above loop does not return, then can't narrow selection any more
IF Selectable[expr] THEN RETURN[expr] ELSE ERROR noSelection;
};
m: MatrixDisplayObj => {
see if selection can be further narrowed
FOR l: LIST OF DisplayExpr ← m.elements, l.rest UNTIL l = NIL DO
IF MathBox.Inside[l.first.absoluteBox, x, y] THEN
RETURN[DisplayExprFromCoords[l.first, x, y ! noSelection => CONTINUE]];
ENDLOOP;
if above loop does not return, then can't narrow selection any more
IF Selectable[expr] THEN RETURN[expr] ELSE ERROR noSelection;
};
ENDCASE => ERROR;
};
Selectable: PUBLIC PROC[expr: DisplayExpr] RETURNS[BOOL] ~ {
effects: Returns TRUE iff expr is selectable.
IF expr = NIL THEN RETURN[FALSE];
WITH expr.expression SELECT FROM
a: AtomDisplayObj => {
IF a.class.flavor = symbol THEN RETURN[FALSE] ELSE RETURN[TRUE];
};
c: CompoundDisplayObj => RETURN[TRUE];
m: MatrixDisplayObj => RETURN[TRUE];
ENDCASE => ERROR;
};
SelectableParent: PUBLIC PROC[expr: DisplayExpr] RETURNS[DisplayExpr] ~ {
effects: Returns the parent (enclosing expression) of expr.
SIGNALS noParent if no selectable parent exists.
complain if no expr or no parent
IF (expr = NIL) OR (expr.parent) = NIL THEN ERROR noSelection;
RETURN[expr.parent];
};
SelectableChild: PUBLIC PROC[expr: DisplayExpr] RETURNS[DisplayExpr] ~ {
effects: Returns a child expressions (subexpression) for expr.
SIGNALS noSelection if no selectable child exists.
local declarations
childExpr: DisplayExpr ← NIL;
IF expr = NIL THEN ERROR noSelection;
WITH expr.expression SELECT FROM
a: AtomDisplayObj => ERROR noSelection; -- atom has no children
c: CompoundDisplayObj => {
FOR l: LIST OF Argument ← c.class.arguments, l.rest UNTIL l = NIL DO
keep looking for selectable child
childExpr ← GetDisplayExpr[l.first.name, c.subExprs];
IF Selectable[childExpr] THEN RETURN[childExpr]; -- return selected expression
ENDLOOP;
if there are no selectable children, signal noSelection
ERROR noSelection;
};
m: MatrixDisplayObj => {
** FOO FIX THIS **
ERROR noSelection;
};
ENDCASE => ERROR;
};
SelectableSibling: PUBLIC PROC[expr: DisplayExpr] RETURNS[DisplayExpr] ~ {
effects: Returns the "next" sibling expression from expr.
SIGNALS noSelection if no selectable sibling exists.
local declarations
afterExpr: LIST OF Argument ← NIL; -- sibling after expr in parent's subexpr list
siblingExpr: DisplayExpr ← NIL;
IF (expr = NIL) OR (expr.parent = NIL) THEN ERROR noSelection;
"cycle" thru parent's children and return "next" selectable sibling in list
WITH expr.parent.expression SELECT FROM
a: AtomDisplayObj => ERROR noSelection;
c: CompoundDisplayObj => {
find starting point to begin looking for next selectable sibling expr
FOR l: LIST OF Argument ← c.class.arguments, l.rest UNTIL l = NIL DO
IF l.first.name = expr.tag THEN {afterExpr ← l.rest; EXIT};
ENDLOOP;
look for next selectable sibling in exprs after expr in list (i.e. in afterExpr)
FOR l: LIST OF Argument ← afterExpr, l.rest UNTIL l = NIL DO
siblingExpr ← GetDisplayExpr[l.first.name, c.subExprs];
IF Selectable[siblingExpr] THEN RETURN[siblingExpr]; -- return selected expression
ENDLOOP;
if no selectable sibling exists after expr, then wraparound list to before expr
FOR l: LIST OF Argument ← c.class.arguments, l.rest UNTIL l = NIL DO
IF l.first.name = expr.tag THEN EXIT; -- already looked thru entire list
siblingExpr ← GetDisplayExpr[l.first.name, c.subExprs];
IF Selectable[siblingExpr] THEN RETURN[siblingExpr]; -- return selected expression
ENDLOOP;
if we haven't RETURNed yet, then there are no selectable siblings
ERROR noSelection;
};
m: MatrixDisplayObj => {
** FOO FIX THIS **
ERROR noSelection;
};
ENDCASE => ERROR;
};
Replace: PUBLIC PROC[old: DisplayExpr, new: DisplayExpr] ~ {
expects: old is a subexpression enclosed by some expresion, call it PARENT[old]
modifies: PARENT[old]
effects: Substitutes new for old as a subexpression of PARENT[old].
Sets TAG[new] = TAG[old]
SIGNALS replaceAll if PARENT[old] does not exist.
caveats: Enclosing expression must be reformatted before Paint[]'ing
IF old.parent = NIL THEN ERROR replaceAll; -- complain if no parent
new.tag ← old.tag; -- replacement has same tag (e.g. $integrand, etc.)
new.parent ← old.parent; -- replacement has same parent as before
WITH old.parent.expression SELECT FROM
impossible case since atoms have no children
a: AtomDisplayObj => ERROR invalidReplacement;
c: CompoundDisplayObj => {
newSubExprs: LIST OF DisplayExpr ← NIL; -- build up new list containing new
FOR l: LIST OF DisplayExpr ← c.subExprs, l.rest UNTIL l = NIL DO
IF l.first.tag = old.tag THEN newSubExprs ← CONS[new, newSubExprs]
ELSE newSubExprs ← CONS[l.first, newSubExprs];
ENDLOOP;
mutate subexpression list (in PARENT[old])
old.parent.expression ← NEW[CompoundDisplayObjRep ← [compound[class: c.class, subExprs: newSubExprs]]];
};
m: MatrixDisplayObj => {
newElements: LIST OF DisplayExpr ← NIL;
FOR l: LIST OF DisplayExpr ← m.elements, l.rest UNTIL l = NIL DO
IF l.first.tag = old.tag THEN newElements ← CONS[new, newElements]
ELSE newElements ← CONS[l.first, newElements];
ENDLOOP;
mutate subexpression list (in PARENT[old])
old.parent.expression ← NEW[MatrixDisplayObjRep ← [matrix[flavor: m.flavor, nRows: m.nRows, nCols: m.nCols, elements: newElements, openSym: m.openSym, closeSym: m.closeSym, space: m.space]]];
};
ENDCASE => ERROR;
};
Signals & Errors
unable: PUBLIC ERROR[reason: ATOM] = CODE;
noSelection: PUBLIC ERROR = CODE;
exprNotFound: PUBLIC ERROR = CODE;
invalidReplacement: PUBLIC ERROR = CODE;
replaceAll: PUBLIC ERROR = CODE;
END.