Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed

- Fixed a bug in YAML, TOML and dasel query parser that caused it to fail when parsing non-base10 numbers (e.g. hex, binary, octal).
- Fixed a bug in the `toInt` function that caused it to fail when parsing non-base10 numbers (e.g. hex, binary, octal).
- XML child element ordering now has more comprehensive round-trip handling. Thanks @takeokunn.

## [v3.4.1] - 2026-03-30
Expand Down
2 changes: 1 addition & 1 deletion execution/func_to_int.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ var FuncToInt = NewFunc(
return nil, err
}

i, err := strconv.ParseInt(stringValue, 10, 64)
i, err := strconv.ParseInt(stringValue, 0, 64)
Comment thread
TomWright marked this conversation as resolved.
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion parsing/toml/toml_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ func (j *tomlReader) readNode(p *unstable.Parser, n *unstable.Node) (string, *mo
}
return "", model.NewFloatValue(f), nil
case unstable.Integer:
i64, err := strconv.ParseInt(string(n.Data), 10, 64)
i64, err := strconv.ParseInt(string(n.Data), 0, 64)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not an expert in TOML, but I took a quick look at the spec

  • Please add tests for these:

Valid

42, -42, 0x12, 0o7, 0b111, 12_000, -12_000

Valid but as string: "042"

Invalid

-0x12, -0o7, -0b111, 042

All these values are valid with base 0 with ParseInt but not in TOML

Here are also feedbacks provided by an LLM

# ==========================================================
# HEXADECIMAL, OCTAL, AND BINARY BASES
# ==========================================================

# --- Valid (Case Insensitivity for Prefixes and Values) ---
hex_lowercase = 0xff12       # Standard hex
hex_uppercase = 0XFF12       # Uppercase prefix and values are VALID

bin_lowercase = 0b1101
bin_uppercase = 0B1101       # Uppercase 'B' is VALID
oct_lowercase = 0o755
oct_uppercase = 0O755        # Uppercase 'O' is VALID

# --- Invalid ---
# Negative signs are NOT supported for non-decimal bases
invalid_hex = -0xFF12        
invalid_oct = -0o755         
invalid_bin = -0b1101        

# ==========================================================
# SCIENTIFIC NOTATION (FLOATS)
# ==========================================================

# --- Valid ---
standard_sci = 1e6           # 1,000,000
positive_exp = 5e+10         # 50,000,000,000
negative_exp = -1.5E-4       # -0.00015 (Uppercase 'E' is VALID)
large_float  = 6_022.140e23  # Avogadro's number

# --- Invalid ---
# Decimal points must always have digits on both sides
invalid_point_1 = .1e2       
invalid_point_2 = 1.e2       

# Underscores are NOT allowed in the exponent part
invalid_underscore = 1e1_0   

if err != nil {
return "", nil, err
}
Expand Down
24 changes: 22 additions & 2 deletions parsing/yaml/yaml_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"io"
"strconv"
"strings"

"github.com/tomwright/dasel/v3/model"
"github.com/tomwright/dasel/v3/parsing"
Expand Down Expand Up @@ -96,11 +97,11 @@ func (yv *yamlValue) UnmarshalYAML(value *yaml.Node) error {
case "!!bool":
yv.value = model.NewBoolValue(value.Value == "true")
case "!!int":
i, err := strconv.Atoi(value.Value)
i, err := parseYAMLInt(value.Value)
if err != nil {
return err
}
yv.value = model.NewIntValue(int64(i))
yv.value = model.NewIntValue(i)
case "!!float":
f, err := strconv.ParseFloat(value.Value, 64)
if err != nil {
Expand Down Expand Up @@ -186,3 +187,22 @@ func (yv *yamlValue) UnmarshalYAML(value *yaml.Node) error {
}
return nil
}

func parseYAMLInt(s string) (int64, error) {
// Strip leading sign for prefix detection.
clean := s
if len(clean) > 0 && (clean[0] == '+' || clean[0] == '-') {
clean = clean[1:]
}

switch {
case strings.HasPrefix(clean, "0x") || strings.HasPrefix(clean, "0X"):
return strconv.ParseInt(s, 0, 64)
case strings.HasPrefix(clean, "0o") || strings.HasPrefix(clean, "0O"):
return strconv.ParseInt(s, 0, 64)
case strings.HasPrefix(clean, "0b") || strings.HasPrefix(clean, "0B"):
return strconv.ParseInt(s, 0, 64)
default:
return strconv.ParseInt(s, 10, 64)
}
}
85 changes: 85 additions & 0 deletions parsing/yaml/yaml_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,91 @@ name2: Tom
`,
}.run)

t.Run("base numbers", func(t *testing.T) {
Comment thread
TomWright marked this conversation as resolved.
t.Run("standard", rwTestCase{
in: `10
`,
out: `10
`,
}.run)

t.Run("zero", rwTestCase{
in: `0
`,
out: `0
`,
}.run)

t.Run("negative", rwTestCase{
in: `-42
`,
out: `-42
`,
}.run)

t.Run("hex lowercase", rwTestCase{
in: `0x10
`,
out: `16
`,
}.run)

t.Run("hex uppercase letters", rwTestCase{
in: `0xff
`,
out: `255
`,
}.run)

t.Run("octal", rwTestCase{
in: `0o10
`,
out: `8
`,
}.run)

t.Run("binary", rwTestCase{
in: `0b10
`,
out: `2
`,
}.run)

t.Run("leading zero is decimal", rwTestCase{
in: `010
`,
out: `10
`,
}.run)

t.Run("hex in map", rwTestCase{
in: `val: 0x10
`,
out: `val: 16
`,
}.run)

t.Run("octal in map", rwTestCase{
in: `val: 0o77
`,
out: `val: 63
`,
}.run)

t.Run("mixed types in map", rwTestCase{
in: `dec: 42
hex: 0xff
oct: 0o77
bin: 0b1010
`,
out: `dec: 42
hex: 255
oct: 63
bin: 10
`,
}.run)
})

t.Run("bounded yaml expansion", func(t *testing.T) {
in := `a: &a ["lol","lol","lol","lol","lol","lol","lol","lol","lol"]
b: &b [*a,*a,*a,*a,*a,*a,*a,*a,*a]
Expand Down
2 changes: 1 addition & 1 deletion selector/parser/parse_literal.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func parseNumberLiteral(p *Parser) (ast.Expr, error) {
}, nil

default:
value, err := strconv.ParseInt(token.Value, 10, 64)
value, err := strconv.ParseInt(token.Value, 0, 64)
if err != nil {
return nil, err
}
Expand Down
Loading