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