plum

#treesitter#compiler#wasm

git clone https://git.pyrossh.dev/plum

A statically typed, imperative programming language inspired by rust, python


plum-std/Uuid.plum
141de54 1
module std
ca5fd6f 2
import std/Number
b7071c9 3
import std/Result
b7071c9 4
import std/Str
73b5e55 5
import std/Bool
141de54 6
a271f34 7
# A UUID, stored as its canonical 36-character
a271f34 8
# `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` text form.
5f2f962 9
enum Uuid(ToStr) =
5f2f962 10
  | Uuid(value: Str)
a271f34 11
a271f34 12
  fun toStr(self) -> Str =
a271f34 13
    return self.value
a271f34 14
a271f34 15
  fun equals(self, other: Uuid) -> Bool =
a271f34 16
    return self.value == other.value
a271f34 17
2ec05c8 18
  fun isNil(self) -> Bool =
2ec05c8 19
    return self.value == NIL
2ec05c8 20
2ec05c8 21
  # The version nibble (1-5 for a well-formed RFC 4122 UUID), the first hex
2ec05c8 22
  # digit of the third group (`xxxxxxxx-xxxx-Vxxx-...`), at string index 14.
2ec05c8 23
  fun version(self) -> Int =
2ec05c8 24
    return hexValue(self.value.byteAt(14))
2ec05c8 25
2ec05c8 26
  # The variant bits, decoded from the high bits of the first hex digit of
2ec05c8 27
  # the fourth group (`...-Nxxx-...`), at string index 19. Returns 0 for the
2ec05c8 28
  # NCS-backward-compatible variant (`0xxx`), 2 for the RFC 4122 variant
2ec05c8 29
  # (`10xx`, what `v4` produces), 6 for Microsoft's (`110x`), 7 for the
2ec05c8 30
  # reserved future variant (`111x`).
2ec05c8 31
  fun variant(self) -> Int =
2ec05c8 32
    n := hexValue(self.value.byteAt(19))
2ec05c8 33
    if n & 0x8 == 0
2ec05c8 34
      return 0
2ec05c8 35
    if n & 0x4 == 0
2ec05c8 36
      return 2
2ec05c8 37
    if n & 0x2 == 0
2ec05c8 38
      return 6
2ec05c8 39
    return 7
2ec05c8 40
a271f34 41
NIL = "00000000-0000-0000-0000-000000000000"
a271f34 42
a271f34 43
fun nil() -> Uuid =
a271f34 44
  return Uuid(value: NIL)
a271f34 45
2ec05c8 46
# Parses the canonical `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` text form
2ec05c8 47
# (case-insensitive), returning `Err` if `s` isn't exactly that shape.
2ec05c8 48
fun fromStr(s: Str) -> Result[Uuid, Str] =
2ec05c8 49
  if s.length() != 36
2ec05c8 50
    return Err("invalid UUID length: '{s}'")
2ec05c8 51
  i := 0
2ec05c8 52
  while i < 36
2ec05c8 53
    if i == 8 || i == 13 || i == 18 || i == 23
2ec05c8 54
      # '-'
2ec05c8 55
      if s.byteAt(i) != 45
2ec05c8 56
        return Err("invalid UUID: '{s}'")
2ec05c8 57
    else
2ec05c8 58
      if hexValue(s.byteAt(i)) < 0
2ec05c8 59
        return Err("invalid UUID: '{s}'")
2ec05c8 60
    i = i + 1
2ec05c8 61
  return Ok(Uuid(value: s.toLower()))
2ec05c8 62
2ec05c8 63
fun isValid(s: Str) -> Bool =
2ec05c8 64
  match fromStr(s)
2ec05c8 65
    Ok(_) => True
2ec05c8 66
    Err(_) => False
2ec05c8 67
2ec05c8 68
# The numeric value (0-15) of an ASCII hex digit byte, or -1 if it isn't one.
2ec05c8 69
fun hexValue(byte: Int) -> Int =
2ec05c8 70
  # '0'-'9'
