Change Set: tallyLiterals.1.cs
Christoph Thiede via Squeak-dev <[email protected]>
| Newsgroups | gmane.comp.lang.smalltalk.squeak.general |
|---|---|
| Message-ID | <7bca6b95-86e4-4ced-8817-0aef384e0fba@MX2025-DAG1.hpi.uni-potsdam.de> |
=============== Summary ===============
Change Set: tallyLiterals
Date: 1 June 2026
Author: Christoph Thiede
Adds support for tally bases and radix 1 to number parser. Also updates Shout and tests.
At first I considered making 1r111 = 3 as supposed earlier on the mailing list but then I found the following more consistent:
1r000 = 0.
1t = 0.
1t1 = 1.
1t111 = 3.
=============== Diff ===============
ExtendedNumberParser>>nextFraction {parsing-public} · ct 6/1/2026 21:45 (changed)
nextFraction
| numerator denominator numberOfTrailingZeroInIntegerPart |
base := self defaultBase.
neg := self peekSignIsMinus.
(integerPart := self nextUnsignedIntegerOrNilBase: base)
ifNil: [numberOfTrailingZeroInIntegerPart := 0]
ifNotNil: [
numberOfTrailingZeroInIntegerPart := nDigits - lastNonZero.
(sourceStream peekFor: $r)
ifTrue: ["<base>r<integer>"
- (base := integerPart) < 2
+ (base := integerPart) < 1
ifTrue: [
sourceStream skip: -1.
- ^ self expected: 'an integer greater than 1 as valid radix'].
+ ^ self expected: 'an integer greater than 0 as valid radix'].
self peekSignIsMinus
ifTrue: [neg := neg not].
integerPart := self nextUnsignedIntegerBase: base.
numberOfTrailingZeroInIntegerPart := nDigits - lastNonZero]].
(sourceStream peekFor: $.)
ifTrue:
[^self readFractionPartNumberOfTrailingZeroInIntegerPart: numberOfTrailingZeroInIntegerPart].
integerPart
ifNil:
["No integerPart, raise an error"
^ self expected: 'a digit'].
numerator := neg
ifTrue: [integerPart negated]
ifFalse: [integerPart].
self readExponent ifTrue: [numerator := numerator * (base raisedToInteger: exponent)].
(sourceStream peekFor: $/) ifFalse: [^numerator].
base := 10.
base := self defaultBase.
(denominator := self nextUnsignedIntegerOrNilBase: base)
ifNil:
[sourceStream skip: -1. "Not a valid denominator, ungobble / and return numerator"
^numerator].
(sourceStream peekFor: $r)
ifTrue: ["<base>r<integer>"
- (base := denominator) < 2
+ (base := denominator) < 1
ifTrue: [
sourceStream skip: -1.
- ^ self expected: 'an integer greater than 1 as valid radix'].
+ ^ self expected: 'an integer greater than 0 as valid radix'].
denominator := self nextUnsignedIntegerBase: base].
self readExponent ifTrue: [denominator := denominator * (base raisedToInteger: exponent)].
^numerator / denominator
ExtendedNumberParser>>nextNumber {parsing-public} · ct 6/1/2026 22:52 (changed)
nextNumber
"main method for reading a number.
This one can read Float Integer and ScaledDecimal"
| numberOfTrailingZeroInIntegerPart |
base := self defaultBase.
neg := self peekSignIsMinus.
integerPart := self nextUnsignedIntegerOrNilBase: base.
integerPart ifNil: [(sourceStream peekFor: $.)
ifTrue: [
"Try .1 syntax"
^self readNumberWithoutIntegerPart]
ifFalse: [
"This is not a regular number beginning with a digit
It is time to check for exceptional condition NaN and Infinity"
^self readNamedFloatOrFail]].
numberOfTrailingZeroInIntegerPart := nDigits - lastNonZero.
(sourceStream peekFor: $r)
ifTrue: ["<base>r<integer>"
| oldNeg pos |
pos := sourceStream position - 1.
- (base := integerPart) < 2
- ifTrue: ["A radix currently need to be greater than 1, ungobble the r and return the integer part"
+ (base := integerPart) < 1
+ ifTrue: ["A radix currently need to be greater than 0, ungobble the r and return the integer part"
sourceStream skip: -1.
^neg
ifTrue: [base negated]
ifFalse: [base]].
oldNeg := neg.
self peekSignIsMinus ifTrue: [neg := neg not].
integerPart := self nextUnsignedIntegerOrNilBase: base.
integerPart ifNil: [
(sourceStream peekFor: $.) ifTrue: [self readNumberWithoutIntegerPartOrNil ifNotNil: [:aNumber | ^aNumber]].
sourceStream position: pos.
^oldNeg
ifTrue: [base negated]
ifFalse: [base]].
numberOfTrailingZeroInIntegerPart := nDigits - lastNonZero].
+ (sourceStream peekFor: $t)
+ ifTrue: ["1t<integer>"
+ integerPart = 1t1
+ ifTrue: [
+ | char |
+ nDigits := lastNonZero := 0.
+ [(char := sourceStream next) = $1 ifFalse: [
+ char ifNotNil: [sourceStream skip: -1].
+ ^ neg ifTrue: [nDigits negated] ifFalse: [nDigits]].
+ nDigits := nDigits + 1] repeat]
+ ifFalse: [
+ sourceStream skip: -1]].
^ (sourceStream peekFor: $.)
ifTrue: [self readNumberWithFractionPartNumberOfTrailingZeroInIntegerPart: numberOfTrailingZeroInIntegerPart]
ifFalse: [self makeIntegerOrScaledInteger]
NumberParser>>nextElementaryLargeIntegerBase: {parsing-large int} · ct 6/1/2026 22:14 (changed)
nextElementaryLargeIntegerBase: aRadix
"Form an unsigned integer with incoming digits from sourceStream.
Return this integer, or zero if no digits found.
Stop reading if end of digits or if a LargeInteger is formed.
Count the number of digits and the position of lastNonZero digit and store them in instVar."
| value digit char |
- value := 0.
nDigits := 0.
lastNonZero := 0.
+ value := 0.
[
value isLarge ifTrue: [ ^value ].
char := sourceStream next ifNil: [ ^value ].
((digit := char digitValue) < 0 or: [digit >= aRadix]) ifTrue: [
sourceStream skip: -1.
^value ].
nDigits := nDigits + 1.
digit = 0
ifFalse: [
lastNonZero := nDigits.
value := value * aRadix + digit ]
ifTrue: [ value := value * aRadix ] ] repeat
SHParserST80>>scanNumber {scan} · ct 6/1/2026 22:34 (changed)
scanNumber
| c |
currentTokenSourcePosition := sourcePosition.
self skipDigits.
(c := self currentChar) == $r
ifTrue: [
| base |
base := (source copyFrom: currentTokenSourcePosition to: sourcePosition - 1) asUnsignedInteger.
- base < 2 ifTrue: [ self fail ": radix must be greater than 1" ].
+ base < 1 ifTrue: [ self fail ": radix must be greater than 1" ].
self peekChar == $- ifTrue: [ self nextChar ].
self skipDigitsBase: base.
(c := self currentChar) == $. ifTrue: [
(self isDigit: self peekChar base: base) ifTrue: [
self skipDigitsBase: base].
c := self currentChar ] ]
ifFalse: [
+ c == $t ifTrue: [
+ | base |
+ base := (source copyFrom: currentTokenSourcePosition to: sourcePosition - 1) asUnsignedInteger.
+ base = 1t1 ifTrue: [
+ self peekChar == $- ifTrue: [ self nextChar ].
+ [ self nextChar = $1 ] whileTrue]].
c == $. ifTrue: [
self peekChar isDigit ifFalse: [
^currentToken := source copyFrom: currentTokenSourcePosition to: sourcePosition - 1 ].
self skipDigits.
c := self currentChar ] ].
c == $s
ifTrue: [
(c := self nextChar) isDigit
ifFalse: [ c isLetter ifTrue: [sourcePosition := sourcePosition - 1 ] ]
ifTrue: [ self skipDigits ] ]
ifFalse: [
(c == $d
or: [ c == $e
or: [ c == $q ] ])
ifTrue: [
((c := self nextChar) isDigit or: [ c == $- and: [ self peekChar isDigit ] ])
ifFalse: [ sourcePosition := sourcePosition - 1 ]
ifTrue: [ self skipDigits ] ] ].
currentToken := source copyFrom: currentTokenSourcePosition to: sourcePosition - 1
SqNumberParser>>nextFraction {parsing-public} · ct 6/1/2026 21:45 (changed)
nextFraction
| numerator denominator numberOfTrailingZeroInIntegerPart |
base := self defaultBase.
neg := self peekSignIsMinus.
(integerPart := self nextUnsignedIntegerOrNilBase: base)
ifNil: ["No integerPart, raise an error"
^ self expected: 'a digit'].
numberOfTrailingZeroInIntegerPart := nDigits - lastNonZero.
(sourceStream peekFor: $r)
ifTrue: ["<base>r<integer>"
- (base := integerPart) < 2
+ (base := integerPart) < 1
ifTrue: [
sourceStream skip: -1.
- ^ self expected: 'an integer greater than 1 as valid radix'].
+ ^ self expected: 'an integer greater than 0 as valid radix'].
self peekSignIsMinus
ifTrue: [neg := neg not].
integerPart := self nextUnsignedIntegerBase: base.
numberOfTrailingZeroInIntegerPart := nDigits - lastNonZero].
(sourceStream peekFor: $.)
ifTrue:
[^self readFractionPartNumberOfTrailingZeroInIntegerPart: numberOfTrailingZeroInIntegerPart].
numerator := neg
ifTrue: [integerPart negated]
ifFalse: [integerPart].
self readExponent ifTrue: [numerator := numerator * (base raisedToInteger: exponent)].
(sourceStream peekFor: $/) ifFalse: [^numerator].
base := self defaultBase.
(denominator := self nextUnsignedIntegerOrNilBase: base)
ifNil:
[sourceStream skip: -1. "Not a valid denominator, ungobble / and return numerator"
^numerator].
(sourceStream peekFor: $r)
ifTrue: ["<base>r<integer>"
- (base := denominator) < 2
+ (base := denominator) < 1
ifTrue: [
sourceStream skip: -1.
- ^ self expected: 'an integer greater than 1 as valid radix'].
+ ^ self expected: 'an integer greater than 0 as valid radix'].
denominator := self nextUnsignedIntegerBase: base].
self readExponent ifTrue: [denominator := denominator * (base raisedToInteger: exponent)].
^numerator / denominator
SqNumberParser>>nextInteger {parsing-public} · ct 6/1/2026 21:45 (changed)
nextInteger
"Read an Integer from sourceStream, asnwser that Integer.
In Smalltalk syntax, a radix can be specified, and an exponent too."
| numberOfTrailingZeroInIntegerPart |
base := self defaultBase.
neg := self peekSignIsMinus.
integerPart := self nextUnsignedIntegerOrNilBase: base.
numberOfTrailingZeroInIntegerPart := nDigits - lastNonZero.
(sourceStream peekFor: $r)
ifTrue: ["<base>r<integer>"
- (base := integerPart) < 2
+ (base := integerPart) < 1
ifTrue: [
sourceStream skip: -1.
- ^ self expected: 'an integer greater than 1 as valid radix'].
+ ^ self expected: 'an integer greater than 0 as valid radix'].
self peekSignIsMinus
ifTrue: [neg := neg not].
integerPart := self nextUnsignedIntegerBase: base.
numberOfTrailingZeroInIntegerPart := nDigits - lastNonZero].
^ self makeIntegerOrScaledInteger
SqNumberParser>>nextNumber {parsing-public} · ct 6/1/2026 22:51 (changed)
nextNumber
"main method for reading a number.
This one can read Float Integer and ScaledDecimal"
| numberOfTrailingZeroInIntegerPart |
base := self defaultBase.
neg := self peekSignIsMinus.
integerPart := self nextUnsignedIntegerOrNilBase: base.
integerPart ifNil: [
"This is not a regular number beginning with a digit
It is time to check for exceptional condition NaN and Infinity"
^self readNamedFloatOrFail].
numberOfTrailingZeroInIntegerPart := nDigits - lastNonZero.
(sourceStream peekFor: $r)
ifTrue: ["<base>r<integer>"
- (base := integerPart) < 2
+ (base := integerPart) < 1
ifTrue: [
sourceStream skip: -1.
- ^ self expected: 'an integer greater than 1 as valid radix'].
+ ^ self expected: 'an integer greater than 0 as valid radix'].
self peekSignIsMinus
ifTrue: [neg := neg not].
integerPart := self nextUnsignedIntegerBase: base.
- numberOfTrailingZeroInIntegerPart := nDigits - lastNonZero].
+ numberOfTrailingZeroInIntegerPart := nDigits - lastNonZero]
+ ifFalse: [
+ (sourceStream peekFor: $t)
+ ifTrue: ["1t<integer>"
+ integerPart = 1t1
+ ifTrue: [
+ | char |
+ nDigits := lastNonZero := 0.
+ [(char := sourceStream next) = $1 ifFalse: [
+ char ifNotNil: [sourceStream skip: -1].
+ ^ neg ifTrue: [nDigits negated] ifFalse: [nDigits]].
+ nDigits := nDigits + 1] repeat]
+ ifFalse: [
+ sourceStream skip: -1]]].
^ (sourceStream peekFor: $.)
ifTrue: [self readNumberWithFractionPartNumberOfTrailingZeroInIntegerPart: numberOfTrailingZeroInIntegerPart]
ifFalse: [self makeIntegerOrScaledInteger]
SqNumberParser>>nextUnsignedInteger {parsing-public} · ct 6/1/2026 21:45 (changed)
nextUnsignedInteger
"Read an unsigned Integer from sourceStream, asnwser that Integer.
In Smalltalk syntax, a radix can be specified, and an exponent too."
base := self defaultBase.
neg := false.
integerPart := self nextUnsignedIntegerOrNilBase: base.
(sourceStream peekFor: $r)
ifTrue: ["<base>r<integer>"
- (base := integerPart) < 2
+ (base := integerPart) < 1
ifTrue: [
sourceStream skip: -1.
- ^ self expected: 'an integer greater than 1 as valid radix'].
+ ^ self expected: 'an integer greater than 0 as valid radix'].
integerPart := self nextUnsignedIntegerBase: base].
^ self makeIntegerOrScaledInteger
SqNumberParserTest>>testBases {tests - fail} · ct 6/1/2026 22:51 (changed)
testBases
self assert: (SqNumberParser parse: '16r20') equals: 32.
self assert: (SqNumberParser parse: '2r10') equals: 2.
+ self assert: (SqNumberParser parse: '1r000') equals: 0.
self assert: ((SqNumberParser on: '10')
nextIntegerBase: 2) equals: 2.
self assert: ((SqNumberParser on: '10')
defaultBase: 2;
nextNumber) equals: 2.
self assert: ((SqNumberParser on: '1000r10')
defaultBase: 2;
nextNumber) equals: 8.
SqNumberParserTest>>testTally {tests - fail} · ct 6/1/2026 22:49
+ testTally
+
+ | rs |
+ self assert: (SqNumberParser parse: '1t') equals: 0.
+ self assert: (SqNumberParser parse: '1t1') equals: 1.
+ self assert: (SqNumberParser parse: '1t111') equals: 3.
+ self assert: (SqNumberParser parse: '-1t11') equals: -2.
+
+ rs := '1t11e1' readStream.
+ self assert: (SqNumberParser parse: rs) equals: 2.
+ self assert: rs upToEnd equals: 'e1'.
+
+ rs := '2t1' readStream.
+ self assert: (SqNumberParser parse: rs) equals: 2.
+ self assert: rs upToEnd equals: 't1'.
+
+ rs := '1t0' readStream.
+ self assert: (SqNumberParser parse: rs) equals: 0.
+ self assert: rs upToEnd equals: '0'.
+
+ rs := '1t22' readStream.
+ self assert: (SqNumberParser parse: rs) equals: 0.
+ self assert: rs upToEnd equals: '22'.
--
Sent from Squeak Inbox Talk
Squeak-dev mailing list -- [email protected]
To unsubscribe send an email to [email protected]
tallyLiterals.1.cs
(text/squeak-changes, 12.2 KB)
'From Squeak6.1beta of 29 May 2026 [latest update: #23792] on 1 June 2026 at 10:55:01 pm'! "Change Set: tallyLiterals Date: 1 June 2026 Author: Christoph Thiede Adds support for tally bases and radix 1 to number parser. Also updates Shout and tests. 1r000 = 0. 1t = 0. 1t1 = 1. 1t111 = 3."! !NumberParser methodsFor: 'parsing-large int' stamp: 'ct 6/1/2026 22:14'! nextElementaryLargeIntegerBase: aRadix "Form an unsigned integer with incoming digits from sourceStream. Return this integer, or zero if no digits found. Stop reading if end of digits or if a LargeInteger is formed. Count the number of digits and the position of lastNonZero digit and store them in instVar." | value digit char | nDigits := 0. lastNonZero := 0. value := 0. [ value isLarge ifTrue: [ ^value ]. char := sourceStream next ifNil: [ ^value ]. ((digit := char digitValue) < 0 or: [digit >= aRadix]) ifTrue: [ sourceStream skip: -1. ^value ]. nDigits := nDigits + 1. digit = 0 ifFalse: [ lastNonZero := nDigits. value := value * aRadix + digit ] ifTrue: [ value := value * aRadix ] ] repeat! ! !SHParserST80 methodsFor: 'scan' stamp: 'ct 6/1/2026 22:34'! scanNumber | c | currentTokenSourcePosition := sourcePosition. self skipDigits. (c := self currentChar) == $r ifTrue: [ | base | base := (source copyFrom: currentTokenSourcePosition to: sourcePosition - 1) asUnsignedInteger. base < 1 ifTrue: [ self fail ": radix must be greater than 1" ]. self peekChar == $- ifTrue: [ self nextChar ]. self skipDigitsBase: base. (c := self currentChar) == $. ifTrue: [ (self isDigit: self peekChar base: base) ifTrue: [ self skipDigitsBase: base]. c := self currentChar ] ] ifFalse: [ c == $t ifTrue: [ | base | base := (source copyFrom: currentTokenSourcePosition to: sourcePosition - 1) asUnsignedInteger. base = 1t1 ifTrue: [ self peekChar == $- ifTrue: [ self nextChar ]. [ self nextChar = $1 ] whileTrue]]. c == $. ifTrue: [ self peekChar isDigit ifFalse: [ ^currentToken := source copyFrom: currentTokenSourcePosition to: sourcePosition - 1 ]. self skipDigits. c := self currentChar ] ]. c == $s ifTrue: [ (c := self nextChar) isDigit ifFalse: [ c isLetter ifTrue: [sourcePosition := sourcePosition - 1 ] ] ifTrue: [ self skipDigits ] ] ifFalse: [ (c == $d or: [ c == $e or: [ c == $q ] ]) ifTrue: [ ((c := self nextChar) isDigit or: [ c == $- and: [ self peekChar isDigit ] ]) ifFalse: [ sourcePosition := sourcePosition - 1 ] ifTrue: [ self skipDigits ] ] ]. currentToken := source copyFrom: currentTokenSourcePosition to: sourcePosition - 1! ! !SqNumberParser methodsFor: 'parsing-public' stamp: 'ct 6/1/2026 21:45'! nextFraction | numerator denominator numberOfTrailingZeroInIntegerPart | base := self defaultBase. neg := self peekSignIsMinus. (integerPart := self nextUnsignedIntegerOrNilBase: base) ifNil: ["No integerPart, raise an error" ^ self expected: 'a digit']. numberOfTrailingZeroInIntegerPart := nDigits - lastNonZero. (sourceStream peekFor: $r) ifTrue: ["<base>r<integer>" (base := integerPart) < 1 ifTrue: [ sourceStream skip: -1. ^ self expected: 'an integer greater than 0 as valid radix']. self peekSignIsMinus ifTrue: [neg := neg not]. integerPart := self nextUnsignedIntegerBase: base. numberOfTrailingZeroInIntegerPart := nDigits - lastNonZero]. (sourceStream peekFor: $.) ifTrue: [^self readFractionPartNumberOfTrailingZeroInIntegerPart: numberOfTrailingZeroInIntegerPart]. numerator := neg ifTrue: [integerPart negated] ifFalse: [integerPart]. self readExponent ifTrue: [numerator := numerator * (base raisedToInteger: exponent)]. (sourceStream peekFor: $/) ifFalse: [^numerator]. base := self defaultBase. (denominator := self nextUnsignedIntegerOrNilBase: base) ifNil: [sourceStream skip: -1. "Not a valid denominator, ungobble / and return numerator" ^numerator]. (sourceStream peekFor: $r) ifTrue: ["<base>r<integer>" (base := denominator) < 1 ifTrue: [ sourceStream skip: -1. ^ self expected: 'an integer greater than 0 as valid radix']. denominator := self nextUnsignedIntegerBase: base]. self readExponent ifTrue: [denominator := denominator * (base raisedToInteger: exponent)]. ^numerator / denominator! ! !SqNumberParser methodsFor: 'parsing-public' stamp: 'ct 6/1/2026 21:45'! nextInteger "Read an Integer from sourceStream, asnwser that Integer. In Smalltalk syntax, a radix can be specified, and an exponent too." | numberOfTrailingZeroInIntegerPart | base := self defaultBase. neg := self peekSignIsMinus. integerPart := self nextUnsignedIntegerOrNilBase: base. numberOfTrailingZeroInIntegerPart := nDigits - lastNonZero. (sourceStream peekFor: $r) ifTrue: ["<base>r<integer>" (base := integerPart) < 1 ifTrue: [ sourceStream skip: -1. ^ self expected: 'an integer greater than 0 as valid radix']. self peekSignIsMinus ifTrue: [neg := neg not]. integerPart := self nextUnsignedIntegerBase: base. numberOfTrailingZeroInIntegerPart := nDigits - lastNonZero]. ^ self makeIntegerOrScaledInteger! ! !SqNumberParser methodsFor: 'parsing-public' stamp: 'ct 6/1/2026 22:51'! nextNumber "main method for reading a number. This one can read Float Integer and ScaledDecimal" | numberOfTrailingZeroInIntegerPart | base := self defaultBase. neg := self peekSignIsMinus. integerPart := self nextUnsignedIntegerOrNilBase: base. integerPart ifNil: [ "This is not a regular number beginning with a digit It is time to check for exceptional condition NaN and Infinity" ^self readNamedFloatOrFail]. numberOfTrailingZeroInIntegerPart := nDigits - lastNonZero. (sourceStream peekFor: $r) ifTrue: ["<base>r<integer>" (base := integerPart) < 1 ifTrue: [ sourceStream skip: -1. ^ self expected: 'an integer greater than 0 as valid radix']. self peekSignIsMinus ifTrue: [neg := neg not]. integerPart := self nextUnsignedIntegerBase: base. numberOfTrailingZeroInIntegerPart := nDigits - lastNonZero] ifFalse: [ (sourceStream peekFor: $t) ifTrue: ["1t<integer>" integerPart = 1t1 ifTrue: [ | char | nDigits := lastNonZero := 0. [(char := sourceStream next) = $1 ifFalse: [ char ifNotNil: [sourceStream skip: -1]. ^ neg ifTrue: [nDigits negated] ifFalse: [nDigits]]. nDigits := nDigits + 1] repeat] ifFalse: [ sourceStream skip: -1]]]. ^ (sourceStream peekFor: $.) ifTrue: [self readNumberWithFractionPartNumberOfTrailingZeroInIntegerPart: numberOfTrailingZeroInIntegerPart] ifFalse: [self makeIntegerOrScaledInteger]! ! !SqNumberParser methodsFor: 'parsing-public' stamp: 'ct 6/1/2026 21:45'! nextUnsignedInteger "Read an unsigned Integer from sourceStream, asnwser that Integer. In Smalltalk syntax, a radix can be specified, and an exponent too." base := self defaultBase. neg := false. integerPart := self nextUnsignedIntegerOrNilBase: base. (sourceStream peekFor: $r) ifTrue: ["<base>r<integer>" (base := integerPart) < 1 ifTrue: [ sourceStream skip: -1. ^ self expected: 'an integer greater than 0 as valid radix']. integerPart := self nextUnsignedIntegerBase: base]. ^ self makeIntegerOrScaledInteger! ! !ExtendedNumberParser methodsFor: 'parsing-public' stamp: 'ct 6/1/2026 21:45'! nextFraction | numerator denominator numberOfTrailingZeroInIntegerPart | base := self defaultBase. neg := self peekSignIsMinus. (integerPart := self nextUnsignedIntegerOrNilBase: base) ifNil: [numberOfTrailingZeroInIntegerPart := 0] ifNotNil: [ numberOfTrailingZeroInIntegerPart := nDigits - lastNonZero. (sourceStream peekFor: $r) ifTrue: ["<base>r<integer>" (base := integerPart) < 1 ifTrue: [ sourceStream skip: -1. ^ self expected: 'an integer greater than 0 as valid radix']. self peekSignIsMinus ifTrue: [neg := neg not]. integerPart := self nextUnsignedIntegerBase: base. numberOfTrailingZeroInIntegerPart := nDigits - lastNonZero]]. (sourceStream peekFor: $.) ifTrue: [^self readFractionPartNumberOfTrailingZeroInIntegerPart: numberOfTrailingZeroInIntegerPart]. integerPart ifNil: ["No integerPart, raise an error" ^ self expected: 'a digit']. numerator := neg ifTrue: [integerPart negated] ifFalse: [integerPart]. self readExponent ifTrue: [numerator := numerator * (base raisedToInteger: exponent)]. (sourceStream peekFor: $/) ifFalse: [^numerator]. base := 10. base := self defaultBase. (denominator := self nextUnsignedIntegerOrNilBase: base) ifNil: [sourceStream skip: -1. "Not a valid denominator, ungobble / and return numerator" ^numerator]. (sourceStream peekFor: $r) ifTrue: ["<base>r<integer>" (base := denominator) < 1 ifTrue: [ sourceStream skip: -1. ^ self expected: 'an integer greater than 0 as valid radix']. denominator := self nextUnsignedIntegerBase: base]. self readExponent ifTrue: [denominator := denominator * (base raisedToInteger: exponent)]. ^numerator / denominator! ! !ExtendedNumberParser methodsFor: 'parsing-public' stamp: 'ct 6/1/2026 22:52'! nextNumber "main method for reading a number. This one can read Float Integer and ScaledDecimal" | numberOfTrailingZeroInIntegerPart | base := self defaultBase. neg := self peekSignIsMinus. integerPart := self nextUnsignedIntegerOrNilBase: base. integerPart ifNil: [(sourceStream peekFor: $.) ifTrue: [ "Try .1 syntax" ^self readNumberWithoutIntegerPart] ifFalse: [ "This is not a regular number beginning with a digit It is time to check for exceptional condition NaN and Infinity" ^self readNamedFloatOrFail]]. numberOfTrailingZeroInIntegerPart := nDigits - lastNonZero. (sourceStream peekFor: $r) ifTrue: ["<base>r<integer>" | oldNeg pos | pos := sourceStream position - 1. (base := integerPart) < 1 ifTrue: ["A radix currently need to be greater than 0, ungobble the r and return the integer part" sourceStream skip: -1. ^neg ifTrue: [base negated] ifFalse: [base]]. oldNeg := neg. self peekSignIsMinus ifTrue: [neg := neg not]. integerPart := self nextUnsignedIntegerOrNilBase: base. integerPart ifNil: [ (sourceStream peekFor: $.) ifTrue: [self readNumberWithoutIntegerPartOrNil ifNotNil: [:aNumber | ^aNumber]]. sourceStream position: pos. ^oldNeg ifTrue: [base negated] ifFalse: [base]]. numberOfTrailingZeroInIntegerPart := nDigits - lastNonZero]. (sourceStream peekFor: $t) ifTrue: ["1t<integer>" integerPart = 1t1 ifTrue: [ | char | nDigits := lastNonZero := 0. [(char := sourceStream next) = $1 ifFalse: [ char ifNotNil: [sourceStream skip: -1]. ^ neg ifTrue: [nDigits negated] ifFalse: [nDigits]]. nDigits := nDigits + 1] repeat] ifFalse: [ sourceStream skip: -1]]. ^ (sourceStream peekFor: $.) ifTrue: [self readNumberWithFractionPartNumberOfTrailingZeroInIntegerPart: numberOfTrailingZeroInIntegerPart] ifFalse: [self makeIntegerOrScaledInteger]! ! !SqNumberParserTest methodsFor: 'tests - fail' stamp: 'ct 6/1/2026 22:51'! testBases self assert: (SqNumberParser parse: '16r20') equals: 32. self assert: (SqNumberParser parse: '2r10') equals: 2. self assert: (SqNumberParser parse: '1r000') equals: 0. self assert: ((SqNumberParser on: '10') nextIntegerBase: 2) equals: 2. self assert: ((SqNumberParser on: '10') defaultBase: 2; nextNumber) equals: 2. self assert: ((SqNumberParser on: '1000r10') defaultBase: 2; nextNumber) equals: 8.! ! !SqNumberParserTest methodsFor: 'tests - fail' stamp: 'ct 6/1/2026 22:49'! testTally | rs | self assert: (SqNumberParser parse: '1t') equals: 0. self assert: (SqNumberParser parse: '1t1') equals: 1. self assert: (SqNumberParser parse: '1t111') equals: 3. self assert: (SqNumberParser parse: '-1t11') equals: -2. rs := '1t11e1' readStream. self assert: (SqNumberParser parse: rs) equals: 2. self assert: rs upToEnd equals: 'e1'. rs := '2t1' readStream. self assert: (SqNumberParser parse: rs) equals: 2. self assert: rs upToEnd equals: 't1'. rs := '1t0' readStream. self assert: (SqNumberParser parse: rs) equals: 0. self assert: rs upToEnd equals: '0'. rs := '1t22' readStream. self assert: (SqNumberParser parse: rs) equals: 0. self assert: rs upToEnd equals: '22'.! !