Skip to content

Commit

Permalink
Add support for hexadecimal and octal numbers
Browse files Browse the repository at this point in the history
I switched from `read` to our custom parsing because `read` expects
the `0o` prefix instead of C-style `0`.
We only handle integers for now, as `read` for `Double` does not support
decimal numbers with `0x` prefix and I did not want to implement decimals myself.

Additionally, add support for + sign.

Also merge `negative.settings` test file into `dconf.settings` since
that one aggregates configuration keys from real dconf dumps.
Instead create `numbers.settings` that tests all possible numeric values.

Currently unhandled cases:

    zero-trailing=0.
    zero-leading=.0
    hex-float=0x10.1

GLib’s GVariant parses octal decimals a base 10.
This is probably expected, as the docs only mention hex floats.

    oct-float=010.1
    oct-sci=010e10

GLib’s GVariant parses the following as a double,
probably confusing it with scientific notation:

    hex-neg-e=-0x1e
  • Loading branch information
jtojnar committed May 19, 2024
1 parent 2f35ffa commit fb34a46
Show file tree
Hide file tree
Showing 8 changed files with 140 additions and 35 deletions.
6 changes: 6 additions & 0 deletions data/dconf.settings
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,9 @@ initial-size=(890, 550)
maximized=false
sidebar-width=189

[org/gnome/settings-daemon/plugins/color]
night-light-last-coordinates=(43.684199280057591, -79.347200000000001)

[org/gnome/settings-daemon/plugins/media-keys]
custom-keybindings=['/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/']

Expand Down Expand Up @@ -296,3 +299,6 @@ foo_bar='test'

[com/github/wwmm/easyeffects/streamoutputs/compressor#0]
sidechain-input-device='alsa_input.platform-snd_aloop.0.analog-stereo'

[org/gnome/gnome-commander/preferences/general]
file-list-tabs=[('file:///home/alice', byte 0x00, byte 0x01, false, false), ('file:///home/alice', 0x01, 0x01, false, false)]
2 changes: 0 additions & 2 deletions data/negative.settings

This file was deleted.

35 changes: 35 additions & 0 deletions data/numbers.settings
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
[floats]
avogadro=6.283185307179586
basic=1234567890.1234567890
negative=-72.02
positive=+72.02
sci=6.022e23
sci-exp-neg=7.51e-9
sci-neg=-72.02e3
sci-neg-exp-neg=-9.11e-17
zero=0.0
zero-neg=-0.0
zero-pos=+0.0

[int]
dec=1234567890
dec-neg=-987654321
dec-pos=+987654321
dec-one=1
dec-zero=0
hex=0x123456789abcdef0
hex-dec-only=0x1234567890
hex-neg=-0x1234
hex-neg-e=-0x1e
hex-neg-e2=-0x1e2
hex-pos=+0x1234
hex-zero=0x0
hex-zero-neg=-0x0
hex-zero-pos=+0x0
oct=012345670
oct-byte-max=0377
oct-neg=-0377
oct-pos=+0377
oct-zero=00
oct-zero-neg=-00
oct-zero-pos=+00
8 changes: 8 additions & 0 deletions output/dconf.nix
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,10 @@ with lib.hm.gvariant;
sidebar-width = 189;
};

"org/gnome/settings-daemon/plugins/color" = {
night-light-last-coordinates = mkTuple [ 43.68419928005759 (-79.3472) ];
};

"org/gnome/settings-daemon/plugins/media-keys" = {
custom-keybindings = [ "/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/" ];
};
Expand Down Expand Up @@ -374,5 +378,9 @@ with lib.hm.gvariant;
sidechain-input-device = "alsa_input.platform-snd_aloop.0.analog-stereo";
};

"org/gnome/gnome-commander/preferences/general" = {
file-list-tabs = [ (mkTuple [ "file:///home/alice" (mkUchar 0) (mkUchar 1) false false ]) (mkTuple [ "file:///home/alice" 1 1 false false ]) ];
};

};
}
13 changes: 0 additions & 13 deletions output/negative.nix

This file was deleted.

47 changes: 47 additions & 0 deletions output/numbers.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Generated via dconf2nix: https://github.com/nix-commmunity/dconf2nix
{ lib, ... }:

with lib.hm.gvariant;

{
dconf.settings = {
"floats" = {
avogadro = 6.283185307179586;
basic = 1.2345678901234567e9;
negative = -72.02;
positive = 72.02;
sci = 6.022e23;
sci-exp-neg = 7.51e-9;
sci-neg = -72020.0;
sci-neg-exp-neg = -9.11e-17;
zero = 0.0;
zero-neg = -0.0;
zero-pos = 0.0;
};

"int" = {
dec = 1234567890;
dec-neg = -987654321;
dec-one = 1;
dec-pos = 987654321;
dec-zero = 0;
hex = 1311768467463790320;
hex-dec-only = 78187493520;
hex-neg = -4660;
hex-neg-e = -30;
hex-neg-e2 = -482;
hex-pos = 4660;
hex-zero = 0;
hex-zero-neg = 0;
hex-zero-pos = 0;
oct = 2739128;
oct-byte-max = 255;
oct-neg = -255;
oct-pos = 255;
oct-zero = 0;
oct-zero-neg = 0;
oct-zero-pos = 0;
};

};
}
60 changes: 42 additions & 18 deletions src/DConf.hs
Original file line number Diff line number Diff line change
Expand Up @@ -53,19 +53,36 @@ vBool = B False <$ string "false" <|> B True <$ string "true"
vNothing :: Parsec Text () Value
vNothing = No <$ string "nothing"


