IPExecImpl.mesa
Copyright © 1984, 1985 by Xerox Corporation. All rights reserved.
Doug Wyatt, July 8, 1985 4:58:44 pm PDT
Michael Plass, February 17, 1986 9:03:24 am PST
DIRECTORY
Atom USING [MakeAtomFromRefText],
FS,
Imager USING [Context, ShowText],
ImagerFont USING [MapText, XStringProc],
Interpress USING [AddMaster, LogProc, OpenMaster, OpenMasterRep],
IO,
IPInterpreter USING [Apply, Call, Context, Count, DoSaveAll, Env, Frame, Identifier, IdentifierRep, Integer, Mark, Marker, MasterError, MasterWarning, NoPool, Operator, OperatorClass, OperatorClassRep, OperatorRep, Pool, PopToActiveMark, PopVector, PushIdentifier, PushNum, PushVector, Ref, Rep, StackArrayRep, topFrameSize, Unmark, Vector, VectorFromBits, VectorFromBytes, VectorFromString, ZeroVec],
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 Atom, FS, IO, Imager, ImagerFont, Interpress, 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;
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;
id: Identifier ~ NEW[IdentifierRep ← [atom: NIL, rope: NIL]];
warn: BOOL ← FALSE;
id.rope ← Rope.FromRefText[text]; -- before conversion to lower case
FOR i:
NAT
IN[0..len)
DO
char: CHAR ~ text[i];
SELECT char
FROM
IN['a..'z] => NULL;
IN['A..'Z] => text[i] ← char+('a-'A); -- force lower case
IN['0..'9], '- => IF i=0 THEN warn ← TRUE;
ENDCASE => warn ← TRUE;
ENDLOOP;
IF len=0 THEN warn ← TRUE;
id.atom ← Atom.MakeAtomFromRefText[text];
PushIdentifier[self, id];
IF warn THEN MasterWarning[$invalidEncoding, "Invalid Identifier"];
};
DoInteger:
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: ROPE ← NIL;
ok: BOOL ← TRUE;
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:
ROPE ←
NIL] ~ {
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 ROPE ← ALL[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: ROPE ← NIL;
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 TEXT ← NIL;
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, sequenceReal => 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 => DoInteger[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
AND self.showVec.font#
NIL
THEN {
done: BOOL ← TRUE;
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:
BOOL ←
FALSE;
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, pool: Pool, env: Vector] ~ {
action: PROC ~ { ExecuteInlineBody[self] };
Call[self: self, action: action, frame: frame, pool: pool, env: env];
};
CallBody:
PROC [self: Ref, body: Body, frame: Vector, pool: Pool, env: Vector] ~ {
stream: STREAM ~ self.stream;
next: INT ~ IO.GetIndex[stream];
IO.SetIndex[stream, body.index];
CallInlineBody[self: self, frame: frame, pool: pool, env: env
! UNWIND => IO.SetIndex[stream, next]];
IO.SetIndex[stream, next];
};
DoWithMarkProtection:
PUBLIC
PROC [self: Ref, action:
PROC] ~ {
error: BOOL ← FALSE;
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
pool: Pool, -- shared pool
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, pool: data.pool, env: data.env];
};
MakeCO:
PUBLIC
PROC [frame: Vector, pool: Pool, env: Vector, body: Body]
RETURNS [Operator] ~ {
data: ComposedData ~
NEW[ComposedDataRep ← [
frame: frame, pool: pool, env: env, body: body]];
RETURN[NEW[OperatorRep ← [class: composedClass, data: data]]];
};
compiledImageClass: OperatorClass ~
NEW[OperatorClassRep ← [
type: $CompiledImage, do: CompiledImageDo]];
CompiledImageData: TYPE ~ REF CompiledImageDataRep;
CompiledImageDataRep:
TYPE ~
RECORD[frame: Vector, env: Vector, body: Body];
CompiledImageDo:
PROC [op: Operator, state: Ref] ~ {
data: CompiledImageData ~ NARROW[op.data];
CallBody[self: state, body: data.body, frame: data.frame, pool: NoPool[], env: data.env];
};
MakeCompiledImage:
PUBLIC
PROC [frame: Vector, env: Vector, body: Body]
RETURNS [Operator] ~ {
data: CompiledImageData ~
NEW[CompiledImageDataRep ← [
frame: frame, env: env, body: body]];
RETURN[NEW[OperatorRep ← [class: compiledImageClass, 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, pool: NoPool[], 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, pool: NoPool[], 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
OpenMaster: TYPE ~ Interpress.OpenMaster;
OpenMasterRep: TYPE ~ Interpress.OpenMasterRep;
OpenMasterImplRep: PUBLIC TYPE ~ IPInterpreter.Rep; -- exported to Interpress
LogProc: TYPE ~ Interpress.LogProc;
DoTopAction:
PROC [master: OpenMaster, action:
PROC] ~ {
self: Ref ~ master.impl;
protect: PROC ~ { DoWithMarkProtection[self, action] };
call: PROC ~ { Call[self, protect, emptyVec, NoPool[], emptyVec] };
save: PROC ~ { IF self.imager=NIL THEN call ELSE DoSaveAll[self, call] };
Interpress.AddMaster[master, save];
};
DoPreamble:
PROC [master: OpenMaster] ~ {
self: Ref ~ master.impl;
block: Block ~ master.skeleton.topBlock;
action:
PROC ~ {
CallPreamble[self: self, node: block.preamble, frame: topFrame, env: topEnv];
self.topFrame ← PopVector[self];
self.topEnv ← PopVector[self];
};
DoTopAction[master, action];
};
defaultMaxStackLength: Integer ~ 1000;
Open:
PUBLIC
PROC [fileName:
ROPE, logProc: LogProc, logData:
REF ←
NIL]
RETURNS [OpenMaster] ~ {
stream: STREAM ~ FS.StreamOpen[fileName];
RETURN [FromStream[stream, logProc, logData]];
};
FromStream:
PUBLIC
PROC [stream:
STREAM, logProc: LogProc, logData:
REF ←
NIL]
RETURNS [OpenMaster] ~ {
restOfHeader: ROPE ~ IPMaster.GetHeader[stream, "Interpress/Xerox/"];
skeleton: IPMaster.Skeleton ~ IPMaster.GetSkeleton[stream];
self: Ref ~ NEW[Rep ← [stream: stream]];
master: OpenMaster ~ NEW[OpenMasterRep ← [
pages: skeleton.topBlock.size, skeleton: skeleton,
logProc: logProc, logData: logData, impl: self]];
self.buffer ← NEW[TEXT[200]];
self.stackArray ← NEW[StackArrayRep ← ALL[[zero[]]]];
self.stackCountMax ← defaultMaxStackLength;
self.imager ← imager; -- dummy imager context
DoPreamble[master];
RETURN[master];
};
DoPage:
PUBLIC
PROC [master: OpenMaster, page:
INT, context: Imager.Context] ~ {
IF page
IN[1..master.pages]
THEN {
block: Block ~ master.skeleton.topBlock;
node: Node ~ block[page-1];
self: Ref ~ master.impl;
action: PROC ~ { CallNode[self: self, node: node, frame: self.topFrame, env: self.topEnv] };
self.imager ← context;
DoTopAction[master, action];
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;
};