strings.nbt 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. use core::scalar
  2. use core::functions
  3. use core::error
  4. @description("The length of a string")
  5. @example("str_length(\"Numbat\")")
  6. fn str_length(s: String) -> Scalar
  7. @description("Subslice of a string")
  8. @example("str_slice(3, 6, \"Numbat\")")
  9. fn str_slice(start: Scalar, end: Scalar, s: String) -> String
  10. @description("Get a single-character string from a Unicode code point.")
  11. @example("0x2764 -> chr")
  12. fn chr(n: Scalar) -> String
  13. @description("Get the Unicode code point of the first character in a string.")
  14. @example("\"❤\" -> ord")
  15. fn ord(s: String) -> Scalar
  16. @description("Convert a string to lowercase")
  17. @example("lowercase(\"Numbat\")")
  18. fn lowercase(s: String) -> String
  19. @description("Convert a string to uppercase")
  20. @example("uppercase(\"Numbat\")")
  21. fn uppercase(s: String) -> String
  22. @description("Concatenate two strings")
  23. @example("\"Numbat\" |> str_append(\"!\")")
  24. fn str_append(a: String, str: String) -> String = "{str}{a}"
  25. @description("Concatenate two strings")
  26. @example("\"!\" |> str_prepend(\"Numbat\")")
  27. fn str_prepend(a: String, str: String) -> String = "{a}{str}"
  28. @description("Find the first occurrence of a substring in a string")
  29. @example("str_find(\"typed\", \"Numbat is a statically typed programming language.\")")
  30. fn str_find(needle: String, haystack: String) -> Scalar =
  31. if len_haystack == 0
  32. then -1
  33. else if str_slice(0, str_length(needle), haystack) == needle
  34. then 0
  35. else if str_find(needle, tail_haystack) == -1
  36. then -1
  37. else 1 + str_find(needle, tail_haystack)
  38. where len_haystack = str_length(haystack)
  39. and tail_haystack = str_slice(1, len_haystack, haystack)
  40. @description("Check if a string contains a substring")
  41. @example("str_contains(\"typed\", \"Numbat is a statically typed programming language.\")")
  42. fn str_contains(needle: String, haystack: String) -> Bool =
  43. str_find(needle, haystack) != -1
  44. @description("Replace all occurrences of a substring in a string")
  45. @example("str_replace(\"statically typed programming language\", \"scientific calculator\", \"Numbat is a statically typed programming language.\")")
  46. fn str_replace(pattern: String, replacement: String, s: String) -> String =
  47. if pattern == ""
  48. then s
  49. else if str_contains(pattern, s)
  50. then if str_slice(0, pattern_length, s) == pattern
  51. then (s |> str_slice(pattern_length, s_length) |> str_replace(pattern, replacement) |> str_prepend(replacement))
  52. else (s |> str_slice( 1, s_length) |> str_replace(pattern, replacement) |> str_prepend(str_slice(0, 1, s)))
  53. else s
  54. where s_length = str_length(s)
  55. and pattern_length = str_length(pattern)
  56. @description("Repeat the input string `n` times")
  57. @example("str_repeat(4, \"abc\")")
  58. fn str_repeat(n: Scalar, a: String) -> String =
  59. if n > 0
  60. then str_prepend(a, str_repeat(n - 1, a))
  61. else ""
  62. fn _bin_digit(x: Scalar) -> String =
  63. chr(48 + mod(x, 2))
  64. fn _oct_digit(x: Scalar) -> String =
  65. chr(48 + mod(x, 8))
  66. fn _hex_digit(x: Scalar) -> String =
  67. if x_16 < 10 then chr(48 + x_16) else chr(97 + x_16 - 10)
  68. where
  69. x_16 = mod(x, 16)
  70. fn _digit_in_base(base: Scalar, x: Scalar) -> String =
  71. if base < 2 || base > 16
  72. then error("base must be between 2 and 16")
  73. else if x_16 < 10 then chr(48 + x_16) else chr(97 + x_16 - 10)
  74. where
  75. x_16 = mod(x, 16)
  76. # TODO: once we have anonymous functions / closures, we can implement base in a way
  77. # that it returns a partially-applied version of '_number_in_base'. This would allow
  78. # arbitrary 'x -> base(b)' conversions.
  79. @description("Convert a number to the given base.")
  80. @example("42 |> base(16)")
  81. fn base(b: Scalar, x: Scalar) -> String =
  82. if x < 0
  83. then "-{base(b, -x)}"
  84. else if x < b
  85. then _digit_in_base(b, x)
  86. else str_prepend(base(b, floor(x / b)), _digit_in_base(b, mod(x, b)))
  87. @description("Get a binary representation of a number.")
  88. @example("42 -> bin")
  89. fn bin(x: Scalar) -> String = if x < 0 then "-{bin(-x)}" else "0b{base(2, x)}"
  90. @description("Get an octal representation of a number.")
  91. @example("42 -> oct")
  92. fn oct(x: Scalar) -> String = if x < 0 then "-{oct(-x)}" else "0o{base(8, x)}"
  93. @description("Get a decimal representation of a number.")
  94. @example("0b111 -> dec")
  95. fn dec(x: Scalar) -> String = base(10, x)
  96. @description("Get a hexadecimal representation of a number.")
  97. @example("2^31-1 -> hex")
  98. fn hex(x: Scalar) -> String = if x < 0 then "-{hex(-x)}" else "0x{base(16, x)}"