IPExecImpl.mesa
Last edited by:
Doug Wyatt, July 7, 1983 3:36 pm
DIRECTORY
Atom USING [MakeAtom],
IO USING [PutChar, PutRope, STREAM],
IPBasic USING [Any, ComposedOp, Identifier, Index, Integer, Marker, nullIndex, nullMarker, Number, Operator, OperatorRep, PrimitiveOp, SaveType, Vec, Vector, VectorShape],
IPControl USING [],
IPConvert USING [IntToNumber],
IPEncoding USING [GetInt, GetToken, GetReal, GetRope, GetStartingIndex, OpenReader, Reader, Token],
IPErrors USING [AppearanceError, AppearanceErrorType, AppearanceWarning, AppearanceWarningType, Bug, Error, MasterError, MasterErrorType, MasterWarning, MasterWarningType, RopeFromError],
IPExec USING [],
IPImagerOps USING [Init, Restore, Save],
IPLimits USING [topFrameSize],
IPPrimitive USING [Apply],
IPStack USING [Count, Init, PopInteger, PopMark, PopToMark, PushIdentifier, PushInteger, PushNumber, PushReal, PushVector],
IPState USING [Context, ContextRep, nullContextRep, Skeleton, SkeletonRep, State, StateRep],
IPVector USING [CopyVec, NewVec, VectorToVec],
IPWritten USING [RopeFromOp],
Rope USING [ROPE, Translate];
IPExecImpl: CEDAR PROGRAM
IMPORTS Atom, IO, IPConvert, IPEncoding, IPErrors, IPImagerOps, IPPrimitive, IPStack, IPVector, IPWritten, Rope
EXPORTS IPExec, IPControl, IPBasic
= BEGIN OPEN IPEncoding, IPState, IPBasic;
ROPE: TYPE = Rope.ROPE;
State: TYPE = IPState.State;
StateRep: PUBLIC TYPE = IPState.StateRep; -- export to IPBasic
Master: TYPE = IPEncoding.Reader;
MarkRecovery: ERROR = CODE;
Frame: PROC[self: State] RETURNS[Vec] = {
f: Vec = self.context.frame;
RETURN[IF f#NIL THEN IPVector.CopyVec[f]
ELSE self.context.initialFrame]
};
ReadonlyFrame: PROC[context: Context] RETURNS[Vec] = INLINE { RETURN[
IF context.frame#NIL THEN context.frame
ELSE context.initialFrame] };
MutableFrame: PROC[context: Context] RETURNS[Vec] = INLINE { RETURN[
IF context.frame#NIL THEN context.frame
ELSE context.frame ← IPVector.CopyVec[context.initialFrame]] };
FGet: PUBLIC PROC[self: State, n: Integer] RETURNS[Any] = {
frame: Vec = ReadonlyFrame[self.context];
shape: VectorShape = frame.shape;
IF n IN[shape.l..shape.l+shape.n) THEN RETURN[frame[n-shape.l]]
ELSE ERROR IPErrors.MasterError[BoundsFault];
};
FSet: PUBLIC PROC[self: State, x: Any, n: Integer] = {
frame: Vec = MutableFrame[self.context];
shape: VectorShape = frame.shape;
IF n IN[shape.l..shape.l+shape.n) THEN frame[n-shape.l] ← x
ELSE ERROR IPErrors.MasterError[BoundsFault];
};
MakeCO: PUBLIC PROC[self: State, f: Vector] RETURNS[Operator] = {
frame: Vec = IPVector.VectorToVec[f];
index: Index = SkipBody[self];
RETURN[NEW[OperatorRep[composed] ← [composed[start: index, initialFrame: frame]]]];
};
MakeSimpleCO: PUBLIC PROC[self: State] RETURNS[Operator] = {
frame: Vec = Frame[self]; -- copy the current frame
index: Index = SkipBody[self];
RETURN[NEW[OperatorRep[composed] ← [composed[start: index, initialFrame: frame]]]];
};
Do: PUBLIC PROC[self: State, o: Operator, sv: SaveType ← nil] = {
WITH o SELECT FROM
op: ComposedOp => {
caller: Context = self.context;
savedPC: Index = self.pc;
self.pc ← op.start; -- set the pc to the beginning of the operator's body
[] ← CallBody[self, op.initialFrame, sv ! MarkRecovery => self.pc ← savedPC];
self.pc ← savedPC; -- restore caller's pc
IF self.context#caller THEN ERROR IPErrors.Bug;
};
ENDCASE => ERROR IPErrors.MasterError[Unimplemented];
};
DoBody: PUBLIC PROC[self: State, f: Vector, sv: SaveType ← nil] = {
frame: Vec = IPVector.VectorToVec[f];
[] ← CallBody[self, frame, sv];
};
DoSimpleBody: PUBLIC PROC[self: State, sv: SaveType ← nil] = {
frame: Vec = Frame[self];
[] ← CallBody[self, frame, sv];
};
If: PUBLIC PROC[self: State, b: BOOL] = {
IF b THEN DoSimpleBody[self] ELSE [] ← SkipBody[self];
};
IfCopy: PUBLIC PROC[self: State, testCopy: Operator] = {
ERROR IPErrors.MasterError[Unimplemented];
};
CopyNumberAndName: PUBLIC PROC[self: State]
RETURNS[copyNumber: Number, copyName: Identifier] = {
ERROR;
};
Here is an excerpt from the Interpress 2.0 standard describing error recovery:
2.4.1 Errors
If the value named ai doesn't have the type Ti, there is a master error; note that during execution of the master there are always marks on the stack which will prevent underflow (see ¶Ÿ3.1). The definition may specify further conditions which must be satisfied; if the current state does not satisfy these conditions there is also a master error. In case of a master error, unless the operator definition specifies otherwise, there is a mark recovery.
A mark recovery also occurs whenever any attempt is made to pop a mark except by a matching UNMARK or COUNT (¶Ÿ2.4.6); when this happens, the mark remains on the stack.
On a mark recovery, (a) the stack is popped until a mark is on top; (b) if the context that placed the mark on the stack no longer exists, the mark is popped and there is another mark recovery; otherwise, composed operators in execution are exited until the one which placed the mark is executing; and (c) literals are skipped in this operator until an UNMARK0 literal is found. The UNMARK0 found in step (c) is executed, thus popping the mark from the stack, and execution proceeds from this point. Note that the rules for executing a master given in ¶Ÿ3.1 insure that step (c) will eventually succeed. In addition, master errors are logged as specified in ¶Ÿ5.3.
Marks thus serve two major purposes: to protect the stack from damage by an operator, and to indicate possible error recovery points.
End of excerpt.
NewMark: PROC[self: State] RETURNS[Marker] = {
last: Marker = self.lastMark;
IF last<LAST[Marker] THEN RETURN[self.lastMark ← last+1]
ELSE ERROR IPErrors.Bug;
};
MarkIsOnCallStack: PROC[self: State, mark: Marker] RETURNS[BOOL] = {
FOR context: Context ← self.context, context.caller UNTIL context=NIL DO
IF context.mark=mark THEN RETURN[TRUE];
ENDLOOP;
RETURN[FALSE];
};
PopToMarkOnCallStack: PROC[self: State] RETURNS[Marker] = {
DO
marker: Marker = IPStack.PopToMark[self];
IF MarkIsOnCallStack[self, marker] THEN RETURN[marker]
ELSE IPStack.PopMark[self];
ENDLOOP;
};
PushContext: PROC[self: State, initialFrame: Vec] = {
context: Context = NEW[ContextRep ← nullContextRep];
context.mark ← NewMark[self]; -- assign a unique mark
context.initialFrame ← initialFrame;
context.caller ← self.context; self.context ← context; -- link to caller
};
PopContext: PROC[self: State] RETURNS[finalFrame: Vec] = {
context: Context = self.context;
self.context ← context.caller; -- return to caller's context
RETURN[ReadonlyFrame[context]];
};
Fetch: PROC[self: State] RETURNS[Token] = {
DO
token: Token = self.master.GetToken[self.pc]; -- fetch the next token
IF token.type=comment THEN { -- ignore -- }
ELSE { self.pc ← token.next; RETURN[token] };
ENDLOOP;
};
CallBody: PROC[self: State, initialFrame: Vec, sv: SaveType]
RETURNS[finalFrame: Vec] = {
master: Master = self.master;
error: BOOLFALSE;
[] ← BeginBody[self];
PushContext[self, initialFrame];
{ ENABLE UNWIND => [] ← PopContext[self];
IF sv#nil THEN IPImagerOps.Save[self, sv=saveAll];
DO
token: Token = master.GetToken[self.pc]; -- fetch the next token
self.pc ← token.next;
IF token.op=endBody THEN EXIT
ELSE {
DoToken[self, token !
IPErrors.MasterError => { LogError[self, [Me[type]], token];
self.pc ← token.index; GOTO Recover };
IPErrors.MasterWarning => { LogError[self, [Mw[type]], token]; RESUME };
IPErrors.AppearanceError => { LogError[self, [Ae[type]], token]; RESUME };
IPErrors.AppearanceWarning => { LogError[self, [Aw[type]], token]; RESUME };
MarkRecovery => GOTO Recover;
];
EXITS
Recover => {
marker: Marker = PopToMarkOnCallStack[self];
IF marker=self.context.mark THEN {
IF SkipToUnmark0OrEndOfBody[self] THEN { -- press on -- }
ELSE GOTO Exit; -- marked by this context, but unmark0 not found
}
ELSE { SkipToEndOfBody[self]; GOTO Exit }; -- not marked by this context
};
};
REPEAT Exit => error ← TRUE;
ENDLOOP;
IF sv#nil THEN IPImagerOps.Restore[self];
};
finalFrame ← PopContext[self];
IF error THEN ERROR MarkRecovery;
};
DoToken: PROC[self: State, token: Token] = {
SELECT token.type FROM
op => IPPrimitive.Apply[self, token.op];
number => IPStack.PushNumber[self, IPConvert.IntToNumber[token.number]];
int => IPStack.PushNumber[self, IPConvert.IntToNumber[self.master.GetInt[token]]];
rational, real => IPStack.PushReal[self, self.master.GetReal[token]];
identifier => IPStack.PushIdentifier[self, MakeID[self.master.GetRope[token]]];
string, largeVec => IPStack.PushVector[self, self.master.GetRope[token]];
comment => { };
ENDCASE => ERROR IPErrors.Bug; -- unexpected token type
};
MakeID: PUBLIC PROC[rope: ROPE] RETURNS[Identifier] = {
LowerCase: PROC[c: CHAR] RETURNS[CHAR] = {
RETURN[IF c IN['A..'Z] THEN (c-'A)+'a ELSE c] };
RETURN[Atom.MakeAtom[Rope.Translate[base: rope, translator: LowerCase]]];
};
SetErrorLog: PUBLIC PROC[self: State, log: IO.STREAM] = {
self.log ← log;
};
LogError: PROC[self: State, error: IPErrors.Error, token: Token] = {
log: IO.STREAM = self.log;
rope: Rope.ROPE = IPErrors.RopeFromError[error];
IF log#NIL THEN {
log.PutRope[rope];
IF token.op#nil THEN {
log.PutRope[" -- "];
log.PutRope[IPWritten.RopeFromOp[token.op]];
};
log.PutChar['\n];
};
};
BeginBody: PROC[self: State] RETURNS[Index] = {
token: Token = Fetch[self];
IF token.op=beginBody THEN RETURN[token.index]
ELSE { self.pc ← token.index; ERROR IPErrors.MasterError[MissingBody] };
};
SkipBody: PROC[self: State] RETURNS[Index] = {
index: Index = BeginBody[self];
SkipToEndOfBody[self];
RETURN[index];
};
SkipToEndOfBody: PROC[self: State] = {
DO
token: Token = Fetch[self];
SELECT token.op FROM
beginBody => SkipToEndOfBody[self];
endBody => EXIT;
ENDCASE;
ENDLOOP;
};
SkipToUnmark0OrEndOfBody: PROC[self: State] RETURNS[BOOL] = {
DO
token: Token = Fetch[self];
SELECT token.op FROM
unmark0 => { self.pc ← token.index; RETURN[TRUE] };
beginBody => SkipToEndOfBody[self];
endBody => EXIT;
ENDCASE;
ENDLOOP;
RETURN[FALSE];
};
initialTopFrame: Vec = IPVector.NewVec[[l: 0, n: IPLimits.topFrameSize]];
OpenMaster: PUBLIC PROC[name: ROPE] RETURNS[State] = {
master: Master = OpenReader[name];
self: State = NEW[StateRep ← [master: NIL, skeleton: NIL, pc: 0,
stack: NIL, topFrame: NIL, context: NIL, lastMark: nullMarker, imager: NIL]];
self.master ← master;
self.pc ← master.GetStartingIndex[];
IPStack.Init[self, 1000];
IPImagerOps.Init[self];
self.skeleton ← GetSkeleton[self];
IF self.skeleton.size<2 THEN ERROR IPErrors.MasterError[MalformedSkeleton];
self.topFrame ← DoPreamble[self, initialTopFrame];
RETURN[self];
};
PageCount: PUBLIC PROC[self: State] RETURNS[NAT] = {
RETURN[self.skeleton.size-1];
};
DoPreamble: PROC[self: State, frame: Vec] RETURNS[Vec] = {
self.pc ← self.skeleton[0].body; -- beginning of the preamble
RETURN[CallBody[self, frame, saveAll]];
};
DoPage: PUBLIC PROC[self: State, n: NAT] = {
self.pc ← self.skeleton[n].body; -- beginning of the page body
[] ← CallBody[self, self.topFrame, saveAll];
};
GetSkeleton: PROC[self: State] RETURNS[Skeleton] = {
token: Token = Fetch[self];
SELECT token.op FROM
beginBody => {
IPStack.PushInteger[self, token.index]; -- instructions body
SkipToEndOfBody[self];
IF Fetch[self].op=beginBlock THEN GetBlock[self]
ELSE ERROR IPErrors.MasterError[MalformedSkeleton];
};
beginBlock => {
IPStack.PushInteger[self, nullIndex]; -- no instructions
GetBlock[self];
};
ENDCASE => ERROR IPErrors.MasterError[MalformedSkeleton];
{
count: NAT = IPStack.Count[self];
size: NAT = count/2;
skeleton: Skeleton = NEW[SkeletonRep[size]];
FOR i: NAT DECREASING IN[0..size) DO
body: Index = IPStack.PopInteger[self];
instructions: Index = IPStack.PopInteger[self];
skeleton[i] ← [instructions: instructions, body: body];
ENDLOOP;
RETURN[skeleton];
};
};
GetBlock: PROC[self: State] = {
IPStack.PushInteger[self, SkipBody[self]]; -- preamble
DO token: Token = Fetch[self];
SELECT token.op FROM
endBlock => EXIT;
pageInstructions => {
IPStack.PushInteger[self, SkipBody[self]]; -- page instructions body
IPStack.PushInteger[self, SkipBody[self]]; -- page body
};
beginBody => {
IPStack.PushInteger[self, nullIndex]; -- no page instructions
IPStack.PushInteger[self, token.index]; -- page body
SkipToEndOfBody[self];
};
ENDCASE => ERROR IPErrors.MasterError[MalformedSkeleton];
ENDLOOP;
};
END.