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