--- a/Integer.st Wed Jul 31 10:39:56 2002 +0200
+++ b/Integer.st Wed Jul 31 12:05:53 2002 +0200
@@ -402,8 +402,11 @@
"Modified: / 14.4.1998 / 19:16:46 / cg"
!
-readFromRomanString:aStringOrStream onError:exceptionalValue
- "convert a string or stream containing a roman representation into an integer"
+readFromRomanString:aStringOrStream
+ "convert a string or stream containing a roman representation into an integer.
+ Raises an exception, if the inputs format is wrong.
+ Will read both real and pidgin roman numbers (see printRomanOn: vs. printRomanOn:naive:),
+ however, a proceedable exception is raised for pidgin numbers."
|romanValues s c val digitVal prevDigitVal countSame delta
stopOnSeparator finish|
@@ -416,8 +419,7 @@
s := aStringOrStream readStream.
].
s atEnd ifTrue:[
- "RomanException raiseErrorStirng:'empty string'"
- ^ exceptionalValue value
+ ^ RomanNumberFormatError raiseErrorString:'empty string'
].
val := 0.
prevDigitVal := 99999.
@@ -428,28 +430,31 @@
c := s next asUppercase.
c isSeparator ifTrue:[
stopOnSeparator ifFalse:[
- "RomanException raiseErrorStirng:'garbage at the end'"
- ^ exceptionalValue value
+ ^ RomanNumberFormatError raiseErrorString:'garbage at the end'
].
finish := true.
] ifFalse:[
digitVal := romanValues at:c ifAbsent:nil.
digitVal isNil ifTrue:[
- "RomanException raiseErrorStirng:'invalid character'"
- ^ exceptionalValue value
+ ^ RomanNumberFormatError raiseErrorString:'invalid character'
].
digitVal = prevDigitVal ifTrue:[
( #( 1 10 100 1000) includes:digitVal) ifFalse:[
- "RomanException raiseErrorStirng:'character may not be repeated'"
- ^ exceptionalValue value
+ ^ RomanNumberFormatError raiseErrorString:'character may not be repeated'
].
val := val + digitVal.
countSame := countSame + 1.
- countSame == 4 ifTrue:[
+ countSame >= 4 ifTrue:[
digitVal ~= 1000 ifTrue:[
- "RomanException raiseErrorStirng:'more than 3 occurrences of same character'"
- ^ exceptionalValue value
+ countSame > 4 ifTrue:[
+ ^ RomanNumberFormatError raiseErrorString:'more than 4 occurrences of same character'
+ ].
+ "/ this is a naive roman number (such as VIIII);
+ "/ Its not correct, but sometimes encountered (especially as page numbers).
+ "/ If you do not want to be too picky,
+ "/ provide a proceeding handler in order to proceed the parsing.
+ NaiveRomanNumberFormatError raiseRequestErrorString:'more than 3 occurrences of same character'.
]
].
] ifFalse:[
@@ -457,13 +462,11 @@
val := val + digitVal.
] ifFalse:[
countSame == 1 ifFalse:[
- "RomanException raiseErrorStirng:'invalid character combination'"
- ^ exceptionalValue value
+ ^ RomanNumberFormatError raiseErrorString:'invalid character combination'
].
delta := digitVal - prevDigitVal.
( #( 4 9 40 90 400 900) includes:delta) ifFalse:[
- "RomanException raiseErrorStirng:'invalid character combination'"
- ^ exceptionalValue value
+ ^ RomanNumberFormatError raiseErrorString:'invalid character combination'
].
val := val - prevDigitVal.
val := val + delta.
@@ -475,21 +478,24 @@
].
].
"/ val > 5000 ifTrue:[
-"/ "RomanException raiseErrorStirng:'number out of range (1..5000)'"
-"/ ^ exceptionalValue value
+"/ ^ RomanNumberFormatError raiseErrorStirng:'number out of range (1..5000)'
"/ ].
^ val.
"
- self readFromRomanString:'I' onError:nil
- self readFromRomanString:'II' onError:nil
- self readFromRomanString:'III' onError:nil
- self readFromRomanString:'IV' onError:nil
- self readFromRomanString:'LC' onError:nil
- self readFromRomanString:'clix' onError:nil
- self readFromRomanString:'MCMXCIX' onError:nil
+ Integer readFromRomanString:'I'
+ Integer readFromRomanString:'II'
+ Integer readFromRomanString:'III'
+ Integer readFromRomanString:'IV'
+ Integer readFromRomanString:'clix'
+ Integer readFromRomanString:'MIX'
+ Integer readFromRomanString:'MCMXCIX'
+
+ Error case:
+ Integer readFromRomanString:'LC'
"
+
"error cases:
#(
''
@@ -503,7 +509,7 @@
'LL'
'DD'
) do:[:badString |
- (self readFromRomanString:badString onError:nil) notNil ifTrue:[self halt].
+ (Integer readFromRomanString:badString onError:nil) notNil ifTrue:[self halt].
]
"
@@ -542,7 +548,7 @@
'MMMMCMXCIX' 4999
'MMMMMMMMMCMXCIX' 9999
) pairWiseDo:[:goodString :expectedValue |
- (self readFromRomanString:goodString onError:nil) ~= expectedValue ifTrue:[self halt].
+ (Integer readFromRomanString:goodString onError:nil) ~= expectedValue ifTrue:[self halt].
]
"
@@ -551,7 +557,124 @@
|romanString|
romanString := String streamContents:[:stream | n printRomanOn:stream].
- (self readFromRomanString:romanString onError:nil) ~= n ifTrue:[self halt].
+ (Integer readFromRomanString:romanString onError:nil) ~= n ifTrue:[self halt].
+ ]
+ "
+!
+
+readFromRomanString:aStringOrStream onError:exceptionalValue
+ "convert a string or stream containing a roman representation into an integer.
+ Raises an exception, if the inputs format is wrong. Does not allow naive roman numbers."
+
+ |val|
+
+ RomanNumberFormatError
+ handle:[:ex |
+ ex signal == NaiveRomanNumberFormatError ifTrue:[
+ NaiveRomanNumberFormatError isHandled ifTrue:[
+ ex reject
+ ]
+ ].
+ val := exceptionalValue value
+ ]
+ do:[
+ val := self readFromRomanString:aStringOrStream
+ ].
+ ^ val
+
+
+ "
+ Integer readFromRomanString:'I' onError:nil
+ Integer readFromRomanString:'II' onError:nil
+ Integer readFromRomanString:'III' onError:nil
+ Integer readFromRomanString:'IV' onError:nil
+ Integer readFromRomanString:'clix' onError:nil
+ Integer readFromRomanString:'MCMXCIX' onError:nil
+
+ Error cases:
+ Integer readFromRomanString:'LC' onError:nil
+ Integer readFromRomanString:'IIII' onError:nil
+
+ However, the last one can be suppressed:
+ NaiveRomanNumberFormatError ignoreIn:[
+ Integer readFromRomanString:'IIII' onError:nil
+ ]
+ "
+
+ "error cases:
+ #(
+ ''
+ 'IIII'
+ 'XIIX'
+ 'VV'
+ 'VVV'
+ 'XXL'
+ 'XLX'
+ 'LC'
+ 'LL'
+ 'DD'
+ ) do:[:badString |
+ (Integer readFromRomanString:badString onError:nil) notNil ifTrue:[self halt].
+ ]
+ "
+
+ "good cases:
+ #( 'I' 1
+ 'II' 2
+ 'III' 3
+ 'IV' 4
+ 'V' 5
+ 'VI' 6
+ 'VII' 7
+ 'VIII' 8
+ 'IX' 9
+ 'X' 10
+ 'XI' 11
+ 'XII' 12
+ 'XIII' 13
+ 'XIV' 14
+ 'XV' 15
+ 'XVI' 16
+ 'XVII' 17
+ 'XVIII' 18
+ 'XIX' 19
+ 'XX' 20
+ 'XXX' 30
+ 'L' 50
+ 'XL' 40
+ 'LX' 60
+ 'LXX' 70
+ 'LXXX' 80
+ 'CXL' 140
+ 'CL' 150
+ 'CLX' 160
+ 'MMM' 3000
+ 'MMMM' 4000
+ 'MMMMCMXCIX' 4999
+ 'MMMMMMMMMCMXCIX' 9999
+ ) pairWiseDo:[:goodString :expectedValue |
+ (Integer readFromRomanString:goodString onError:nil) ~= expectedValue ifTrue:[self halt].
+ ]
+ "
+
+ "
+ 1 to:9999 do:[:n |
+ |romanString|
+
+ romanString := String streamContents:[:stream | n printRomanOn:stream].
+ (Integer readFromRomanString:romanString onError:nil) ~= n ifTrue:[self halt].
+ ]
+ "
+
+ "reading naive numbers:
+
+ 1 to:9999 do:[:n |
+ |romanString|
+
+ romanString := String streamContents:[:stream | n printRomanOn:stream naive:true].
+ NaiveRomanNumberFormatError ignoreIn:[
+ (Integer readFromRomanString:romanString onError:nil) ~= n ifTrue:[self halt].
+ ]
]
"
! !
@@ -2440,31 +2563,13 @@
!
printRomanOn:aStream
- "print the receiver as roman number to the receiver, aStream"
-
- "convert a string or stream containing a roman representation into an integer"
-
- |restValue|
-
- restValue := self.
- restValue > 0 ifFalse:[self error:'negative roman'].
-
- [restValue >= 1000] whileTrue:[ aStream nextPutAll:'M'. restValue := restValue - 1000. ].
- (restValue >= 900) ifTrue: [ aStream nextPutAll:'CM'. restValue := restValue - 900. ].
- (restValue >= 500) ifTrue: [ aStream nextPutAll:'D'. restValue := restValue - 500. ].
- (restValue >= 400) ifTrue: [ aStream nextPutAll:'CD'. restValue := restValue - 400. ].
- [restValue >= 100] whileTrue:[ aStream nextPutAll:'C'. restValue := restValue - 100. ].
- (restValue >= 90) ifTrue: [ aStream nextPutAll:'XC'. restValue := restValue - 90. ].
- (restValue >= 50) ifTrue: [ aStream nextPutAll:'L'. restValue := restValue - 50. ].
- (restValue >= 40) ifTrue: [ aStream nextPutAll:'XL'. restValue := restValue - 40. ].
- [restValue >= 10] whileTrue:[ aStream nextPutAll:'X'. restValue := restValue - 10. ].
- (restValue >= 9) ifTrue: [ aStream nextPutAll:'IX'. restValue := restValue - 9. ].
- (restValue >= 5) ifTrue: [ aStream nextPutAll:'V'. restValue := restValue - 5. ].
- (restValue >= 4) ifTrue: [ aStream nextPutAll:'IV'. restValue := restValue - 4. ].
- [restValue >= 1] whileTrue:[ aStream nextPutAll:'I'. restValue := restValue - 1. ].
+ "print the receiver as roman number to the receiver, aStream.
+ This converts correct (i.e. prefix notation for 4,9,40,90, etc.)."
+
+ ^ self printRomanOn:aStream naive:false
"
- 1 printRomanOn:Transcript. Transcript cr.
+ 1 to:10 do:[:i | i printRomanOn:Transcript. Transcript cr.].
1999 printRomanOn:Transcript. Transcript cr.
Date today year printRomanOn:Transcript. Transcript cr.
"
@@ -2474,7 +2579,90 @@
|romanString|
romanString := String streamContents:[:stream | n printRomanOn:stream].
- (self readFromRomanString:romanString onError:nil) ~= n ifTrue:[self halt].
+ (Integer readFromRomanString:romanString onError:nil) ~= n ifTrue:[self halt].
+ ]
+ "
+!
+
+printRomanOn:aStream naive:naive
+ "print the receiver as roman number to the receiver, aStream.
+ The naive argument controls if the conversion is
+ correct (i.e. prefix notation for 4,9,40,90, etc.),
+ or naive (i.e. print 4 as IIII and 9 as VIIII).
+ The naive version is often used for page numbers in documents."
+
+ |restValue spec|
+
+ restValue := self.
+ restValue > 0 ifFalse:[self error:'negative roman'].
+
+ naive ifTrue:[
+ spec := #(
+ " value string repeat "
+ 1000 'M' true
+ 500 'D' false
+ 100 'C' true
+ 50 'L' false
+ 10 'X' true
+ 5 'V' false
+ 1 'I' true
+ ).
+ ] ifFalse:[
+ spec := #(
+ " value string repeat "
+ 1000 'M' true
+ 900 'CM' false
+ 500 'D' false
+ 400 'CD' false
+ 100 'C' true
+ 90 'XC' false
+ 50 'L' false
+ 40 'XL' false
+ 10 'X' true
+ 9 'IX' false
+ 5 'V' false
+ 4 'IV' false
+ 1 'I' true
+ ).
+ ].
+
+ spec
+ inGroupsOf:3
+ do:[:rValue :rString :repeatFlag |
+
+ [
+ (restValue >= rValue) ifTrue:[
+ aStream nextPutAll:rString.
+ restValue := restValue - rValue.
+ ].
+ ] doWhile:[ repeatFlag and:[ restValue >= rValue] ].
+ ].
+
+ "
+ 1 to:10 do:[:i | i printRomanOn:Transcript naive:false. Transcript cr.].
+ 1 to:10 do:[:i | i printRomanOn:Transcript naive:true. Transcript cr.].
+
+ 1999 printRomanOn:Transcript. Transcript cr.
+ Date today year printRomanOn:Transcript. Transcript cr.
+ "
+
+ "test all between 1 and 9999:
+ 1 to:9999 do:[:n |
+ |romanString|
+
+ romanString := String streamContents:[:stream | n printRomanOn:stream naive:false].
+ (Integer readFromRomanString:romanString onError:nil) ~= n ifTrue:[self halt].
+ ]
+ "
+
+ "test naive all between 1 and 9999:
+ 1 to:9999 do:[:n |
+ |romanString|
+
+ romanString := String streamContents:[:stream | n printRomanOn:stream naive:true].
+ NaiveRomanNumberFormatError ignoreIn:[
+ (Integer readFromRomanString:romanString onError:nil) ~= n ifTrue:[self halt].
+ ]
]
"
!
@@ -2971,6 +3159,6 @@
!Integer class methodsFor:'documentation'!
version
- ^ '$Header: /cvs/stx/stx/libbasic/Integer.st,v 1.149 2002-07-16 13:21:47 cg Exp $'
+ ^ '$Header: /cvs/stx/stx/libbasic/Integer.st,v 1.150 2002-07-31 10:05:53 cg Exp $'
! !
Integer initialize!