1(* $Id: IntConv.Mod,v 1.6 2002/05/26 12:15:17 mva Exp $ *) 2MODULE IntConv; 3(* 4 IntConv - Low-level integer/string conversions. 5 Copyright (C) 2000, 2002 Michael van Acken 6 Copyright (C) 1995 Michael Griebling 7 8 This module is free software; you can redistribute it and/or modify 9 it under the terms of the GNU Lesser General Public License as 10 published by the Free Software Foundation; either version 2 of the 11 License, or (at your option) any later version. 12 13 This module is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU Lesser General Public License for more details. 17 18 You should have received a copy of the GNU Lesser General Public 19 License along with this program; if not, write to the Free Software 20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 22*) 23 24IMPORT 25 Char := CharClass, Conv := ConvTypes; 26 27TYPE 28 ConvResults* = Conv.ConvResults; 29 (**One of @oconst{strAllRight}, @oconst{strOutOfRange}, 30 @oconst{strWrongFormat}, or @oconst{strEmpty}. *) 31 32CONST 33 strAllRight*=Conv.strAllRight; 34 (**The string format is correct for the corresponding conversion. *) 35 strOutOfRange*=Conv.strOutOfRange; 36 (**The string is well-formed but the value cannot be represented. *) 37 strWrongFormat*=Conv.strWrongFormat; 38 (**The string is in the wrong format for the conversion. *) 39 strEmpty*=Conv.strEmpty; 40 (**The given string is empty. *) 41 42 43VAR 44 W, S, SI: Conv.ScanState; 45 minInt, maxInt: ARRAY 11 OF CHAR; 46 47CONST 48 maxDigits = 10; (* length of minInt, maxInt *) 49 50 51(* internal state machine procedures *) 52 53PROCEDURE WState(inputCh: CHAR; VAR chClass: Conv.ScanClass; VAR nextState: Conv.ScanState); 54BEGIN 55 IF Char.IsNumeric(inputCh) THEN chClass:=Conv.valid; nextState:=W 56 ELSE chClass:=Conv.terminator; nextState:=NIL 57 END 58END WState; 59 60 61PROCEDURE SState(inputCh: CHAR; VAR chClass: Conv.ScanClass; VAR nextState: Conv.ScanState); 62BEGIN 63 IF Char.IsNumeric(inputCh) THEN chClass:=Conv.valid; nextState:=W 64 ELSE chClass:=Conv.invalid; nextState:=S 65 END 66END SState; 67 68 69PROCEDURE ScanInt*(inputCh: CHAR; VAR chClass: Conv.ScanClass; VAR nextState: Conv.ScanState); 70 (**Represents the start state of a finite state scanner for signed whole 71 numbers---assigns class of @oparam{inputCh} to @oparam{chClass} and a 72 procedure representing the next state to @oparam{nextState}. 73 74 The call of @samp{ScanInt(inputCh,chClass,nextState)} shall assign values 75 to @oparam{chClass} and @oparam{nextState} depending upon the value of 76 @oparam{inputCh} as shown in the following table. 77 78 @example 79 Procedure inputCh chClass nextState (a procedure 80 with behaviour of) 81 --------- --------- -------- --------- 82 ScanInt space padding ScanInt 83 sign valid SState 84 decimal digit valid WState 85 other invalid ScanInt 86 SState decimal digit valid WState 87 other invalid SState 88 WState decimal digit valid WState 89 other terminator -- 90 @end example 91 92 NOTE 1 -- The procedure @oproc{ScanInt} corresponds to the start state of a 93 finite state machine to scan for a character sequence that forms a signed 94 whole number. It may be used to control the actions of a finite state 95 interpreter. As long as the value of @oparam{chClass} is other than 96 @oconst{Conv.terminator} or @oconst{Conv.invalid}, the 97 interpreter should call the procedure whose value is assigned to 98 @oparam{nextState} by the previous call, supplying the next character from 99 the sequence to be scanned. It may be appropriate for the interpreter to 100 ignore characters classified as @oconst{Conv.invalid}, and proceed 101 with the scan. This would be the case, for example, with interactive 102 input, if only valid characters are being echoed in order to give 103 interactive users an immediate indication of badly-formed data. If the 104 character sequence end before one is classified as a terminator, the 105 string-terminator character should be supplied as input to the finite state 106 scanner. If the preceeding character sequence formed a complete number, 107 the string-terminator will be classified as @oconst{Conv.terminator}, 108 otherwise it will be classified as @oconst{Conv.invalid}. *) 109BEGIN 110 IF Char.IsWhiteSpace(inputCh) THEN chClass:=Conv.padding; nextState:=SI 111 ELSIF (inputCh="+") OR (inputCh="-") THEN chClass:=Conv.valid; nextState:=S 112 ELSIF Char.IsNumeric(inputCh) THEN chClass:=Conv.valid; nextState:=W 113 ELSE chClass:=Conv.invalid; nextState:=SI 114 END 115END ScanInt; 116 117 118PROCEDURE FormatInt*(str: ARRAY OF CHAR): ConvResults; 119(**Returns the format of the string value for conversion to LONGINT. *) 120VAR 121 ch: CHAR; 122 index, start: INTEGER; 123 state: Conv.ScanState; 124 positive: BOOLEAN; 125 prev, class: Conv.ScanClass; 126 127PROCEDURE LessOrEqual (VAR high: ARRAY OF CHAR; start, end: INTEGER): BOOLEAN; 128 VAR 129 i: INTEGER; 130 BEGIN (* pre: index-start = maxDigits *) 131 i := 0; 132 WHILE (start # end) DO 133 IF (str[start] < high[i]) THEN 134 RETURN TRUE; 135 ELSIF (str[start] > high[i]) THEN 136 RETURN FALSE; 137 ELSE (* str[start] = high[i] *) 138 INC (start); INC (i); 139 END; 140 END; 141 RETURN TRUE; (* full match *) 142 END LessOrEqual; 143 144BEGIN 145 index:=0; prev:=Conv.padding; state:=SI; positive:=TRUE; start := -1; 146 LOOP 147 ch:=str[index]; 148 state.p(ch, class, state); 149 CASE class OF 150 | Conv.padding: (* nothing to do *) 151 152 | Conv.valid: 153 IF ch="-" THEN positive:=FALSE 154 ELSIF ch="+" THEN positive:=TRUE 155 ELSIF (start < 0) & (ch # "0") THEN 156 start := index; 157 END 158 159 | Conv.invalid: 160 IF (prev = Conv.padding) & (ch = 0X) THEN 161 RETURN strEmpty; 162 ELSE 163 RETURN strWrongFormat; 164 END; 165 166 | Conv.terminator: 167 IF (ch = 0X) THEN 168 IF (index-start < maxDigits) OR 169 (index-start = maxDigits) & 170 (positive & LessOrEqual (maxInt, start, index) OR 171 ~positive & LessOrEqual (minInt, start, index)) THEN 172 RETURN strAllRight; 173 ELSE 174 RETURN strOutOfRange; 175 END; 176 ELSE 177 RETURN strWrongFormat; 178 END; 179 END; 180 prev:=class; INC(index) 181 END; 182END FormatInt; 183 184 185PROCEDURE ValueInt*(str: ARRAY OF CHAR): LONGINT; 186(**Returns the value corresponding to the signed whole number string value 187 @oparam{str} if @oparam{str} is well-formed. Otherwise, result is 188 undefined. *) 189VAR 190 i: INTEGER; 191 int: LONGINT; 192 positive: BOOLEAN; 193BEGIN 194 IF FormatInt(str)=strAllRight THEN 195 (* here holds: `str' is a well formed string and its value is in range *) 196 197 i:=0; positive:=TRUE; 198 WHILE (str[i] < "0") OR (str[i] > "9") DO (* skip whitespace and sign *) 199 IF (str[i] = "-") THEN 200 positive := FALSE; 201 END; 202 INC (i); 203 END; 204 205 int := 0; 206 IF positive THEN 207 WHILE (str[i] # 0X) DO 208 int:=int*10 + (ORD(str[i]) - ORD("0")); 209 INC (i); 210 END; 211 ELSE 212 WHILE (str[i] # 0X) DO 213 int:=int*10 - (ORD(str[i]) - ORD("0")); 214 INC (i); 215 END; 216 END; 217 RETURN int; 218 ELSE (* result is undefined *) 219 RETURN 0; 220 END 221END ValueInt; 222 223 224PROCEDURE LengthInt*(int: LONGINT): INTEGER; 225(**Returns the number of characters in the string representation of 226 @oparam{int}. This value corresponds to the capacity of an array @samp{str} 227 which is of the minimum capacity needed to avoid truncation of the result in 228 the call @samp{IntStr.IntToStr(int,str)}. *) 229VAR 230 cnt: INTEGER; 231BEGIN 232 IF int=MIN(LONGINT) THEN 233 RETURN maxDigits+1; 234 ELSE 235 IF int<=0 THEN int:=-int; cnt:=1 236 ELSE cnt:=0 237 END; 238 WHILE int>0 DO INC(cnt); int:=int DIV 10 END; 239 RETURN cnt; 240 END; 241END LengthInt; 242 243BEGIN 244 (* kludge necessary because of recursive procedure declaration *) 245 NEW(S); NEW(W); NEW(SI); 246 S.p:=SState; W.p:=WState; SI.p:=ScanInt; 247 minInt := "2147483648"; 248 maxInt := "2147483647"; 249END IntConv. 250