6 files changed, 157 insertions(+), 38 deletions(-)

M gt/core/cmp.gt
A gt/core/convert.gt
M gt/core/hash.gt
M gt/core/ops.gt
M gt/core/range.gt
M gt/core/result.gt
M gt/core/cmp.gt +35 -2
@@ 1,4 1,11 @@ 
 --- Basic module for equality and comparisons.
+---
+--- Now that I think of it, we don't have PartialEq or
+--- PartialOrd.  We don't need them yet 'cause we don't
+--- have floats.  I'm kinda okay with this tbh, iirc there's
+--- a semi-official standard out for ordering NaN's we
+--- should look into, and there's a broad design space of
+--- Rust crates for NaN-preventing floats and such.
 
 type Eq(T) = struct(T)
   eq: fn(T, T) Bool,

          
@@ 105,7 112,7 @@ const OrdI32 Ord(I32) = Ord {
 --- `None` is always less than `Some(x)` for any `x`.
 fn option_ord(|T| ord Ord(T)) Ord(Option(T)) =
   Ord {
-    .cmp = fn(|T| x Option(T), y Option(T)) Ordering =
+    .cmp = fn(x Option(T), y Option(T)) Ordering =
       -- There's probably some fancy Option combinator to use
       -- here but I can never keep em straight.
       match {x, y} with

          
@@ 119,4 126,30 @@ fn option_ord(|T| ord Ord(T)) Ord(Option
 end
 
 const OrdOptionI32: Ord(Option(I32)) = option_ord(OrdI32)
- 
+
+--- A functor that implements `Ord(Result(T, E)) where T, E: Ord`.
+---
+--- We take Rust's approch, ofc, any Ok is less than any Err
+fn result_ord(|T, E| ord_t Ord(T), ord_e Ord(E)) Ord(Result(T, E)) =
+  .cmp = fn(x Result(T, E), y Result(T, E)) Ordering =
+    match {x, y} with
+      | {Ok xx, Ok yy} -> ord_t.cmp(xx, yy)
+      | {Err xx, Err yy} -> ord_e.cmp(xx, yy)
+      | {Ok _, Err _} -> Ordering.Less
+      | {Err _, Ok _} -> Ordering.Greater
+  end
+end
+
+--- Create an implementation of Eq from the given implementation
+--- of Ord.
+---
+--- TODO: This is also kinda a classic coherence problem with modules
+--- iirc, so investigate further.  I thiiiiink we have to bundle
+--- the Ord impl into the Eq one?
+fn eq_from_ord(|T| ord Ord(T)) Eq(T) =
+  Ord {
+    .eq = fn(x T, y T) Bool =
+      ord.ord(x, y).is_eq()
+    end
+  }
+end

          
A gt/core/convert.gt +39 -0
@@ 0,0 1,39 @@ 
+--- Conversion modules/interfaces.
+--- 
+--- Design ticket for stuff here: TODO
+--- from/tryfrom
+--- try (? operator)
+---
+--- Rust's `Try` trait is absolutely fascinating and bonkers, so
+--- look into it a bit more.
+
+
+--- Infallible conversions.
+type From(Self, Out) = struct(Self, Out) =
+  from: fn(Self) Out
+end
+
+--- Fallible conversions.
+type TryFrom(Self, Out, E) = struct(Self, Out, E) =
+  try_from: fn(Self) Result(Out, E)
+end
+
+--- Truncation, for example cutting off half of an U64 to make
+--- a U32.
+type Truncate(Self, Out) = struct(Self, Out) =
+  trunc: fn(Self) Out
+end
+
+--- Extend, for example sign-extending I32->I64 or zero-extending
+--- U32->U64
+type Extend(Self, Out) = struct(Self, Out) =
+  extend: fn(Self) Out
+end
+
+--- Bitcast.  For example converting an I32 into a U32 or F32.
+---
+--- TODO: Should require both types have the same size and
+--- compatible layouts.
+type bitcast(Self, Out) = struct(Self, Out) =
+  bitcast: fn(Self) Out
+end

          
M gt/core/hash.gt +27 -34
@@ 1,11 1,14 @@ 
---! Basic hashing function.
---!
---! For now this is SipHash 2-4, shamelessly stolen from
---! Rust's `std::hash::SipHasher`.  Implemented from the
---! reference impl at https://github.com/veorq/SipHash
---!
---! We might want a stable hash function sometime too,
---! probably FNV.
+--- Basic hashing function.
+---
+--- For now this is SipHash 2-4, shamelessly stolen from
+--- Rust's `std::hash::SipHasher`.  Implemented from the
+--- reference impl at https://github.com/veorq/SipHash
+---
+--- We might want a stable hash function sometime too,
+--- probably FNV.  (There's also fxHash, xxHash, wyhash, etc...)
+---
+--- TODO: clean up with our built-in bit ops and conversions.
+--- Heck, just implement bit ops.
 
 const C_ROUNDS: U32 = 2
 const D_ROUNDS: U32 = 4

          
@@ 18,11 21,11 @@ fn u64_from_bytes_le(bytes ^[]U8) U64 =
     todo("this")
 end
 
-fn u64_to_bytes_le(bytes: U64): [8]U8 =
+fn u64_to_bytes_le(bytes U64) [8]U8 =
     todo("this too")
 end
 
-fn sipround(v0: U64, v1: U64, v2: U64, v3: U64): {U64, U64, U64, U64} =
+fn sipround(v0 U64, v1 U64, v2 U64, v3 U64) {U64, U64, U64, U64} =
     let v0 = v0 + v1
     let v1 = rotl(v1, 13)
     let v1 = v1 bxor v0

          
@@ 41,38 44,28 @@ fn sipround(v0: U64, v1: U64, v2: U64, v
 end
 
 
-fn siphash(input: ^[]U8, k: U128, out: ^uniq[]U8): {} =
-    assert(out.len() == 8 or out.len() == 16)
-    let mut v0: U64 = 0x736f6d6570736575
-    let mut v1: U64 = 0x646f72616e646f6d
-    let mut v2: U64 = 0x6c7967656e657261
-    let mut v3: U64 = 0x7465646279746573
+fn siphash(input ^[]U8, k U128, out ^uniq[]U8) {} =
+    assert(out:len() == 8 or out:len() == 16)
+    let mut v0 U64 = 0x736f6d6570736575
+    let mut v1 U64 = 0x646f72616e646f6d
+    let mut v2 U64 = 0x6c7967656e657261
+    let mut v3 U64 = 0x7465646279746573
     -- TODO: Figure out how casts work
-    -- Erlang-like bit patterns?
-    -- let <<k0:64, k1:64>> = k
-    -- Or maybe the bit pattern is an operator that extracts bits
-    -- and returns a tuple, and then we define what types those
-    -- turn into?
-    -- https://www.erlang.org/docs/25/programming_examples/bit_syntax.html#segments
-    -- has some info on it, basically we need to say both how many bits
-    -- we extract and what type those bits get turned into.
-    -- let {k0, k1}: {U64, U64} = <<k0:64, k1:64>> k
-    -- let {k0, k1} = <<k0:64/U64, k1:64/U64>> k
-    let k0: U64 = (k band 0xFFFF_FFFF) as U32
-    let k1: U64 = ((k >> 32) band 0xFFFF_FFFF) as U32
-    let left: Size = input.len() band 7
-    let mut b: U64 = input.len() << 56
+    -- Using convert.gt seems like a fine place to start.
+    let k0 U64 = Convert.trunc(|U128, U64| k band 0xFFFF_FFFF)
+    let k1 U64 = Convert.trunc(|U128, U64| (k >> 32) band 0xFFFF_FFFF)
+    let left Size = input:len() band 7
+    let mut b U64 = input:len() << 56
     v3 = v3 bxor k1
     v2 = v2 bxor k0
     v1 = v1 bxor k1
     v0 = v0 bxor k0
 
-    if out.len() == 16 then
+    if out:len() == 16 then
         v1 = v1 bxor 0xEE
     end
 
-    -- TODO: This hypothetical `range()` function is `start, end, step` like Python's
-    for i in range(0, input.len(), 8) do
+    for i in range(0, input:len()):step_by(8) do
         -- TODO: We assume Rust's slice syntax for now?
         let bytes = input[i..(i+8)]
         let m = u64_from_bytes_le(bytes)

          
@@ 89,7 82,7 @@ fn siphash(input: ^[]U8, k: U128, out: ^
     -- for us.
     for i in range(1,8) do
         -- Round input.len() down to the closest multiple of 8
-        let input_tail_idx = (input.len() / 8) * 8;
+        let input_tail_idx = (input:len() / 8) * 8;
         b = b bor (input[input_tail_idx + i] << i * 8)
     end
 

          
M gt/core/ops.gt +12 -0
@@ 1,3 1,8 @@ 
+--- Fundamental operators.  In Rust this is anything that is overloadable.
+--- We don't really have any overloading yet, so it's more a a place to
+--- play with ideas for such things.
+
+
 --- Kinda an equivalent of Rust's Fn() trait.
 ---
 --- Just pretend borrowing doesn't matter right now.

          
@@ 40,3 45,10 @@ fn use_apply() =
   apply(thing2, {}) -- 102
 end
 
+
+--- Module for indexing values, a la `foo[3]`
+type Index(Self, Out, Idx) = struct(Self, Out, Idx)
+  --- Technically maybe should borrow Self and Out,
+  --- this isn't terriblt interesting without that distinction.
+  index: fn(Self, Idx) Out
+end

          
M gt/core/range.gt +39 -0
@@ 20,3 20,42 @@ it results in unexpected behavior (hopef
 𝔰𝔠𝔬𝔱𝔱𝔪𝔠𝔪 — Today at 5:58 PM
 see https://github.com/rust-lang/rfcs/pull/3550
 -/
+
+--- A half-open range bounded between
+--- `[start, end)`.
+---
+--- TODO: Should this contain an Ord impl?  Not very
+--- meaningful without it.  For now I'll leave it as no, though.
+type Range(Idx) = struct(Idx)
+  start: Idx,
+  end: Idx,
+end
+
+--- Whether the range contains the item.
+fn contains(|Idx| ord Ord(Idx), self Range(Idx), itm Idx) Bool =
+  ord.ord(self.start, itm).is_ge() and ord.ord(self.end, itm).is_lt()
+end
+
+--- Technically this only needs Eq, not Ord.
+fn is_empty(|Idx| ord Ord(Idx), self Range(Idx)) Bool =
+  ord.ord(self.start, self.end).is_eq()
+end
+
+--- Basically Python's range function that creates an iterator
+---
+--- TODO: Figure out how we're implementing iterators.
+fn range(|Idx| start Idx, end Idx) Iterator(Idx) =
+  todo()
+  Range {
+    .start = start,
+    .end = end,
+  }
+end
+
+--- Should return an iterator that steps multiple items at a time,
+--- ideally more efficiently than the naive approach...
+fn step_by(|Idx| self Range(Idx)) Iterator(Idx) =
+  todo()
+end
+
+-- todo: Iterator, Index(?)

          
M gt/core/result.gt +5 -2
@@ 1,8 1,6 @@ 
 --- Basic Result type
 ---
 --- TODO:
---- Match syntax --
---- Sum type details -- pattern match syntax, etc.
 --- Panics, strings...
 
 --- Our general-purpose Result type.

          
@@ 23,6 21,11 @@ end
 --- Takes a Result and returns the value contained if it is Ok, 
 --- otherwise panics with the given message
 ---
+--- TODO: I honestly kinda don't like how hard it is to stick a sane
+--- format string in here in Rust, where you have to do
+--- `thing.expect_with(|e| format!("help I got an {}", e))`.
+--- But that's a problem for Future Garnet.
+---
 --- Properties: Panics.
 fn expect(|T, E| self Result(T, E), message Str) T =
     match self with