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'.! !
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.