IPExecImpl.mesa
Copyright © 1984, 1985, 1986 by Xerox Corporation. All rights reserved.
Michael Plass, February 17, 1986 9:03:24 am PST
Doug Wyatt, October 13, 1986 5:35:40 pm PDT
DIRECTORY
FS,
Imager USING [Context, DoSave, DoSaveAll, ShowText],
ImagerFont USING [MapText, XStringProc],
Interpress,
IO,
IPInterpreter,
IPMaster USING [Block, Body, BodyRep, BYTE, GetHeader, GetSkeleton, GetToken, IntFromSequenceData, Node, NodeRep, RealFromSequenceData, SequenceType, Skeleton, SkipBytes, SkipToEndOfBody, Token],
RefText USING [ReserveChars],
RopeFile,
Rope;
IPExecImpl: CEDAR PROGRAM
IMPORTS FS, IO, Imager, ImagerFont, IPInterpreter, IPMaster, RefText, Rope, RopeFile
EXPORTS Interpress, IPInterpreter
~ BEGIN OPEN IPInterpreter, IPMaster;
ROPE: TYPE ~ Rope.ROPE;
STREAM: TYPE ~ IO.STREAM;
Token: TYPE ~ IPMaster.Token;
Body: TYPE ~ IPMaster.Body;
Block: TYPE ~ IPMaster.Block;
Node: TYPE ~ IPMaster.Node;
NodeRep: TYPE ~ IPMaster.NodeRep;
BYTE: TYPE ~ IPMaster.BYTE;
MarkRecovery: PUBLIC ERROR ~ CODE;
Bug: PUBLIC ERROR ~ CODE;
contextFreeCountMax: INT ← 8;
AllocContext: PROC [self: Ref] RETURNS [context: Context] ~ {
IF self.contextFreeCount>0 THEN {
context ← self.contextFree;
self.contextFree ← context.caller;
self.contextFreeCount ← self.contextFreeCount-1;
}
ELSE context ← NEW [ContextRep];
};
FreeContext: PROC [self: Ref, context: Context] ~ {
IF self.contextFreeCount<contextFreeCountMax THEN {
context.caller ← self.contextFree;
context.initialFrame ← NIL;
context.frame ← NIL;
context.env ← NIL;
self.contextFree ← context;
self.contextFreeCount ← self.contextFreeCount+1;
};
};
Call: PUBLIC PROC[self: Ref, action: PROC, frame: Vector, env: Vector] ~ {
context: Context ~ AllocContext[self];
context^ ← [caller: self.context, marker: self.lastMarker+1,
token: [], initialFrame: frame, frame: NIL, env: env];
self.lastMarker ← context.marker;
self.context ← context;
action[! UNWIND => { self.context ← context.caller; FreeContext[self, context] }];
IF self.context=context THEN { self.context ← context.caller; FreeContext[self, context] }
ELSE ERROR Bug;
};
Frame: PUBLIC PROC[self: Ref] RETURNS[Vector] ~ {
context: Context ~ self.context;
IF context.frame=NIL THEN RETURN[context.initialFrame]
ELSE RETURN[VectorFromArray[context.frame]];
};
FGet: PUBLIC PROC[self: Ref, i: Cardinal] RETURNS[Any] ~ {
context: Context ~ self.context;
IF context.frame=NIL THEN RETURN[Get[context.initialFrame, i]]
ELSE { array: Array ~ context.frame; RETURN[array[i-array.lowerBound]] };
};
FSet: PUBLIC PROC[self: Ref, x: Any, i: Cardinal] ~ {
context: Context ~ self.context;
IF context.frame=NIL THEN context.frame ← ArrayFromVector[context.initialFrame];
{ array: Array ~ context.frame; array[i-array.lowerBound] ← x };
};
Env: PUBLIC PROC[self: Ref] RETURNS[Vector] ~ {
context: Context ~ self.context;
RETURN[context.env];
};
DoSave: PUBLIC PROC[self: Ref, action: PROC] ~ {
Someday, this might worry about pools other than the Imager pool
Imager.DoSave[self.imager, action];
};
DoSaveAll: PUBLIC PROC[self: Ref, action: PROC] ~ {
Someday, this might worry about pools other than the Imager pool
Imager.DoSaveAll[self.imager, action];
};
Run: TYPE ~ RECORD[start, len: INT];
RunList: TYPE ~ LIST OF Run;
DoString: PROC [self: Ref, text: REF TEXT] ~ {
string: ImagerFont.XStringProc ~ { ImagerFont.MapText[text: text, charAction: charAction] };
PushVector[self, VectorFromString[string]];
};
DoIdentifier: PROC [self: Ref, text: REF TEXT] ~ {
len: NAT ~ text.length;
warn: BOOLFALSE;
FOR i: NAT IN[0..len) DO
char: CHAR ~ text[i];
SELECT char FROM
IN['a..'z], IN['A..'Z] => NULL;
IN['0..'9], '- => IF i=0 THEN warn ← TRUE;
ENDCASE => warn ← TRUE;
ENDLOOP;
IF len=0 THEN warn ← TRUE;
PushIdentifier[self, Rope.FromRefText[text]];
IF warn THEN MasterWarning[$invalidEncoding, "Invalid Identifier"];
};
DoCardinal: PROC [self: Ref, text: REF TEXT] ~ {
len: NAT ~ text.length;
IF len<=4 THEN {
val: INT ~ IPMaster.IntFromSequenceData[text];
PushNum[self, [int[val]]];
}
ELSE {
val: REAL ~ IPMaster.RealFromSequenceData[text];
PushNum[self, [real[val]]];
};
};
DoRational: PROC [self: Ref, text: REF TEXT] ~ {
len: NAT ~ text.length;
half: NAT ~ len/2;
IF half<=4 THEN {
n: INT ~ IPMaster.IntFromSequenceData[text: text, start: 0, len: half];
d: INT ~ IPMaster.IntFromSequenceData[text: text, start: half, len: half];
IF n IN INTEGER AND d IN INTEGER THEN PushNum[self, [rational[n: n, d: d]]]
ELSE PushNum[self, [real[REAL[n]/REAL[d]]]];
}
ELSE {
n: REAL ~ IPMaster.RealFromSequenceData[text: text, start: 0, len: half];
d: REAL ~ IPMaster.RealFromSequenceData[text: text, start: half, len: half];
PushNum[self, [real[n/d]]];
};
IF (half+half)#len THEN MasterWarning[$invalidEncoding,
IO.PutFR1["Invalid sequenceRational (length=%g)", IO.int[len]]];
};
DoInsertFile: PROC [self: Ref, text: REF TEXT] ~ {
MasterWarning[$unimplemented, "Not implemented: sequenceInsertFile"];
};
Fetch16: PROC [rope: ROPE, startByte: INT] RETURNS [CARDINAL] ~ {
b0: CARDINAL ~ Rope.Fetch[rope, startByte]-'\000;
b1: CARDINAL ~ Rope.Fetch[rope, startByte+1]-'\000;
RETURN [b0*256+b1];
};
OpenFileFromStream: PROC [stream: IO.STREAM] RETURNS [FS.OpenFile] ~ {
openFile: FS.OpenFile ← FS.nullOpenFile;
name: ROPENIL;
ok: BOOLTRUE;
openFile ← FS.OpenFileFromStream[stream ! FS.Error, IO.Error => {ok ← FALSE; CONTINUE}];
Can't just return this openFile, because it can get closed unpredictably.
name ← FS.GetName[openFile ! FS.Error => {ok ← FALSE; CONTINUE}].fullFName;
IF ok THEN openFile ← FS.Open[
name: name,
wantedCreatedTime: FS.GetInfo[openFile].created,
remoteCheck: FALSE
! FS.Error => {ok ← FALSE; CONTINUE}
];
RETURN [IF ok THEN openFile ELSE FS.nullOpenFile]
};
smallish: INT ← 30000;
maxChunkSize: INT ~ LAST[NAT]-SIZE[TEXT[0]]*2-4;
maxRopeStreams: NAT ~ 4;
nRopeStreams: NAT ← 3;
ropeBuffers: NAT ← 1;
pagesPerFileBuffer: NAT ← 10;
numberOfFilebuffers: NAT ← 2;
ropeFileStreamOptions: FS.StreamOptions ~ [
tiogaRead: FALSE,
commitAndReopenTransOnFlush: FALSE,
truncatePagesOnClose: FALSE,
finishTransOnClose: FALSE,
closeFSOpenFileOnClose: FALSE
];
RopeFromRuns: PROC [stream: IO.STREAM, runs: LIST OF Run, sequenceLength: INT] RETURNS [rope: ROPENIL] ~ {
size: INT ← 0;
openFile: FS.OpenFile ~ IF sequenceLength > smallish THEN OpenFileFromStream[stream] ELSE FS.nullOpenFile;
IF openFile # FS.nullOpenFile THEN {
This is a performance hack to make the rope out out several rope-files, each on a separate stream. This is so switching between several regions of the file can be done without excessive thrashing of the disk arm. Ideally, we would have more information about the typical access pattern to make a better decision here.
ropes: ARRAY [0..maxRopeStreams) OF ROPEALL[NIL];
FOR i: NAT IN [0..nRopeStreams) DO
ropeFile: ROPE ~ RopeFile.FromStream[stream: FS.StreamFromOpenFile[openFile: openFile, streamOptions: ropeFileStreamOptions, streamBufferParms: [vmPagesPerBuffer: pagesPerFileBuffer, nBuffers: numberOfFilebuffers]], buffers: ropeBuffers];
t: ROPENIL;
size ← 0;
FOR r: LIST OF Run ← runs, r.rest UNTIL r = NIL DO
run: Run ~ r.first;
t ← Rope.Concat[t, Rope.Substr[ropeFile, run.start, run.len]];
size ← size + run.len;
ENDLOOP;
ropes[i] ← t;
ENDLOOP;
FOR i: NAT IN [0..nRopeStreams) DO
start: INT ~ size*i/nRopeStreams;
end: INT ~ size*(i+1)/nRopeStreams;
rope ← Rope.Concat[rope, Rope.Substr[ropes[i], start, end-start]];
ENDLOOP;
}
ELSE {
saveIndex: INT ~ IO.GetIndex[stream];
FOR r: LIST OF Run ← runs, r.rest UNTIL r = NIL DO
run: Run ← r.first;
UNTIL run.len = 0 DO
chunkSize: NAT ~ MIN[run.len, maxChunkSize];
text: Rope.Text ~ Rope.NewText[chunkSize];
zero: [0..0];
IO.SetIndex[stream, run.start];
TRUSTED {zero ← IO.GetBlock[stream, LOOPHOLE[text], 0, chunkSize]-chunkSize};
rope ← Rope.Concat[rope, text];
size ← size + chunkSize;
run.start ← run.start + chunkSize;
run.len ← run.len - chunkSize;
ENDLOOP;
ENDLOOP;
IO.SetIndex[stream, saveIndex];
};
IF Rope.Size[rope] # sequenceLength THEN ERROR;
IF size # sequenceLength THEN ERROR;
};
ExecuteToEndOfBody: PROC [self: Ref] ~ {
stream: STREAM ~ self.stream;
sequenceData: {nil, text, runs, skip} ← nil;
sequenceType: SequenceType ← nil;
sequenceLength: INT ← 0;
sequenceRuns: INT ← 0;
text: REF TEXTNIL;
buffer: REF TEXT ~ self.buffer;
runsHead, runsTail: LIST OF Run ← NIL;
BeginSequence: PROC [seq: SequenceType] ~ {
SELECT sequenceType ← seq FROM
sequenceString, sequenceIdentifier, sequenceInsertFile, sequenceComment,
sequenceInteger, sequenceRational => sequenceData ← text;
sequenceLargeVector, sequencePackedPixelVector, sequenceCompressedPixelVector, sequenceAdaptivePixelVector => sequenceData ← runs;
ENDCASE => sequenceData ← skip;
SELECT sequenceData FROM
text => { text ← buffer; text.length ← 0 };
runs => { runsHead ← runsTail ← NIL };
ENDCASE;
sequenceLength ← 0;
};
ExtendSequence: PROC [length: INT] ~ {
SELECT sequenceData FROM
nil => {
MasterWarning[$invalidEncoding, "Misplaced sequenceContinued"];
SkipBytes[stream, length];
};
text => {
len: NAT ~ length;
nBytesRead: NAT ← 0;
IF (text.maxLength-text.length)<len THEN text ← RefText.ReserveChars[text, len];
nBytesRead ← IO.GetBlock[self: stream, block: text, startIndex: text.length, count: len];
IF nBytesRead#len THEN ERROR IO.EndOfStream[stream];
};
runs => {
prevTail: LIST OF Run ~ runsTail;
runsTail ← LIST[[start: IO.GetIndex[stream], len: length]];
IF prevTail=NIL THEN runsHead ← runsTail ELSE prevTail.rest ← runsTail;
SkipBytes[stream, length];
};
skip => SkipBytes[stream, length];
ENDCASE => ERROR;
sequenceRuns ← sequenceRuns+1;
sequenceLength ← sequenceLength+length;
};
FinishSequence: PROC ~ {
SELECT sequenceType FROM
sequenceString => DoString[self, text];
sequenceIdentifier => DoIdentifier[self, text];
sequenceInteger => DoCardinal[self, text];
sequenceRational => DoRational[self, text];
sequenceInsertFile => DoInsertFile[self, text];
sequenceComment => NULL;
sequenceLargeVector => {
rope: ROPE ~ RopeFromRuns[self.stream, runsHead, sequenceLength];
b: NAT ~ Rope.Fetch[rope, 0]-'\000;
vector: Vector ~ VectorFromBytes[bytes: Rope.Substr[rope, 1], bytesPerElement: b, signed: TRUE];
PushVector[self, vector];
};
sequencePackedPixelVector => {
rope: ROPE ~ RopeFromRuns[self.stream, runsHead, sequenceLength];
bitsPerSample: [1..1] ~ Fetch16[rope, 0]; -- only one bit per sample supported here
scanLength: NAT ~ Fetch16[rope, 2];
dataBitsPerLine: NAT ~ bitsPerSample*scanLength;
padBitsPerLine: NAT ~ NAT[32 - (dataBitsPerLine MOD 32)] MOD 32;
vector: Vector ~ VectorFromBits[bytes: Rope.Substr[rope, 4], dataBitsPerLine: dataBitsPerLine, padBitsPerLine: padBitsPerLine];
PushVector[self, vector];
};
sequenceCompressedPixelVector => MasterWarning[$unimplemented,
"Not implemented: sequenceCompressedPixelVector"];
sequenceAdaptivePixelVector => MasterWarning[$unimplemented,
"Not implemented: sequenceAdaptivePixelVector"];
sequenceContinued => MasterWarning[$invalidEncoding,
"Misplaced sequenceContinued"];
ENDCASE => MasterWarning[$invalidEncoding,
IO.PutFR1["Invalid sequence type (%g)", IO.int[ORD[sequenceType]]]];
sequenceData ← nil;
sequenceType ← nil;
};
DO -- for each Token
token: Token ~ GetToken[stream: self.stream, flushComments: FALSE];
IF token.seq=sequenceContinued THEN { ExtendSequence[token.len]; LOOP };
self.context.token ← token;
IF sequenceType=sequenceString THEN {
done: BOOLTRUE;
SELECT token.op FROM
show => Imager.ShowText[context: self.imager, text: text];
showandxrel => Imager.ShowText[context: self.imager, text: text, xrel: TRUE];
ENDCASE => done ← FALSE;
IF done THEN { sequenceData ← nil; LOOP };
};
IF sequenceData#nil THEN FinishSequence[];
IF token.op=endBody THEN EXIT;
SELECT token.type FROM
op => Apply[self, token.op];
num => PushNum[self, [int[token.num]]];
seq => { BeginSequence[token.seq]; ExtendSequence[token.len] };
ENDCASE => ERROR;
ENDLOOP;
};
BeginBody: PROC [self: Ref] RETURNS [INT] ~ {
token: Token ~ IPMaster.GetToken[self.stream];
self.context.token ← token;
IF NOT token.op=beginBody THEN MasterError[$missingBody, "Missing body"];
RETURN[token.index];
};
SkipInlineBody: PUBLIC PROC [self: Ref] ~ {
index: INT ~ BeginBody[self];
IPMaster.SkipToEndOfBody[self.stream];
};
GetInlineBody: PUBLIC PROC [self: Ref] RETURNS [Body] ~ {
start, stop: INT ← 0;
start ← BeginBody[self];
IPMaster.SkipToEndOfBody[self.stream];
stop ← IO.GetIndex[self.stream];
RETURN[NEW[IPMaster.BodyRep ← [index: start, length: stop-start]]];
};
ExecuteInlineBody: PROC [self: Ref] ~ {
index: INT ~ BeginBody[self];
DO error: BOOLFALSE;
ExecuteToEndOfBody[self ! MarkRecovery => { error ← TRUE; CONTINUE }];
IF error THEN { -- do mark recovery
marker: Marker ~ PopToActiveMark[self];
IF marker=self.context.marker THEN {
IO.SetIndex[self.stream, self.context.token.index];
DO token: Token ~ IPMaster.GetToken[self.stream];
SELECT token.op FROM
endBody => ERROR MarkRecovery; -- end of body
beginBody => IPMaster.SkipToEndOfBody[self.stream]; -- skip body literal
unmark0 => { IO.SetIndex[self.stream, token.index]; EXIT }; -- found UNMARK0
ENDCASE;
IF token.type=seq THEN IPMaster.SkipBytes[self.stream, token.len];
ENDLOOP;
}
ELSE ERROR MarkRecovery; -- not this context's marker
}
ELSE EXIT; -- normal completion
ENDLOOP;
};
CallInlineBody: PUBLIC PROC [self: Ref, frame: Vector, env: Vector] ~ {
action: PROC ~ { ExecuteInlineBody[self] };
Call[self: self, action: action, frame: frame, env: env];
};
CallBody: PROC [self: Ref, body: Body, frame: Vector, env: Vector] ~ {
stream: STREAM ~ self.stream;
next: INT ~ IO.GetIndex[stream];
IO.SetIndex[stream, body.index];
CallInlineBody[self: self, frame: frame, env: env ! UNWIND => IO.SetIndex[stream, next]];
IO.SetIndex[stream, next];
};
DoWithMarkProtection: PUBLIC PROC [self: Ref, action: PROC] ~ {
error: BOOLFALSE;
inner: PROC ~ { Mark[self, 0]; action[]; Unmark[self, 0] };
inner[! MarkRecovery => { error ← TRUE; CONTINUE}];
IF error THEN { -- do mark recovery
marker: Marker ~ PopToActiveMark[self];
IF marker=self.context.marker THEN Unmark[self, 0]
ELSE ERROR MarkRecovery;
};
};
Do: PUBLIC PROC [self: Ref, op: Operator] ~ { op.class.do[op, self] };
composedClass: OperatorClass ~ NEW[OperatorClassRep ← [
type: $Composed, do: ComposedDo]];
ComposedData: TYPE ~ REF ComposedDataRep;
ComposedDataRep: TYPE ~ RECORD[
frame: Vector, -- initial frame
env: Vector, -- environment
body: Body -- body of the operator
];
ComposedDo: PROC [op: Operator, state: Ref] ~ {
data: ComposedData ~ NARROW[op.data];
CallBody[self: state, body: data.body, frame: data.frame, env: data.env];
};
MakeCO: PUBLIC PROC [frame: Vector, env: Vector, body: Body]
RETURNS
[Operator] ~ {
data: ComposedData ~ NEW[ComposedDataRep ← [frame: frame, env: env, body: body]];
RETURN[NEW[OperatorRep ← [class: composedClass, data: data]]];
};
CallPreamble: PROC [self: Ref, node: Node, frame: Vector, env: Vector] ~ {
WITH node SELECT FROM
node: REF NodeRep.body => {
preamble: PROC ~ {
Mark[self, 0];
ExecuteInlineBody[self];
IF Count[self]<1 THEN PushVector[self, Env[self]];
IF Count[self]<2 THEN PushVector[self, Frame[self]];
Unmark[self, 2];
};
IO.SetIndex[self.stream, node.body.index];
Call[self: self, action: preamble, frame: frame, env: env];
};
node: REF NodeRep.block => {
MasterError[$unimplemented, "Preamble cannot be a block."];
ERROR Error;
};
ENDCASE => ERROR;
};
CallNode: PROC [self: Ref, node: Node, frame: Vector, env: Vector] ~ {
WITH node SELECT FROM
node: REF NodeRep.body => {
body: Body ~ node.body;
IO.SetIndex[self.stream, body.index];
CallInlineBody[self: self, frame: frame, env: env];
};
node: REF NodeRep.block => {
block: Block ~ node.block;
pageFrame, pageEnv: Vector;
CallPreamble[self, block.preamble, frame, env];
pageFrame ← PopVector[self];
pageEnv ← PopVector[self];
FOR i: NAT IN[0..block.size) DO
CallNode[self, block[i], pageFrame, pageEnv];
ENDLOOP;
};
ENDCASE => ERROR;
};
emptyVec: Vector ~ ZeroVec[0];
topFrame: Vector ~ ZeroVec[topFrameSize];
topEnv: Vector ~ emptyVec; -- empty vector, for now
Master: TYPE ~ Interpress.Master;
MasterRep: TYPE ~ Interpress.MasterRep;
MasterImpl: TYPE ~ REF MasterImplRep;
MasterImplRep: PUBLIC TYPE ~ RECORD [ -- exported to Interpress
skeleton: IPMaster.Skeleton,
interpreter: IPInterpreter.Ref
];
LogProc: TYPE ~ Interpress.LogProc;
Error: SIGNAL [class: INT, code: ATOM, explanation: ROPE] ~ CODE;
ReportError: PUBLIC PROC [class: INT, code: ATOM, explanation: ROPE] ~ {
SIGNAL Error[class: class, code: code, explanation: explanation];
IF class=Interpress.classMasterError THEN ERROR MarkRecovery;
};
MasterError: PUBLIC PROC [code: ATOM, explanation: ROPE] ~ {
ReportError[class: Interpress.classMasterError, code: code, explanation: explanation];
};
MasterWarning: PUBLIC PROC [code: ATOM, explanation: ROPE ←] ~ {
ReportError[class: Interpress.classMasterWarning, code: code, explanation: explanation];
};
DoTopAction: PROC [self: Ref, action: PROC, log: LogProc] ~ {
protect: PROC ~ { DoWithMarkProtection[self, action] };
call: PROC ~ { Call[self, protect, emptyVec, emptyVec] };
save: PROC ~ { IF self.imager=NIL THEN call[] ELSE DoSaveAll[self, call] };
save[! Error => { IF log#NIL THEN log[class, code, explanation]; RESUME }];
};
DoPreamble: PROC [master: Master, log: LogProc] ~ {
impl: MasterImpl ~ master.impl;
self: Ref ~ impl.interpreter;
block: Block ~ impl.skeleton.topBlock;
action: PROC ~ {
CallPreamble[self: self, node: block.preamble, frame: topFrame, env: topEnv];
self.topFrame ← PopVector[self];
self.topEnv ← PopVector[self];
};
DoTopAction[self, action, log];
};
defaultMaxStackLength: Cardinal ~ 1000;
Open: PUBLIC PROC [fileName: ROPE, log: LogProc] RETURNS [Master] ~ {
stream: STREAM ~ FS.StreamOpen[fileName];
RETURN [FromStream[stream, log]];
};
FromStream: PUBLIC PROC [stream: STREAM, log: LogProc] RETURNS [Master] ~ {
restOfHeader: ROPE ~ IPMaster.GetHeader[stream, "Interpress/Xerox/"];
skeleton: IPMaster.Skeleton ~ IPMaster.GetSkeleton[stream];
self: Ref ~ NEW[Rep ← [stream: stream]];
impl: MasterImpl ~ NEW[MasterImplRep ← [skeleton: skeleton, interpreter: self]];
master: Master ~ NEW[MasterRep ← [pages: skeleton.topBlock.size, impl: impl]];
self.buffer ← NEW[TEXT[200]];
self.stackArray ← NEW[StackArrayRep ← ALL[[zero[]]]];
self.stackCountMax ← defaultMaxStackLength;
self.imager ← imager; -- dummy imager context
DoPreamble[master, log];
RETURN[master];
};
DoPage: PUBLIC PROC [master: Master, page: INT, context: Imager.Context, log: LogProc] ~ {
IF page IN[1..master.pages] THEN {
impl: MasterImpl ~ master.impl;
block: Block ~ impl.skeleton.topBlock;
node: Node ~ block[page-1];
self: Ref ~ impl.interpreter;
action: PROC ~ { CallNode[self: self, node: node, frame: self.topFrame, env: self.topEnv] };
self.imager ← context;
DoTopAction[self, action, log];
self.imager ← NIL;
};
};
END.
ObtainExternalInstructions: PROC [self: Ref] RETURNS [Vector] ~ {
RETURN[VectorFromAny[GetP[Env[self], $externalInstructions]]];
};
AddInstructionDefaults: PROC [self: Ref, computedInstructions, externalInstructions: Vector]
RETURNS [Vector] ~ {
RETURN[computedInstructions]
};
ExecuteInstructionsBody: PROC [self: Ref, externalInstructions: Vector] RETURNS [Vector] ~ {
body: Index ~ self.skeleton.preamble.instructions;
IF body=nullIndex THEN RETURN[externalInstructions]
ELSE {
reader: Reader ~ NARROW[self.reader];
PushVector[self, externalInstructions];
reader.SetIndex[body];
DoBody[self, NoPool[], initialTopFrame, $saveAll];
WHILE Count[self]>1 DO Apply[self, $mergeprop] ENDLOOP;
RETURN[PopVector[self]];
};
};
ComputePrintingInstructions: PROC [self: Ref] ~ {
externalInstructions: Vector ~ ObtainExternalInstructions[self];
instructions: Vector ← emptyVector;
Mark[self, 0];
instructions ← ExecuteInstructionsBody[self, externalInstructions ! Error => CONTINUE];
Unmark0[self];
instructions ← AddInstructionDefaults[self, instructions, externalInstructions];
self.media ← GetP[instructions, $media];
self.copySelect ← GetP[instructions, $copySelect];
self.pageSelect ← GetP[instructions, $pageSelect];
self.onSimplex ← GetP[instructions, $onSimplex];
self.mediaSelect ← GetP[instructions, $mediaSelect];
self.copyName ← GetP[instructions, $copyName];
self.instructions ← instructions;
};