<> <> <> <> <<>> DIRECTORY PS, Ascii, FS, IO, PrincOps, Rope; PSLanguage2Impl: CEDAR MONITOR IMPORTS PS, FS, IO, Rope EXPORTS PS ~ BEGIN OPEN PS; <> 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[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> 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[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 found _ TRUE; ENDLOOP; IF found OR anchor THEN EXIT ELSE index _ index+1; ENDLOOP; }; <> 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] _ NIL 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 tuple: Tuple ~ dict.ref[index]; SELECT TRUE FROM tuple=NIL => RETURN [FALSE, null]; InlineEq[tuple.key, key] => RETURN [TRUE, tuple.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 tuple: Tuple ~ dict.ref[index]; SELECT TRUE FROM tuple=NIL => RETURN [FALSE, null]; EqText[tuple.key, text] => RETURN [TRUE, tuple.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[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 { IF NOT dict.ref.length { tuple.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 tuple: Tuple ~ dict.ref[index]; IF Type[tuple.key]#null THEN 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]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 [INT] ~ { 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]; }; <> 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]]]; }; <<>> <> 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: 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]]]; }; < xxx;>> < xxx;>> < 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 { 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 { 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] ~ { }; END.