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
module std
import std/Number
import std/Result
import std/Str
import std/Bool

# A UUID, stored as its canonical 36-character
# `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` text form.
enum Uuid(ToStr) =
  | Uuid(value: Str)

  fun toStr(self) -> Str =
    return self.value

  fun equals(self, other: Uuid) -> Bool =
    return self.value == other.value

  fun isNil(self) -> Bool =
    return self.value == NIL

  # The version nibble (1-5 for a well-formed RFC 4122 UUID), the first hex
  # digit of the third group (`xxxxxxxx-xxxx-Vxxx-...`), at string index 14.
  fun version(self) -> Int =
    return hexValue(self.value.byteAt(14))

  # The variant bits, decoded from the high bits of the first hex digit of
  # the fourth group (`...-Nxxx-...`), at string index 19. Returns 0 for the
  # NCS-backward-compatible variant (`0xxx`), 2 for the RFC 4122 variant
  # (`10xx`, what `v4` produces), 6 for Microsoft's (`110x`), 7 for the
  # reserved future variant (`111x`).
  fun variant(self) -> Int =
    n := hexValue(self.value.byteAt(19))
    if n & 0x8 == 0
      return 0
    if n & 0x4 == 0
      return 2
    if n & 0x2 == 0
      return 6
    return 7

NIL = "00000000-0000-0000-0000-000000000000"

fun nil() -> Uuid =
  return Uuid(value: NIL)

# Parses the canonical `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` text form
# (case-insensitive), returning `Err` if `s` isn't exactly that shape.
fun fromStr(s: Str) -> Result[Uuid, Str] =
  if s.length() != 36
    return Err("invalid UUID length: '{s}'")
  i := 0
  while i < 36
    if i == 8 || i == 13 || i == 18 || i == 23
      # '-'
      if s.byteAt(i) != 45
        return Err("invalid UUID: '{s}'")
    else
      if hexValue(s.byteAt(i)) < 0
        return Err("invalid UUID: '{s}'")
    i = i + 1
  return Ok(Uuid(value: s.toLower()))

fun isValid(s: Str) -> Bool =
  match fromStr(s)
    Ok(_) => True
    Err(_) => False

# The numeric value (0-15) of an ASCII hex digit byte, or -1 if it isn't one.
fun hexValue(byte: Int) -> Int =
  # '0'-'9'
  if byte >= 48 && byte <= 57
    return byte - 48
  # 'a'-'f'
  if byte >= 97 && byte <= 102
    return byte - 97 + 10
  # 'A'-'F'
  if byte >= 65 && byte <= 70
    return byte - 65 + 10
  return -1

# A random (version 4, RFC 4122) UUID.
fun v4() -> Uuid =
  a := rawRandomInt()
  b := rawRandomInt()
  time_low := { a >> 32 } & 0xFFFFFFFF
  time_mid := { a >> 16 } & 0xFFFF
  time_hi_and_version := { a & 0x0FFF } | 0x4000
  clock_seq := { { b >> 48 } & 0x3FFF } | 0x8000
  node := b & 0xFFFFFFFFFFFF
  s := hexDigits(time_low, 8) + "-" + hexDigits(time_mid, 4) + "-"
    + hexDigits(time_hi_and_version, 4) + "-" + hexDigits(clock_seq, 4) + "-"
    + hexDigits(node, 12)
  return Uuid(value: s)

fun hexDigit(n: Int) -> Str =
  d := n & 0xF
  if d < 10
    return digitChar(d)
  match d
    10 => "a"
    11 => "b"
    12 => "c"
    13 => "d"
    14 => "e"
    _ => "f"

# The low `count` hex digits of `n`, most-significant first.
fun hexDigits(n: Int, count: Int) -> Str =
  if count <= 0
    return ""
  return hexDigits(n >> 4, count - 1) + hexDigit(n)

test "nil is the all-zero UUID and is recognized as such"
  assert nil().toStr() == "00000000-0000-0000-0000-000000000000"
  assert nil().isNil() == True

test "v4 produces a valid version-4, variant-2 UUID"
  u := v4()
  assert isValid(u.toStr()) == True
  assert u.version() == 4
  assert u.variant() == 2
  assert u.isNil() == False

test "fromStr parses a valid UUID and normalizes case"
  match fromStr("550E8400-E29B-41D4-A716-446655440000")
    Ok(u) =>
      assert u.toStr() == "550e8400-e29b-41d4-a716-446655440000"
    Err(_) =>
      assert True == False

test "fromStr rejects malformed input"
  assert isValid("not-a-uuid") == False
  assert isValid("550e8400-e29b-41d4-a716-44665544000") == False
  assert isValid("550e8400xe29b-41d4-a716-446655440000") == False

test "equals compares by value"
  a := Uuid(value: "550e8400-e29b-41d4-a716-446655440000")
  b := Uuid(value: "550e8400-e29b-41d4-a716-446655440000")
  assert a.equals(b) == True
  assert a.equals(nil()) == False