-- 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).