Strings.Mos 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. MODULE Strings;
  2. (**
  3. This is a small library of common string manipulation command such as find, compare,
  4. append, etc.
  5. 001 2006-06-16 tt: Added copy, append
  6. 002 2006-06-14 fof : Equals -> Equal
  7. 003 2006-06-29 tt: changed header format
  8. 004 2006-11-16 tt: Added EqualIgnoreCase, changed Equal
  9. 005 2006-12-07 fof: added terminator for non-null terminated strings in Copy
  10. 006 2006-12-08 fof: added IntToString, RealToString Append* methods
  11. 007 2007-02-07 tt: Added Length
  12. 008 2007-02-08 tt: Added AppendSet
  13. 009 2007-07-03 tt: Formatted and updated documentation
  14. *)
  15. IMPORT SYSTEM, Utils, Trace;
  16. CONST
  17. (* The Ascii value of char "0" *)
  18. ToLowerCaseDiff = 30H;
  19. (* MAX(LONGINT)*)
  20. (*MAXLONGINT = 7FFFFFFFH; *)
  21. (* MIN(LONGINT)*)
  22. (*MINLONGINT = 80000000H; *)
  23. (* Get the length of a string including terminating 0X *)
  24. PROCEDURE Length*(CONST s: ARRAY OF CHAR): LONGINT;
  25. VAR length: LONGINT;
  26. BEGIN
  27. length := 0;
  28. REPEAT INC(length)
  29. UNTIL (length >= LEN(s)) OR (s[length - 1] = 0X);
  30. RETURN length
  31. END Length;
  32. (* Return the capital letter of character "ch" *)
  33. (*
  34. PROCEDURE CAP*(ch: CHAR): CHAR;
  35. BEGIN
  36. IF (ch >= 'a') & (ch <= 'z') THEN
  37. ch := CHR(ORD(ch) - 32); (* Convert small letter to capital letter *)
  38. END;
  39. RETURN ch
  40. END CAP;
  41. *)
  42. (* Return the minumum vaule of two given integers *)
  43. PROCEDURE Min(a, b: LONGINT): LONGINT;
  44. BEGIN
  45. IF b < a THEN a := b END;
  46. RETURN a
  47. END Min;
  48. (* Compares two strings.
  49. 0: The two Strings are equal
  50. <0: The first unequal character in the first string of the two strings is smaller (ascii value)
  51. >0: The first unequal character in the first string of the two strings is larger (ascii value) *)
  52. PROCEDURE Compare*(CONST s1, s2: ARRAY OF CHAR): LONGINT;
  53. VAR i, len: LONGINT;
  54. BEGIN
  55. i := 0;
  56. len := Min(LEN(s1) - 1, LEN(s2) - 1);
  57. WHILE (i < len) & (s1[i] = s2[i]) & (s1[i] # 0X) & (s2[i] # 0X) DO INC(i) END;
  58. RETURN ORD(s1[i]) - ORD(s2[i])
  59. END Compare;
  60. (* Returns TRUE if s1 and s2 are equal. The case of all characters is ignored *)
  61. PROCEDURE EqualIgnoreCase*(CONST s1, s2: ARRAY OF CHAR): BOOLEAN;
  62. VAR i, len: LONGINT;
  63. BEGIN
  64. i := 0; len := Min(LEN(s1) - 1, LEN(s2) - 1);
  65. WHILE (i < len) & (CAP(s1[i]) = CAP(s2[i])) & (s1[i] # 0X) & (s2[i] # 0X) DO INC(i); END;
  66. RETURN CAP(s1[i]) = CAP(s2[i])
  67. END EqualIgnoreCase;
  68. (* Convert a string (ascii reoresentation of a number) to an integer *)
  69. PROCEDURE StrToInt*(CONST str: ARRAY OF CHAR; VAR val: LONGINT; res: BOOLEAN);
  70. VAR i, d: LONGINT; neg: BOOLEAN; ch: CHAR;
  71. BEGIN
  72. res := TRUE;
  73. i := 0; ch := str[0];
  74. WHILE (ch # 0X) & (ch <= ' ') DO INC(i); ch := str[i] END;
  75. neg := FALSE;
  76. IF ch = '+' THEN INC(i); ch := str[i] END;
  77. IF ch = '-' THEN neg := TRUE; INC(i); ch := str[i] END;
  78. WHILE (ch # 0X) & (ch <= ' ') DO INC(i); ch := str[i] END;
  79. val := 0;
  80. WHILE (ch >= '0') & (ch <= '9') DO
  81. d := ORD(ch) - ORD('0'); INC(i); ch := str[i];
  82. IF val <= ((MAX(LONGINT) - d) DIV 10) THEN
  83. val := 10 * val + d
  84. ELSIF neg & (val = 214748364) & (d = 8) & ((ch < '0') OR (ch > '9')) THEN
  85. val := MIN(LONGINT); neg := FALSE
  86. ELSE
  87. (* Invalid number found -> set res to FALSE and abort loop *)
  88. res := FALSE;
  89. ch := 0X;
  90. END
  91. END;
  92. IF neg THEN val := -val END
  93. END StrToInt;
  94. (* Finds the first occurrence of character ch in string s starting at pos start in s and returns the
  95. index. Returns -1 if ch cannot be found *)
  96. PROCEDURE Find*(ch: CHAR; CONST s: ARRAY OF CHAR; start: LONGINT): LONGINT;
  97. VAR found, i: LONGINT;
  98. BEGIN
  99. found := -1;
  100. WHILE (start < LEN(s)) & (s[start] # 0X) & (s[start] # ch) DO INC(start); END;
  101. IF (start < LEN(s)) & (s[start] = ch) THEN found := start; END;
  102. RETURN found
  103. END Find;
  104. (* Find in s the string stored in pat and start searching in s at location start.
  105. Returns -1 if not found, otherwise the index of the first found character in s *)
  106. PROCEDURE FindString*(CONST pat, s: ARRAY OF CHAR; start: LONGINT): LONGINT;
  107. VAR found, i, patLen, sLen: LONGINT;
  108. BEGIN
  109. found := -1; patLen := LEN(pat); sLen := LEN(s);
  110. WHILE (start < sLen) & (s[start] # 0X) & (found = -1) DO
  111. i := 0;
  112. WHILE (i < patLen) & (pat[i] = s[start + i]) & (pat[i] # 0X) & (s[i] # 0X) DO
  113. INC(i)
  114. END;
  115. IF i = patLen THEN found := start END;
  116. INC(start)
  117. END;
  118. RETURN found
  119. END FindString;
  120. (* Copy the whole string (0X terminated) from source to derst
  121. In contrast to the assignment dest := source, only the 0X terminated
  122. part of source is copied to dest. *)
  123. PROCEDURE Copy*(CONST source: ARRAY OF CHAR; VAR dest: ARRAY OF CHAR);
  124. VAR i: LONGINT;
  125. BEGIN
  126. i := 0;
  127. REPEAT dest[i] := source[i]; INC(i)
  128. UNTIL (dest[i - 1] = 0X) OR (LEN(source) = i) OR (LEN(dest) = i);
  129. IF i < LEN(dest) THEN dest[i] := 0X ELSE dest[i - 1] := 0X END; (*@4 fof: if source was not 0X terminated *)
  130. END Copy;
  131. (* Convert integer val to a 0X terminated string and store it in str. *)
  132. PROCEDURE IntToStr*(val: LONGINT; VAR str: ARRAY OF CHAR);
  133. VAR i, j: LONGINT; digits: ARRAY 16 OF LONGINT;
  134. BEGIN
  135. IF val = MIN(LONGINT) THEN
  136. str := "-2147483648";
  137. ELSE
  138. i := 0;
  139. IF val < 0 THEN
  140. val := -val; str[0] := '-'; j := 1
  141. ELSE
  142. j := 0
  143. END;
  144. REPEAT
  145. digits[i] := val MOD 10;
  146. INC(i);
  147. val := val DIV 10
  148. UNTIL val = 0;
  149. DEC(i);
  150. WHILE i >= 0 DO
  151. str[j] := CHR(digits[i] + ORD('0'));
  152. INC(j);
  153. DEC(i)
  154. END;
  155. str[j] := 0X;
  156. END;
  157. END IntToStr;
  158. (* Convert boolean value into a string *)
  159. PROCEDURE BoolToStr*(CONST bool: BOOLEAN; VAR str: ARRAY OF CHAR);
  160. BEGIN
  161. IF bool THEN
  162. str := "True";
  163. ELSE
  164. str := "False";
  165. END;
  166. END BoolToStr;
  167. (* Convert a string into a boolean *)
  168. PROCEDURE StrToBool*(CONST str: ARRAY OF CHAR; VAR bool: BOOLEAN; VAR res: BOOLEAN);
  169. BEGIN
  170. res := TRUE;
  171. IF EqualIgnoreCase(str, "true") THEN
  172. bool := TRUE;
  173. ELSIF EqualIgnoreCase(str, "false") THEN
  174. bool := FALSE;
  175. ELSE
  176. res := FALSE;
  177. END;
  178. END StrToBool;
  179. (* Returns the shifted binary exponent of a real (0 <= e < 256 *)
  180. PROCEDURE Expo*(x: REAL): LONGINT;
  181. VAR e: LONGINT;
  182. BEGIN
  183. (* Replaced the following code with safe variant *)
  184. (* RETURN ASR(SYSTEM.VAL(LONGINT, x), 23) MOD 256 *)
  185. Utils.UNPK(x, e);
  186. RETURN (e + 127) MOD 256
  187. END Expo;
  188. (* Returns 10^e (e <= 308, 308 < e delivers IEEE-code +INF). *)
  189. PROCEDURE Ten(e: LONGINT): REAL;
  190. VAR res: REAL;
  191. BEGIN
  192. (* hack! *)
  193. IF e < -38 THEN
  194. res := 0.0;
  195. ELSIF e > 38 THEN
  196. res := MAX(REAL);
  197. ELSE
  198. res := 1.0;
  199. WHILE (e > 0) DO res := res * 10.0; DEC(e); END;
  200. WHILE (e < 0) DO res := res / 10.0; INC(e); END;
  201. END;
  202. RETURN res
  203. END Ten;
  204. (* Returns the NaN code (0 <= c < 8399608) or -1 if not NaN/Infinite. *)
  205. PROCEDURE NaNCode(x: REAL): LONGINT;
  206. VAR l: LONGINT;
  207. BEGIN
  208. IF Expo(x) = 255 THEN (* Infinite or NaN *)
  209. l := SYSTEM.VAL(LONGINT, x) MOD 800000H;
  210. ELSE
  211. l := -1;
  212. END;
  213. RETURN l
  214. END NaNCode;
  215. (** truncates string to length *)
  216. PROCEDURE Truncate* (VAR string: ARRAY OF CHAR; length: LONGINT);
  217. BEGIN
  218. IF LEN(string) > length THEN string[length] := 0X END;
  219. END Truncate;
  220. (** copies src[soff ... soff + len - 1] to dst[doff ... doff + len - 1] *)
  221. PROCEDURE Move* (CONST src: ARRAY OF CHAR; soff, len: LONGINT; VAR dst: ARRAY OF CHAR; doff: LONGINT);
  222. BEGIN
  223. (* reverse copy direction in case src and dst denote the same string *)
  224. IF soff < doff THEN
  225. INC (soff, len - 1); INC (doff, len - 1);
  226. WHILE len > 0 DO dst[doff] := src[soff]; DEC (soff); DEC (doff); DEC (len) END
  227. ELSE
  228. WHILE len > 0 DO dst[doff] := src[soff]; INC (soff); INC (doff); DEC (len) END
  229. END;
  230. END Move;
  231. (** concatenates s1 and s2: s := s1 || s2 *)
  232. PROCEDURE Concat* (CONST s1, s2: ARRAY OF CHAR; VAR s: ARRAY OF CHAR);
  233. VAR len1, len2 : LONGINT;
  234. BEGIN
  235. len1 := Length (s1); len2 := Length (s2);
  236. Move(s2, 0, len2, s, len1);
  237. Move (s1, 0, len1, s, 0);
  238. Truncate (s, len1 + len2);
  239. END Concat;
  240. (** concatenates s1 and s2: s := s1 || s2. The resulting string is truncated to the length of s if necessary *)
  241. PROCEDURE ConcatX*(CONST s1, s2 : ARRAY OF CHAR; VAR s : ARRAY OF CHAR);
  242. VAR len1, len2 : LONGINT;
  243. BEGIN
  244. len1 := Length (s1); len2 := Length (s2);
  245. IF (len1 + 1 >= LEN(s)) THEN
  246. COPY(s1, s);
  247. ELSE
  248. IF (len1 + len2 + 1 > LEN(s)) THEN
  249. len2 := LEN(s) - 1 - len1;
  250. END;
  251. Move(s2, 0, len2, s, len1);
  252. Move (s1, 0, len1, s, 0);
  253. Truncate (s, len1 + len2);
  254. END;
  255. END ConcatX;
  256. (* Append "this" to "to". Copies as much as is possible to "to" (0X terminated) *)
  257. PROCEDURE Append*(VAR to: ARRAY OF CHAR; CONST this: ARRAY OF CHAR);
  258. VAR i, j: LONGINT;
  259. BEGIN
  260. i := 0; j := 0;
  261. WHILE (i < LEN(to)) & (to[i] # 0X) DO
  262. INC(i)
  263. END;
  264. WHILE (i < LEN(to)) & (j < LEN(this)) & (this[j] # 0X) DO
  265. to[i] := this[j];
  266. INC(i); INC(j)
  267. END;
  268. (* 0X terminate the string *)
  269. IF j > 0 THEN (* tt: Appending of the empty string must not result in a trap *)
  270. IF (this[j - 1] = 0X) OR (i = LEN(to)) THEN
  271. to[i - 1] := 0X
  272. ELSE
  273. to[i] := 0X
  274. END; (*@4 fof: if source was not 0X terminated *)
  275. END;
  276. END Append;
  277. (* Append a character at the end of a string *)
  278. PROCEDURE AppendChar*(VAR to: ARRAY OF CHAR; c: CHAR);
  279. VAR str: ARRAY 4 OF CHAR;
  280. BEGIN
  281. str[0] := c; str[1] := 0X; Append(to, str);
  282. END AppendChar;
  283. (** Write real x to buffer str as ascii text *)
  284. PROCEDURE AppendReal*(VAR str: ARRAY OF CHAR; x: REAL);
  285. VAR
  286. e, h, i, n: LONGINT;
  287. y, z, temp05: REAL;
  288. d: ARRAY 8 OF CHAR;
  289. BEGIN
  290. n := 14; (* larger number of n do not really make sense *)
  291. e := Expo(x);
  292. IF e = 255 THEN
  293. WHILE n > 8 DO AppendChar(str, ' '); DEC(n) END;
  294. h := NaNCode(x);
  295. IF h # 0 THEN Append(str, " NaN")
  296. ELSIF x < 0.0 THEN Append(str, " -INF")
  297. ELSE Append(str, " INF")
  298. END
  299. ELSE
  300. IF n <= 7 THEN n := 0 ELSE DEC(n, 7) END;
  301. WHILE (n > 7) DO AppendChar(str, ' '); DEC(n) END; (* 0 <= n <= 7 fraction digits *)
  302. IF (e # 0) & (x < 0.0) THEN AppendChar(str, '-'); x := -x
  303. ELSE AppendChar(str, ' ')
  304. END;
  305. IF e = 0 THEN
  306. h := 0 (* no denormals *)
  307. ELSE
  308. e := (e - 127) * 301 DIV 1000; (* ln(2)/ln(10) = 0.301029996 *)
  309. IF e < 38 THEN
  310. z := Ten(e + 1);
  311. IF x >= z THEN y := x / z; INC(e) ELSE y := x * Ten(-e) END
  312. ELSE y := x * Ten(-38)
  313. END;
  314. IF y >= 10.0 THEN y := y * Ten(-1); y := y + 0.5E0 / Ten(n); INC(e)
  315. ELSE
  316. temp05 := 0.5E0; (* Otherwise not compilable *)
  317. y := y + temp05 / Ten(n);
  318. IF y >= 10.0 THEN y := y * Ten(-1); INC(e) END
  319. END;
  320. y := y * Ten(7); h := ENTIER(y)
  321. END;
  322. i := 7;
  323. WHILE i >= 0 DO d[i] := CHR(h MOD 10 + ORD('0')); h := h DIV 10; DEC(i) END;
  324. AppendChar(str, d[0]); AppendChar(str, '.'); i := 1;
  325. WHILE i <= n DO AppendChar(str, d[i]); INC(i) END;
  326. IF e < 0 THEN Append(str, "E-"); e := -e ELSE Append(str, "E+") END;
  327. i := e DIV 10; AppendChar(str, CHR(i + ORD('0'))); i := e MOD 10;
  328. AppendChar(str, CHR(i + ORD('0')))
  329. END
  330. END AppendReal;
  331. (* Append an integer to a string *)
  332. PROCEDURE AppendInt*(VAR to: ARRAY OF CHAR; i: LONGINT);
  333. VAR str: ARRAY 64 OF CHAR;
  334. BEGIN
  335. IntToStr(i, str); Append(to, str);
  336. END AppendInt;
  337. (* Append a set to a string *)
  338. PROCEDURE AppendSet*(VAR to: ARRAY OF CHAR; s: SET);
  339. VAR first: BOOLEAN; i: LONGINT;
  340. BEGIN
  341. first := TRUE; AppendChar(to, '{');
  342. FOR i := 0 TO 31 DO
  343. IF i IN s THEN
  344. IF ~first THEN AppendChar(to, ',') END;
  345. first := FALSE; AppendInt(to, i);
  346. END
  347. END;
  348. AppendChar(to, '}');
  349. END AppendSet;
  350. (* Append a boolean to a string *)
  351. PROCEDURE AppendBool*(VAR to: ARRAY OF CHAR; b: BOOLEAN);
  352. BEGIN
  353. IF b THEN Append(to, "TRUE") ELSE Append(to, "FALSE") END;
  354. END AppendBool;
  355. (* Convert a real to a string *)
  356. PROCEDURE RealToStr*(r: REAL; VAR str: ARRAY OF CHAR);
  357. BEGIN
  358. str[0] := 0X; AppendReal(str, r);
  359. END RealToStr;
  360. BEGIN
  361. Trace.StringLn("Strings.");
  362. END Strings.