aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRose Hogenson <rosehogenson@posteo.net>2025-10-26 08:31:12 -0700
committerRose Hogenson <rosehogenson@posteo.net>2025-10-26 08:31:12 -0700
commit121b6d8e6f7a07cd119def27aaddc9e1e83dae1d (patch)
treeca0e2261532e3c6985724ae8bc9164615f4b8a3c
parent2089fa738b2299d864b614567e97d2bc57556fb4 (diff)
downloadccl-121b6d8e6f7a07cd119def27aaddc9e1e83dae1d.tar.zst
Refine numeric literal syntax
Leading zeros are no longer permitted in decimal literals, which is technically a backwards-incompatible change, but I consider it to be something like a bug fix. Anyway, the project is unlikely to have any users that would be impacted.
-rw-r--r--asspb.go15
-rw-r--r--asspb_test.go27
2 files changed, 36 insertions, 6 deletions
diff --git a/asspb.go b/asspb.go
index f8036f8..624f3ca 100644
--- a/asspb.go
+++ b/asspb.go
@@ -18,8 +18,8 @@
// # Numbers
//
// Numbers are written in base 10 and can optionally have a fractional part or
-// an exponent written with "e". As a special case, a number prefixed with "0x"
-// can be written in base 16.
+// an exponent written with "e" or "E". As a special case, a number prefixed
+// with "0x" or "0X" can be written in base 16.
//
// 100
// -30
@@ -28,6 +28,9 @@
// 13.5
// 1e100
//
+// Leading zeros are not permitted in decimal numbers, due to potential
+// confusion with octal (which is not supported).
+//
// # Strings
//
// Strings are written with " or ' and any sequence of intermediate bytes (with
@@ -189,7 +192,7 @@ func (p *parser) parseLit(s string) bool {
return false
}
-var numRE = regexp.MustCompile(`^-?(0x[0-9a-fA-F]+|([0-9]+(\.[0-9]*)?|\.[0-9]+)(e-?[0-9]+)?)`)
+var numRE = regexp.MustCompile(`^[-+]?(0[xX][0-9a-fA-F]+|((0|[1-9][0-9]*)(\.[0-9]*)?|\.[0-9]+)([eE][-+]?[0-9]+)?)`)
func (p *parser) parseNum() (any, bool) {
p.skipSpace()
@@ -197,7 +200,7 @@ func (p *parser) parseNum() (any, bool) {
if numBytes == nil {
return nil, false
}
- if bytes.ContainsAny(numBytes, ".e") {
+ if bytes.ContainsAny(numBytes, ".eE") {
n, err := strconv.ParseFloat(string(numBytes), 64)
if err != nil {
return nil, false
@@ -205,8 +208,8 @@ func (p *parser) parseNum() (any, bool) {
p.i += len(numBytes)
return n, true
}
- if b, ok := bytes.CutPrefix(numBytes, []byte("0x")); ok {
- n, err := strconv.ParseInt(string(b), 16, 64)
+ if bytes.HasPrefix(numBytes, []byte("0x")) || bytes.HasPrefix(numBytes, []byte("0X")) {
+ n, err := strconv.ParseInt(string(numBytes[2:]), 16, 64)
if err != nil {
return nil, false
}
diff --git a/asspb_test.go b/asspb_test.go
index 7600dee..2425571 100644
--- a/asspb_test.go
+++ b/asspb_test.go
@@ -43,18 +43,38 @@ field_repeated [5, 6]
can just span multiple lines"`,
want: map[string]any{"field": "strings\ncan just span multiple lines"},
}, {
+ desc: "Zero",
+ msg: `field: 0`,
+ want: map[string]any{"field": 0.},
+ }, {
desc: "Hex",
msg: `field: 0xff`,
want: map[string]any{"field": 255.},
}, {
+ desc: "CapitalHex",
+ msg: `field: 0XfF`,
+ want: map[string]any{"field": 255.},
+ }, {
+ desc: "HexLeadingZero",
+ msg: `field: 0x0f`,
+ want: map[string]any{"field": 15.},
+ }, {
desc: "Float",
msg: `field: 1.5e10`,
want: map[string]any{"field": 1.5e10},
}, {
+ desc: "FloatCapitalE",
+ msg: `field: 1.5E10`,
+ want: map[string]any{"field": 1.5e10},
+ }, {
desc: "NegativeFloat",
msg: `field: -1.5e-10`,
want: map[string]any{"field": -1.5e-10},
}, {
+ desc: "PositiveFloat",
+ msg: `field: +1.5e+10`,
+ want: map[string]any{"field": 1.5e10},
+ }, {
desc: "Int",
msg: `field: 10`,
want: map[string]any{"field": 10.},
@@ -63,6 +83,10 @@ can just span multiple lines"`,
msg: `field: -10`,
want: map[string]any{"field": -10.},
}, {
+ desc: "PositiveInt",
+ msg: `field: +10`,
+ want: map[string]any{"field": 10.},
+ }, {
desc: "String",
msg: `field: 'asdf'`,
want: map[string]any{"field": "asdf"},
@@ -238,6 +262,9 @@ func TestUnmarshal_Invalid(t *testing.T) {
}, {
desc: "ListBadMsgVal",
msg: `field [{asdf}]`,
+ }, {
+ desc: "IntLeadingZero",
+ msg: `field: 0644`,
}} {
t.Run(tc.desc, func(t *testing.T) {
t.Parallel()