Unverified Commit 39a4ab78 authored by Jacob Abel's avatar Jacob Abel
Browse files

lib/strings: Refactor toInt into toInt and toIntBase10

parent 3d196a5f
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -103,7 +103,7 @@ let
      getName getVersion
      nameFromURL enableFeature enableFeatureAs withFeature
      withFeatureAs fixedWidthString fixedWidthNumber isStorePath
      toInt readPathsFromFile fileContents;
      toInt toIntBase10 readPathsFromFile fileContents;
    inherit (self.stringsWithDeps) textClosureList textClosureMap
      noDepEntry fullDepEntry packEntry stringAfter;
    inherit (self.customisation) overrideDerivation makeOverridable
+51 −3
Original line number Diff line number Diff line
@@ -783,26 +783,74 @@ rec {
    else
      false;

  /* Parse a string as an int.
  /* Parse a string as an int. Does not support parsing of integers with preceding zero due to
  ambiguity between zero-padded and octal numbers.

     Type: string -> int

     Example:

       toInt "1337"
       => 1337

       toInt "-4"
       => -4

       toInt " 123 "
       => 123

       toInt "00024"
       => 24
       => error: [json.exception.parse_error.101] parse error at line 1, column 2: syntax error
       while parsing value - unexpected number literal; expected end of input

       toInt "3.14"
       => error: floating point JSON numbers are not supported
  */
  # Obviously, it is a bit hacky to use fromJSON this way.
  toInt = str:
    let
      # RegEx: Match any leading whitespace, then any zero padding, and capture any remaining
      # RegEx: Match any leading whitespace, then any digits, and finally match any trailing
      # whitespace.
      strippedInput = match "[[:space:]]*([[:digit:]]+)[[:space:]]*" str;

      # RegEx: Match any leading whitespace, then a leading '0', then at least one digit following
      # after, and finally match any trailing whitespace.
      isLeadingZero = match "[[:space:]]*0[[:digit:]]+[[:space:]]*" str == [];

      # Attempt to parse input
      parsedInput = fromJSON (elemAt strippedInput 0);
    in
      if isLeadingZero
      then throw "Ambiguity in ${str} between octal and zero padded integer."
      else if strippedInput != null && isInt parsedInput
      then parsedInput
      else throw "Could not convert ${str} to int.";


  /* Parse a string as a base 10 int. This supports parsing of zero-padded integers.

     Type: string -> int

     Example:
       toIntBase10 "1337"
       => 1337

       toIntBase10 "-4"
       => -4

       toIntBase10 " 123 "
       => 123

       toIntBase10 "00024"
       => 24

       toIntBase10 "3.14"
       => error: floating point JSON numbers are not supported
  */
  # Obviously, it is a bit hacky to use fromJSON this way.
  toIntBase10 = str:
    let
      # RegEx: Match any leading whitespace, then match any zero padding, capture any remaining
      # digits after that, and finally match any trailing whitespace.
      strippedInput = match "[[:space:]]*0*([[:digit:]]+)[[:space:]]*" str;

+44 −16
Original line number Diff line number Diff line
@@ -339,20 +339,6 @@ runTests {
    (0 == toInt " 0")
    (0 == toInt "0 ")
    (0 == toInt " 0 ")
    # Zero Padding
    (123 == toInt "0123")
    (123 == toInt "0000123")
    (0 == toInt "000000")
    # Whitespace and Zero Padding
    (123 == toInt " 0123")
    (123 == toInt "0123 ")
    (123 == toInt " 0123 ")
    (123 == toInt " 0000123")
    (123 == toInt "0000123 ")
    (123 == toInt " 0000123 ")
    (0 == toInt " 000000")
    (0 == toInt "000000 ")
    (0 == toInt " 000000 ")
  ];

  testToIntFails = testAllTrue [
@@ -360,10 +346,52 @@ runTests {
    ( builtins.tryEval (toInt "123 123") == { success = false; value = false; } )
    ( builtins.tryEval (toInt "0 123") == { success = false; value = false; } )
    ( builtins.tryEval (toInt " 0d ") == { success = false; value = false; } )
    ( builtins.tryEval (toInt "00") == { success = false; value = false; } )
    ( builtins.tryEval (toInt "01") == { success = false; value = false; } )
    ( builtins.tryEval (toInt "002") == { success = false; value = false; } )
    ( builtins.tryEval (toInt " 002 ") == { success = false; value = false; } )
    ( builtins.tryEval (toInt " foo ") == { success = false; value = false; } )
    ( builtins.tryEval (toInt " foo 123 ") == { success = false; value = false; } )
    ( builtins.tryEval (toInt " foo 00123 ") == { success = false; value = false; } )
    ( builtins.tryEval (toInt " foo00123 ") == { success = false; value = false; } )
    ( builtins.tryEval (toInt " foo123 ") == { success = false; value = false; } )
  ];

  testToIntBase10 = testAllTrue [
    # Naive
    (123 == toIntBase10 "123")
    (0 == toIntBase10 "0")
    # Whitespace Padding
    (123 == toIntBase10 " 123")
    (123 == toIntBase10 "123 ")
    (123 == toIntBase10 " 123 ")
    (123 == toIntBase10 "   123   ")
    (0 == toIntBase10 " 0")
    (0 == toIntBase10 "0 ")
    (0 == toIntBase10 " 0 ")
    # Zero Padding
    (123 == toIntBase10 "0123")
    (123 == toIntBase10 "0000123")
    (0 == toIntBase10 "000000")
    # Whitespace and Zero Padding
    (123 == toIntBase10 " 0123")
    (123 == toIntBase10 "0123 ")
    (123 == toIntBase10 " 0123 ")
    (123 == toIntBase10 " 0000123")
    (123 == toIntBase10 "0000123 ")
    (123 == toIntBase10 " 0000123 ")
    (0 == toIntBase10 " 000000")
    (0 == toIntBase10 "000000 ")
    (0 == toIntBase10 " 000000 ")
  ];

  testToIntBase10Fails = testAllTrue [
    ( builtins.tryEval (toIntBase10 "") == { success = false; value = false; } )
    ( builtins.tryEval (toIntBase10 "123 123") == { success = false; value = false; } )
    ( builtins.tryEval (toIntBase10 "0 123") == { success = false; value = false; } )
    ( builtins.tryEval (toIntBase10 " 0d ") == { success = false; value = false; } )
    ( builtins.tryEval (toIntBase10 " foo ") == { success = false; value = false; } )
    ( builtins.tryEval (toIntBase10 " foo 123 ") == { success = false; value = false; } )
    ( builtins.tryEval (toIntBase10 " foo 00123 ") == { success = false; value = false; } )
    ( builtins.tryEval (toIntBase10 " foo00123 ") == { success = false; value = false; } )
  ];

# LISTS
+1 −1
Original line number Diff line number Diff line
@@ -162,7 +162,7 @@ checkConfigError 'A definition for option .* is not.*string or signed integer co
# Check coerced value with unsound coercion
checkConfigOutput '^12$' config.value ./declare-coerced-value-unsound.nix
checkConfigError 'A definition for option .* is not of type .*. Definition values:\n\s*- In .*: "1000"' config.value ./declare-coerced-value-unsound.nix ./define-value-string-bigint.nix
checkConfigError 'Could not convert .* to int.' config.value ./declare-coerced-value-unsound.nix ./define-value-string-arbitrary.nix
checkConfigError 'Could not convert .* to int' config.value ./declare-coerced-value-unsound.nix ./define-value-string-arbitrary.nix

# Check mkAliasOptionModule.
checkConfigOutput '^true$' config.enable ./alias-with-priority.nix