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