-- ExpressionEvalImpl.mesa
-- last edit July 12, 1984 2:27:58 pm PDT Sturgis
DIRECTORY
Commander USING[CommandProc, Register],
Convert USING[RealFromRope, RopeFromReal],
Dependencies USING[Action, ActionProc, CreateDependencySet, DataItem, DefineAction, DefineDataItem, DependencySet, NoteThatActionModifiesDataItem, NoteThatDataItemIsReadByAction],
Expressions USING[AnalyzedExpression, AnalyzedHCellExpression, AnalyzedHCellIdentifier, AnalyzedHCellIdentifierBody, AnalyzedIdentifier, Expression, HCellExpression, HCellIdentifier, Identifier, IdTableSet, Operator, ParseRope, SyntaxError, Token, TokenText],
IO USING[PutF, rope, STREAM],
Rope USING[ROPE, Cat, Concat, Equal, Find, FromChar, Length, Substr];
ExpressionEvalImpl: PROGRAM IMPORTS Commander, Convert, Dependencies, Expressions, IO, Rope EXPORTS Expressions =
BEGIN OPEN Dependencies, Expressions;
ParseCase: TYPE = {gCell, hCell};
IdTable: TYPE = REF IdTableBody;
IdTableBody: PUBLIC TYPE = RECORD
[
knownIds: IdentifierDescriptor
];
IdentifierDescriptor: TYPE = REF IdentifierDescriptorBody;
IdentifierDescriptorBody: PUBLIC TYPE = RECORD[
name: Rope.ROPE,
val: REAL,
dataItem: DataItem,
next: IdentifierDescriptor];
-- following retained for compatibility
EvaluateExpression: PUBLIC PROCEDURE[exp: Expression] RETURNS[REAL] =
BEGIN -- note: > 0 is used to represent true, < 0 to represent false
-- note: this procedure will remain as non public
IF exp = NIL THEN RETURN [0];
SELECT exp.operator FROM
times =>
BEGIN
a: REAL ← EvaluateExpression[NARROW[exp.data1, Expression]];
b: REAL ← EvaluateExpression[NARROW[exp.data2, Expression]];
RETURN[a*b];
END;
divide =>
BEGIN
a: REAL ← EvaluateExpression[NARROW[exp.data1, Expression]];
b: REAL ← EvaluateExpression[NARROW[exp.data2, Expression]];
RETURN[a/b];
END;
less =>
BEGIN
a: REAL ← EvaluateExpression[NARROW[exp.data1, Expression]];
b: REAL ← EvaluateExpression[NARROW[exp.data2, Expression]];
RETURN[IF a < b THEN 1.0 ELSE -1.0];
END;
lessEqu =>
BEGIN
a: REAL ← EvaluateExpression[NARROW[exp.data1, Expression]];
b: REAL ← EvaluateExpression[NARROW[exp.data2, Expression]];
RETURN[IF a <= b THEN 1.0 ELSE -1.0];
END;
gtr =>
BEGIN
a: REAL ← EvaluateExpression[NARROW[exp.data1, Expression]];
b: REAL ← EvaluateExpression[NARROW[exp.data2, Expression]];
RETURN[IF a > b THEN 1.0 ELSE -1.0];
END;
gtrEqu =>
BEGIN
a: REAL ← EvaluateExpression[NARROW[exp.data1, Expression]];
b: REAL ← EvaluateExpression[NARROW[exp.data2, Expression]];
RETURN[IF a >= b THEN 1.0 ELSE -1.0];
END;
equ =>
BEGIN
a: REAL ← EvaluateExpression[NARROW[exp.data1, Expression]];
b: REAL ← EvaluateExpression[NARROW[exp.data2, Expression]];
RETURN[IF a = b THEN 1.0 ELSE -1.0];
END;
nEqu =>
BEGIN
a: REAL ← EvaluateExpression[NARROW[exp.data1, Expression]];
b: REAL ← EvaluateExpression[NARROW[exp.data2, Expression]];
RETURN[IF a # b THEN 1.0 ELSE -1.0];
END;
not =>
BEGIN
a: REAL ← EvaluateExpression[NARROW[exp.data1, Expression]];
RETURN[-a];
END;
and =>
BEGIN
a: REAL ← EvaluateExpression[NARROW[exp.data1, Expression]];
b: REAL ← EvaluateExpression[NARROW[exp.data2, Expression]];
RETURN[IF a > 0 AND b > 0 THEN 1.0 ELSE -1.0];
END;
or =>
BEGIN
a: REAL ← EvaluateExpression[NARROW[exp.data1, Expression]];
b: REAL ← EvaluateExpression[NARROW[exp.data2, Expression]];
RETURN[IF a > 0 OR b > 0 THEN 1.0 ELSE -1.0];
END;
token =>
BEGIN
tkn: Token ← NARROW[exp.data1];
SELECT tkn.operator FROM
id =>
WITH exp.data2 SELECT FROM
gCellId: Identifier => RETURN[GetIdValue[gCellId]];
hCellId: AnalyzedHCellIdentifier => RETURN[GetHCellIdValue[hCellId]];
ENDCASE => ERROR;
number =>
BEGIN
item: Rope.ROPE ← NARROW[tkn.data];
position: INT;
IF (position ← Rope.Find[item, "."]) = -1 THEN item ← Rope.Concat[item, ".0"]
ELSE IF position < Rope.Length[item] THEN item ← Rope.Concat[item, "0"];
RETURN[Convert.RealFromRope[item]]
END;
ENDCASE => ERROR;
END;
unaryPlus =>
BEGIN
a: REAL ← EvaluateExpression[NARROW[exp.data1, Expression]];
RETURN[a];
END;
unaryMinus =>
BEGIN
a: REAL ← EvaluateExpression[NARROW[exp.data1, Expression]];
RETURN[-a];
END;
binaryPlus =>
BEGIN
a: REAL ← EvaluateExpression[NARROW[exp.data1, Expression]];
b: REAL ← EvaluateExpression[NARROW[exp.data2, Expression]];
RETURN[a+b];
END;
binaryMinus =>
BEGIN
a: REAL ← EvaluateExpression[NARROW[exp.data1, Expression]];
b: REAL ← EvaluateExpression[NARROW[exp.data2, Expression]];
RETURN[a-b];
END;
conditional =>
BEGIN
a: REAL ← EvaluateExpression[NARROW[exp.data1, Expression]];
IF a > 0 THEN RETURN[EvaluateExpression[NARROW[exp.data2, Expression]]]
ELSE RETURN[EvaluateExpression[NARROW[exp.data3, Expression]]];
END;
parenExp =>
BEGIN
a: REAL ← EvaluateExpression[NARROW[exp.data1, Expression]];
RETURN[a];
END;
topExp =>
BEGIN
a: REAL ← EvaluateExpression[NARROW[exp.data2, Expression]];
RETURN[a];
END;
ENDCASE => ERROR;
END;
AssignVal: PUBLIC PROCEDURE[exp: Expression, val: REAL] =
BEGIN
SELECT exp.operator FROM
topExp =>
BEGIN
idExp: Expression ← NARROW[exp.data1];
id: Identifier;
IF idExp = NIL THEN RETURN;
id ← NARROW[idExp.data2];
SetIdValue[id, val];
END;
ENDCASE => ERROR;
END;
RecognizeIdentifiers: PUBLIC PROCEDURE[exp: Expression, ids: IdTable, dSet: DependencySet, eval: Action, assignVal: Action] RETURNS[Expression] =
BEGIN
idSet: IdTableSet = [self: NIL, n: NIL, e: NIL, s: NIL, w: NIL, page: NIL, global: ids];
IF exp = NIL THEN RETURN[exp];
SELECT exp.operator FROM
topExp =>
BEGIN
[] ← AnalyzeExpIdentifier[NARROW[exp.data1], idSet, dSet, assignVal];
[] ← AnalyzeExpression[NARROW[exp.data2], idSet, dSet, eval];
RETURN[exp];
END;
ENDCASE =>
RETURN[AnalyzeExpression[exp, idSet, dSet, eval]];
END;
AnalyzeExpIdentifier: PROCEDURE[exp: Expression, idSet: IdTableSet, dSet: DependencySet, assignAction: Action] RETURNS[Expression] =
BEGIN
IF exp = NIL THEN RETURN[exp];
SELECT exp.operator FROM
token =>
BEGIN
tkn: Token ← NARROW[exp.data1];
SELECT tkn.operator FROM
id =>
BEGIN
id: Identifier ← NARROW[tkn.data];
id.desc ← FindIdInTables[id, idSet, dSet];
exp.data2 ← id;
NoteThatActionModifiesId[assignAction, id];
END;
ENDCASE => ERROR;
END;
ENDCASE => ERROR;
RETURN[exp];
END;
-- expression section
EvaluateAnalyzedExpression: PUBLIC PROCEDURE[exp: AnalyzedExpression] RETURNS[REAL] =
{RETURN[EvaluateExpression[exp.exp]]};
AnalyzeExpression: PUBLIC PROCEDURE[exp: Expression, ids: IdTableSet, dSet: DependencySet, eval: Action] RETURNS[AnalyzedExpression] =
{RETURN[[AnalyzeBothKindsOfExpression[exp, ids, NIL, NIL, NIL, dSet, eval, gCell]]]};
AnalyzeBothKindsOfExpression: PROCEDURE[exp: Expression, ids: IdTableSet, hCellIdSet: HCellIdSet, pageName, arrayName: Rope.ROPE, dSet: DependencySet, eval: Action, parseCase: ParseCase] RETURNS[Expression] =
BEGIN
IF exp = NIL THEN RETURN[exp];
SELECT exp.operator FROM
conditional =>
BEGIN
[] ← AnalyzeBothKindsOfExpression[NARROW[exp.data1], ids, hCellIdSet, pageName, arrayName, dSet, eval, parseCase];
[] ← AnalyzeBothKindsOfExpression[NARROW[exp.data2], ids, hCellIdSet, pageName, arrayName, dSet, eval, parseCase];
[] ← AnalyzeBothKindsOfExpression[NARROW[exp.data3], ids, hCellIdSet, pageName, arrayName, dSet, eval, parseCase];
END;
times, divide, less, lessEqu, gtr, gtrEqu, equ, nEqu, and, or, binaryPlus, binaryMinus =>
BEGIN
[] ← AnalyzeBothKindsOfExpression[NARROW[exp.data1], ids, hCellIdSet, pageName, arrayName, dSet, eval, parseCase];
[] ← AnalyzeBothKindsOfExpression[NARROW[exp.data2], ids, hCellIdSet, pageName, arrayName, dSet, eval, parseCase];
END;
not, unaryPlus, unaryMinus, parenExp =>
BEGIN
[] ← AnalyzeBothKindsOfExpression[NARROW[exp.data1], ids, hCellIdSet, pageName, arrayName, dSet, eval, parseCase];
END;
token =>
BEGIN
tkn: Token ← NARROW[exp.data1];
SELECT tkn.operator FROM
id =>
SELECT parseCase FROM
gCell =>
BEGIN
id: Identifier ← NARROW[tkn.data];
id.desc ← FindIdInTables[id, ids, dSet];
exp.data2 ← id;
NoteThatIdIsReadByAction[id, eval];
END;
hCell =>
BEGIN
id: Rope.ROPE ← NARROW[tkn.data];
analyzedId: AnalyzedHCellIdentifier ← LookUpHCellId[hCellIdSet, pageName, arrayName, id, dSet];
exp.data2 ← analyzedId;
Dependencies.NoteThatDataItemIsReadByAction[analyzedId.dataItem, eval];
END;
ENDCASE => ERROR;
number => NULL;
ENDCASE => ERROR;
END;
topExp =>
BEGIN
[] ← AnalyzeBothKindsOfExpression[NARROW[exp.data2], ids, hCellIdSet, pageName, arrayName, dSet, eval, parseCase];
exp ← NARROW[exp.data2]; -- force it to be a pure expression
END;
ENDCASE => ERROR;
RETURN[exp];
END;
AnalyzeIdentifier: PUBLIC PROCEDURE[id: Identifier, idSet: IdTableSet, dSet: DependencySet, assign: Action] RETURNS[AnalyzedIdentifier] =
BEGIN
id.desc ← FindIdInTables[id, idSet, dSet];
NoteThatActionModifiesId[assign, id];
RETURN[[id]];
END;
SetAnalyzedIdentifier: PUBLIC PROCEDURE[id: AnalyzedIdentifier, val: REAL] =
BEGIN
IF id.id # NIL AND id.id.desc # NIL THEN
BEGIN --crock
desc: IdentifierDescriptor ← id.id.desc;
desc.val ← val;
END;
END;
-- identifier section
SetIdValue: PUBLIC PROCEDURE[id: Identifier, val: REAL] =
BEGIN
desc: IdentifierDescriptor ← id.desc;
desc.val ← val
END;
GetIdValue: PROCEDURE[id: Identifier] RETURNS[REAL] =
BEGIN
desc: IdentifierDescriptor ← id.desc;
RETURN[desc.val]
END;
FindIdInTables: PROCEDURE[id: Identifier, idSet: IdTableSet, dSet: DependencySet] RETURNS[IdentifierDescriptor] =
BEGIN
table: IdTable;
table ← idSet[id.set];
IF table = NIL THEN table ← idSet[self];
IF table = NIL THEN table ← idSet[global];
IF table = NIL THEN ERROR;
RETURN[FindIdInOneTable[id.name, table, dSet]];
END;
FindIdInOneTable: PROCEDURE[name: Rope.ROPE, ids: IdTable, dSet: DependencySet] RETURNS[IdentifierDescriptor] =
BEGIN
id: IdentifierDescriptor;
FOR id ← ids.knownIds, id.next WHILE id # NIL DO
IF Rope.Equal[id.name, name] THEN EXIT;
ENDLOOP;
IF id # NIL THEN RETURN[id];
id ← NEW[IdentifierDescriptorBody ← [name, 0, Dependencies.DefineDataItem[dSet], ids.knownIds]];
ids.knownIds ← id;
RETURN[id];
END;
NoteThatIdIsReadByAction: PROCEDURE[id: Identifier, action: Dependencies.Action] =
BEGIN -- crock that I have to pull out the desc in a separate step
desc: IdentifierDescriptor ← id.desc;
Dependencies.NoteThatDataItemIsReadByAction[desc.dataItem, action]
END;
NoteThatActionModifiesId: PROCEDURE[action: Dependencies.Action, id: Identifier] =
BEGIN -- crock that I have to pull out the desc in a separate step
desc: IdentifierDescriptor ← id.desc;
Dependencies.NoteThatActionModifiesDataItem[action, desc.dataItem]
END;
MakeIdTable: PUBLIC PROCEDURE RETURNS[IdTable] =
{RETURN[NEW[IdTableBody←[NIL]]]};
-- new code for HCell format identifiers
-- for now: July 12, 1984 3:21:58 pm PDT: will just chain the identifiers together. When we get too many, I will have to go to a hash table mechanism
HCellIdSet: TYPE = REF HCellIdSetBody;
HCellIdSetBody: PUBLIC TYPE = RECORD[first: AnalyzedHCellIdentifier];
PrivateHCellId: TYPE = REF PrivateHCellIdBody;
PrivateHCellIdBody: TYPE = RECORD[
fullName: Rope.ROPE,
value: REAL,
next: AnalyzedHCellIdentifier];
MakeHCellIdSet: PUBLIC PROCEDURE RETURNS[HCellIdSet] =
{RETURN[NEW[HCellIdSetBody←[NIL]]]};
AnalyzeHCellExpression: PUBLIC PROCEDURE[exp: HCellExpression, idSet: HCellIdSet, pageName, arrayName: Rope.ROPE, dSet: Dependencies.DependencySet, evalAction: Dependencies.Action] RETURNS[AnalyzedHCellExpression] =
BEGIN
RETURN[[AnalyzeBothKindsOfExpression[exp, [NIL, NIL, NIL, NIL, NIL, NIL, NIL], idSet, pageName, arrayName, dSet, evalAction, hCell]]];
END;
AnalyzeHCellIdentifier: PUBLIC PROCEDURE[id: HCellIdentifier, idSet: HCellIdSet, pageName, arrayName: Rope.ROPE, dSet: Dependencies.DependencySet] RETURNS[AnalyzedHCellIdentifier] =
{RETURN[LookUpHCellId[idSet, pageName, arrayName, id, dSet]]};
EvaluateAnalyzedHCellExpression: PUBLIC PROCEDURE[exp: AnalyzedHCellExpression] RETURNS[REAL] =
{RETURN[EvaluateExpression[exp.exp]]};
SetAnalyzedHCellIdentifier: PUBLIC PROCEDURE[analId: AnalyzedHCellIdentifier, val: REAL] =
BEGIN
private: PrivateHCellId ← NARROW[analId.privateData];
private.value ← val;
END;
GetHCellIdValue: PROCEDURE[analId: AnalyzedHCellIdentifier] RETURNS[REAL] =
BEGIN
private: PrivateHCellId ← NARROW[analId.privateData];
RETURN[private.value];
END;
LookUpHCellId: PROCEDURE[hCellIdSet: HCellIdSet, pageName, arrayName: Rope.ROPE, id: Rope.ROPE, dSet: Dependencies.DependencySet] RETURNS[AnalyzedHCellIdentifier] =
BEGIN
fullName: Rope.ROPE;
nDots: CARDINAL ← 0; -- initial value
pos1: INT ← 0; -- initial
analId: AnalyzedHCellIdentifier;
next: AnalyzedHCellIdentifier;
private: PrivateHCellId;
WHILE (pos1 ← Rope.Find[id, Rope.FromChar['.], pos1]+1) # 0 DO
nDots ← nDots+1;
IF nDots > 2 THEN ERROR;
ENDLOOP;
SELECT nDots FROM
0 => fullName ← Rope.Cat[pageName, Rope.FromChar['.], arrayName, Rope.FromChar['.], id];
1 => fullName ← Rope.Cat[pageName, Rope.FromChar['.], id];
2 => fullName ← id;
ENDCASE => ERROR;
FOR analId ← hCellIdSet.first, next WHILE analId # NIL DO
private ← NARROW[analId.privateData];
next ← private.next;
IF Rope.Equal[private.fullName, fullName] THEN RETURN[analId];
ENDLOOP;
private ← NEW[PrivateHCellIdBody ← [fullName, 0.0, hCellIdSet.first]];
analId ← NEW[AnalyzedHCellIdentifierBody← [id, Dependencies.DefineDataItem[dSet], private]];
hCellIdSet.first ← analId;
RETURN[analId];
END;
-- test section
Test: Commander.CommandProc = TRUSTED
BEGIN
rope: Rope.ROPE;
expression: Expression;
val: REAL;
dummyAct: ActionProc;
dSet: DependencySet ← CreateDependencySet[];
dummyEvalAction: Action ← DefineAction[dSet, dummyAct, NIL];
dummyAssignAction: Action ← DefineAction[dSet, dummyAct, NIL];
ids: IdTable ← MakeIdTable[];
execOut: IO.STREAM ← cmd.out;
execIn: IO.STREAM ← cmd.in;
BEGIN
IO.PutF[execOut, "this is the supplied commandLine: %g\n", IO.rope[cmd.commandLine]];
rope ← cmd.commandLine;
expression ← ParseRope[rope
! SyntaxError =>
BEGIN
IO.PutF[execOut, "syntax error at @@@@, unexpected token = "];
IO.PutF[execOut, "%g", IO.rope[TokenText[unexpectedToken]]];
IO.PutF[execOut, ", rope = %g@@@@%g\n",
IO.rope[Rope.Substr[rope, 0, unexpectedToken.charIndex]],
IO.rope[Rope.Substr[rope, unexpectedToken.charIndex,
Rope.Length[rope]-unexpectedToken.charIndex]]];
GOTO syntaxError;
END];
expression ← RecognizeIdentifiers[expression, ids, dSet, dummyEvalAction, dummyAssignAction];
val ← EvaluateExpression[expression];
IF expression.operator = topExp AND expression.data1 # NIL THEN
BEGIN
exp1: Expression ← NARROW[expression.data1];
IF exp1.operator # token THEN ERROR;
SetIdValue[NARROW[exp1.data2], val];
END;
IO.PutF[execOut, ", val = %g\n", IO.rope[Convert.RopeFromReal[val]]];
EXITS
syntaxError => RETURN;
END;
END;
-- module main program
Commander.Register[key: "TestEvalExp", proc: Test, doc: ""];
END..
-- July 28, 1982 11:12 am: Sturgis, started ExpressionEvalImpl.mesa
-- RTE: July 30, 1982 10:54 am: recognizeIdentifiers needs to handle NIL exp, due to possible such call from topExp.
-- RTE: T: RecognizeId and eval must pass through a token exp before seeing ids or numbers.
-- RTE: July 30, 1982 11:29 am: must call parse with a template (defaultReal).
-- remark: July 30, 1982 11:36 am: add, mul, gtr work with numerical expressions.
-- remark: July 30, 1982 11:56 am: identifiers work in assignments and in subsequent expressions.
-- change: August 1, 1982 4:36 pm: add dataitems and actions and note modifications. add AssignVal.
-- RTE: August 3, 1982 10:41 am: allow EvaluateExpression to accept NIL exp, and return 0. Needed to handle new special top expression forms.
-- August 7, 1982 5:29 pm: convert to NCGlobal. (ie, now have an idTable) fix conditional problem in recognize evaluated identifiers.
-- September 26, 1982 12:31 pm: make changes to support id new idformats, including for example n.name. Also, export idhandles and implement something like analyzepureexp, analyzeid, evaluatepureexp, assigntoid.
RET: September 26, 1982 4:44 pm: lots of trouble due to inconsistency in changing exp.data2 from IdentifierDescriptor to Identifier, in the case of an identifier expression. Also, BugBane went off the deep end when a NARROW was tried from what was acutally an Identifier to IdenfifierDescriptor. Also, COPILOT was also unable to display the REF any.
RTE: October 1, 1982 4:29 pm: analyzeExpression was presented with the result of a parse call, which currently produces a TopLevelExpression.
CTE: February 10, 1983 5:30 pm: convert to 4.0
Change: June 22, 1984 1:59:13 pm PDTT: convert to 5.2
CTE: June 22, 1984 5:13:50 pm PDT: the new IOConvertImpl.RealFromRope insists on a "." in the number, as well as at least one trailing digit.
Change: July 12, 1984 2:28:06 pm PDT: add HCell format identifiers (i.e. three part names).