2ec05c8 71
  if byte >= 48 && byte <= 57
2ec05c8 72
    return byte - 48
2ec05c8 73
  # 'a'-'f'
2ec05c8 74
  if byte >= 97 && byte <= 102
2ec05c8 75
    return byte - 97 + 10
2ec05c8 76
  # 'A'-'F'
2ec05c8 77
  if byte >= 65 && byte <= 70
2ec05c8 78
    return byte - 65 + 10
2ec05c8 79
  return -1
2ec05c8 80
a271f34 81
# A random (version 4, RFC 4122) UUID.
a271f34 82
fun v4() -> Uuid =
a271f34 83
  a := rawRandomInt()
a271f34 84
  b := rawRandomInt()
a271f34 85
  time_low := { a >> 32 } & 0xFFFFFFFF
a271f34 86
  time_mid := { a >> 16 } & 0xFFFF
a271f34 87
  time_hi_and_version := { a & 0x0FFF } | 0x4000
a271f34 88
  clock_seq := { { b >> 48 } & 0x3FFF } | 0x8000
a271f34 89
  node := b & 0xFFFFFFFFFFFF
a271f34 90
  s := hexDigits(time_low, 8) + "-" + hexDigits(time_mid, 4) + "-"
a271f34 91
    + hexDigits(time_hi_and_version, 4) + "-" + hexDigits(clock_seq, 4) + "-"
a271f34 92
    + hexDigits(node, 12)
a271f34 93
  return Uuid(value: s)
a271f34 94
a271f34 95
fun hexDigit(n: Int) -> Str =
a271f34 96
  d := n & 0xF
a271f34 97
  if d < 10
a271f34 98
    return digitChar(d)
a271f34 99
  match d
a271f34 100
    10 => "a"
a271f34 101
    11 => "b"
a271f34 102
    12 => "c"
a271f34 103
    13 => "d"
a271f34 104
    14 => "e"
a271f34 105
    _ => "f"
a271f34 106
a271f34 107
# The low `count` hex digits of `n`, most-significant first.
a271f34 108
fun hexDigits(n: Int, count: Int) -> Str =
a271f34 109
  if count <= 0
a271f34 110
    return ""
a271f34 111
  return hexDigits(n >> 4, count - 1) + hexDigit(n)
2ec05c8 112
2ec05c8 113
test "nil is the all-zero UUID and is recognized as such"
a9a0147 114
  assert nil().toStr() == "00000000-0000-0000-0000-000000000000"
a9a0147 115
  assert nil().isNil() == True
2ec05c8 116
2ec05c8 117
test "v4 produces a valid version-4, variant-2 UUID"
2ec05c8 118
  u := v4()
a9a0147 119
  assert isValid(u.toStr()) == True
a9a0147 120
  assert u.version() == 4
a9a0147 121
  assert u.variant() == 2
a9a0147 122
  assert u.isNil() == False
2ec05c8 123
2ec05c8 124
test "fromStr parses a valid UUID and normalizes case"
2ec05c8 125
  match fromStr("550E8400-E29B-41D4-A716-446655440000")
2ec05c8 126
    Ok(u) =>
a9a0147 127
      assert u.toStr() == "550e8400-e29b-41d4-a716-446655440000"
2ec05c8 128
    Err(_) =>
a9a0147 129
      assert True == False
2ec05c8 130
2ec05c8 131
test "fromStr rejects malformed input"
a9a0147 132
  assert isValid("not-a-uuid") == False
a9a0147 133
  assert isValid("550e8400-e29b-41d4-a716-44665544000") == False
a9a0147 134
  assert isValid("550e8400xe29b-41d4-a716-446655440000") == False
2ec05c8 135
2ec05c8 136
test "equals compares by value"
2ec05c8 137
  a := Uuid(value: "550e8400-e29b-41d4-a716-446655440000")
2ec05c8 138
  b := Uuid(value: "550e8400-e29b-41d4-a716-446655440000")
a9a0147 139
  assert a.equals(b) == True
a9a0147 140
  assert a.equals(nil()) == False