PSStringImpl.mesa
Copyright Ó 1986, 1987 by Xerox Corporation. All rights reserved.
Doug Wyatt, May 12, 1987 3:53:52 pm PDT
PostScript implementation: string, and name operations.
DIRECTORY
PS,
Atom USING [MakeAtom],
Rope USING [Fetch, Size];
PSStringImpl: CEDAR PROGRAM
IMPORTS PS, Atom, Rope
~ BEGIN OPEN PS;
Basic String operations
StringCreate: PUBLIC PROC [size: INT] RETURNS [String] ~ {
IF size<0 THEN ERROR Error[rangecheck];
IF size IN StringIndex THEN {
ref: StringRef ~ NEW[TEXT[size]];
FOR i: StringIndex IN[0..ref.maxLength) DO ref[i] ← VAL[0] ENDLOOP;
RETURN[[
val: [executable: FALSE, variant: string[access: unlimited, start: 0, length: size]],
ref: ref
]];
}
ELSE ERROR Error[limitcheck];
};
StringAccess: PUBLIC PROC [string: String] RETURNS [Access] ~ {
RETURN [string.val.access];
};
StringLength: PUBLIC PROC [string: String] RETURNS [INT] ~ {
RETURN [string.val.length];
};
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]];
};
StringPut: PUBLIC PROC [string: String, index: INT, x: CHAR] ~ {
IF index NOT IN[0..string.val.length) THEN ERROR Error[rangecheck];
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
]];
};
MesaStringGet: PUBLIC PROC [string: String, index: INT] RETURNS [CHAR] ~ {
IF index NOT IN [0..string.length) THEN ERROR Error[rangecheck];
RETURN [string.pointer[string.offset+index]];
};
MesaStringPut: PUBLIC PROC [string: String, index: INT, x: CHAR] ~ {
IF index NOT IN[0..string.length) THEN ERROR Error[rangecheck];
string.pointer[string.offset+index] ← x;
};
MesaStringGetInterval: PUBLIC PROC [string: String, index, count: INT] RETURNS [String] ~ {
IF index NOT IN [0..string.length] THEN ERROR Error[rangecheck];
IF count NOT IN [0..(string.length-index)] THEN ERROR Error[rangecheck];
{
byteOffset: StringLength ~ string.offset+index;
RETURN[[
executable: string.executable,
variant: string[
access: string.access,
offset: byteOffset MOD bytesPerWord,
length: count,
pointer: string.pointer+byteOffset/bytesPerWord
]
]];
};
};
Other String operations
StringPutInterval: PUBLIC PROC [string: String, index: INT, interval: String] ~ {
substring: String ~ StringGetInterval[string, index, StringLength[interval]];
***** use ByteBlt here? *****
FOR i: INT IN[0..StringLength[substring]) DO
StringPut[substring, i, StringGet[interval, i]];
ENDLOOP;
};
StringCopy: PUBLIC PROC [string1, string2: String] RETURNS [String] ~ {
substring2: String ~ StringGetInterval[string2, 0, StringLength[string1]];
StringPutInterval[substring2, 0, string1];
RETURN [substring2];
};
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];
};
StringCompare: PUBLIC PROC [string1, string2: String] RETURNS [Comparison] ~ {
length1: INT ~ StringLength[string1];
length2: INT ~ StringLength[string2];
FOR index: INT IN[0..MIN[length1, length2]) DO
char1: CHAR ~ StringGet[string1, index];
char2: CHAR ~ StringGet[string2, index];
IF char1=char2 THEN NULL
ELSE RETURN [IF char1<char2 THEN less ELSE greater];
ENDLOOP;
IF length1=length2 THEN RETURN [equal]
ELSE RETURN [IF length1<length2 THEN less ELSE greater];
};
StringSearch: PUBLIC PROC [string, seek: String, anchor: BOOL]
RETURNS [found: BOOLFALSE, index: INT ← 0] ~ {
stringLength: INT ~ StringLength[string];
seekLength: INT ~ StringLength[seek];
UNTIL found OR (stringLength-index)<seekLength 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 anchor THEN EXIT ELSE index ← index+1;
ENDLOOP;
};
StringFromRope: PUBLIC PROC [rope: ROPE, executable: BOOL] RETURNS [String] ~ {
length: INT ~ Rope.Size[rope];
string: String ~ StringCreate[length];
FOR i: INT IN [0..length) DO
StringPut[string, i, Rope.Fetch[rope, i]];
ENDLOOP;
RETURN [string];
};
Name operations
AtomFromString: PROC [string: String] RETURNS [atom: ATOM] ~ {
scratch: REF TEXT ~ RefText.ObtainScratch[string.length];
text: REF TEXT ← scratch;
text ← RefText.Append[to: text, from: string.base, start: string.start, len: string.length];
atom ← Atom.MakeAtomFromRefText[text];
RefText.ReleaseScratch[scratch];
};
NameFromString: PROC [string: String, executable: BOOLTRUE] RETURNS [Name] ~ {
RETURN [[executable: executable, atom: AtomFromString[string]]];
};
NameFromRope: PROC [rope: ROPE, executable: BOOLTRUE] RETURNS [Name] ~ {
RETURN [[
val: [executable: executable, variant: name[]],
ref: Atom.MakeAtom[rope]
]];
};
NameToString: PROC [name: Name, string: String] RETURNS [String] ~ {
rope: Rope.ROPE ~ Atom.GetPName[name.atom];
count: INT ~ Rope.Size[rope];
substring: String ~ StringGetInterval[string, 0, count];
FOR i: INT IN[0..count) DO
substring.base[substring.start+i] ← Rope.Fetch[rope, i];
ENDLOOP;
RETURN [substring];
};
Primitives
Pstring: PROC [self: Root] ~ {
size: INT ~ PopInt[self];
PushString[self, StringCreate[size]];
};
Panchorsearch: PROC [self: Root] ~ {
seek: String ~ PopString[self];
string: String ~ PopString[self];
found: BOOL; index: INT;
IF StringAccess[string]<readOnly THEN ERROR Error[invalidaccess];
IF StringAccess[seek]<readOnly THEN ERROR Error[invalidaccess];
[found: found, index: index] ← StringSearch[string: string, seek: seek, anchor: TRUE];
IF index#0 THEN ERROR Bug;
IF found THEN {
matchLength: INT ~ StringLength[seek];
match: String ~ StringGetInterval[string, 0, matchLength];
post: String ~ StringGetInterval[string, matchLength, StringLength[string]-matchLength];
PushString[self, post];
PushString[self, match];
PushBool[self, TRUE];
}
ELSE {
PushString[self, string];
PushBool[self, FALSE];
};
};
Psearch: PROC [self: Root] ~ {
seek: String ~ PopString[self];
string: String ~ PopString[self];
found: BOOL; matchIndex: INT;
IF StringAccess[string]<readOnly THEN ERROR Error[invalidaccess];
IF StringAccess[seek]<readOnly THEN ERROR Error[invalidaccess];
[found: found, index: matchIndex] ← StringSearch[string: string, seek: seek, anchor: FALSE];
IF found THEN {
matchLength: INT ~ StringLength[seek];
postIndex: INT ~ matchIndex+matchLength;
pre: String ~ StringGetInterval[string, 0, matchIndex];
match: String ~ StringGetInterval[string, matchIndex, matchLength];
post: String ~ StringGetInterval[string, postIndex, StringLength[string]-postIndex];
PushString[self, post];
PushString[self, match];
PushString[self, pre];
PushBool[self, TRUE];
}
ELSE {
PushString[self, string];
PushBool[self, FALSE];
};
};
StringPrimitives: PROC [self: Root] ~ {
Register[self, "string", Pstring];
Register[self, "anchorsearch", Panchorsearch];
Register[self, "search", Psearch];
};
RegisterPrimitives[StringPrimitives];
END.