123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397 |
- MODULE Strings;
- (**
- This is a small library of common string manipulation command such as find, compare,
- append, etc.
-
- 001 2006-06-16 tt: Added copy, append
- 002 2006-06-14 fof : Equals -> Equal
- 003 2006-06-29 tt: changed header format
- 004 2006-11-16 tt: Added EqualIgnoreCase, changed Equal
- 005 2006-12-07 fof: added terminator for non-null terminated strings in Copy
- 006 2006-12-08 fof: added IntToString, RealToString Append* methods
- 007 2007-02-07 tt: Added Length
- 008 2007-02-08 tt: Added AppendSet
- 009 2007-07-03 tt: Formatted and updated documentation
- *)
- IMPORT SYSTEM, Utils, Trace;
- CONST
- (* The Ascii value of char "0" *)
- ToLowerCaseDiff = 30H;
- (* MAX(LONGINT)*)
- (*MAXLONGINT = 7FFFFFFFH; *)
- (* MIN(LONGINT)*)
- (*MINLONGINT = 80000000H; *)
-
- (* Get the length of a string including terminating 0X *)
- PROCEDURE Length*(CONST s: ARRAY OF CHAR): LONGINT;
- VAR length: LONGINT;
- BEGIN
- length := 0;
- REPEAT INC(length)
- UNTIL (length >= LEN(s)) OR (s[length - 1] = 0X);
- RETURN length
- END Length;
- (* Return the capital letter of character "ch" *)
- (*
- PROCEDURE CAP*(ch: CHAR): CHAR;
- BEGIN
- IF (ch >= 'a') & (ch <= 'z') THEN
- ch := CHR(ORD(ch) - 32); (* Convert small letter to capital letter *)
- END;
- RETURN ch
- END CAP;
- *)
-
- (* Return the minumum vaule of two given integers *)
- PROCEDURE Min(a, b: LONGINT): LONGINT;
- BEGIN
- IF b < a THEN a := b END;
- RETURN a
- END Min;
- (* Compares two strings.
- 0: The two Strings are equal
- <0: The first unequal character in the first string of the two strings is smaller (ascii value)
- >0: The first unequal character in the first string of the two strings is larger (ascii value) *)
- PROCEDURE Compare*(CONST s1, s2: ARRAY OF CHAR): LONGINT;
- VAR i, len: LONGINT;
- BEGIN
- i := 0;
- len := Min(LEN(s1) - 1, LEN(s2) - 1);
- WHILE (i < len) & (s1[i] = s2[i]) & (s1[i] # 0X) & (s2[i] # 0X) DO INC(i) END;
- RETURN ORD(s1[i]) - ORD(s2[i])
- END Compare;
- (* Returns TRUE if s1 and s2 are equal. The case of all characters is ignored *)
- PROCEDURE EqualIgnoreCase*(CONST s1, s2: ARRAY OF CHAR): BOOLEAN;
- VAR i, len: LONGINT;
- BEGIN
- i := 0; len := Min(LEN(s1) - 1, LEN(s2) - 1);
- WHILE (i < len) & (CAP(s1[i]) = CAP(s2[i])) & (s1[i] # 0X) & (s2[i] # 0X) DO INC(i); END;
- RETURN CAP(s1[i]) = CAP(s2[i])
- END EqualIgnoreCase;
- (* Convert a string (ascii reoresentation of a number) to an integer *)
- PROCEDURE StrToInt*(CONST str: ARRAY OF CHAR; VAR val: LONGINT; res: BOOLEAN);
- VAR i, d: LONGINT; neg: BOOLEAN; ch: CHAR;
- BEGIN
- res := TRUE;
- i := 0; ch := str[0];
- WHILE (ch # 0X) & (ch <= ' ') DO INC(i); ch := str[i] END;
- neg := FALSE;
- IF ch = '+' THEN INC(i); ch := str[i] END;
- IF ch = '-' THEN neg := TRUE; INC(i); ch := str[i] END;
- WHILE (ch # 0X) & (ch <= ' ') DO INC(i); ch := str[i] END;
- val := 0;
- WHILE (ch >= '0') & (ch <= '9') DO
- d := ORD(ch) - ORD('0'); INC(i); ch := str[i];
- IF val <= ((MAX(LONGINT) - d) DIV 10) THEN
- val := 10 * val + d
- ELSIF neg & (val = 214748364) & (d = 8) & ((ch < '0') OR (ch > '9')) THEN
- val := MIN(LONGINT); neg := FALSE
- ELSE
- (* Invalid number found -> set res to FALSE and abort loop *)
- res := FALSE;
- ch := 0X;
- END
- END;
- IF neg THEN val := -val END
- END StrToInt;
- (* Finds the first occurrence of character ch in string s starting at pos start in s and returns the
- index. Returns -1 if ch cannot be found *)
- PROCEDURE Find*(ch: CHAR; CONST s: ARRAY OF CHAR; start: LONGINT): LONGINT;
- VAR found, i: LONGINT;
- BEGIN
- found := -1;
- WHILE (start < LEN(s)) & (s[start] # 0X) & (s[start] # ch) DO INC(start); END;
- IF (start < LEN(s)) & (s[start] = ch) THEN found := start; END;
- RETURN found
- END Find;
- (* Find in s the string stored in pat and start searching in s at location start.
- Returns -1 if not found, otherwise the index of the first found character in s *)
- PROCEDURE FindString*(CONST pat, s: ARRAY OF CHAR; start: LONGINT): LONGINT;
- VAR found, i, patLen, sLen: LONGINT;
- BEGIN
- found := -1; patLen := LEN(pat); sLen := LEN(s);
- WHILE (start < sLen) & (s[start] # 0X) & (found = -1) DO
- i := 0;
- WHILE (i < patLen) & (pat[i] = s[start + i]) & (pat[i] # 0X) & (s[i] # 0X) DO
- INC(i)
- END;
- IF i = patLen THEN found := start END;
- INC(start)
- END;
- RETURN found
- END FindString;
- (* Copy the whole string (0X terminated) from source to derst
- In contrast to the assignment dest := source, only the 0X terminated
- part of source is copied to dest. *)
- PROCEDURE Copy*(CONST source: ARRAY OF CHAR; VAR dest: ARRAY OF CHAR);
- VAR i: LONGINT;
- BEGIN
- i := 0;
- REPEAT dest[i] := source[i]; INC(i)
- UNTIL (dest[i - 1] = 0X) OR (LEN(source) = i) OR (LEN(dest) = i);
- IF i < LEN(dest) THEN dest[i] := 0X ELSE dest[i - 1] := 0X END; (*@4 fof: if source was not 0X terminated *)
- END Copy;
- (* Convert integer val to a 0X terminated string and store it in str. *)
- PROCEDURE IntToStr*(val: LONGINT; VAR str: ARRAY OF CHAR);
- VAR i, j: LONGINT; digits: ARRAY 16 OF LONGINT;
- BEGIN
- IF val = MIN(LONGINT) THEN
- str := "-2147483648";
- ELSE
- i := 0;
- IF val < 0 THEN
- val := -val; str[0] := '-'; j := 1
- ELSE
- j := 0
- END;
- REPEAT
- digits[i] := val MOD 10;
- INC(i);
- val := val DIV 10
- UNTIL val = 0;
- DEC(i);
-
- WHILE i >= 0 DO
- str[j] := CHR(digits[i] + ORD('0'));
- INC(j);
- DEC(i)
- END;
- str[j] := 0X;
- END;
- END IntToStr;
- (* Convert boolean value into a string *)
- PROCEDURE BoolToStr*(CONST bool: BOOLEAN; VAR str: ARRAY OF CHAR);
- BEGIN
- IF bool THEN
- str := "True";
- ELSE
- str := "False";
- END;
- END BoolToStr;
- (* Convert a string into a boolean *)
- PROCEDURE StrToBool*(CONST str: ARRAY OF CHAR; VAR bool: BOOLEAN; VAR res: BOOLEAN);
- BEGIN
- res := TRUE;
- IF EqualIgnoreCase(str, "true") THEN
- bool := TRUE;
- ELSIF EqualIgnoreCase(str, "false") THEN
- bool := FALSE;
- ELSE
- res := FALSE;
- END;
- END StrToBool;
- (* Returns the shifted binary exponent of a real (0 <= e < 256 *)
- PROCEDURE Expo*(x: REAL): LONGINT;
- VAR e: LONGINT;
- BEGIN
- (* Replaced the following code with safe variant *)
- (* RETURN ASR(SYSTEM.VAL(LONGINT, x), 23) MOD 256 *)
- Utils.UNPK(x, e);
- RETURN (e + 127) MOD 256
- END Expo;
- (* Returns 10^e (e <= 308, 308 < e delivers IEEE-code +INF). *)
- PROCEDURE Ten(e: LONGINT): REAL;
- VAR res: REAL;
- BEGIN
- (* hack! *)
- IF e < -38 THEN
- res := 0.0;
- ELSIF e > 38 THEN
- res := MAX(REAL);
- ELSE
- res := 1.0;
- WHILE (e > 0) DO res := res * 10.0; DEC(e); END;
- WHILE (e < 0) DO res := res / 10.0; INC(e); END;
- END;
- RETURN res
- END Ten;
- (* Returns the NaN code (0 <= c < 8399608) or -1 if not NaN/Infinite. *)
- PROCEDURE NaNCode(x: REAL): LONGINT;
- VAR l: LONGINT;
- BEGIN
- IF Expo(x) = 255 THEN (* Infinite or NaN *)
- l := SYSTEM.VAL(LONGINT, x) MOD 800000H;
- ELSE
- l := -1;
- END;
- RETURN l
- END NaNCode;
- (** truncates string to length *)
- PROCEDURE Truncate* (VAR string: ARRAY OF CHAR; length: LONGINT);
- BEGIN
- IF LEN(string) > length THEN string[length] := 0X END;
- END Truncate;
- (** copies src[soff ... soff + len - 1] to dst[doff ... doff + len - 1] *)
- PROCEDURE Move* (CONST src: ARRAY OF CHAR; soff, len: LONGINT; VAR dst: ARRAY OF CHAR; doff: LONGINT);
- BEGIN
- (* reverse copy direction in case src and dst denote the same string *)
- IF soff < doff THEN
- INC (soff, len - 1); INC (doff, len - 1);
- WHILE len > 0 DO dst[doff] := src[soff]; DEC (soff); DEC (doff); DEC (len) END
- ELSE
- WHILE len > 0 DO dst[doff] := src[soff]; INC (soff); INC (doff); DEC (len) END
- END;
- END Move;
- (** concatenates s1 and s2: s := s1 || s2 *)
- PROCEDURE Concat* (CONST s1, s2: ARRAY OF CHAR; VAR s: ARRAY OF CHAR);
- VAR len1, len2 : LONGINT;
- BEGIN
- len1 := Length (s1); len2 := Length (s2);
- Move(s2, 0, len2, s, len1);
- Move (s1, 0, len1, s, 0);
- Truncate (s, len1 + len2);
- END Concat;
- (** concatenates s1 and s2: s := s1 || s2. The resulting string is truncated to the length of s if necessary *)
- PROCEDURE ConcatX*(CONST s1, s2 : ARRAY OF CHAR; VAR s : ARRAY OF CHAR);
- VAR len1, len2 : LONGINT;
- BEGIN
- len1 := Length (s1); len2 := Length (s2);
- IF (len1 + 1 >= LEN(s)) THEN
- COPY(s1, s);
- ELSE
- IF (len1 + len2 + 1 > LEN(s)) THEN
- len2 := LEN(s) - 1 - len1;
- END;
- Move(s2, 0, len2, s, len1);
- Move (s1, 0, len1, s, 0);
- Truncate (s, len1 + len2);
- END;
- END ConcatX;
- (* Append "this" to "to". Copies as much as is possible to "to" (0X terminated) *)
- PROCEDURE Append*(VAR to: ARRAY OF CHAR; CONST this: ARRAY OF CHAR);
- VAR i, j: LONGINT;
- BEGIN
- i := 0; j := 0;
- WHILE (i < LEN(to)) & (to[i] # 0X) DO
- INC(i)
- END;
- WHILE (i < LEN(to)) & (j < LEN(this)) & (this[j] # 0X) DO
- to[i] := this[j];
- INC(i); INC(j)
- END;
-
- (* 0X terminate the string *)
- IF j > 0 THEN (* tt: Appending of the empty string must not result in a trap *)
- IF (this[j - 1] = 0X) OR (i = LEN(to)) THEN
- to[i - 1] := 0X
- ELSE
- to[i] := 0X
- END; (*@4 fof: if source was not 0X terminated *)
- END;
- END Append;
- (* Append a character at the end of a string *)
- PROCEDURE AppendChar*(VAR to: ARRAY OF CHAR; c: CHAR);
- VAR str: ARRAY 4 OF CHAR;
- BEGIN
- str[0] := c; str[1] := 0X; Append(to, str);
- END AppendChar;
- (** Write real x to buffer str as ascii text *)
- PROCEDURE AppendReal*(VAR str: ARRAY OF CHAR; x: REAL);
- VAR
- e, h, i, n: LONGINT;
- y, z, temp05: REAL;
- d: ARRAY 8 OF CHAR;
- BEGIN
- n := 14; (* larger number of n do not really make sense *)
- e := Expo(x);
- IF e = 255 THEN
- WHILE n > 8 DO AppendChar(str, ' '); DEC(n) END;
- h := NaNCode(x);
- IF h # 0 THEN Append(str, " NaN")
- ELSIF x < 0.0 THEN Append(str, " -INF")
- ELSE Append(str, " INF")
- END
- ELSE
- IF n <= 7 THEN n := 0 ELSE DEC(n, 7) END;
- WHILE (n > 7) DO AppendChar(str, ' '); DEC(n) END; (* 0 <= n <= 7 fraction digits *)
- IF (e # 0) & (x < 0.0) THEN AppendChar(str, '-'); x := -x
- ELSE AppendChar(str, ' ')
- END;
- IF e = 0 THEN
- h := 0 (* no denormals *)
- ELSE
- e := (e - 127) * 301 DIV 1000; (* ln(2)/ln(10) = 0.301029996 *)
- IF e < 38 THEN
- z := Ten(e + 1);
- IF x >= z THEN y := x / z; INC(e) ELSE y := x * Ten(-e) END
- ELSE y := x * Ten(-38)
- END;
- IF y >= 10.0 THEN y := y * Ten(-1); y := y + 0.5E0 / Ten(n); INC(e)
- ELSE
- temp05 := 0.5E0; (* Otherwise not compilable *)
- y := y + temp05 / Ten(n);
- IF y >= 10.0 THEN y := y * Ten(-1); INC(e) END
- END;
- y := y * Ten(7); h := ENTIER(y)
- END;
- i := 7;
- WHILE i >= 0 DO d[i] := CHR(h MOD 10 + ORD('0')); h := h DIV 10; DEC(i) END;
- AppendChar(str, d[0]); AppendChar(str, '.'); i := 1;
- WHILE i <= n DO AppendChar(str, d[i]); INC(i) END;
- IF e < 0 THEN Append(str, "E-"); e := -e ELSE Append(str, "E+") END;
- i := e DIV 10; AppendChar(str, CHR(i + ORD('0'))); i := e MOD 10;
- AppendChar(str, CHR(i + ORD('0')))
- END
- END AppendReal;
- (* Append an integer to a string *)
- PROCEDURE AppendInt*(VAR to: ARRAY OF CHAR; i: LONGINT);
- VAR str: ARRAY 64 OF CHAR;
- BEGIN
- IntToStr(i, str); Append(to, str);
- END AppendInt;
- (* Append a set to a string *)
- PROCEDURE AppendSet*(VAR to: ARRAY OF CHAR; s: SET);
- VAR first: BOOLEAN; i: LONGINT;
- BEGIN
- first := TRUE; AppendChar(to, '{');
- FOR i := 0 TO 31 DO
- IF i IN s THEN
- IF ~first THEN AppendChar(to, ',') END;
- first := FALSE; AppendInt(to, i);
- END
- END;
- AppendChar(to, '}');
- END AppendSet;
- (* Append a boolean to a string *)
- PROCEDURE AppendBool*(VAR to: ARRAY OF CHAR; b: BOOLEAN);
- BEGIN
- IF b THEN Append(to, "TRUE") ELSE Append(to, "FALSE") END;
- END AppendBool;
- (* Convert a real to a string *)
- PROCEDURE RealToStr*(r: REAL; VAR str: ARRAY OF CHAR);
- BEGIN
- str[0] := 0X; AppendReal(str, r);
- END RealToStr;
- BEGIN
- Trace.StringLn("Strings.");
- END Strings.
|