Daily bump.
[official-gcc.git] / gcc / m2 / gm2-libs / StringConvert.mod
blob2214134938d6d0a1bfd5c277ae6c2619dca84fbc
1 (* StringConvert.mod provides functions to convert numbers to and from strings.
3 Copyright (C) 2001-2024 Free Software Foundation, Inc.
4 Contributed by Gaius Mulley <gaius.mulley@southwales.ac.uk>.
6 This file is part of GNU Modula-2.
8 GNU Modula-2 is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3, or (at your option)
11 any later version.
13 GNU Modula-2 is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 Under Section 7 of GPL version 3, you are granted additional
19 permissions described in the GCC Runtime Library Exception, version
20 3.1, as published by the Free Software Foundation.
22 You should have received a copy of the GNU General Public License and
23 a copy of the GCC Runtime Library Exception along with this program;
24 see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
25 <http://www.gnu.org/licenses/>. *)
27 IMPLEMENTATION MODULE StringConvert ;
29 FROM SYSTEM IMPORT ADDRESS, ADR ;
30 FROM libc IMPORT free, printf ;
31 FROM libm IMPORT powl ;
32 FROM M2RTS IMPORT ErrorMessage ;
34 IMPORT DynamicStrings ;
36 FROM DynamicStrings IMPORT InitString,
37 InitStringChar, InitStringCharStar,
38 Mark, ConCat, Dup, string,
39 Slice, Index, char, Assign, Length, Mult,
40 RemoveWhitePrefix, ConCatChar, KillString,
41 InitStringDB, InitStringCharStarDB,
42 InitStringCharDB, MultDB, DupDB, SliceDB ;
44 FROM ldtoa IMPORT Mode, strtold, ldtoa ;
45 IMPORT dtoa ; (* this fixes linking - as the C ldtoa uses dtoa *)
49 #undef GM2_DEBUG_STRINGCONVERT
50 #if defined(GM2_DEBUG_STRINGCONVERT)
51 # define InitString(X) InitStringDB(X, __FILE__, __LINE__)
52 # define InitStringCharStar(X) InitStringCharStarDB(X, __FILE__, __LINE__)
53 # define InitStringChar(X) InitStringCharDB(X, __FILE__, __LINE__)
54 # define Mult(X,Y) MultDB(X, Y, __FILE__, __LINE__)
55 # define Dup(X) DupDB(X, __FILE__, __LINE__)
56 # define Slice(X,Y,Z) SliceDB(X, Y, Z, __FILE__, __LINE__)
57 #endif
61 Assert - implement a simple assert.
64 PROCEDURE Assert (b: BOOLEAN; file: ARRAY OF CHAR; line: CARDINAL; func: ARRAY OF CHAR) ;
65 BEGIN
66 IF NOT b
67 THEN
68 ErrorMessage('assert failed', file, line, func)
69 END
70 END Assert ;
74 Max -
77 PROCEDURE Max (a, b: CARDINAL) : CARDINAL ;
78 BEGIN
79 IF a>b
80 THEN
81 RETURN( a )
82 ELSE
83 RETURN( b )
84 END
85 END Max ;
89 Min -
92 PROCEDURE Min (a, b: CARDINAL) : CARDINAL ;
93 BEGIN
94 IF a<b
95 THEN
96 RETURN( a )
97 ELSE
98 RETURN( b )
99 END
100 END Min ;
104 LongMin - returns the smallest LONGCARD
107 PROCEDURE LongMin (a, b: LONGCARD) : LONGCARD ;
108 BEGIN
109 IF a<b
110 THEN
111 RETURN( a )
112 ELSE
113 RETURN( b )
115 END LongMin ;
119 IsDigit - returns TRUE if, ch, lies between '0'..'9'.
122 PROCEDURE IsDigit (ch: CHAR) : BOOLEAN ;
123 BEGIN
124 RETURN (ch>='0') AND (ch<='9')
125 END IsDigit ;
129 IsDecimalDigitValid - returns the TRUE if, ch, is a base legal decimal digit.
130 If legal then the value is appended numerically onto, c.
133 PROCEDURE IsDecimalDigitValid (ch: CHAR; base: CARDINAL; VAR c: CARDINAL) : BOOLEAN ;
134 BEGIN
135 IF IsDigit(ch) AND (ORD(ch)-ORD('0')<base)
136 THEN
137 c := c*base + (ORD(ch)-ORD('0')) ;
138 RETURN( TRUE )
139 ELSE
140 RETURN( FALSE )
142 END IsDecimalDigitValid ;
146 IsHexidecimalDigitValid - returns the TRUE if, ch, is a base legal hexidecimal digit.
147 If legal then the value is appended numerically onto, c.
150 PROCEDURE IsHexidecimalDigitValid (ch: CHAR; base: CARDINAL; VAR c: CARDINAL) : BOOLEAN ;
151 BEGIN
152 IF (ch>='a') AND (ch<='f') AND (ORD(ch)-ORD('a')+10<base)
153 THEN
154 c := c*base + (ORD(ch)-ORD('a')+10) ;
155 RETURN( TRUE )
156 ELSIF (ch>='A') AND (ch<='F') AND (ORD(ch)-ORD('F')+10<base)
157 THEN
158 c := c*base + (ORD(ch)-ORD('A')+10) ;
159 RETURN( TRUE )
160 ELSE
161 RETURN( FALSE )
163 END IsHexidecimalDigitValid ;
167 IsDecimalDigitValidLong - returns the TRUE if, ch, is a base legal decimal digit.
168 If legal then the value is appended numerically onto, c.
171 PROCEDURE IsDecimalDigitValidLong (ch: CHAR; base: CARDINAL;
172 VAR c: LONGCARD) : BOOLEAN ;
173 BEGIN
174 IF IsDigit(ch) AND (ORD(ch)-ORD('0')<base)
175 THEN
176 c := c * VAL(LONGCARD, base + (ORD(ch)-ORD('0'))) ;
177 RETURN( TRUE )
178 ELSE
179 RETURN( FALSE )
181 END IsDecimalDigitValidLong ;
185 IsHexidecimalDigitValidLong - returns the TRUE if, ch, is a base legal hexidecimal digit.
186 If legal then the value is appended numerically onto, c.
189 PROCEDURE IsHexidecimalDigitValidLong (ch: CHAR; base: CARDINAL; VAR c: LONGCARD) : BOOLEAN ;
190 BEGIN
191 IF (ch>='a') AND (ch<='f') AND (ORD(ch)-ORD('a')+10<base)
192 THEN
193 c := c * VAL(LONGCARD, base + (ORD(ch)-ORD('a')+10)) ;
194 RETURN( TRUE )
195 ELSIF (ch>='A') AND (ch<='F') AND (ORD(ch)-ORD('F')+10<base)
196 THEN
197 c := c * VAL(LONGCARD, base + (ORD(ch)-ORD('A')+10)) ;
198 RETURN( TRUE )
199 ELSE
200 RETURN( FALSE )
202 END IsHexidecimalDigitValidLong ;
206 IsDecimalDigitValidShort - returns the TRUE if, ch, is a base legal decimal digit.
207 If legal then the value is appended numerically onto, c.
210 PROCEDURE IsDecimalDigitValidShort (ch: CHAR; base: CARDINAL; VAR c: SHORTCARD) : BOOLEAN ;
211 BEGIN
212 IF IsDigit(ch) AND (ORD(ch)-ORD('0')<base)
213 THEN
214 c := c * VAL(SHORTCARD, base + (ORD(ch)-ORD('0'))) ;
215 RETURN( TRUE )
216 ELSE
217 RETURN( FALSE )
219 END IsDecimalDigitValidShort ;
223 IsHexidecimalDigitValidShort - returns the TRUE if, ch, is a base legal hexidecimal digit.
224 If legal then the value is appended numerically onto, c.
227 PROCEDURE IsHexidecimalDigitValidShort (ch: CHAR; base: CARDINAL; VAR c: SHORTCARD) : BOOLEAN ;
228 BEGIN
229 IF (ch>='a') AND (ch<='f') AND (ORD(ch)-ORD('a')+10<base)
230 THEN
231 c := c * VAL(SHORTCARD, base + (ORD(ch)-ORD('a')+10)) ;
232 RETURN( TRUE )
233 ELSIF (ch>='A') AND (ch<='F') AND (ORD(ch)-ORD('F')+10<base)
234 THEN
235 c := c * VAL(SHORTCARD, base + (ORD(ch)-ORD('A')+10)) ;
236 RETURN( TRUE )
237 ELSE
238 RETURN( FALSE )
240 END IsHexidecimalDigitValidShort ;
244 IntegerToString - converts INTEGER, i, into a String. The field with can be specified
245 if non zero. Leading characters are defined by padding and this
246 function will prepend a + if sign is set to TRUE.
247 The base allows the caller to generate binary, octal, decimal, hexidecimal
248 numbers. The value of lower is only used when hexidecimal numbers are
249 generated and if TRUE then digits abcdef are used, and if FALSE then ABCDEF
250 are used.
253 PROCEDURE IntegerToString (i: INTEGER; width: CARDINAL; padding: CHAR; sign: BOOLEAN;
254 base: CARDINAL; lower: BOOLEAN) : String ;
256 s: String ;
257 c: CARDINAL ;
258 BEGIN
259 IF i<0
260 THEN
261 IF i=MIN(INTEGER)
262 THEN
263 (* remember that -15 MOD 4 = 1 in Modula-2 *)
264 c := VAL(CARDINAL, ABS(i+1))+1 ;
265 IF width>0
266 THEN
267 RETURN( ConCat(IntegerToString(-VAL(INTEGER, c DIV base),
268 width-1, padding, sign, base, lower),
269 Mark(IntegerToString(c MOD base, 0, ' ', FALSE, base, lower))) )
270 ELSE
271 RETURN( ConCat(IntegerToString(-VAL(INTEGER, c DIV base),
272 0, padding, sign, base, lower),
273 Mark(IntegerToString(c MOD base, 0, ' ', FALSE, base, lower))) )
275 ELSE
276 s := InitString('-')
277 END ;
278 i := -i
279 ELSE
280 IF sign
281 THEN
282 s := InitString('+')
283 ELSE
284 s := InitString('')
286 END ;
287 IF i>VAL(INTEGER, base)-1
288 THEN
289 s := ConCat(ConCat(s, Mark(IntegerToString(VAL(CARDINAL, i) DIV base, 0, ' ', FALSE, base, lower))),
290 Mark(IntegerToString(VAL(CARDINAL, i) MOD base, 0, ' ', FALSE, base, lower)))
291 ELSE
292 IF i<=9
293 THEN
294 s := ConCat(s, Mark(InitStringChar(CHR(VAL(CARDINAL, i)+ORD('0')))))
295 ELSE
296 IF lower
297 THEN
298 s := ConCat(s, Mark(InitStringChar(CHR(VAL(CARDINAL, i)+ORD('a')-10))))
299 ELSE
300 s := ConCat(s, Mark(InitStringChar(CHR(VAL(CARDINAL, i)+ORD('A')-10))))
303 END ;
304 IF width>DynamicStrings.Length(s)
305 THEN
306 RETURN( ConCat(Mult(Mark(InitStringChar(padding)), width-DynamicStrings.Length(s)), Mark(s)) )
307 END ;
308 RETURN( s )
309 END IntegerToString ;
313 CardinalToString - converts CARDINAL, c, into a String. The field with can be specified
314 if non zero. Leading characters are defined by padding.
315 The base allows the caller to generate binary, octal, decimal, hexidecimal
316 numbers. The value of lower is only used when hexidecimal numbers are
317 generated and if TRUE then digits abcdef are used, and if FALSE then ABCDEF
318 are used.
321 PROCEDURE CardinalToString (c: CARDINAL; width: CARDINAL; padding: CHAR;
322 base: CARDINAL; lower: BOOLEAN) : String ;
324 s: String ;
325 BEGIN
326 s := InitString('') ;
327 IF c>base-1
328 THEN
329 s := ConCat(ConCat(s, Mark(CardinalToString(c DIV base, 0, ' ', base, lower))),
330 Mark(CardinalToString(c MOD base, 0, ' ', base, lower)))
331 ELSE
332 IF c<=9
333 THEN
334 s := ConCat(s, Mark(InitStringChar(CHR(c+ORD('0')))))
335 ELSE
336 IF lower
337 THEN
338 s := ConCat(s, Mark(InitStringChar(CHR(c+ORD('a')-10))))
339 ELSE
340 s := ConCat(s, Mark(InitStringChar(CHR(c+ORD('A')-10))))
343 END ;
344 IF width>DynamicStrings.Length(s)
345 THEN
346 RETURN( ConCat(Mult(Mark(InitStringChar(padding)), width-DynamicStrings.Length(s)), s) )
347 END ;
348 RETURN( s )
349 END CardinalToString ;
353 LongIntegerToString - converts LONGINT, i, into a String. The field with
354 can be specified if non zero. Leading characters
355 are defined by padding and this function will
356 prepend a + if sign is set to TRUE.
357 The base allows the caller to generate binary,
358 octal, decimal, hexidecimal numbers.
359 The value of lower is only used when hexidecimal
360 numbers are generated and if TRUE then digits
361 abcdef are used, and if FALSE then ABCDEF are used.
364 PROCEDURE LongIntegerToString (i: LONGINT; width: CARDINAL; padding: CHAR;
365 sign: BOOLEAN; base: CARDINAL; lower: BOOLEAN) : String ;
368 s: String ;
369 c: LONGCARD ;
370 BEGIN
371 IF i<0
372 THEN
373 IF i=MIN(LONGINT)
374 THEN
375 (* remember that -15 MOD 4 is 1 in Modula-2, and although ABS(MIN(LONGINT)+1)
376 is very likely MAX(LONGINT), it is safer not to assume this is the case *)
377 c := VAL(LONGCARD, ABS(i+1))+1 ;
378 IF width>0
379 THEN
380 RETURN( ConCat(LongIntegerToString(-VAL(LONGINT, c DIV VAL(LONGCARD, base)),
381 width-1, padding, sign, base, lower),
382 Mark(LongIntegerToString(c MOD VAL(LONGCARD, base), 0, ' ', FALSE, base, lower))) )
383 ELSE
384 RETURN( ConCat(LongIntegerToString(-VAL(LONGINT, c DIV VAL(LONGCARD, base)),
385 0, padding, sign, base, lower),
386 Mark(LongIntegerToString(c MOD VAL(LONGCARD, base), 0, ' ', FALSE, base, lower))) )
388 ELSE
389 s := InitString('-')
390 END ;
391 i := -i
392 ELSE
393 IF sign
394 THEN
395 s := InitString('+')
396 ELSE
397 s := InitString('')
399 END ;
400 IF i>VAL(LONGINT, base-1)
401 THEN
402 s := ConCat(ConCat(s, Mark(LongIntegerToString(i DIV VAL(LONGINT, base), 0, ' ', FALSE, base, lower))),
403 Mark(LongIntegerToString(i MOD VAL(LONGINT, base), 0, ' ', FALSE, base, lower)))
404 ELSE
405 IF i<=9
406 THEN
407 s := ConCat(s, Mark(InitStringChar(CHR(VAL(CARDINAL, i)+ORD('0')))))
408 ELSE
409 IF lower
410 THEN
411 s := ConCat(s, Mark(InitStringChar(CHR(VAL(CARDINAL, i)+ORD('a')-10))))
412 ELSE
413 s := ConCat(s, Mark(InitStringChar(CHR(VAL(CARDINAL, i)+ORD('A')-10))))
416 END ;
417 IF width>DynamicStrings.Length(s)
418 THEN
419 RETURN( ConCat(Mult(Mark(InitStringChar(padding)), width-DynamicStrings.Length(s)), s) )
420 END ;
421 RETURN( s )
422 END LongIntegerToString ;
426 StringToLongInteger - converts a string, s, of, base, into an LONGINT.
427 Leading white space is ignored. It stops converting
428 when either the string is exhausted or if an illegal
429 numeral is found.
430 The parameter found is set TRUE if a number was found.
433 PROCEDURE StringToLongInteger (s: String; base: CARDINAL; VAR found: BOOLEAN) : LONGINT ;
435 n, l : CARDINAL ;
436 c : LONGCARD ;
437 negative: BOOLEAN ;
438 BEGIN
439 s := RemoveWhitePrefix(s) ; (* returns a new string, s *)
440 l := DynamicStrings.Length(s) ;
441 c := 0 ;
442 n := 0 ;
443 negative := FALSE ;
444 IF n<l
445 THEN
446 (* parse leading + and - *)
447 WHILE (char(s, n)='-') OR (char(s, n)='+') DO
448 IF char(s, n)='-'
449 THEN
450 negative := NOT negative
451 END ;
452 INC(n)
453 END ;
454 WHILE (n<l) AND (IsDecimalDigitValidLong(char(s, n), base, c) OR
455 IsHexidecimalDigitValidLong(char(s, n), base, c)) DO
456 found := TRUE ;
457 INC(n)
459 END ;
460 s := KillString(s) ;
461 IF negative
462 THEN
463 RETURN( -VAL(LONGINT, LongMin(VAL(LONGCARD, MAX(LONGINT))+1, c)) )
464 ELSE
465 RETURN( VAL(LONGINT, LongMin(MAX(LONGINT), c)) )
467 END StringToLongInteger ;
471 StringToInteger - converts a string, s, of, base, into an INTEGER.
472 Leading white space is ignored. It stops converting
473 when either the string is exhausted or if an illegal
474 numeral is found.
475 The parameter found is set TRUE if a number was found.
478 PROCEDURE StringToInteger (s: String; base: CARDINAL;
479 VAR found: BOOLEAN) : INTEGER ;
481 n, l : CARDINAL ;
482 c : CARDINAL ;
483 negative: BOOLEAN ;
484 BEGIN
485 s := RemoveWhitePrefix(s) ; (* returns a new string, s *)
486 l := DynamicStrings.Length(s) ;
487 c := 0 ;
488 n := 0 ;
489 negative := FALSE ;
490 IF n<l
491 THEN
492 (* parse leading + and - *)
493 WHILE (char(s, n)='-') OR (char(s, n)='+') DO
494 IF char(s, n)='-'
495 THEN
496 negative := NOT negative
497 END ;
498 INC(n)
499 END ;
500 WHILE (n<l) AND (IsDecimalDigitValid(char(s, n), base, c) OR
501 IsHexidecimalDigitValid(char(s, n), base, c)) DO
502 found := TRUE ;
503 INC(n)
505 END ;
506 s := KillString(s) ;
507 IF negative
508 THEN
509 RETURN( -VAL(INTEGER, Min(VAL(CARDINAL, MAX(INTEGER))+1, c)) )
510 ELSE
511 RETURN( VAL(INTEGER, Min(MAX(INTEGER), c)) )
513 END StringToInteger ;
517 StringToCardinal - converts a string, s, of, base, into a CARDINAL.
518 Leading white space is ignored. It stops converting
519 when either the string is exhausted or if an illegal
520 numeral is found.
521 The parameter found is set TRUE if a number was found.
524 PROCEDURE StringToCardinal (s: String; base: CARDINAL;
525 VAR found: BOOLEAN) : CARDINAL ;
527 n, l: CARDINAL ;
528 c : CARDINAL ;
529 BEGIN
530 s := RemoveWhitePrefix(s) ; (* returns a new string, s *)
531 l := DynamicStrings.Length(s) ;
532 c := 0 ;
533 n := 0 ;
534 IF n<l
535 THEN
536 (* parse leading + *)
537 WHILE (char(s, n)='+') DO
538 INC(n)
539 END ;
540 WHILE (n<l) AND (IsDecimalDigitValid(char(s, n), base, c) OR
541 IsHexidecimalDigitValid(char(s, n), base, c)) DO
542 found := TRUE ;
543 INC(n)
545 END ;
546 s := KillString(s) ;
547 RETURN( c )
548 END StringToCardinal ;
552 stoi - decimal string to INTEGER
555 PROCEDURE stoi (s: String) : INTEGER ;
557 found: BOOLEAN ;
558 BEGIN
559 RETURN( StringToInteger(s, 10, found) )
560 END stoi ;
564 itos - integer to decimal string.
567 PROCEDURE itos (i: INTEGER; width: CARDINAL; padding: CHAR; sign: BOOLEAN) : String ;
568 BEGIN
569 RETURN( IntegerToString(i, width, padding, sign, 10, FALSE) )
570 END itos ;
574 ctos - cardinal to decimal string.
577 PROCEDURE ctos (c: CARDINAL; width: CARDINAL; padding: CHAR) : String ;
578 BEGIN
579 RETURN( CardinalToString(c, width, padding, 10, FALSE) )
580 END ctos ;
584 stoc - decimal string to CARDINAL
587 PROCEDURE stoc (s: String) : CARDINAL ;
589 found: BOOLEAN ;
590 BEGIN
591 RETURN( StringToCardinal(s, 10, found) )
592 END stoc ;
596 hstoi - hexidecimal string to INTEGER
599 PROCEDURE hstoi (s: String) : INTEGER ;
601 found: BOOLEAN ;
602 BEGIN
603 RETURN( StringToInteger(s, 16, found) )
604 END hstoi ;
608 ostoi - octal string to INTEGER
611 PROCEDURE ostoi (s: String) : INTEGER ;
613 found: BOOLEAN ;
614 BEGIN
615 RETURN( StringToInteger(s, 8, found) )
616 END ostoi ;
620 bstoi - binary string to INTEGER
623 PROCEDURE bstoi (s: String) : INTEGER ;
625 found: BOOLEAN ;
626 BEGIN
627 RETURN( StringToInteger(s, 2, found) )
628 END bstoi ;
632 hstoc - hexidecimal string to CARDINAL
635 PROCEDURE hstoc (s: String) : CARDINAL ;
637 found: BOOLEAN ;
638 BEGIN
639 RETURN( StringToCardinal(s, 16, found) )
640 END hstoc ;
644 ostoc - octal string to CARDINAL
647 PROCEDURE ostoc (s: String) : CARDINAL ;
649 found: BOOLEAN ;
650 BEGIN
651 RETURN( StringToCardinal(s, 8, found) )
652 END ostoc ;
656 bstoc - binary string to CARDINAL
659 PROCEDURE bstoc (s: String) : CARDINAL ;
661 found: BOOLEAN ;
662 BEGIN
663 RETURN( StringToCardinal(s, 2, found) )
664 END bstoc ;
667 (* **********************************************************************
668 R e a l a n d L o n g R e a l c o n v e r s i o n
669 ********************************************************************** *)
673 ToThePower10 - returns a LONGREAL containing the value of v * 10^power.
676 PROCEDURE ToThePower10 (v: LONGREAL; power: INTEGER) : LONGREAL;
678 i: INTEGER ;
679 BEGIN
680 i := 0 ;
681 IF power>0
682 THEN
683 WHILE i<power DO
684 v := v * 10.0 ;
685 INC(i)
687 ELSE
688 WHILE i>power DO
689 v := v / 10.0 ;
690 DEC(i)
692 END ;
693 RETURN( v )
694 END ToThePower10 ;
698 DetermineSafeTruncation - we wish to use TRUNC when converting REAL/LONGREAL
699 into a string for the non fractional component.
700 However we need a simple method to
701 determine the maximum safe truncation value.
704 PROCEDURE DetermineSafeTruncation () : CARDINAL ;
706 MaxPowerOfTen: REAL ;
707 LogPower : CARDINAL ;
708 BEGIN
709 MaxPowerOfTen := 1.0 ;
710 LogPower := 0 ;
711 WHILE MaxPowerOfTen*10.0<FLOAT(MAX(INTEGER) DIV 10) DO
712 MaxPowerOfTen := MaxPowerOfTen * 10.0 ;
713 INC(LogPower)
714 END ;
715 RETURN( LogPower )
716 END DetermineSafeTruncation ;
720 LongrealToString - converts a LONGREAL number, Real, which has,
721 TotalWidth, and FractionWidth into a string.
722 It uses decimal notation.
724 So for example:
726 LongrealToString(1.0, 4, 2) -> '1.00'
727 LongrealToString(12.3, 5, 2) -> '12.30'
728 LongrealToString(12.3, 6, 2) -> ' 12.30'
729 LongrealToString(12.3, 6, 3) -> '12.300'
731 if total width is too small then the fraction
732 becomes truncated.
734 LongrealToString(12.3, 5, 3) -> '12.30'
736 Positive numbers do not have a '+' prepended.
737 Negative numbers will have a '-' prepended and
738 the TotalWidth will need to be large enough
739 to contain the sign, whole number, '.' and
740 fractional components.
743 PROCEDURE LongrealToString (x: LONGREAL;
744 TotalWidth, FractionWidth: CARDINAL) : String ;
746 maxprecision: BOOLEAN ;
747 s : String ;
748 r : ADDRESS ;
749 point : INTEGER ;
750 sign : BOOLEAN ;
751 l : INTEGER ;
752 BEGIN
753 IF TotalWidth=0
754 THEN
755 maxprecision := TRUE ;
756 r := ldtoa(x, decimaldigits, 100, point, sign)
757 ELSE
758 r := ldtoa(x, decimaldigits, 100, point, sign)
759 END ;
760 s := InitStringCharStar(r) ;
761 free(r) ;
762 l := DynamicStrings.Length(s) ;
763 IF point>l
764 THEN
765 s := ConCat(s, Mark(Mult(Mark(InitStringChar('0')), point-l))) ;
766 s := ConCat(s, Mark(InitString('.0'))) ;
767 IF (NOT maxprecision) AND (FractionWidth>0)
768 THEN
769 DEC(FractionWidth) ;
770 IF VAL(INTEGER, FractionWidth)>point-l
771 THEN
772 s := ConCat(s, Mark(Mult(Mark(InitString('0')), FractionWidth)))
775 ELSIF point<0
776 THEN
777 s := ConCat(Mult(Mark(InitStringChar('0')), -point), Mark(s)) ;
778 l := DynamicStrings.Length(s) ;
779 s := ConCat(InitString('0.'), Mark(s)) ;
780 IF (NOT maxprecision) AND (l<VAL(INTEGER, FractionWidth))
781 THEN
782 s := ConCat(s, Mark(Mult(Mark(InitString('0')), VAL(INTEGER, FractionWidth)-l)))
784 ELSE
785 IF point=0
786 THEN
787 s := ConCat(InitString('0.'), Mark(Slice(Mark(s), point, 0)))
788 ELSE
789 s := ConCat(ConCatChar(Slice(Mark(s), 0, point), '.'),
790 Mark(Slice(Mark(s), point, 0)))
791 END ;
792 IF (NOT maxprecision) AND (l-point<VAL(INTEGER, FractionWidth))
793 THEN
794 s := ConCat(s, Mark(Mult(Mark(InitString('0')), VAL(INTEGER, FractionWidth)-(l-point))))
796 END ;
797 IF DynamicStrings.Length(s)>TotalWidth
798 THEN
799 IF TotalWidth>0
800 THEN
801 IF sign
802 THEN
803 s := Slice(Mark(ToDecimalPlaces(s, FractionWidth)), 0, TotalWidth-1) ;
804 s := ConCat(InitStringChar('-'), Mark(s)) ;
805 sign := FALSE
806 ELSE
807 (* minus 1 because all results will include a '.' *)
808 s := Slice(Mark(ToDecimalPlaces(s, FractionWidth)), 0, TotalWidth) ;
810 ELSE
811 IF sign
812 THEN
813 s := ToDecimalPlaces(s, FractionWidth) ;
814 s := ConCat(InitStringChar('-'), Mark(s)) ;
815 sign := FALSE
816 ELSE
817 (* minus 1 because all results will include a '.' *)
818 s := ToDecimalPlaces(s, FractionWidth)
821 END ;
822 IF DynamicStrings.Length(s)<TotalWidth
823 THEN
824 s := ConCat(Mult(Mark(InitStringChar(' ')), TotalWidth-DynamicStrings.Length(s)), Mark(s))
825 END ;
826 RETURN( s )
827 END LongrealToString ;
831 StringToLongreal - returns a LONGREAL and sets found to TRUE if a legal number is seen.
834 PROCEDURE StringToLongreal (s: String; VAR found: BOOLEAN) : LONGREAL ;
836 error: BOOLEAN ;
837 value: LONGREAL ;
838 BEGIN
839 s := RemoveWhitePrefix(s) ; (* new string is created *)
840 value := strtold(string(s), error) ;
841 s := KillString(s) ;
842 found := NOT error ;
843 RETURN value
844 END StringToLongreal ;
848 rtos -
851 PROCEDURE rtos (r: REAL; TotalWidth, FractionWidth: CARDINAL) : String ;
852 BEGIN
853 HALT ;
854 RETURN ( NIL )
855 END rtos ;
859 stor - returns a REAL given a string.
862 PROCEDURE stor (s: String) : REAL ;
864 found: BOOLEAN ;
865 BEGIN
866 RETURN( VAL(REAL, StringToLongreal(s, found)) )
867 END stor ;
871 lrtos -
874 PROCEDURE lrtos (r: LONGREAL; TotalWidth, FractionWidth: CARDINAL) : String ;
875 BEGIN
876 HALT ;
877 RETURN ( NIL )
878 END lrtos ;
882 stolr - returns a LONGREAL given a string.
885 PROCEDURE stolr (s: String) : LONGREAL ;
887 found: BOOLEAN ;
888 BEGIN
889 RETURN( StringToLongreal(s, found) )
890 END stolr ;
894 LongCardinalToString - converts LONGCARD, c, into a String. The field
895 width can be specified if non zero. Leading
896 characters are defined by padding.
897 The base allows the caller to generate binary,
898 octal, decimal, hexidecimal numbers.
899 The value of lower is only used when hexidecimal
900 numbers are generated and if TRUE then digits
901 abcdef are used, and if FALSE then ABCDEF are used.
904 PROCEDURE LongCardinalToString (c: LONGCARD; width: CARDINAL; padding: CHAR;
905 base: CARDINAL; lower: BOOLEAN) : String ;
907 s: String ;
908 BEGIN
909 s := InitString('') ;
910 IF c>VAL(LONGCARD, base-1)
911 THEN
912 s := ConCat(ConCat(s, LongCardinalToString(c DIV VAL(LONGCARD, base), 0, ' ', base, lower)),
913 LongCardinalToString(c MOD VAL(LONGCARD, base), 0, ' ', base, lower))
914 ELSE
915 IF c<=9
916 THEN
917 s := ConCat(s, InitStringChar(CHR(VAL(CARDINAL, c)+ORD('0'))))
918 ELSE
919 IF lower
920 THEN
921 s := ConCat(s, InitStringChar(CHR(VAL(CARDINAL, c)+ORD('a')-10)))
922 ELSE
923 s := ConCat(s, InitStringChar(CHR(VAL(CARDINAL, c)+ORD('A')-10)))
926 END ;
927 IF width>DynamicStrings.Length(s)
928 THEN
929 RETURN( ConCat(Mult(Mark(InitStringChar(padding)), width-DynamicStrings.Length(s)), s) )
930 END ;
931 RETURN( s )
932 END LongCardinalToString ;
936 StringToLongCardinal - converts a string, s, of, base, into a LONGCARD.
937 Leading white space is ignored. It stops converting
938 when either the string is exhausted or if an illegal
939 numeral is found.
940 The parameter found is set TRUE if a number was found.
943 PROCEDURE StringToLongCardinal (s: String; base: CARDINAL; VAR found: BOOLEAN) : LONGCARD ;
945 n, l: CARDINAL ;
946 c : LONGCARD ;
947 BEGIN
948 s := RemoveWhitePrefix(s) ; (* returns a new string, s *)
949 l := DynamicStrings.Length(s) ;
950 c := 0 ;
951 n := 0 ;
952 IF n<l
953 THEN
954 (* parse leading + *)
955 WHILE (char(s, n)='+') DO
956 INC(n)
957 END ;
958 WHILE (n<l) AND (IsDecimalDigitValidLong(char(s, n), base, c) OR
959 IsHexidecimalDigitValidLong(char(s, n), base, c)) DO
960 found := TRUE ;
961 INC(n)
963 END ;
964 s := KillString(s) ;
965 RETURN( c )
966 END StringToLongCardinal ;
970 ShortCardinalToString - converts SHORTCARD, c, into a String. The field
971 width can be specified if non zero. Leading
972 characters are defined by padding.
973 The base allows the caller to generate binary,
974 octal, decimal, hexidecimal numbers.
975 The value of lower is only used when hexidecimal
976 numbers are generated and if TRUE then digits
977 abcdef are used, and if FALSE then ABCDEF are used.
980 PROCEDURE ShortCardinalToString (c: SHORTCARD; width: CARDINAL; padding: CHAR;
981 base: CARDINAL; lower: BOOLEAN) : String ;
983 s: String ;
984 BEGIN
985 s := InitString('') ;
986 IF VAL(CARDINAL, c)>base-1
987 THEN
988 s := ConCat(ConCat(s, ShortCardinalToString(c DIV VAL(SHORTCARD, base), 0, ' ', base, lower)),
989 ShortCardinalToString(c MOD VAL(SHORTCARD, base), 0, ' ', base, lower))
990 ELSE
991 IF c<=9
992 THEN
993 s := ConCat(s, InitStringChar(CHR(VAL(CARDINAL, c)+ORD('0'))))
994 ELSE
995 IF lower
996 THEN
997 s := ConCat(s, InitStringChar(CHR(VAL(CARDINAL, c)+ORD('a')-10)))
998 ELSE
999 s := ConCat(s, InitStringChar(CHR(VAL(CARDINAL, c)+ORD('A')-10)))
1002 END ;
1003 IF width>DynamicStrings.Length(s)
1004 THEN
1005 RETURN( ConCat(Mult(Mark(InitStringChar(padding)), width-DynamicStrings.Length(s)), s) )
1006 END ;
1007 RETURN( s )
1008 END ShortCardinalToString ;
1012 StringToShortCardinal - converts a string, s, of, base, into a SHORTCARD.
1013 Leading white space is ignored. It stops converting
1014 when either the string is exhausted or if an illegal
1015 numeral is found.
1016 The parameter found is set TRUE if a number was found.
1019 PROCEDURE StringToShortCardinal (s: String; base: CARDINAL;
1020 VAR found: BOOLEAN) : SHORTCARD ;
1022 n, l: CARDINAL ;
1023 c : SHORTCARD ;
1024 BEGIN
1025 s := RemoveWhitePrefix(s) ; (* returns a new string, s *)
1026 l := DynamicStrings.Length(s) ;
1027 c := 0 ;
1028 n := 0 ;
1029 IF n<l
1030 THEN
1031 (* parse leading + *)
1032 WHILE (char(s, n)='+') DO
1033 INC(n)
1034 END ;
1035 WHILE (n<l) AND (IsDecimalDigitValidShort(char(s, n), base, c) OR
1036 IsHexidecimalDigitValidShort(char(s, n), base, c)) DO
1037 found := TRUE ;
1038 INC(n)
1040 END ;
1041 s := KillString(s) ;
1042 RETURN( c )
1043 END StringToShortCardinal ;
1047 ToDecimalPlaces - returns a floating point or base 10 integer
1048 string which is accurate to, n, decimal
1049 places. It will return a new String
1050 and, s, will be destroyed.
1051 Decimal places yields, n, digits after
1052 the .
1054 So: 12.345
1056 rounded to the following decimal places yields
1058 5 12.34500
1059 4 12.3450
1060 3 12.345
1061 2 12.34
1062 1 12.3
1065 PROCEDURE ToDecimalPlaces (s: String; n: CARDINAL) : String ;
1067 point: INTEGER ;
1068 BEGIN
1069 Assert(IsDigit(char(s, 0)) OR (char(s, 0)='.'), __FILE__, __LINE__, __FUNCTION__) ;
1070 point := Index(s, '.', 0) ;
1071 IF point<0
1072 THEN
1073 IF n>0
1074 THEN
1075 RETURN( ConCat(ConCat(s, Mark(InitStringChar('.'))), Mult(Mark(InitStringChar('0')), n)) )
1076 ELSE
1077 RETURN( s )
1079 END ;
1080 s := doDecimalPlaces(s, n) ;
1081 (* if the last character is '.' remove it *)
1082 IF (DynamicStrings.Length(s)>0) AND (char(s, -1)='.')
1083 THEN
1084 RETURN( Slice(Mark(s), 0, -1) )
1085 ELSE
1086 RETURN( s )
1088 END ToDecimalPlaces ;
1092 doDecimalPlaces - returns a string which is accurate to
1093 n decimal places. It returns a new String
1094 and, s, will be destroyed.
1097 PROCEDURE doDecimalPlaces (s: String; n: CARDINAL) : String ;
1099 i, l,
1100 point : INTEGER ;
1102 tenths,
1103 hundreths: String ;
1104 BEGIN
1105 l := DynamicStrings.Length(s) ;
1106 i := 0 ;
1107 (* remove '.' *)
1108 point := Index(s, '.', 0) ;
1109 IF point=0
1110 THEN
1111 s := Slice(Mark(s), 1, 0)
1112 ELSIF point<l
1113 THEN
1114 s := ConCat(Slice(Mark(s), 0, point),
1115 Mark(Slice(Mark(s), point+1, 0)))
1116 ELSE
1117 s := Slice(Mark(s), 0, point)
1118 END ;
1119 l := DynamicStrings.Length(s) ;
1120 i := 0 ;
1121 IF l>0
1122 THEN
1123 (* skip over leading zeros *)
1124 WHILE (i<l) AND (char(s, i)='0') DO
1125 INC(i)
1126 END ;
1127 (* was the string full of zeros? *)
1128 IF (i=l) AND (char(s, i-1)='0')
1129 THEN
1130 s := KillString(s) ;
1131 s := ConCat(InitString('0.'), Mark(Mult(Mark(InitStringChar('0')), n))) ;
1132 RETURN( s )
1134 END ;
1135 (* add a leading zero in case we need to overflow the carry *)
1136 (* insert leading zero *)
1137 s := ConCat(InitStringChar('0'), Mark(s)) ;
1138 INC(point) ; (* and move point position to correct place *)
1139 l := DynamicStrings.Length(s) ; (* update new length *)
1140 i := point ;
1141 WHILE (n>1) AND (i<l) DO
1142 DEC(n) ;
1143 INC(i)
1144 END ;
1145 IF i+3<=l
1146 THEN
1147 t := Dup(s) ;
1148 hundreths := Slice(Mark(s), i+1, i+3) ;
1149 s := t ;
1150 IF stoc(hundreths)>=50
1151 THEN
1152 s := carryOne(Mark(s), i)
1153 END ;
1154 hundreths := KillString(hundreths)
1155 ELSIF i+2<=l
1156 THEN
1157 t := Dup(s) ;
1158 tenths := Slice(Mark(s), i+1, i+2) ;
1159 s := t ;
1160 IF stoc(tenths)>=5
1161 THEN
1162 s := carryOne(Mark(s), i)
1163 END ;
1164 tenths := KillString(tenths)
1165 END ;
1166 (* check whether we need to remove the leading zero *)
1167 IF char(s, 0)='0'
1168 THEN
1169 s := Slice(Mark(s), 1, 0) ;
1170 DEC(l) ;
1171 DEC(point)
1172 END ;
1173 IF i<l
1174 THEN
1175 s := Slice(Mark(s), 0, i) ;
1176 l := DynamicStrings.Length(s) ;
1177 IF l<point
1178 THEN
1179 s := ConCat(s, Mult(Mark(InitStringChar('0')), point-l))
1181 END ;
1182 (* re-insert the point *)
1183 IF point>=0
1184 THEN
1185 IF point=0
1186 THEN
1187 s := ConCat(InitStringChar('.'), Mark(s))
1188 ELSE
1189 s := ConCat(ConCatChar(Slice(Mark(s), 0, point), '.'),
1190 Mark(Slice(Mark(s), point, 0)))
1192 END ;
1193 RETURN( s )
1194 END doDecimalPlaces ;
1198 ToSigFig - returns a floating point or base 10 integer
1199 string which is accurate to, n, significant
1200 figures. It will return a new String
1201 and, s, will be destroyed.
1204 So: 12.345
1206 rounded to the following significant figures yields
1208 5 12.345
1209 4 12.34
1210 3 12.3
1211 2 12
1212 1 10
1215 PROCEDURE ToSigFig (s: String; n: CARDINAL) : String ;
1217 point: INTEGER ;
1218 poTen: CARDINAL ;
1219 BEGIN
1220 Assert(IsDigit(char(s, 0)) OR (char(s, 0)='.'), __FILE__, __LINE__, __FUNCTION__) ;
1221 point := Index(s, '.', 0) ;
1222 IF point<0
1223 THEN
1224 poTen := DynamicStrings.Length(s)
1225 ELSE
1226 poTen := point
1227 END ;
1228 s := doSigFig(s, n) ;
1229 (* if the last character is '.' remove it *)
1230 IF (DynamicStrings.Length(s)>0) AND (char(s, -1)='.')
1231 THEN
1232 RETURN( Slice(Mark(s), 0, -1) )
1233 ELSE
1234 IF poTen>DynamicStrings.Length(s)
1235 THEN
1236 s := ConCat(s, Mark(Mult(Mark(InitStringChar('0')), poTen-DynamicStrings.Length(s))))
1237 END ;
1238 RETURN( s )
1240 END ToSigFig ;
1244 doSigFig - returns a string which is accurate to
1245 n decimal places. It returns a new String
1246 and, s, will be destroyed.
1249 PROCEDURE doSigFig (s: String; n: CARDINAL) : String ;
1251 i, l, z,
1252 point : INTEGER ;
1254 tenths,
1255 hundreths: String ;
1256 BEGIN
1257 l := DynamicStrings.Length(s) ;
1258 i := 0 ;
1259 (* remove '.' *)
1260 point := Index(s, '.', 0) ;
1261 IF point>=0
1262 THEN
1263 IF point=0
1264 THEN
1265 s := Slice(Mark(s), 1, 0)
1266 ELSIF point<l
1267 THEN
1268 s := ConCat(Slice(Mark(s), 0, point),
1269 Mark(Slice(Mark(s), point+1, 0)))
1270 ELSE
1271 s := Slice(Mark(s), 0, point)
1273 ELSE
1274 s := Dup(Mark(s))
1275 END ;
1276 l := DynamicStrings.Length(s) ;
1277 i := 0 ;
1278 IF l>0
1279 THEN
1280 (* skip over leading zeros *)
1281 WHILE (i<l) AND (char(s, i)='0') DO
1282 INC(i)
1283 END ;
1284 (* was the string full of zeros? *)
1285 IF (i=l) AND (char(s, i-1)='0')
1286 THEN
1287 (* truncate string *)
1288 s := Slice(Mark(s), 0, n) ;
1289 i := n
1291 END ;
1292 (* add a leading zero in case we need to overflow the carry *)
1293 z := i ; (* remember where we inserted zero *)
1294 IF z=0
1295 THEN
1296 s := ConCat(InitStringChar('0'), Mark(s))
1297 ELSE
1298 s := ConCat(ConCatChar(Slice(Mark(s), 0, i), '0'),
1299 Mark(Slice(Mark(s), i, 0)))
1300 END ;
1301 INC(n) ; (* and increase the number of sig figs needed *)
1302 l := DynamicStrings.Length(s) ;
1303 WHILE (n>1) AND (i<l) DO
1304 DEC(n) ;
1305 INC(i)
1306 END ;
1307 IF i+3<=l
1308 THEN
1309 t := Dup(s) ;
1310 hundreths := Slice(Mark(s), i+1, i+3) ;
1311 s := t ;
1312 IF stoc(hundreths)>=50
1313 THEN
1314 s := carryOne(Mark(s), i)
1315 END ;
1316 hundreths := KillString(hundreths)
1317 ELSIF i+2<=l
1318 THEN
1319 t := Dup(s) ;
1320 tenths := Slice(Mark(s), i+1, i+2) ;
1321 s := t ;
1322 IF stoc(tenths)>=5
1323 THEN
1324 s := carryOne(Mark(s), i)
1325 END ;
1326 tenths := KillString(tenths)
1327 END ;
1328 (* check whether we need to remove the leading zero *)
1329 IF char(s, z)='0'
1330 THEN
1331 IF z=0
1332 THEN
1333 s := Slice(Mark(s), z+1, 0)
1334 ELSE
1335 s := ConCat(Slice(Mark(s), 0, z),
1336 Mark(Slice(Mark(s), z+1, 0)))
1337 END ;
1338 l := DynamicStrings.Length(s)
1339 ELSE
1340 INC(point)
1341 END ;
1342 IF i<l
1343 THEN
1344 s := Slice(Mark(s), 0, i) ;
1345 l := DynamicStrings.Length(s) ;
1346 IF l<point
1347 THEN
1348 s := ConCat(s, Mult(Mark(InitStringChar('0')), point-l))
1350 END ;
1351 (* re-insert the point *)
1352 IF point>=0
1353 THEN
1354 IF point=0
1355 THEN
1356 s := ConCat(InitStringChar('.'), Mark(s))
1357 ELSE
1358 s := ConCat(ConCatChar(Slice(Mark(s), 0, point), '.'),
1359 Mark(Slice(Mark(s), point, 0)))
1361 END ;
1362 RETURN( s )
1363 END doSigFig ;
1367 carryOne - add a carry at position, i.
1370 PROCEDURE carryOne (s: String; i: CARDINAL) : String ;
1371 BEGIN
1372 IF i>=0
1373 THEN
1374 IF IsDigit(char(s, i))
1375 THEN
1376 IF char(s, i)='9'
1377 THEN
1378 IF i=0
1379 THEN
1380 s := ConCat(InitStringChar('1'), Mark(s)) ;
1381 RETURN s
1382 ELSE
1383 s := ConCat(ConCatChar(Slice(Mark(s), 0, i), '0'),
1384 Mark(Slice(Mark(s), i+1, 0))) ;
1385 RETURN carryOne(s, i-1)
1387 ELSE
1388 IF i=0
1389 THEN
1390 s := ConCat(InitStringChar(CHR(ORD(char(s, i))+1)),
1391 Mark(Slice(Mark(s), i+1, 0)))
1392 ELSE
1393 s := ConCat(ConCatChar(Slice(Mark(s), 0, i),
1394 CHR(ORD(char(s, i))+1)),
1395 Mark(Slice(Mark(s), i+1, 0)))
1399 END ;
1400 RETURN s
1401 END carryOne ;
1404 END StringConvert.