PSLanguage2Impl.mesa
Copyright Ó 1986, 1987 by Xerox Corporation. All rights reserved.
Doug Wyatt, August 21, 1987 6:23:20 pm PDT
PostScript implementation: array, string, dictionary, and file operators.
DIRECTORY
PS,
Ascii,
FS,
IO,
PrincOps,
Rope;
PSLanguage2Impl: CEDAR MONITOR
IMPORTS PS, FS, IO, Rope
EXPORTS PS
~ BEGIN OPEN PS;
Array operators
ArrayCreate: PUBLIC PROC [self: Root, size: INT] RETURNS [Array] ~ {
IF size<0 THEN ERROR Error[rangecheck];
IF size IN ArrayIndex THEN {
length: ArrayIndex ~ size;
ref: ArrayRef ~ self.zone.NEW[ArrayRep[length]];
ref.level ← self.level;
FOR i: ArrayIndex IN[0..length) DO ref[i] ← null ENDLOOP;
RETURN[[
val: [executable: FALSE, variant: array[access: unlimited, start: 0, length: length]],
ref: ref
]];
}
ELSE ERROR Error[limitcheck];
};
ArrayGet: PUBLIC PROC [array: Array, index: INT] RETURNS [Any] ~ {
IF index NOT IN [0..array.val.length) THEN ERROR Error[rangecheck];
RETURN [array.ref[array.val.start+index]];
};
ArraySave: PROC [self: Root, ref: ArrayRef] ~ {
top: Level ~ self.level-1;
copy: ArrayRef ~ self.zone.NEW[ArrayRep[ref.size]];
copy.level ← ref.level;
FOR i: ArrayIndex IN[0..copy.size) DO copy[i] ← ref[i] ENDLOOP;
self.restore[top] ← NEW [RestoreItemRep.array ← [self.restore[top], array[ref, copy]]];
ref.level ← self.level;
};
ArrayPut: PUBLIC PROC [self: Root, array: Array, index: INT, x: Any] ~ {
IF index NOT IN[0..array.val.length) THEN ERROR Error[rangecheck];
IF array.ref.level<self.level THEN ArraySave[self, array.ref];
array.ref[array.val.start+index] ← x;
};
ArrayGetInterval: PUBLIC PROC [array: Array, index, count: INT] RETURNS [Array] ~ {
IF index NOT IN [0..array.val.length] THEN ERROR Error[rangecheck];
IF count NOT IN [0..(array.val.length-index)] THEN ERROR Error[rangecheck];
RETURN[[
val: [executable: array.val.executable, variant: array[
access: array.val.access, start: array.val.start+index, length: count]],
ref: array.ref
]];
};
ArrayPutInterval: PUBLIC PROC [self: Root, array: Array, index: INT, interval: Array] ~ {
subarray: Array ~ ArrayGetInterval[array, index, ArrayLength[interval]];
IF subarray.ref.level<self.level THEN ArraySave[self, subarray.ref];
FOR i: INT IN[0..ArrayLength[subarray]) DO
ArrayPut[self, subarray, i, ArrayGet[interval, i]];
ENDLOOP;
};
ArrayCopy: PUBLIC PROC [self: Root, array: Array, from: Array] RETURNS [Array] ~ {
subarray: Array ~ ArrayGetInterval[array, 0, ArrayLength[from]];
ArrayPutInterval[self, subarray, 0, from];
RETURN [subarray];
};
ArrayForAll: PUBLIC PROC [array: Array, action: PROC [Any]] ~ {
FOR index: INT IN [0..ArrayLength[array]) DO
action[ArrayGet[array, index]];
ENDLOOP;
};
ALoad: PUBLIC PROC [self: Root, array: Array] ~ {
length: INT ~ ArrayLength[array];
FOR index: INT IN [0..length) DO
PushAny[self, ArrayGet[array, index]];
ENDLOOP;
};
AStore: PUBLIC PROC [self: Root, array: Array] ~ {
length: INT ~ ArrayLength[array];
IF Count[self]<length THEN ERROR Error[stackunderflow];
FOR index: INT DECREASING IN [0..length) DO
ArrayPut[self, array, index, PopAny[self]];
ENDLOOP;
};
String operators
StringCreate: PUBLIC PROC [self: Root, size: INT] RETURNS [String] ~ {
IF size<0 THEN ERROR Error[rangecheck];
IF size IN StringIndex THEN {
length: StringIndex ~ size;
ref: StringRef ~ self.zone.NEW[StringRep[length]];
ref.level ← self.level;
FOR i: StringIndex IN[0..length) DO ref[i] ← VAL[0] ENDLOOP;
RETURN[[
val: [executable: FALSE, variant: string[access: unlimited, start: 0, length: length]],
ref: ref
]];
}
ELSE ERROR Error[limitcheck];
};
StringCreateFromText: PUBLIC PROC [self: Root, text: Text] RETURNS [String] ~ {
RETURN [StringFromText[self, StringCreate[self, text.length], text]];
};
StringCreateFromString: PUBLIC PROC [self: Root, string: String] RETURNS [String] ~ {
RETURN [StringCopy[self, StringCreate[self, string.val.length], string]];
};
StringGet: PUBLIC PROC [string: String, index: INT] RETURNS [CHAR] ~ {
IF index NOT IN [0..string.val.length) THEN ERROR Error[rangecheck];
RETURN [string.ref[string.val.start+index]];
};
StringSave: PROC [self: Root, ref: StringRef] ~ {
top: Level ~ self.level-1;
copy: StringRef ~ self.zone.NEW[StringRep[ref.size]];
copy.level ← ref.level;
FOR i: StringIndex IN[0..copy.size) DO copy[i] ← ref[i] ENDLOOP;
self.restore[top] ← NEW [RestoreItemRep.string ← [self.restore[top], string[ref, copy]]];
ref.level ← self.level;
};
StringPut: PUBLIC PROC [self: Root, string: String, index: INT, x: CHAR] ~ {
IF index NOT IN[0..string.val.length) THEN ERROR Error[rangecheck];
IF string.ref.level<self.level THEN StringSave[self, string.ref];
string.ref[string.val.start+index] ← x;
};
StringGetInterval: PUBLIC PROC [string: String, index, count: INT] RETURNS [String] ~ {
IF index NOT IN [0..string.val.length] THEN ERROR Error[rangecheck];
IF count NOT IN [0..(string.val.length-index)] THEN ERROR Error[rangecheck];
RETURN[[
val: [executable: string.val.executable, variant: string[
access: string.val.access, start: string.val.start+index, length: count]],
ref: string.ref
]];
};
StringPutInterval: PUBLIC PROC [self: Root, string: String, index: INT, interval: String] ~ {
target: String ~ StringGetInterval[string, index, StringLength[interval]];
IF target.ref.level<self.level THEN StringSave[self, target.ref];
FOR i: StringIndex IN[0..target.val.length) DO
target.ref[target.val.start+i] ← interval.ref[interval.val.start+i];
ENDLOOP;
};
StringCopy: PUBLIC PROC [self: Root, string: String, from: String] RETURNS [String] ~ {
substring: String ~ StringGetInterval[string, 0, StringLength[from]];
StringPutInterval[self, substring, 0, from];
RETURN [substring];
};
StringForAll: PUBLIC PROC [string: String, action: PROC [CHAR]] ~ {
FOR index: INT IN [0..StringLength[string]) DO
action[StringGet[string, index]];
ENDLOOP;
};
StringEq: PUBLIC PROC [string1: String, string2: String] RETURNS [BOOL] ~ {
IF StringLength[string1]#StringLength[string2] THEN RETURN [FALSE];
RETURN [StringCompare[string1, string2]=equal];
};
StringEqText: PUBLIC PROC [string: String, text: Text] RETURNS [BOOL] ~ {
length: StringIndex ~ StringLength[string];
IF length#text.length THEN RETURN [FALSE];
FOR index: StringIndex IN[0..length) DO
char1: CHAR ~ string.ref[string.val.start+index];
char2: CHAR ~ text[index];
IF char1#char2 THEN RETURN [FALSE];
ENDLOOP;
RETURN [TRUE];
};
StringCompare: PUBLIC PROC [string1, string2: String] RETURNS [Comparison] ~ {
length1: StringIndex ~ string1.val.length;
length2: StringIndex ~ string2.val.length;
FOR index: StringIndex IN[0..MIN[length1, length2]) DO
char1: CHAR ~ string1.ref[string1.val.start+index];
char2: CHAR ~ string2.ref[string2.val.start+index];
IF char1=char2 THEN NULL
ELSE RETURN [IF char1<char2 THEN less ELSE greater];
ENDLOOP;
IF length1=length1 THEN RETURN [equal]
ELSE RETURN [IF length1<length2 THEN less ELSE greater];
};
Search: PUBLIC PROC [string, seek: String, anchor: BOOL]
RETURNS [found: BOOLFALSE, index: INT ← 0] ~ {
stringLength: INT ~ StringLength[string];
seekLength: INT ~ StringLength[seek];
WHILE seekLength<=(stringLength-index) DO
FOR i: INT IN[0..seekLength) DO
char1: CHAR ~ StringGet[string, index+i];
char2: CHAR ~ StringGet[seek, i];
IF char1#char2 THEN EXIT;
REPEAT FINISHED => found ← TRUE;
ENDLOOP;
IF found OR anchor THEN EXIT ELSE index ← index+1;
ENDLOOP;
};
Dictionary operators
DictCreate: PUBLIC PROC [self: Root, size: INT] RETURNS [Dict] ~ {
IF size<0 THEN ERROR Error[rangecheck];
IF size IN DictIndex THEN {
maxlength: DictIndex ~ size;
mod: DictIndex ~ maxlength+MIN[DictIndex.LAST-maxlength, 1+maxlength/10];
ref: DictRef ~ self.zone.NEW[DictRep[mod]];
ref.level ← self.level;
ref.access ← unlimited;
ref.length ← 0;
ref.maxlength ← maxlength;
FOR i: DictIndex IN[0..mod) DO ref[i] ← nullTuple ENDLOOP;
RETURN[[val: [executable: FALSE, variant: dict[]], ref: ref]];
}
ELSE ERROR Error[limitcheck];
};
TextHash: PROC [text: Text] RETURNS [hash: CARDINAL ← 0] ~ {
start: NAT ~ 0;
length: NAT ~ text.length;
FOR i: NAT IN[0..MIN[16, length]) DO
hash ← hash*2+ORD[text[start+i]];
ENDLOOP;
};
StringHash: PROC [string: String] RETURNS [hash: CARDINAL ← 0] ~ {
text: StringRef ~ string.ref;
start: StringIndex ~ string.val.start;
length: StringIndex ~ string.val.length;
FOR i: NAT IN[0..MIN[16, length]) DO
hash ← hash*2+ORD[text[start+i]];
ENDLOOP;
};
Hash: PROC [x: Any] RETURNS [hash: CARDINAL ← 42] ~ {
Munch: PROC [CARD32] RETURNS [CARD16] ~ TRUSTED MACHINE CODE { PrincOps.zXOR };
WITH val: x.val SELECT FROM
name => WITH x.ref SELECT FROM
ref: NameRef => hash ← ref.hash;
ENDCASE => ERROR Bug;
string => WITH x.ref SELECT FROM
ref: StringRef => hash ← StringHash[[val, ref]];
ENDCASE => ERROR Bug;
integer => hash ← Munch[LOOPHOLE[val.int]];
boolean => hash ← ORD[val.bool];
operator, array, dict, file, font => hash ← Munch[LOOPHOLE[x.ref]];
ENDCASE;
};
InlineEq: PROC [x1, x2: Any] RETURNS [BOOL] ~ INLINE {
RETURN [IF x1.val.type=name AND x2.val.type=name THEN x1.ref=x2.ref ELSE Eq[x1, x2]];
};
EqText: PROC [x1: Any, text: Text] RETURNS [BOOL] ~ {
WITH v1: x1.val SELECT FROM
name => WITH x1.ref SELECT FROM
r1: NameRef => RETURN [StringEqText[r1.string, text]];
ENDCASE => ERROR Bug;
string => WITH x1.ref SELECT FROM
r1: StringRef => RETURN [StringEqText[[v1, r1], text]];
ENDCASE => ERROR Bug;
ENDCASE;
RETURN [FALSE];
};
DictFetch: PROC [dict: Dict, key: Any] RETURNS [found: BOOL, val: Any] ~ {
mod: DictIndex ~ dict.ref.mod;
index: DictIndex ← Hash[key] MOD mod;
THROUGH [0..mod) DO
SELECT TRUE FROM
dict.ref[index].key.val.type=null => RETURN [FALSE, null];
InlineEq[dict.ref[index].key, key] => RETURN [TRUE, dict.ref[index].val];
ENDCASE => index ← (index+1) MOD mod;
ENDLOOP;
ERROR Bug;
};
DictFetchText: PROC [dict: Dict, text: Text] RETURNS [found: BOOL, val: Any] ~ {
mod: DictIndex ~ dict.ref.mod;
index: DictIndex ← TextHash[text] MOD mod;
THROUGH [0..mod) DO
SELECT TRUE FROM
dict.ref[index].key.val.type=null => RETURN [FALSE, null];
EqText[dict.ref[index].key, text] => RETURN [TRUE, dict.ref[index].val];
ENDCASE => index ← (index+1) MOD mod;
ENDLOOP;
ERROR Bug;
};
DictGet: PUBLIC PROC [dict: Dict, key: Any] RETURNS [Any] ~ {
found: BOOL; val: Any;
[found, val] ← DictFetch[dict, key];
IF found THEN RETURN [val]
ELSE ERROR Error[undefined];
};
DictGetText: PUBLIC PROC [dict: Dict, text: Text] RETURNS [Any] ~ {
found: BOOL; val: Any;
[found, val] ← DictFetchText[dict, text];
IF found THEN RETURN [val]
ELSE ERROR Error[undefined];
};
DictSave: PROC [self: Root, ref: DictRef] ~ {
top: Level ~ self.level-1;
copy: DictRef ~ self.zone.NEW[DictRep[ref.mod]];
copy.level ← ref.level;
copy.access ← ref.access;
copy.length ← ref.length;
copy.maxlength ← ref.maxlength;
FOR i: DictIndex IN[0..copy.mod) DO copy[i] ← ref[i] ENDLOOP;
self.restore[top] ← NEW [RestoreItemRep.dict ← [self.restore[top], dict[ref, copy]]];
ref.level ← self.level;
};
DictPut: PUBLIC PROC [self: Root, dict: Dict, key: Any, val: Any, name: BOOLFALSE] ~ {
mod: DictIndex ~ dict.ref.mod;
index: DictIndex ← Hash[key] MOD mod;
IF dict.ref.level<self.level THEN DictSave[self, dict.ref];
THROUGH [0..mod) DO
SELECT TRUE FROM
dict.ref[index].key.val.type=null => {
IF NOT dict.ref.length<dict.ref.maxlength THEN ERROR Error[dictfull];
IF Type[key]=string AND NOT name THEN {
string: String ~ StringFromAny[key, readOnly];
name: Name ~ NameFromString[self, string];
key ← AnyFromName[name];
};
dict.ref[index] ← [key, val];
dict.ref.length ← dict.ref.length+1;
RETURN;
};
InlineEq[dict.ref[index].key, key] => { dict.ref[index].val ← val; RETURN };
ENDCASE => index ← (index+1) MOD mod;
ENDLOOP;
ERROR Bug;
};
DictForAll: PUBLIC PROC [dict: Dict, action: PROC [key: Any, val: Any]] ~ {
FOR index: DictIndex IN [0..dict.ref.mod) DO
IF dict.ref[index].key.val.type#null THEN {
tuple: Tuple ~ dict.ref[index];
action[tuple.key, tuple.val];
};
ENDLOOP;
};
Known: PUBLIC PROC [dict: Dict, key: Any] RETURNS [BOOL] ~ {
RETURN [DictFetch[dict, key].found];
};
DictCopy: PUBLIC PROC [self: Root, dict: Dict, from: Dict] RETURNS [Dict] ~ {
put: PROC [key: Any, val: Any] ~ { DictPut[self, dict, key, val] };
IF DictLength[dict]#0 THEN ERROR Error[rangecheck];
IF DictMaxLength[dict]<DictLength[from] THEN Error[rangecheck];
DictForAll[from, put];
RETURN [DictSetAccess[self, dict, DictAccess[from]]];
};
Begin: PUBLIC PROC [self: Root, dict: Dict] ~ {
IF NOT self.dcount<self.dsize THEN ERROR Error[dictstackoverflow];
self.dstack[self.dcount] ← dict;
self.dcount ← self.dcount+1;
};
End: PUBLIC PROC [self: Root] ~ {
IF NOT self.dcount>2 THEN ERROR Error[dictstackunderflow];
self.dcount ← self.dcount-1;
};
CurrentDict: PUBLIC PROC [self: Root] RETURNS [Dict] ~ {
IF NOT self.dcount>0 THEN ERROR Bug; -- should always be >=2
RETURN [self.dstack[self.dcount-1]];
};
CountDictStack: PUBLIC PROC [self: Root] RETURNS [StackIndex] ~ {
RETURN [self.dcount];
};
DictStack: PUBLIC PROC [self: Root, array: Array] RETURNS [Array] ~ {
subarray: Array ~ ArrayGetInterval[array, 0, self.dcount];
FOR i: StackIndex IN [0..self.dcount) DO
dict: Dict ~ self.dstack[i];
ArrayPut[self, subarray, i, AnyFromDict[dict]];
ENDLOOP;
RETURN [subarray];
};
Def: PUBLIC PROC [self: Root, key: Any, val: Any] ~ {
DictPut[self, CurrentDict[self], key, val];
};
nullDict: Dict ~ [val: [executable: FALSE, variant: dict[]], ref: NIL];
Where: PUBLIC PROC [self: Root, key: Any] RETURNS [found: BOOL, where: Dict] ~ {
FOR i: StackIndex DECREASING IN [0..self.dcount) DO
dict: Dict ~ self.dstack[i];
IF DictFetch[dict, key].found THEN RETURN [TRUE, dict];
ENDLOOP;
RETURN [FALSE, nullDict];
};
Load: PUBLIC PROC [self: Root, key: Any] RETURNS [Any] ~ {
FOR i: StackIndex DECREASING IN [0..self.dcount) DO
dict: Dict ~ self.dstack[i];
found: BOOL; val: Any;
[found, val] ← DictFetch[dict, key];
IF found THEN RETURN [val];
ENDLOOP;
ERROR Error[undefined];
};
Store: PUBLIC PROC [self: Root, key: Any, val: Any] ~ {
found: BOOL; dict: Dict;
[found, dict] ← Where[self, key];
IF NOT found THEN dict ← CurrentDict[self];
DictPut[self, dict, key, val];
};
Name operators
NameCreate: PROC [self: Root, string: String] RETURNS [Name] ~ {
hash: CARDINAL ~ StringHash[string];
ref: NameRef ~ self.zone.NEW[NameRep ← [level: self.level, string: string, hash: hash]];
name: Name ~ [val: [executable: TRUE, variant: name[]], ref: ref];
DictPut[self, self.nameDict, AnyFromString[string], AnyFromName[name], TRUE];
RETURN [name];
};
NameLength: PUBLIC PROC [name: Name] RETURNS [INT] ~ {
RETURN [StringLength[name.ref.string]];
};
NameFromText: PUBLIC PROC [self: Root, text: Text] RETURNS [Name] ~ {
found: BOOL; val: Any;
[found, val] ← DictFetchText[self.nameDict, text];
IF found THEN RETURN [NameFromAny[val]]
ELSE RETURN [NameCreate[self, StringCreateFromText[self, text]]];
};
NameFromString: PUBLIC PROC [self: Root, string: String] RETURNS [Name] ~ {
found: BOOL; val: Any;
[found, val] ← DictFetch[self.nameDict, AnyFromString[string]];
IF found THEN RETURN [NameFromAny[val]]
ELSE RETURN [NameCreate[self, StringCreateFromString[self, string]]];
};
File operators
EndOfFile: PUBLIC ERROR ~ CODE;
ROPE: TYPE ~ Rope.ROPE;
RopeFromString: PROC [string: String] RETURNS [ROPE] ~ {
text: Rope.Text ~ Rope.NewText[string.val.length];
FOR i: NAT IN[0..string.val.length) DO text[i] ← string.ref[string.val.start+i] ENDLOOP;
RETURN [text];
};
FileCreate: PUBLIC PROC [self: Root, string: String, accessMode: FileAccessMode] RETURNS [File] ~ {
fileName: ROPE ~ RopeFromString[string];
stream: STREAMNIL;
fsError: FS.ErrorDesc;
SELECT TRUE FROM
Rope.Equal[fileName, "%statementedit"] => {
line: ROPE ~ IO.GetLineRope[self.stdin.ref.stream];
RETURN [FileFromStream[self, IO.RIS[line]]];
};
Rope.Equal[fileName, "%stdin"] => xxx;
Rope.Equal[fileName, "%stdout"] => xxx;
Rope.Equal[fileName, "%stderr"] => xxx;
ENDCASE;
stream ← FS.StreamOpen[fileName: fileName, accessOptions: accessMode !
FS.Error => { fsError ← error; CONTINUE };
];
IF stream=NIL THEN SELECT fsError.group FROM
user => ERROR Error[undefinedfilename];
ENDCASE => ERROR Error[ioerror];
RETURN [FileFromStream[self, stream]];
};
FileFromStream: PUBLIC PROC [self: Root, stream: STREAM] RETURNS [File] ~ {
ref: FileRef ~ self.zone.NEW[FileRep ← [stream: stream]];
RETURN [[val: [executable: FALSE, variant: file[access: unlimited]], ref: ref]];
};
CloseFile: PUBLIC PROC [file: File] ~ {
stream: STREAM ~ file.ref.stream;
IO.Close[stream];
};
Read: PUBLIC PROC [file: File] RETURNS [CHAR] ~ {
ENABLE {
IO.EndOfStream => GOTO End;
IO.Error => IF ec=StreamClosed THEN GOTO Closed ELSE GOTO IOError;
};
stream: STREAM ~ file.ref.stream;
RETURN[IO.GetChar[stream]];
EXITS
End, Closed => ERROR EndOfFile;
IOError => ERROR Error[ioerror];
};
Write: PUBLIC PROC [file: File, char: CHAR] ~ {
ENABLE {
IO.Error => GOTO IOError;
};
stream: STREAM ~ file.ref.stream;
IO.PutChar[stream, char];
EXITS
IOError => ERROR Error[ioerror];
};
HexDigit: TYPE ~ [0..16);
ReadHexString: PUBLIC PROC [self: Root, file: File, string: String] RETURNS [String, BOOL] ~ {
state: {hex0, hex1} ← hex0;
d0, d1: HexDigit ← 0;
length: INT ~ StringLength[string];
index: INT ← 0;
eof: BOOLFALSE;
WHILE index<length DO
char: CHAR ~ Read[file ! EndOfFile => { eof ← TRUE; EXIT }];
SELECT char FROM
IN ['0..'9] => d1 ← char-'0;
IN ['A..'F] => d1 ← 10+(char-'A);
IN ['a..'f] => d1 ← 10+(char-'a);
ENDCASE => LOOP;
SELECT state FROM
hex0 => { d0 ← d1; state ← hex1 };
hex1 => { StringPut[self, string, index, VAL[d0*16+d1]]; index ← index+1; state ← hex0 };
ENDCASE => ERROR Bug;
ENDLOOP;
IF state=hex1 THEN { StringPut[self, string, index, VAL[d0*16]]; index ← index+1 };
RETURN [StringGetInterval[string, 0, index], NOT eof];
};
HexArray: TYPE ~ PACKED ARRAY HexDigit OF CHAR;
hex: REF HexArray ~ NEW[HexArray ← ['0, '1, '2, '3, '4, '5, '6, '7, '8, '9, 'a, 'b, 'c, 'd, 'e, 'f]];
WriteHexString: PUBLIC PROC [file: File, string: String] ~ {
length: INT ~ StringLength[string];
FOR index: INT IN [0..length) DO
char: CHAR ~ StringGet[string, index];
d0: HexDigit ~ ORD[char]/16;
d1: HexDigit ~ ORD[char] MOD 16;
Write[file, hex[d0]];
Write[file, hex[d1]];
ENDLOOP;
};
ReadString: PUBLIC PROC [self: Root, file: File, string: String] RETURNS [String, BOOL] ~ {
length: INT ~ StringLength[string];
index: INT ← 0;
eof: BOOLFALSE;
WHILE index<length DO
char: CHAR ~ Read[file ! EndOfFile => { eof ← TRUE; EXIT }];
StringPut[self, string, index, char];
index ← index+1;
ENDLOOP;
RETURN [StringGetInterval[string, 0, index], NOT eof];
};
WriteString: PUBLIC PROC [file: File, string: String] ~ {
length: INT ~ StringLength[string];
FOR index: INT IN [0..length) DO
char: CHAR ~ StringGet[string, index];
Write[file, char];
ENDLOOP;
};
ReadLine: PUBLIC PROC [self: Root, file: File, string: String] RETURNS [String, BOOL] ~ {
index: INT ← 0;
eof: BOOLFALSE;
DO
char: CHAR ~ Read[file ! EndOfFile => { eof ← TRUE; EXIT }];
SELECT char FROM Ascii.CR, Ascii.LF => EXIT ENDCASE;
StringPut[self, string, index, char];
index ← index+1;
ENDLOOP;
RETURN [StringGetInterval[string, 0, index], NOT eof];
};
BytesAvailable: PUBLIC PROC [file: File] RETURNS [INT] ~ {
ENABLE {
IO.Error => IF ec=StreamClosed THEN GOTO Closed ELSE GOTO IOError;
};
stream: STREAM ~ file.ref.stream;
IF IO.EndOf[stream] THEN RETURN [-1];
RETURN [IO.CharsAvail[stream]];
EXITS
Closed => RETURN [-1];
IOError => ERROR Error[ioerror];
};
FlushFile: PUBLIC PROC [file: File] ~ {
ENABLE {
IO.Error => IF ec=StreamClosed THEN GOTO Closed ELSE GOTO IOError;
};
stream: STREAM ~ file.ref.stream;
variety: IO.StreamVariety ~ IO.GetInfo[stream].variety;
SELECT variety FROM
input, inputOutput => UNTIL IO.EndOf[stream] DO [] ← IO.GetChar[stream] ENDLOOP;
ENDCASE;
SELECT variety FROM
output, inputOutput => IO.Flush[stream];
ENDCASE;
EXITS
Closed => NULL;
IOError => ERROR Error[ioerror];
};
ResetFile: PUBLIC PROC [file: File] ~ {
ENABLE {
IO.Error => IF ec=StreamClosed THEN GOTO Closed ELSE GOTO IOError;
};
stream: STREAM ~ file.ref.stream;
IO.Reset[stream];
EXITS
Closed => NULL;
IOError => ERROR Error[ioerror];
};
Status: PUBLIC PROC [file: File] RETURNS [BOOL] ~ {
ENABLE {
IO.Error => GOTO IOError;
};
stream: STREAM ~ file.ref.stream;
RETURN [IO.GetInfo[stream].class#$Closed];
EXITS
IOError => RETURN [FALSE];
};
Echo: PUBLIC PROC [self: Root, echo: BOOL] ~ {
};
Virtual memory operators
Save: PUBLIC PROC [self: Root] RETURNS [Level] ~ {
IF self.level<Level.LAST THEN {
level: Level ~ self.level;
self.level ← level+1;
RETURN [level];
}
ELSE ERROR Error[limitcheck];
};
Restore: PUBLIC PROC [self: Root, level: Level] ~ {
IF NOT level<self.level THEN ERROR Error[invalidrestore];
*** should check the stacks here ***
WHILE self.level>level DO
self.level ← self.level-1;
FOR item: RestoreItem ← self.restore[self.level], item.next UNTIL item=NIL DO
WITH item SELECT FROM
item: REF RestoreItemRep.array => {
item.ref.level ← item.copy.level;
FOR i: ArrayIndex IN[0..item.ref.size) DO item.ref[i] ← item.copy[i] ENDLOOP;
};
item: REF RestoreItemRep.string => {
item.ref.level ← item.copy.level;
FOR i: StringIndex IN[0..item.ref.size) DO item.ref[i] ← item.copy[i] ENDLOOP;
};
item: REF RestoreItemRep.dict => {
item.ref.level ← item.copy.level;
item.ref.access ← item.copy.access;
item.ref.length ← item.copy.length;
item.ref.maxlength ← item.copy.maxlength;
FOR i: DictIndex IN[0..item.ref.mod) DO item.ref[i] ← item.copy[i] ENDLOOP;
};
ENDCASE => ERROR Bug;
ENDLOOP;
self.restore[self.level] ← NIL;
ENDLOOP;
};
END.