Skip to content

Commit 96bd617

Browse files
authored
Fix radix numeric literals (#87)
They didn't fully work before: $ echo 10r123 | glj repl:1:6: invalid number: 10r123 $ echo 16rabc | glj repl:1:6: invalid number: 16rabc
1 parent 043d917 commit 96bd617

2 files changed

Lines changed: 88 additions & 5 deletions

File tree

pkg/reader/reader.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1013,7 +1013,7 @@ func (r *Reader) readSymbolicValue() (interface{}, error) {
10131013

10141014
var (
10151015
numPrefixRegex = regexp.MustCompile(`^[-+]?([0-9]+|[1-9]+r)`)
1016-
radixRegex = regexp.MustCompile(`^[-+]?([1-9]+)r(\d(\d|[a-zA-Z])*N?)$`)
1016+
radixRegex = regexp.MustCompile(`^[-+]?([2-9]|[12][0-9]|3[0-6])r([0-9a-zA-Z]+N?)$`)
10171017
intRegex = regexp.MustCompile(`^[-+]?\d+N?$`)
10181018
ratioRegex = regexp.MustCompile(`^[-+]?\d+\/\d+$`)
10191019
hexRegex = regexp.MustCompile(`^[-+]?0[xX]([a-fA-F]|\d)*N?$`)
@@ -1046,6 +1046,7 @@ func (r *Reader) readNumber(numStr string) (interface{}, error) {
10461046
}
10471047

10481048
base := 0 // infer from prefix
1049+
isRadixNumber := false
10491050
if match := radixRegex.FindStringSubmatch(numStr); match != nil {
10501051
sign := ""
10511052
if numStr[0] == '-' || numStr[0] == '+' {
@@ -1060,9 +1061,10 @@ func (r *Reader) readNumber(numStr string) (interface{}, error) {
10601061
}
10611062
base = radix
10621063
numStr = sign + match[2]
1064+
isRadixNumber = true
10631065
}
10641066

1065-
if intRegex.MatchString(numStr) || hexRegex.MatchString(numStr) {
1067+
if isRadixNumber || intRegex.MatchString(numStr) || hexRegex.MatchString(numStr) {
10661068
if strings.HasSuffix(numStr, "N") {
10671069
bi, err := lang.NewBigIntWithBase(numStr[:len(numStr)-1], base)
10681070
if err != nil {

test/glojure/test_glojure/numbers.glj

Lines changed: 84 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@
376376
; divide by zero
377377
(is (thrown? go/any (rem 9 0))) ;; TODO: replace w/ arithmetic exception
378378
(is (thrown? go/any (rem 0 0)))
379-
379+
380380
(are [x y] (= x y)
381381
(rem 4 2) 0
382382
(rem 3 2) 1
@@ -407,7 +407,7 @@
407407
(rem 2 -5) 2
408408
(rem -2 5) -2
409409
(rem -2 -5) -2
410-
410+
411411
; num = 0, div != 0
412412
(rem 0 3) 0
413413
(rem 0 -3) 0
@@ -423,7 +423,7 @@
423423
; divide by zero
424424
(is (thrown? go/any (quot 9 0))) ;; TODO: replace w/ arithmetic exception
425425
(is (thrown? go/any (quot 0 0)))
426-
426+
427427
(are [x y] (= x y)
428428
(quot 4 2) 2
429429
(quot 3 2) 1
@@ -940,4 +940,85 @@ Math/pow overflows to Infinity."
940940
(/ nan onan)
941941
(/ onan nan) ))))
942942

943+
(deftest test-arbitrary-base-numbers
944+
(testing "Arbitrary base numbers (2r-36r) should work correctly"
945+
;; Test base 2 (binary)
946+
(are [x y] (= x y)
947+
(read-string "2r1010") 10
948+
(read-string "2r1111") 15
949+
(read-string "2r1000000") 64)
950+
951+
;; Test base 8 (octal)
952+
(are [x y] (= x y)
953+
(read-string "8r77") 63
954+
(read-string "8r100") 64
955+
(read-string "8r777") 511)
956+
957+
;; Test base 10 (decimal) - should work the same as regular numbers
958+
(are [x y] (= x y)
959+
(read-string "10r90") 90
960+
(read-string "10r123") 123
961+
(read-string "10r0") 0)
962+
963+
;; Test base 16 (hexadecimal) - the original bug case
964+
(are [x y] (= x y)
965+
(read-string "16rFF") 255
966+
(read-string "16r99") 153
967+
(read-string "16rAB") 171
968+
(read-string "16r0") 0
969+
(read-string "16rABCD") 43981)
970+
971+
;; Test base 15 (example from user query)
972+
(are [x y] (= x y)
973+
(read-string "15rAB3") 2418)
974+
975+
;; Test base 36 (maximum supported base)
976+
(are [x y] (= x y)
977+
(read-string "36rZ") 35
978+
(read-string "36r10") 36
979+
(read-string "36rZZ") 1295
980+
(read-string "36r0") 0
981+
(read-string "36rA") 10
982+
(read-string "36r9") 9)
983+
984+
;; Test with negative numbers
985+
(are [x y] (= x y)
986+
(read-string "-16rFF") -255
987+
(read-string "-36rZ") -35
988+
(read-string "-2r1010") -10)
989+
990+
;; Test with BigInt suffix N
991+
(are [x y] (= x y)
992+
(read-string "16rFFN") 255N
993+
(read-string "36rZZN") 1295N
994+
(read-string "2r1000000N") 64N)
995+
996+
;; Test mixed case letters (should be case insensitive)
997+
(are [x y] (= x y)
998+
(read-string "16rff") 255
999+
(read-string "16rFf") 255
1000+
(read-string "16rfF") 255
1001+
(read-string "36rz") 35
1002+
(read-string "36rZz") 1295)))
1003+
1004+
(deftest test-arbitrary-base-error-cases
1005+
(testing "Arbitrary base numbers should reject invalid cases"
1006+
;; Base 37 should be rejected (max is 36)
1007+
(is (thrown? go/any (read-string "37rZ")))
1008+
(is (thrown? go/any (read-string "100r10")))
1009+
1010+
;; Base 0 should be rejected
1011+
(is (thrown? go/any (read-string "0r10")))
1012+
1013+
;; Invalid digits for the base should be rejected
1014+
(is (thrown? go/any (read-string "16rG"))) ; G is not valid in base 16
1015+
(is (thrown? go/any (read-string "10rA"))) ; A is not valid in base 10
1016+
(is (thrown? go/any (read-string "2r2"))) ; 2 is not valid in base 2
1017+
1018+
;; Empty number part should be rejected
1019+
(is (thrown? go/any (read-string "16r")))
1020+
1021+
;; Invalid format should be rejected
1022+
(is (thrown? go/any (read-string "16x10"))))) ; should be 'r' not 'x'
1023+
9431024
(run-tests)

0 commit comments

Comments
 (0)