numSign :: Num a => Parsec Text () a
numSign =
(pure (-1) <* string "-")
<|> (pure 1 <* string "+")
<|> pure 1

vDouble :: Parsec Text () Value
vDouble = try $ do
s <- option "" $ string "-"
s <- numSign
n <- many1 digit
d <- string "."
e <- many1 (digit <|> oneOf "eEdD-")
pure . D $ read (s <> n <> d <> e)
pure . D $ s * read (n <> d <> e)

vInt :: Parsec Text () Value
vInt = try $ do
s <- option "" $ string "-"
n <- many1 digit <* notFollowedBy (char '.')
pure . I $ read (s <> n)
s <- numSign
(base, digitParser) <- prefix
n <- (many1 digitParser <* notFollowedBy (char '.'))
-- There was only a lone zero digit, already consumed by the octal prefix.
<|> (if base == 8 then pure [0] else fail "Not a number")
pure . I $ s * digitsToNum base n
where
prefix =
((pure (16, fromHexDigit <$> hexDigit) <* (string' "0x" <|> string' "0X"))
-- @notFollowedBy@ to prevent ambiguity with @vDouble@.
<|> (pure (8, fromOctDigit <$> octDigit) <* (string' "0" <* notFollowedBy (char '.')))
<|> (pure (10, fromDecDigit <$> digit)))


vCast :: Parsec Text () Value
vCast = do
Expand Down Expand Up @@ -102,34 +119,41 @@ vString = T.pack <$> (single <|> double)
lchar :: [Char] -> Parsec Text () Char
lchar extra = charExcept $ "\r\n\\" <> extra

fromHexDigit :: Char -> Int
fromHexDigit n | inRange ('A', 'F') n = ord n - ord 'A' + 0xA
fromHexDigit n | inRange ('a', 'f') n = ord n - ord 'a' + 0xA
fromHexDigit n | inRange ('0', '9') n = ord n - ord '0'
fromHexDigit n = error $ "Expected a hexadecimal digit, '" ++ n : "' given"

hexNum :: Int -> Parsec Text () Int
hexNum l = do
digits <- replicateM l (fromHexDigit <$> hexDigit)
return $ foldl (\acc d -> 16 * acc + d) 0 digits

qchar :: Parsec Text () (Maybe Char)
qchar = do
_ <- char '\\'
(
-- Unicode escapes of the form `\uxxxx` and `\Uxxxxxxxx` are supported, in hexadecimal.
(char 'u' *> (Just <$> (chr <$> hexNum 4)))
<|> (char 'U' *> (Just <$> (chr <$> hexNum 8)))
(char 'u' *> (Just <$> (chr <$> hexNumFix 4)))
<|> (char 'U' *> (Just <$> (chr <$> hexNumFix 8)))
<|> commonEscapes)

inputs :: [Char] -> Parsec Text () String
inputs extra = catMaybes <$> (many $ qchar <|> (Just <$> lchar extra))

fromDecDigit :: Char -> Int
fromDecDigit n | inRange ('0', '9') n = ord n - ord '0'
fromDecDigit n = error $ "Expected a decimal digit, '" ++ n : "' given"

fromHexDigit :: Char -> Int
fromHexDigit n | inRange ('A', 'F') n = ord n - ord 'A' + 0xA
fromHexDigit n | inRange ('a', 'f') n = ord n - ord 'a' + 0xA
fromHexDigit n | inRange ('0', '9') n = ord n - ord '0'
fromHexDigit n = error $ "Expected a hexadecimal digit, '" ++ n : "' given"

fromOctDigit :: Char -> Int
fromOctDigit n | inRange ('0', '7') n = ord n - ord '0'
fromOctDigit n = error $ "Expected an octal digit, '" ++ n : "' given"

-- | Parses a hexadecimal number of precisely @l@ digits.
hexNumFix :: Int -> Parsec Text () Int
hexNumFix l = do
digits <- replicateM l (fromHexDigit <$> hexDigit)
return $ digitsToNum 16 digits

digitsToNum :: Int -> [Int] -> Int
digitsToNum base = foldl (\acc d -> base * acc + d) 0

-- | Parses an octal number between 1 and @maxlen@ digits.
octNumMax :: Int -> Parsec Text () Int
octNumMax maxLen = octNum' 0 maxLen
Expand Down
4 changes: 2 additions & 2 deletions test/DConf2NixTest.hs
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@ prop_dconf2nix_indexer = withTests (10 :: TestLimit) dconf2nixIndexer

dconf2nixNegative :: Property
dconf2nixNegative =
let input = "data/negative.settings"
output = "output/negative.nix"
let input = "data/numbers.settings"
output = "output/numbers.nix"
root = Root T.empty
in baseProperty input output root

Expand Down

0 comments on commit fb34a46

Please sign in to comment.