diff options
| author | Rose Hogenson <rosehogenson@posteo.net> | 2025-10-26 08:31:12 -0700 |
|---|---|---|
| committer | Rose Hogenson <rosehogenson@posteo.net> | 2025-10-26 08:31:12 -0700 |
| commit | 121b6d8e6f7a07cd119def27aaddc9e1e83dae1d (patch) | |
| tree | ca0e2261532e3c6985724ae8bc9164615f4b8a3c | |
| parent | 2089fa738b2299d864b614567e97d2bc57556fb4 (diff) | |
| download | ccl-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.go | 15 | ||||
| -rw-r--r-- | asspb_test.go | 27 |
2 files changed, 36 insertions, 6 deletions
@@ -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() |
