MathDisplayExprImpl.mesa
Carl Waldspurger, August 29, 1986 2:58:56 pm PDT
Arnon, December 11, 1986 11:23:05 am PST
DIRECTORY
MathDisplayExpr,
MathExpr,
MathBox,
MathConstructors,
MathDB,
MathTypes,
MathRules,
Convert USING [RopeFromAtom],
Rope USING [ROPE, Length, Find, Replace, Cat],
Imager USING [Context, black, SetColor, MaskRectangle],
XRope;
MathDisplayExprImpl: CEDAR PROGRAM
IMPORTS MathBox, MathExpr, MathDB, MathConstructors, Imager, MathRules, Rope, Convert, XRope
EXPORTS MathDisplayExpr ~
BEGIN
Type Abbreviations from Imported Interfaces
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;
MatrixClass: TYPE ~ MathExpr.MatrixClass;
Size: TYPE ~ MathRules.Size;
Selection: TYPE ~ MathDisplayExpr.Selection;
ReplacePair: TYPE ~ MathDisplayExpr.ReplacePair;
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, compound expr, or matrix
relativeBox: BOX, -- bounding box in relative units
absoluteBox: BOX, -- bounding box in absolute units
iconic: BOOLFALSE, -- TRUE iff expression is currently iconic (2/87apparentlyUnused)
parent: DisplayExpr ← NIL -- enclosing parent expression
];
DisplayObj: TYPE ~ REF DisplayObjRep;
DisplayObjRep: TYPE ~ RECORD [
SELECT type:* FROM
atom => [class: AtomClass, value: ROPE],
compound => [class: CompoundClass, subExprs: LIST OF DisplayExpr ← NIL],
matrix => [
class: MatrixClass, -- type of matrix op, e.g. matrix, vector, 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
Display Expression Constructors
MakeAtomicDisplayExpr: PUBLIC PROC[tag: ATOM, class: AtomClass, value: ROPE, 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, class: MatrixClass, 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[class: class, nRows: nRows, nCols: nCols, elements: elements, openSym: openSym, closeSym: closeSym, space: space]]]]]];
};
Parse/UnParse Routines
ASRopeFromDisplayExpr: PUBLIC PROC[expr: DisplayExpr, flavor: ATOM ← $AS] RETURNS[ROPE] ~ {
effects: Returns a ROPE in selected format
WITH expr.expression SELECT FROM
a: AtomDisplayObj => SELECT flavor FROM
$Reduce => IF a.class.cvtReduceRope # NIL THEN RETURN[a.class.cvtReduceRope[a.value]] ELSE RETURN[a.value];
$SMP => IF a.class.cvtSMPRope # NIL THEN RETURN[a.class.cvtSMPRope[a.value]] ELSE RETURN[a.value];
ENDCASE => IF a.class.cvtASRope # NIL THEN RETURN[a.class.cvtASRope[a.value]] ELSE RETURN[a.value];
c: CompoundDisplayObj => {
template: ROPESELECT flavor FROM
$Reduce => c.class.cvtReduce, -- template for return value
$SMP => c.class.cvtSMP, -- template for return value
ENDCASE => c.class.cvtAS; -- template for return value
argName: ROPENIL;
position: INT ← -1;
IF Rope.Length[template] < 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[template, argName];
IF position # -1 THEN template ← Rope.Replace[base: template, start: position, len: Rope.Length[argName], with: ASRopeFromDisplayExpr[GetDisplayExpr[l.first.name, c.subExprs], flavor] ];
ENDLOOP;
Hack for complexes with negative imaginary part. December 11, 1986 11:24:18 am PST
IF c.class.name = $complex THEN {
position ← Rope.Find[template, "+ -"];
IF position # -1 THEN template ← Rope.Replace[base: template, start: position, len: 3, with: "- "];
};
RETURN[template];
};
m: MatrixDisplayObj => {
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
];
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;
SELECT m.class.name FROM
$matrix => {
result ← "List[ ";
FOR r:INT IN [0..m.nRows) DO
result ← Rope.Cat[result, "List[ "]; -- row start char
FOR c:INT IN [0..m.nCols) DO
result ← Rope.Cat[result, ASRopeFromDisplayExpr[a[r][c].expr, flavor]];
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
};
$vector, $sequence, $set, $point => {
result ← "List[ "; -- should select set, sequence, etc.
IF m.nRows = 1 THEN { -- row vector
FOR c: INT IN [0..m.nCols) DO
result ← Rope.Cat[result, ASRopeFromDisplayExpr[a[0][c].expr, flavor]];
add a comma if not last col in vector
IF c # (m.nCols - 1) THEN result ← Rope.Cat[result, ", "]; -- col separator
ENDLOOP;
}
ELSE { -- column vector
FOR r: INT IN [0..m.nRows) DO
result ← Rope.Cat[result, ASRopeFromDisplayExpr[a[r][0].expr, flavor]];
add a comma if not last row in vector
IF r # (m.nRows - 1) THEN result ← Rope.Cat[result, ", "]; -- row separator
ENDLOOP;
};
result ← Rope.Cat[result, " ]"];
};
ENDCASE => result ← "unknownMatrixType";
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];
class: MatrixClass ← MathExpr.GetMatrixClass[m];
displayElts, displayEltsPointer: 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;
convert elements from expr to displayexpr -- changed 3/4/87
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
IF displayElts = NIL THEN displayElts ← displayEltsPointer ← LIST[tempDisplayExpr] ELSE displayEltsPointer ← displayEltsPointer.rest ← LIST[tempDisplayExpr];
ENDLOOP;
openSym ← DisplayExprFromExpr[class.openSym];
closeSym ← DisplayExprFromExpr[class.closeSym];
space ← DisplayExprFromExpr[class.space];
construct return value now so can set "parent" component of subexpressions
myDisplayExpr ← MakeMatrixDisplayExpr[emptyAtom, class, 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.
2/87 - amounts to just stripping away the formatting info from the DisplayExpr, i.e. stripping relativeBox, absoluteBox, and parent pointers.
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, eltsPointer: LIST OF MathExpr.TaggedMathExpr ← NIL;
FOR l: LIST OF DisplayExpr ← m.elements, l.rest UNTIL l = NIL DO
elts ← CONS[[l.first.tag, ExprFromDisplayExpr[l.first]], elts];
ENDLOOP;
FOR l: LIST OF DisplayExpr ← m.elements, l.rest UNTIL l = NIL DO -- changed 3/4/87
taggedMathExpr: MathExpr.TaggedMathExpr ← [l.first.tag, ExprFromDisplayExpr[l.first]];
IF elts = NIL THEN elts ← eltsPointer ← LIST[taggedMathExpr] ELSE eltsPointer ← eltsPointer.rest ← LIST[taggedMathExpr];
ENDLOOP;
RETURN[MathExpr.MakeMatrixExpr[m.class.name, m.nRows, m.nCols, elts]];
};
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, copiedEltsPtr: 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;
recursively copy elements -- changed 3/4/87
FOR l: LIST OF DisplayExpr ← m.elements, l.rest UNTIL l = NIL DO
IF copiedElts = NIL THEN copiedElts ← copiedEltsPtr ← LIST[Copy[l.first] ] ELSE copiedEltsPtr ← copiedEltsPtr.rest ← LIST[Copy[l.first] ];
ENDLOOP;
construct return value now so we can set parent pointers for subexprs
exprCopy ← MakeMatrixDisplayExpr[expr.tag, m.class, 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.class.name];
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;
};
Format and Paint
Format: PUBLIC PROC[expr: DisplayExpr, size: Size] RETURNS[BOX] ~ {
modifies: expr
effects: expr is updated into a "paintable" form (dsa - perhaps in the sense that all subExprs have been given correct relative bounding boxes)
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[], tempBox.Offset[], tempBox.SubscriptHint[], tempBox.SuperscriptHint[]], 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[], tempBox.Offset[], tempBox.SubscriptHint[], tempBox.SuperscriptHint[]], 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, elementBoxesPtr: 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[], tempBox.Offset[], tempBox.SubscriptHint[], tempBox.SuperscriptHint[]], elementBoxes];
ENDLOOP;
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]];
IF elementBoxes = NIL THEN
elementBoxes ← elementBoxesPtr ← LIST[ MathBox.MakeBox[l.first.tag, NIL, tempBox.FormatClass[], tempBox.Type[], tempBox.Extents[], tempBox.Offset[], tempBox.SubscriptHint[], tempBox.SuperscriptHint[] ] ]
ELSE
elementBoxesPtr ← elementBoxesPtr.rest ← LIST[ MathBox.MakeBox[l.first.tag, NIL, tempBox.FormatClass[], tempBox.Type[], tempBox.Extents[], tempBox.Offset[], tempBox.SubscriptHint[], tempBox.SuperscriptHint[] ] ];
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.class.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;
};
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;
};
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;
};
ExtendFunctionSelectSibling: PROC [expr: DisplayExpr] RETURNS [DisplayExpr] ~ {
Hack 7/10/87: Assuming expr is the last arg of a function, change its parent to be larger function class and select the new last arg.
emptyAtom: ATOM ← $expr; -- generic identifier
emptyRelBox: BOX ← MathBox.MakeBox[emptyAtom, NIL, other, relative];
emptyAbsBox: BOX ← MathBox.MakeBox[emptyAtom, NIL, other, absolute];
arg12Space: DisplayExpr ← MakeAtomicDisplayExpr[$arg12Space, MathDB.LookupAtomClass[$space], Convert.RopeFromAtom[$medium, FALSE], emptyRelBox, emptyAbsBox];
arg23Space: DisplayExpr ← MakeAtomicDisplayExpr[$arg23Space, MathDB.LookupAtomClass[$space], Convert.RopeFromAtom[$medium, FALSE], emptyRelBox, emptyAbsBox];
arg34Space: DisplayExpr ← MakeAtomicDisplayExpr[$arg34Space, MathDB.LookupAtomClass[$space], Convert.RopeFromAtom[$medium, FALSE], emptyRelBox, emptyAbsBox];
comma12: DisplayExpr ← MakeAtomicDisplayExpr[$comma12, MathDB.LookupAtomClass[$italicSym], XRope.FromChar[',] , emptyRelBox, emptyAbsBox];
comma23: DisplayExpr ← MakeAtomicDisplayExpr[$comma23, MathDB.LookupAtomClass[$italicSym], XRope.FromChar[',] , emptyRelBox, emptyAbsBox];
comma34: DisplayExpr ← MakeAtomicDisplayExpr[$comma34, MathDB.LookupAtomClass[$italicSym], XRope.FromChar[',] , emptyRelBox, emptyAbsBox];
newExprs: LIST OF DisplayExpr;
sibling: DisplayExpr ← DisplayExprFromExpr[MathConstructors.MakePlaceHolder[] ];
sibling.parent ← expr.parent;
WITH expr.parent.expression SELECT FROM
c: CompoundDisplayObj => SELECT c.class.name FROM
$nullaryFunction, $unaryFunction, $binaryFunction, $ternaryFunction => {
SELECT c.class.name FROM
$nullaryFunction => {
c.class ← MathDB.LookupCompoundClass[$unaryFunction];
sibling.tag ← $arg1;
newExprs ← LIST[sibling]
};
$unaryFunction => {
c.class ← MathDB.LookupCompoundClass[$binaryFunction];
sibling.tag ← $arg2;
newExprs ← LIST[sibling, arg12Space, comma12]
};
$binaryFunction => {
c.class ← MathDB.LookupCompoundClass[$ternaryFunction];
sibling.tag ← $arg3;
newExprs ← LIST[sibling, arg23Space, comma23]
};
$ternaryFunction => {
c.class ← MathDB.LookupCompoundClass[$quaternaryFunction];
sibling.tag ← $arg4;
newExprs ← LIST[sibling, arg34Space, comma34]
};
ENDCASE => ERROR; -- can't extend quaternaryFunction
IF c.subExprs = NIL THEN c.subExprs ← newExprs ELSE {
p: LIST OF DisplayExpr ← c.subExprs;
WHILE p.rest#NIL DO p ← p.rest ENDLOOP;
p.rest ← newExprs;
};
};
ENDCASE => ERROR noSelection;
ENDCASE => ERROR noSelection;
RETURN[sibling];
};
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;
SELECT c.class.name FROM
$nullaryFunction, $unaryFunction, $binaryFunction, $ternaryFunction => RETURN[ExtendFunctionSelectSibling[expr]]; -- hack
ENDCASE;
*** hack 7/16/87 to handle extra inserted parentheses
IF c.class.name=$paren THEN RETURN[SelectableSibling[expr.parent] ];
*** end hack
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[expr, old, new: DisplayExpr] RETURNS[DisplayExpr] ~ {
effects: Returns a copy of display expression expr with new substituted for old.
caveats: Return value must be reformatted before Paint[]'ing.
Note that new is used (not a copy). Hence if expr = old, then new returned, i.e. a "copy" returned iff old is a proper subexpression of expr.
RETURN[ReplaceN[expr, LIST[[old: old, new: new]]]];
};
ReplaceN: PUBLIC PROC[expr: DisplayExpr, replacements: LIST OF ReplacePair] RETURNS[DisplayExpr] ~ {
effects: Returns a copy of display expression expr with new substituted for old for each pair in replacements [old, new].
If expr = old←i, then new←i i.e. a "copy" returned iff each old←i is a proper subexpression of expr.
Note that replacements can "interfere" with each other; outcome depends on their ordering.
caveats: Return value must be reformatted before Paint[]'ing.
Note that "old" components of replace pairs must be the actual pointers to the exprs we wish to replace, i.e. we only replace that actual expr, and not any other expr which is equivalent but physically distinct from it.
Note that the "new" components of replace pairs are used (not copies). Hence anyone who holds the pointer to a "new" component can e.g. later ask to have it replaced by something.
Note that recursion terminates as soon as we find a match, i.e. if expr = one of the olds (i.e. expr is one of the things to be replaced), we won't examine any subexprs of expr for possible additional matches
FOR l: LIST OF ReplacePair ← replacements, l.rest UNTIL l = NIL DO
IF expr = l.first.old THEN {
parent: DisplayExpr ← l.first.old.parent;
new: DisplayExpr ← l.first.new;
Parenthesize replacement if its op has lower precedence than parent
IF parent #NIL AND MathDB.CompareOps[Class[new], Class[parent] ] = less THEN {
-- 7/10/87 -- turn off op precedence for functional notation parsing
IF FALSE THEN {
parenArgTag: ATOM = $a;
argDisplayExpr: DisplayExpr;
Create skeleton for parenthesized expression (i.e. dummy arg)
parenExpr: DisplayExpr ← DisplayExprFromExpr[MathExpr.MakeCompoundExpr[$paren, LIST[[parenArgTag, MathConstructors.MakePlaceHolder[] ]] ] ];
Grab onto dummy arg
FOR l: LIST OF DisplayExpr ← GetSubExprs[parenExpr], l.rest UNTIL l = NIL DO
IF Tag[l.first] = parenArgTag THEN { argDisplayExpr ← l.first; EXIT };
ENDLOOP;
Stuff actual pointer to new into parenExpr
parenExpr ← Replace[parenExpr, argDisplayExpr, new];
Update pointers
new.tag ← $a; new.parent ← parenExpr;
parenExpr.tag ← l.first.old.tag; -- replacement has same tag (e.g. $integrand, etc.)
parenExpr.parent ← parent; -- replacement has same parent as before
RETURN[parenExpr]
}
ELSE {
new.tag ← l.first.old.tag; -- replacement has same tag (e.g. $integrand, etc.)
new.parent ← parent; -- replacement has same parent as before
RETURN[new]
};
};
ENDLOOP;
WITH expr.expression SELECT FROM
a: AtomDisplayObj => {
RETURN[Copy[expr]];
};
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[ReplaceN[l.first, replacements], 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, copiedEltsPtr: 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[ReplaceN[l.first, replacements], copiedElts];
ENDLOOP;
recursively copy elements - changed 3/4/87
FOR l: LIST OF DisplayExpr ← m.elements, l.rest UNTIL l = NIL DO
IF copiedElts = NIL THEN copiedElts ← copiedEltsPtr ← LIST[ReplaceN[l.first, replacements] ] ELSE copiedEltsPtr ← copiedEltsPtr.rest ← LIST[ReplaceN[l.first, replacements] ];
ENDLOOP;
construct return value now so we can set parent pointers for subexprs
exprCopy ← MakeMatrixDisplayExpr[expr.tag, m.class, 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;
};
Signals & Errors
unable: PUBLIC ERROR[reason: ATOM] = CODE;
noSelection: PUBLIC ERROR = CODE;
exprNotFound: PUBLIC ERROR = CODE;
END.