mirror of
https://github.com/vishapoberon/compiler.git
synced 2026-04-06 12:12:25 +00:00
447 lines
13 KiB
Modula-2
447 lines
13 KiB
Modula-2
(* $Id: Real0.Mod,v 1.3 2002/08/12 18:11:30 mva Exp $ *)
|
|
MODULE ooc2Real0;
|
|
(* Helper functions used by the real conversion modules.
|
|
Copyright (C) 2002 Michael van Acken
|
|
|
|
This module is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public License
|
|
as published by the Free Software Foundation; either version 2 of
|
|
the License, or (at your option) any later version.
|
|
|
|
This module is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with OOC. If not, write to the Free Software Foundation,
|
|
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*)
|
|
|
|
IMPORT
|
|
CharClass := ooc2CharClass, ConvTypes := ooc2ConvTypes, Strings := ooc2Strings;
|
|
|
|
|
|
TYPE
|
|
ConvResults = ConvTypes.ConvResults;
|
|
|
|
CONST
|
|
strAllRight=ConvTypes.strAllRight;
|
|
strOutOfRange=ConvTypes.strOutOfRange;
|
|
strWrongFormat=ConvTypes.strWrongFormat;
|
|
strEmpty=ConvTypes.strEmpty;
|
|
|
|
CONST
|
|
padding=ConvTypes.padding;
|
|
valid=ConvTypes.valid;
|
|
invalid=ConvTypes.invalid;
|
|
terminator=ConvTypes.terminator;
|
|
|
|
TYPE
|
|
ScanClass = ConvTypes.ScanClass;
|
|
ScanState = ConvTypes.ScanState;
|
|
|
|
CONST
|
|
expChar* = "E";
|
|
|
|
VAR
|
|
RS-, P-, F-, E-, SE-, WE-, SR-: ScanState;
|
|
|
|
|
|
(* internal state machine procedures *)
|
|
|
|
PROCEDURE IsSign (ch: CHAR): BOOLEAN;
|
|
(* Return TRUE for '+' or '-' *)
|
|
BEGIN
|
|
RETURN (ch='+') OR (ch='-')
|
|
END IsSign;
|
|
|
|
PROCEDURE RSState(inputCh: CHAR;
|
|
VAR chClass: ScanClass; VAR nextState: ScanState);
|
|
BEGIN
|
|
IF CharClass.IsNumeric(inputCh) THEN
|
|
chClass:=valid; nextState:=P
|
|
ELSE
|
|
chClass:=invalid; nextState:=RS
|
|
END
|
|
END RSState;
|
|
|
|
PROCEDURE PState(inputCh: CHAR;
|
|
VAR chClass: ScanClass; VAR nextState: ScanState);
|
|
BEGIN
|
|
IF CharClass.IsNumeric(inputCh) THEN
|
|
chClass:=valid; nextState:=P
|
|
ELSIF inputCh="." THEN
|
|
chClass:=valid; nextState:=F
|
|
ELSIF inputCh=expChar THEN
|
|
chClass:=valid; nextState:=E
|
|
ELSE
|
|
chClass:=terminator; nextState:=NIL
|
|
END
|
|
END PState;
|
|
|
|
PROCEDURE FState(inputCh: CHAR;
|
|
VAR chClass: ScanClass; VAR nextState: ScanState);
|
|
BEGIN
|
|
IF CharClass.IsNumeric(inputCh) THEN
|
|
chClass:=valid; nextState:=F
|
|
ELSIF inputCh=expChar THEN
|
|
chClass:=valid; nextState:=E
|
|
ELSE
|
|
chClass:=terminator; nextState:=NIL
|
|
END
|
|
END FState;
|
|
|
|
PROCEDURE EState(inputCh: CHAR;
|
|
VAR chClass: ScanClass; VAR nextState: ScanState);
|
|
BEGIN
|
|
IF IsSign(inputCh) THEN
|
|
chClass:=valid; nextState:=SE
|
|
ELSIF CharClass.IsNumeric(inputCh) THEN
|
|
chClass:=valid; nextState:=WE
|
|
ELSE
|
|
chClass:=invalid; nextState:=E
|
|
END
|
|
END EState;
|
|
|
|
PROCEDURE SEState(inputCh: CHAR;
|
|
VAR chClass: ScanClass; VAR nextState: ScanState);
|
|
BEGIN
|
|
IF CharClass.IsNumeric(inputCh) THEN
|
|
chClass:=valid; nextState:=WE
|
|
ELSE
|
|
chClass:=invalid; nextState:=SE
|
|
END
|
|
END SEState;
|
|
|
|
PROCEDURE WEState(inputCh: CHAR;
|
|
VAR chClass: ScanClass; VAR nextState: ScanState);
|
|
BEGIN
|
|
IF CharClass.IsNumeric(inputCh) THEN
|
|
chClass:=valid; nextState:=WE
|
|
ELSE
|
|
chClass:=terminator; nextState:=NIL
|
|
END
|
|
END WEState;
|
|
|
|
PROCEDURE ScanReal*(inputCh: CHAR;
|
|
VAR chClass: ScanClass; VAR nextState: ScanState);
|
|
BEGIN
|
|
IF CharClass.IsWhiteSpace(inputCh) THEN
|
|
chClass:=padding; nextState:=SR
|
|
ELSIF IsSign(inputCh) THEN
|
|
chClass:=valid; nextState:=RS
|
|
ELSIF CharClass.IsNumeric(inputCh) THEN
|
|
chClass:=valid; nextState:=P
|
|
ELSE
|
|
chClass:=invalid; nextState:=SR
|
|
END
|
|
END ScanReal;
|
|
|
|
PROCEDURE FormatReal* (str: ARRAY OF CHAR; maxExp: LONGINT;
|
|
maxValue: ARRAY OF CHAR): ConvResults;
|
|
VAR
|
|
i: LONGINT;
|
|
ch: CHAR;
|
|
state: ConvTypes.ScanState;
|
|
class: ConvTypes.ScanClass;
|
|
wSigFigs, fLeadingZeros, exp, startOfExp: LONGINT;
|
|
expNegative, allZeroDigit: BOOLEAN;
|
|
|
|
CONST
|
|
expCutoff = 100000000;
|
|
(* assume overflow if the value of the exponent is larger than this *)
|
|
|
|
PROCEDURE NonZeroDigit (): LONGINT;
|
|
(* locate first non-zero digit in str *)
|
|
BEGIN
|
|
i := 0;
|
|
WHILE (i # startOfExp) & ((str[i] < "1") OR (str[i] > "9")) DO
|
|
INC (i);
|
|
END;
|
|
RETURN i;
|
|
END NonZeroDigit;
|
|
|
|
PROCEDURE LessOrEqual (upperBound: ARRAY OF CHAR): BOOLEAN;
|
|
VAR
|
|
i, j: LONGINT;
|
|
BEGIN
|
|
i := NonZeroDigit();
|
|
IF (i # startOfExp) THEN (* str[i] is non-zero digit *)
|
|
j := 0;
|
|
WHILE (i # startOfExp) & (upperBound[j] # 0X) DO
|
|
IF (str[i] < upperBound[j]) THEN
|
|
RETURN TRUE;
|
|
ELSIF (str[i] > upperBound[j]) THEN
|
|
RETURN FALSE;
|
|
ELSE
|
|
INC (j); INC (i);
|
|
IF (str[i] = ".") THEN (* skip decimal point *)
|
|
INC (i);
|
|
END;
|
|
END;
|
|
END;
|
|
|
|
IF (upperBound[j] = 0X) THEN
|
|
(* any trailing zeros don't change the outcome: skip them *)
|
|
WHILE (str[i] = "0") OR (str[i] = ".") DO
|
|
INC (i);
|
|
END;
|
|
END;
|
|
END;
|
|
RETURN (i = startOfExp);
|
|
END LessOrEqual;
|
|
|
|
BEGIN
|
|
(* normalize exponent character *)
|
|
i := 0;
|
|
WHILE (str[i] # 0X) & (str[i] # "e") DO
|
|
INC (i);
|
|
END;
|
|
IF (str[i] = "e") THEN
|
|
str[i] := expChar;
|
|
END;
|
|
|
|
(* move index `i' over padding characters *)
|
|
i := 0;
|
|
state := SR;
|
|
REPEAT
|
|
ch := str[i];
|
|
state.p(ch, class, state);
|
|
INC (i);
|
|
UNTIL (class # ConvTypes.padding);
|
|
|
|
IF (ch = 0X) THEN
|
|
RETURN strEmpty;
|
|
ELSE
|
|
(* scan part before decimal point or exponent *)
|
|
WHILE (class = ConvTypes.valid) & (state # F) & (state # E) &
|
|
((ch < "1") OR (ch > "9")) DO
|
|
ch := str[i];
|
|
state.p(ch, class, state);
|
|
INC (i);
|
|
END;
|
|
wSigFigs := 0;
|
|
WHILE (class = ConvTypes.valid) & (state # F) & (state # E) DO
|
|
INC (wSigFigs);
|
|
ch := str[i];
|
|
state.p(ch, class, state);
|
|
INC (i);
|
|
END;
|
|
(* here holds: wSigFigs is the number of significant digits in
|
|
the whole number part of the number; 0 means there are only
|
|
zeros before the decimal point *)
|
|
|
|
(* scan fractional part exponent *)
|
|
fLeadingZeros := 0; allZeroDigit := TRUE;
|
|
WHILE (class = ConvTypes.valid) & (state # E) DO
|
|
ch := str[i];
|
|
IF allZeroDigit THEN
|
|
IF (ch = "0") THEN
|
|
INC (fLeadingZeros);
|
|
ELSIF (ch # ".") THEN
|
|
allZeroDigit := FALSE;
|
|
END;
|
|
END;
|
|
state.p(ch, class, state);
|
|
INC (i);
|
|
END;
|
|
(* here holds: fLeadingZeros holds the number of zeros after
|
|
the decimal point *)
|
|
|
|
(* scan exponent *)
|
|
startOfExp := i-1; exp := 0; expNegative := FALSE;
|
|
WHILE (class = ConvTypes.valid) DO
|
|
ch := str[i];
|
|
IF (ch = "-") THEN
|
|
expNegative := TRUE;
|
|
ELSIF ("0" <= ch) & (ch <= "9") & (exp < expCutoff) THEN
|
|
exp := exp*10 + (ORD(ch)-ORD("0"));
|
|
END;
|
|
state.p(ch, class, state);
|
|
INC (i);
|
|
END;
|
|
IF expNegative THEN
|
|
exp := -exp;
|
|
END;
|
|
(* here holds: exp holds the value of the exponent; if it's absolute
|
|
value is larger than expCutoff, then there has been an overflow *)
|
|
|
|
IF (class = ConvTypes.invalid) OR (ch # 0X) THEN
|
|
RETURN strWrongFormat;
|
|
ELSE (* (class = ConvTypes.terminator) & (ch = 0X) *)
|
|
(* normalize the number: calculate the exponent if the number would
|
|
start with a non-zero digit, immediately followed by the
|
|
decimal point *)
|
|
IF (wSigFigs > 0) THEN
|
|
exp := exp+wSigFigs-1;
|
|
ELSE
|
|
exp := exp-fLeadingZeros-1;
|
|
END;
|
|
|
|
IF (exp > maxExp) & (NonZeroDigit() # startOfExp) OR
|
|
(exp = maxExp) & ~LessOrEqual (maxValue) THEN
|
|
RETURN strOutOfRange;
|
|
ELSE
|
|
RETURN strAllRight;
|
|
END;
|
|
END;
|
|
END;
|
|
END FormatReal;
|
|
|
|
PROCEDURE NormalizeFloat* (VAR s: ARRAY OF CHAR);
|
|
VAR
|
|
i, d: INTEGER;
|
|
BEGIN
|
|
(* massage the output of sprintf to match our requirements; note: this
|
|
code should also handle "Inf", "Infinity", "NaN", etc., gracefully
|
|
but this is untested *)
|
|
IF (s[0] = "+") THEN d := 1; ELSE d := 0; END; (* erase "+" sign *)
|
|
i := 1;
|
|
WHILE (s[i] # 0X) DO
|
|
IF (s[i] = ".") & (s[i+1] = expChar) THEN
|
|
INC (d); (* eliminate "." if no digits follow *)
|
|
ELSIF (s[i] = "0") & (i-d-1 >= 0) & IsSign (s[i-d-1]) THEN
|
|
INC (d); (* eliminate zeros after exponent sign *)
|
|
ELSE
|
|
s[i-d] := s[i];
|
|
END;
|
|
INC (i);
|
|
END;
|
|
IF (s[i-d-2] = "E") THEN
|
|
s[i-d-2] := 0X; (* remove "E+" or "E-" *)
|
|
ELSE
|
|
s[i-d] := 0X;
|
|
END;
|
|
END NormalizeFloat;
|
|
|
|
PROCEDURE FormatForEng* (VAR s: ARRAY OF CHAR);
|
|
VAR
|
|
i, d, fract, exp, posExp, offset: INTEGER;
|
|
BEGIN
|
|
(* find out how large the exponent is, and how many digits are in the
|
|
fractional part *)
|
|
fract := 0; exp := 0; posExp := 0;
|
|
IF CharClass.IsNumeric (s[1]) THEN (* skip for NaN, Inf *)
|
|
i := 0; d := 0;
|
|
WHILE (s[i] # "E") DO
|
|
fract := fract + d;
|
|
IF (s[i] = ".") THEN d := 1; END;
|
|
INC (i);
|
|
END;
|
|
INC (i);
|
|
IF (s[i] = "-") THEN d := -1; ELSE d := 1; END;
|
|
posExp := i;
|
|
INC (i);
|
|
WHILE (s[i] # 0X) DO
|
|
exp := exp*10 + d*(ORD (s[i]) - ORD ("0"));
|
|
INC (i);
|
|
END;
|
|
END;
|
|
|
|
offset := exp MOD 3;
|
|
IF (offset # 0) THEN
|
|
WHILE (fract < offset) DO (* need more zeros before "E" *)
|
|
Strings.Insert ("0", posExp-1, s); INC (fract); INC (posExp);
|
|
END;
|
|
i := 2;
|
|
WHILE (i < offset+2) DO (* move "." offset places to right *)
|
|
s[i] := s[i+1]; INC (i);
|
|
END;
|
|
s[i] := ".";
|
|
|
|
(* write new exponent *)
|
|
exp := exp-offset;
|
|
IF (exp < 0) THEN
|
|
exp := -exp; s[posExp] := "-";
|
|
ELSE
|
|
s[posExp] := "+";
|
|
END;
|
|
s[posExp+1] := CHR (exp DIV 100 + ORD("0"));
|
|
s[posExp+2] := CHR (exp DIV 10 MOD 10 + ORD("0"));
|
|
s[posExp+3] := CHR (exp MOD 10 + ORD("0"));
|
|
s[posExp+4] := 0X;
|
|
END;
|
|
NormalizeFloat (s);
|
|
END FormatForEng;
|
|
|
|
PROCEDURE FormatForFixed* (VAR s: ARRAY OF CHAR; place: INTEGER);
|
|
VAR
|
|
i, d, c, fract, point, suffix: INTEGER;
|
|
|
|
PROCEDURE NotZero (VAR s: ARRAY OF CHAR; pos: INTEGER): BOOLEAN;
|
|
BEGIN
|
|
WHILE (s[pos] # 0X) DO
|
|
IF (s[pos] # "0") & (s[pos] # ".") THEN
|
|
RETURN TRUE;
|
|
END;
|
|
INC (pos);
|
|
END;
|
|
RETURN FALSE;
|
|
END NotZero;
|
|
|
|
BEGIN
|
|
IF (place < 0) THEN
|
|
(* locate position of decimal point in string *)
|
|
point := 1;
|
|
WHILE (s[point] # ".") DO INC (point); END;
|
|
|
|
(* number of digits before point is `point-1'; position in string
|
|
of the first digit that will be converted to zero due to rounding:
|
|
`point+place+1'; rightmost digit that may be incremented because
|
|
of rounding: `point+place' *)
|
|
IF (point+place >= 0) THEN
|
|
suffix := point+place+1; IF (s[suffix] = ".") THEN INC (suffix); END;
|
|
IF (s[suffix] > "5") OR
|
|
(s[suffix] = "5") &
|
|
(NotZero (s, suffix+1) OR
|
|
(point+place # 0) & ODD (ORD (s[point+place]))) THEN
|
|
(* we are rounding up *)
|
|
i := point+place;
|
|
WHILE (s[i] = "9") DO s[i] := "0"; DEC (i); END;
|
|
IF (i = 0) THEN (* looking at sign *)
|
|
Strings.Insert ("1", 1, s); INC (point);
|
|
ELSE
|
|
s[i] := CHR (ORD (s[i])+1); (* increment non-"9" digit by one *)
|
|
END;
|
|
END;
|
|
|
|
(* zero everything after the digit at `place' *)
|
|
i := point+place+1;
|
|
IF (i = 1) THEN (* all zero *)
|
|
s[1] := "0"; s[2] := 0X;
|
|
ELSE
|
|
WHILE (s[i] # ".") DO s[i] := "0"; INC (i); END;
|
|
END;
|
|
ELSE (* round to zero *)
|
|
s[1] := "0"; s[2] := 0X;
|
|
END;
|
|
s[point] := 0X;
|
|
END;
|
|
|
|
(* correct sign, and add trailing zeros if necessary *)
|
|
IF (s[0] = "+") THEN d := 1; ELSE d := 0; END; (* erase "+" sign *)
|
|
i := 1; fract := 0; c := 0;
|
|
WHILE (s[i] # 0X) DO
|
|
s[i-d] := s[i];
|
|
fract := fract+c;
|
|
IF (s[i] = ".") THEN
|
|
c := 1;
|
|
END;
|
|
INC (i);
|
|
END;
|
|
WHILE (fract < place) DO
|
|
s[i-d] := "0"; INC (fract); INC (i);
|
|
END;
|
|
s[i-d] := 0X;
|
|
END FormatForFixed;
|
|
|
|
BEGIN
|
|
NEW(RS); RS.p:=RSState;
|
|
NEW(P); P.p:=PState;
|
|
NEW(F); F.p:=FState;
|
|
NEW(E); E.p:=EState;
|
|
NEW(SE); SE.p:=SEState;
|
|
NEW(WE); WE.p:=WEState;
|
|
NEW(SR); SR.p:=ScanReal;
|
|
END ooc2Real0.
|