'From Squeak3.7beta of ''1 April 2004'' [latest update: #5923] on 18 September 2004 at 11:15:09 pm'! "Change Set: NumberReadFromFixes-dtl Date: 18 September 2004 Author: David T. Lewis Fixes a bug in creation of ScaledDecimals from strings and literals. Cleans up and refactors Number>>readFrom: for creating ScaledDecimals, Floats, and Integers. Attempts to avoid suble compiler bugs by ensuring that the stream from which #readFrom: is reading is kept in a valid state if parsing fails after a decimal point (a period intended for the Smalltalk parser). See unit tests for examples. Important: ScaledDecimalTest>>testLiteral must be recompiled after this change set is loaded, otherwise the test will fail. The postscript to this change set should do this. If the test still fails, load the NumberReadFromTests change set to force a recompile. "! !Number class methodsFor: 'private' stamp: 'dtl 9/18/2004 18:28'! canParseAsFloat: baseValue base: base from: aStream "Answer true if parsing a Float will succeed. Read from a copy of aStream to test the parsing." ^ ('edq' includes: aStream peek) and: [(self readFloat: baseValue base: base from: aStream copy) notNil]! ! !Number class methodsFor: 'private' stamp: 'dtl 9/18/2004 19:42'! canParseAsFloatOrScaledDecimal: value integerPart: integerPart fractionPart: fractionPart digits: fractionDigits base: base sign: sign from: aStream "Answer true if aStream contains parseable characters. The state of aStream is not changed." ^ (self canParseAsFloat: value base: base from: aStream) or: [self canParseAsScaledDecimal: integerPart fractionPart: fractionPart digits: fractionDigits base: base sign: sign from: aStream]! ! !Number class methodsFor: 'private' stamp: 'dtl 9/18/2004 18:20'! canParseAsScaledDecimal: integerPart fractionPart: fractionPart digits: fractionDigits base: base sign: sign from: aStream "Answer true if parsing a ScaleDecimal will succeed. Read from a copy of aStream to test the parsing." ^ aStream peek == $s and: [(self readScaledDecimal: integerPart fractionPart: fractionPart digits: fractionDigits base: base sign: sign from: aStream copy) notNil]! ! !Number class methodsFor: 'private' stamp: 'dtl 9/18/2004 19:50'! readFloat: baseValue base: base from: aStream "Complete creation of a Float, reading exponent from aStream. Answer a Float, or nil if parsing fails. (e|d|q)>" | exp value | aStream next. "skip e|d|q" (aStream peek digitValue between: 0 and: base - 1) ifFalse: [^ nil]. "Avoid throwing an error" exp _ Integer readFrom: aStream base: base. value := baseValue * (base raisedTo: exp). ^ value ! ! !Number class methodsFor: 'private' stamp: 'dtl 9/18/2004 19:45'! readRemainderOf: integerPart from: aStream base: base withSign: sign "Read optional fractional part and exponent or decimal scale, and return the final result" "Changed 200/01/19 For ANSI Numeric Literals support." "Number readFrom: '3r-22.2'" | value fraction fractionDigits fracpos fractionPart scaledDecimal | #Numeric. value := integerPart. fractionDigits := 0. (aStream peekFor: $.) ifTrue: ["." (aStream atEnd not and: [aStream peek digitValue between: 0 and: base - 1]) ifTrue: [fracpos := aStream position. fractionPart := Integer readFrom: aStream base: base. fraction := fractionPart asFloat / (base raisedTo: aStream position - fracpos). fractionDigits := aStream position - fracpos. value := value asFloat + fraction] ifFalse: [(self canParseAsFloatOrScaledDecimal: value integerPart: integerPart fractionPart: fractionPart digits: fractionDigits base: base sign: sign from: aStream) ifFalse: ["oops - just ." aStream skip: -1. "un-gobble the period" ^ value * sign]]]. (self canParseAsScaledDecimal: integerPart fractionPart: fractionPart digits: fractionDigits base: base sign: sign from: aStream) ifTrue: ["s[]" (scaledDecimal := self readScaledDecimal: integerPart fractionPart: fractionPart digits: fractionDigits base: base sign: sign from: aStream) ifNotNil: [^ scaledDecimal]]. (self canParseAsFloat: value base: base from: aStream) ifTrue: ["(e|d|q)>" value := self readFloat: value base: base from: aStream]. (value isFloat and: [value = 0.0 and: [sign = -1]]) ifTrue: [^ Float negativeZero] ifFalse: [^ value * sign]! ! !Number class methodsFor: 'private' stamp: 'dtl 9/18/2004 19:07'! readScaledDecimal: integerPart fractionPart: fractionPart digits: fractionDigits base: base sign: sign from: aStream "Complete creation of a ScaledDecimal, reading scale from aStream. Answer a ScaledDecimal, or nil if parsing fails. s[]" | scale decimalMultiplier decimalFraction | aStream atEnd ifTrue: [^ nil]. (aStream next == $s) ifFalse: [^ nil]. "s" (aStream peek digitValue between: 0 and: 10) ifTrue: [scale := Integer readFrom: aStream] ifFalse: [^ nil]. scale isNil ifTrue: ["s" fractionDigits = 0 ifTrue: ["s" scale := 0] ifFalse: [".s" scale := fractionDigits]]. fractionPart isNil ifTrue: [^ ScaledDecimal newFromNumber: integerPart * sign scale: scale] ifFalse: [decimalMultiplier := base raisedTo: fractionDigits. decimalFraction := integerPart * decimalMultiplier + fractionPart * sign / decimalMultiplier. ^ ScaledDecimal newFromNumber: decimalFraction scale: scale]! ! !Number class reorganize! ('instance creation' readFrom: readFrom:base:) ('private' canParseAsFloat:base:from: canParseAsFloatOrScaledDecimal:integerPart:fractionPart:digits:base:sign:from: canParseAsScaledDecimal:fractionPart:digits:base:sign:from: readFloat:base:from: readRemainderOf:from:base:withSign: readScaledDecimal:fractionPart:digits:base:sign:from:) ! "Postscript: Recompile the #testLiteral method if it already exists, otherwise the previously compiled literal will cause the test to fail." (Smalltalk at: #ScaledDecimalTest) ifNotNilDo: [:c | c recompile: #testLiteral] !