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:
BOOL ←
FALSE, 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:
BOOL ←
FALSE] ~ {
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;
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: STREAM ← NIL;
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: BOOL ← FALSE;
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: BOOL ← FALSE;
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: BOOL ← FALSE;
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] ~ {
};