Make unsigned integers work.

Our integer values are all stored as i128, so our own i128 type
has been sacrificed on the altar of simplicity.  But I'm willing
to let that be the case for now.
M src/ast.rs +15 -5
@@ 17,12 17,14 @@ use crate::*;
 pub enum Literal {
     /// An integer of some kind
     Integer(i128),
-    /// An integer with a known size
+    /// An integer with a known size and signedness
     SizedInteger {
         /// Literal value
         vl: i128,
         /// The size of the integer, in bytes
         bytes: u8,
+        /// is_signed
+        signed: bool,
     },
     /// A bool literal
     Bool(bool),

          
@@ 32,9 34,13 @@ impl fmt::Display for Literal {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
             Literal::Integer(i) => write!(f, "{}", i),
-            Literal::SizedInteger { vl, bytes } => {
+            Literal::SizedInteger { vl, bytes, signed } => {
                 let size = bytes * 8;
-                write!(f, "{}_I{}", vl, size)
+                if *signed {
+                    write!(f, "{}_I{}", vl, size)
+                } else {
+                    write!(f, "{}_U{}", vl, size)
+                }
             }
             Literal::Bool(b) => write!(f, "{}", b),
         }

          
@@ 270,9 276,13 @@ impl Expr {
     }
 
     /// Shortcut function for making literal integers of a known size
-    pub const fn sized_int(i: i128, bytes: u8) -> Expr {
+    pub const fn sized_int(i: i128, bytes: u8, signed: bool) -> Expr {
         Expr::Lit {
-            val: Literal::SizedInteger { vl: i, bytes },
+            val: Literal::SizedInteger {
+                vl: i,
+                bytes,
+                signed,
+            },
         }
     }
 

          
M src/backend/rust.rs +29 -8
@@ 22,13 22,18 @@ use crate::*;
 fn compile_typename(t: &Type) -> Cow<'static, str> {
     use crate::Type::*;
     match t {
-        Prim(PrimType::SInt(16)) => "i128".into(),
-        Prim(PrimType::SInt(8)) => "i64".into(),
-        Prim(PrimType::SInt(4)) => "i32".into(),
-        Prim(PrimType::SInt(2)) => "i16".into(),
-        Prim(PrimType::SInt(1)) => "i8".into(),
-        Prim(PrimType::SInt(e)) => {
-            unreachable!("Invalid integer size: {}", e)
+        Prim(PrimType::Int(16, true)) => "i128".into(),
+        Prim(PrimType::Int(8, true)) => "i64".into(),
+        Prim(PrimType::Int(4, true)) => "i32".into(),
+        Prim(PrimType::Int(2, true)) => "i16".into(),
+        Prim(PrimType::Int(1, true)) => "i8".into(),
+        Prim(PrimType::Int(16, false)) => "u128".into(),
+        Prim(PrimType::Int(8, false)) => "u64".into(),
+        Prim(PrimType::Int(4, false)) => "u32".into(),
+        Prim(PrimType::Int(2, false)) => "u16".into(),
+        Prim(PrimType::Int(1, false)) => "u8".into(),
+        Prim(PrimType::Int(e, is_signed)) => {
+            unreachable!("Invalid integer size: {} (is_signed: {})", e, is_signed)
         }
         Prim(PrimType::UnknownInt) => {
             unreachable!("Backend got an integer of unknown size, should never happen!")

          
@@ 347,11 352,27 @@ fn compile_expr(expr: &hir::ExprNode, tc
             val: ast::Literal::Bool(b),
         } => format!("{}", b),
         E::Lit {
-            val: ast::Literal::SizedInteger { vl, bytes },
+            val:
+                ast::Literal::SizedInteger {
+                    vl,
+                    bytes,
+                    signed: true,
+                },
         } => {
             let bits = bytes * 8;
             format!("{}i{}", vl, bits)
         }
+        E::Lit {
+            val:
+                ast::Literal::SizedInteger {
+                    vl,
+                    bytes,
+                    signed: false,
+                },
+        } => {
+            let bits = bytes * 8;
+            format!("{}u{}", vl, bits)
+        }
         E::Var { name, .. } => mangle_name(&INT.fetch(*name)),
         E::BinOp { op, lhs, rhs } => format!(
             "({} {} {})",

          
M src/builtins.rs +15 -0
@@ 98,6 98,12 @@ fn __{name}_to_i32(x: {name}) -> i32 {{
     x as i32
 }}"#
         );
+        let cast_u32 = format!(
+            r#"
+fn __{name}_to_u32(x: {name}) -> u32 {{
+    x as u32
+}}"#
+        );
 
         vec![
             Builtin {

          
@@ 150,6 156,11 @@ fn __{name}_to_i32(x: {name}) -> i32 {{
                 sig: Type::Func(vec![ty.clone()], Box::new(Type::i32()), vec![]),
                 code: BTreeMap::from([(Backend::Null, "".into()), (Backend::Rust, cast_i32)]),
             },
+            Builtin {
+                name: Sym::new(format!("__{name}_to_u32")),
+                sig: Type::Func(vec![ty.clone()], Box::new(Type::u32()), vec![]),
+                code: BTreeMap::from([(Backend::Null, "".into()), (Backend::Rust, cast_u32)]),
+            },
         ]
     }
     /// A function that returns all the compiler builtin info.  Just

          
@@ 186,6 197,10 @@ fn __println_bool(x: bool) {
         funcs.extend(Self::generate_numerics_for("i16", Type::i16()));
         funcs.extend(Self::generate_numerics_for("i32", Type::i32()));
         funcs.extend(Self::generate_numerics_for("i64", Type::i64()));
+        funcs.extend(Self::generate_numerics_for("u8", Type::u8()));
+        funcs.extend(Self::generate_numerics_for("u16", Type::u16()));
+        funcs.extend(Self::generate_numerics_for("u32", Type::u32()));
+        funcs.extend(Self::generate_numerics_for("u64", Type::u64()));
         funcs
     }
 }

          
M src/lib.rs +54 -10
@@ 30,7 30,8 @@ static INT: Lazy<Cx> = Lazy::new(Cx::new
 /// and can't contain other types, so.
 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
 pub enum PrimType {
-    SInt(u8),
+    /// size in bytes, is-signed
+    Int(u8, bool),
     UnknownInt,
     Bool,
     /// erased type, currently unused

          
@@ 40,12 41,20 @@ pub enum PrimType {
 impl PrimType {
     fn get_name(&self) -> Cow<'static, str> {
         match self {
-            PrimType::SInt(16) => Cow::Borrowed("I128"),
-            PrimType::SInt(8) => Cow::Borrowed("I64"),
-            PrimType::SInt(4) => Cow::Borrowed("I32"),
-            PrimType::SInt(2) => Cow::Borrowed("I16"),
-            PrimType::SInt(1) => Cow::Borrowed("I8"),
-            PrimType::SInt(s) => panic!("Undefined integer size {}!", s),
+            PrimType::Int(16, true) => Cow::Borrowed("I128"),
+            PrimType::Int(8, true) => Cow::Borrowed("I64"),
+            PrimType::Int(4, true) => Cow::Borrowed("I32"),
+            PrimType::Int(2, true) => Cow::Borrowed("I16"),
+            PrimType::Int(1, true) => Cow::Borrowed("I8"),
+            PrimType::Int(16, false) => Cow::Borrowed("U128"),
+            PrimType::Int(8, false) => Cow::Borrowed("U64"),
+            PrimType::Int(4, false) => Cow::Borrowed("U32"),
+            PrimType::Int(2, false) => Cow::Borrowed("U16"),
+            PrimType::Int(1, false) => Cow::Borrowed("U8"),
+            PrimType::Int(s, is_signed) => {
+                let prefix = if *is_signed { "I" } else { "U" };
+                panic!("Undefined integer size {}{}!", prefix, s);
+            }
             PrimType::UnknownInt => Cow::Borrowed("{number}"),
             PrimType::Bool => Cow::Borrowed("Bool"),
             PrimType::AnyPtr => Cow::Borrowed("AnyPtr"),

          
@@ 162,9 171,14 @@ impl Type {
         Self::Prim(PrimType::UnknownInt)
     }
 
-    /// Shortcut for getting the Type for an int of a particular size
+    /// Shortcut for getting the Type for a signed int of a particular size
     pub fn isize(size: u8) -> Self {
-        Self::Prim(PrimType::SInt(size))
+        Self::Prim(PrimType::Int(size, true))
+    }
+
+    /// Shortcut for getting the Type for an unsigned signed int of a particular size
+    pub fn usize(size: u8) -> Self {
+        Self::Prim(PrimType::Int(size, false))
     }
 
     /// Shortcut for getting the Type for I128

          
@@ 192,6 206,31 @@ impl Type {
         Self::isize(1)
     }
 
+    /// Shortcut for getting the Type for U128
+    pub fn u128() -> Self {
+        Self::usize(16)
+    }
+
+    /// Shortcut for getting the Type for U64
+    pub fn u64() -> Self {
+        Self::usize(8)
+    }
+
+    /// Shortcut for getting the Type for U32
+    pub fn u32() -> Self {
+        Self::usize(4)
+    }
+
+    /// Shortcut for getting the type for U16
+    pub fn u16() -> Self {
+        Self::usize(2)
+    }
+
+    /// Shortcut for getting the type for U8
+    pub fn u8() -> Self {
+        Self::usize(1)
+    }
+
     /// Shortcut for getting the type for Bool
     pub fn bool() -> Self {
         Self::Prim(PrimType::Bool)

          
@@ 219,7 258,7 @@ impl Type {
 
     fn _is_integer(&self) -> bool {
         match self {
-            Self::Prim(PrimType::SInt(_)) => true,
+            Self::Prim(PrimType::Int(_, _)) => true,
             Self::Prim(PrimType::UnknownInt) => true,
             _ => false,
         }

          
@@ 241,6 280,11 @@ impl Type {
             "I32" => Some(Type::i32()),
             "I64" => Some(Type::i64()),
             "I128" => Some(Type::i128()),
+            "U8" => Some(Type::u8()),
+            "U16" => Some(Type::u16()),
+            "U32" => Some(Type::u32()),
+            "U64" => Some(Type::u64()),
+            "U128" => Some(Type::u128()),
             "Bool" => Some(Type::bool()),
             "Never" => Some(Type::never()),
             _ => None,

          
M src/parser.rs +43 -43
@@ 20,7 20,7 @@ use crate::*;
 
 /// Checks whether the given value can fit in `int_size` number
 /// of bits.
-fn bounds_check(val: i128, int_size: u8) -> Option<(i128, u8)> {
+fn bounds_check_signed(val: i128, int_size: u8) -> Option<(i128, u8)> {
     let bound = 2_i128.pow(int_size as u32 * 8);
     if val > (bound / 2) - 1 || val <= -(bound / 2) {
         None

          
@@ 29,6 29,15 @@ fn bounds_check(val: i128, int_size: u8)
     }
 }
 
+fn bounds_check_unsigned(val: i128, int_size: u8) -> Option<(i128, u8)> {
+    let bound = 2_i128.pow(int_size as u32 * 8);
+    if val > bound || val <= 0 {
+        None
+    } else {
+        Some((val, int_size))
+    }
+}
+
 /// Turn a valid number string into something Rust's `str::parse()` can parse,
 /// so,
 /// 123_456_I32 becomes 123456.

          
@@ 48,36 57,15 @@ fn extract_digits(s: &str) -> String {
         .collect()
 }
 
-fn make_i8(lex: &mut Lexer<TokenKind>) -> Option<(i128, u8)> {
-    let digits = extract_digits(lex.slice());
-    let m = digits.parse().ok()?;
-    bounds_check(m, 1)
-}
-
-fn make_i16(lex: &mut Lexer<TokenKind>) -> Option<(i128, u8)> {
-    let digits = extract_digits(lex.slice());
-    let m = digits.parse().ok()?;
-    bounds_check(m, 2)
-}
-
-fn make_i32(lex: &mut Lexer<TokenKind>) -> Option<(i128, u8)> {
+fn make_int(lex: &mut Lexer<TokenKind>, size: u8, signed: bool) -> Option<(i128, u8, bool)> {
     let digits = extract_digits(lex.slice());
     let m = digits.parse().ok()?;
-    bounds_check(m, 4)
-}
-
-fn make_i64(lex: &mut Lexer<TokenKind>) -> Option<(i128, u8)> {
-    let digits = extract_digits(lex.slice());
-    let m = digits.parse().ok()?;
-    bounds_check(m, 8)
-}
-
-fn make_i128(lex: &mut Lexer<TokenKind>) -> Option<(i128, u8)> {
-    let digits = extract_digits(lex.slice());
-    let m = digits.parse().ok()?;
-    // No bounds check, since our internal type is i128 anyway.
-    //bounds_check(m, 16)
-    Some((m, 16))
+    let (val, size) = if signed {
+        bounds_check_signed(m, size)?
+    } else {
+        bounds_check_unsigned(m, size)?
+    };
+    Some((val, size, signed))
 }
 
 fn eat_block_comment(lex: &mut Lexer<TokenKind>) -> String {

          
@@ 118,12 106,23 @@ pub enum TokenKind {
     //TypeIdent(String),
     #[regex("true|false", |lex| lex.slice().parse())]
     Bool(bool),
-    #[regex("[0-9][0-9_]*I8", make_i8)]
-    #[regex("[0-9][0-9_]*I16", make_i16)]
-    #[regex("[0-9][0-9_]*I32", make_i32)]
-    #[regex("[0-9][0-9_]*I64", make_i64)]
-    #[regex("[0-9][0-9_]*I128", make_i128)]
+    #[regex("[0-9][0-9_]*I8",   |lex| make_int(lex, 1, true))]
+    #[regex("[0-9][0-9_]*I16",  |lex| make_int(lex, 2, true))]
+    #[regex("[0-9][0-9_]*I32",  |lex| make_int(lex, 4, true))]
+    #[regex("[0-9][0-9_]*I64",  |lex| make_int(lex, 8, true))]
+    #[regex("[0-9][0-9_]*U8",   |lex| make_int(lex, 1, false))]
+    #[regex("[0-9][0-9_]*U16",  |lex| make_int(lex, 2, false))]
+    #[regex("[0-9][0-9_]*U32",  |lex| make_int(lex, 4, false))]
+    #[regex("[0-9][0-9_]*U64",  |lex| make_int(lex, 8, false))]
+    IntegerSize((i128, u8, bool)),
+    /*
+    #[regex("[0-9][0-9_]*U8", make_u8)]
+    #[regex("[0-9][0-9_]*U16", make_u16)]
+    #[regex("[0-9][0-9_]*U32", make_u32)]
+    #[regex("[0-9][0-9_]*U64", make_u64)]
+    #[regex("[0-9][0-9_]*U128", make_u128)]
     IntegerSize((i128, u8)),
+    */
     #[regex("[0-9][0-9_]*", |lex| lex.slice().parse())]
     Integer(i128),
 

          
@@ 593,7 592,7 @@ impl<'input> Parser<'input> {
                 ..
             }) => s,
             Some(Token {
-                kind: T::IntegerSize((s, _)),
+                kind: T::IntegerSize((s, _, _)),
                 ..
             }) => s,
             Some(Token { kind, span }) => {

          
@@ 990,7 989,9 @@ impl<'input> Parser<'input> {
                 ast::Expr::bool(*b)
             }
             T::Integer(_) => ast::Expr::int(self.expect_int() as i128),
-            T::IntegerSize((_str, size)) => ast::Expr::sized_int(self.expect_int() as i128, *size),
+            T::IntegerSize((_str, size, signed)) => {
+                ast::Expr::sized_int(self.expect_int() as i128, *size, *signed)
+            }
             // Tuple/struct literal
             T::LBrace => self.parse_constructor(),
             // Array literal

          
@@ 1668,10 1669,10 @@ type blar = I8
             "(x I64, y Bool) Bool",
             "(x I8, y Bool,) {}",
             "(x I32, y Bool,) Bool",
-            "(f fn(I32) I128, x I32) Bool",
-            "(f fn(|| I32) I128, x I32) Bool",
-            "(f fn(||) I128, x I32) Bool",
-            "(f fn(|@T| I32) I128, x I32) Bool",
+            "(f fn(I32) I64, x I32) Bool",
+            "(f fn(|| I32) I64, x I32) Bool",
+            "(f fn(||) I64, x I32) Bool",
+            "(f fn(|@T| I32) I64, x I32) Bool",
             // now without explicit return types
             "()",
             "(x Bool)",

          
@@ 1960,13 1961,12 @@ type blar = I8
             ("22_I16", 22, 2),
             ("33_I32", 33, 4),
             ("91_I64", 91, 8),
-            ("9_I128", 9, 16),
         ];
         for (s, expected_int, expected_bytes) in tests {
             let mut p = Parser::new("unittest.gt", s);
             assert_eq!(
                 p.next().unwrap().kind,
-                TokenKind::IntegerSize((*expected_int, *expected_bytes))
+                TokenKind::IntegerSize((*expected_int, *expected_bytes, true))
             );
             // Make sure we don't lex the "i128" or whatever as the start of
             // another token

          
@@ 1997,7 1997,7 @@ type blar = I8
 
     #[test]
     fn parse_integer_values() {
-        test_expr_is("43_I8", || Expr::sized_int(43, 1));
+        test_expr_is("43_I8", || Expr::sized_int(43, 1, true));
         /*
         test_expr_is("{1,2,3}", |_cx| Expr::TupleCtor {
             body: vec![Expr::int(1), Expr::int(2), Expr::int(3)],

          
M src/typeck.rs +5 -3
@@ 601,11 601,11 @@ impl Tck {
             (Prim(p1), Prim(p2)) if p1 == p2 => Ok(()),
             // Unknown integers unified with known integers become known
             // integers.
-            (Prim(PrimType::UnknownInt), Prim(PrimType::SInt(_))) => {
+            (Prim(PrimType::UnknownInt), Prim(PrimType::Int(_, _))) => {
                 self.vars.insert(a, TypeInfo::Ref(b));
                 Ok(())
             }
-            (Prim(PrimType::SInt(_)), Prim(PrimType::UnknownInt)) => {
+            (Prim(PrimType::Int(_, _)), Prim(PrimType::UnknownInt)) => {
                 self.vars.insert(b, TypeInfo::Ref(a));
                 Ok(())
             }

          
@@ 969,7 969,9 @@ impl Symtbl {
 fn infer_lit(lit: &ast::Literal) -> TypeInfo {
     match lit {
         ast::Literal::Integer(_) => TypeInfo::Prim(PrimType::UnknownInt),
-        ast::Literal::SizedInteger { bytes, .. } => TypeInfo::Prim(PrimType::SInt(*bytes)),
+        ast::Literal::SizedInteger { bytes, signed, .. } => {
+            TypeInfo::Prim(PrimType::Int(*bytes, *signed))
+        }
         ast::Literal::Bool(_) => TypeInfo::Prim(PrimType::Bool),
         //ast::Literal::EnumLit(nm, _) => TypeInfo::Named(*nm, vec![]),
     }

          
M tests/programs/test_pcg.gt +24 -16
@@ 8,6 8,14 @@ 
 --     -1403893721
 --     222523091
 --     -1045347539
+--     -1893201942
+--     1311075464
+--     718749721
+--     -376464712
+--     -249673620
+--     -2027436315
+--     263441840
+
 
 
 

          
@@ 16,36 24,37 @@ 
 --- Fails to give perfect results after the first 3 numbers due to signed/unsigned 
 --- arithmetic nonsense, but it's a start.
 type Rand32 = struct
-    state: I64,
-    inc: I64,
+    state: U64,
+    inc: U64,
 end
 
-const RAND32_DEFAULT_INC I64 = 1442695040888963407
-const RAND32_MULTIPLIER I64 = 6364136223846793005
+const RAND32_DEFAULT_INC U64 = 1442695040888963407
+const RAND32_MULTIPLIER U64 = 6364136223846793005
 
-fn rand32_new_inc(seed I64, inc I64) Rand32 =
+fn rand32_new_inc(seed U64, inc U64) Rand32 =
     let rng = Rand32({
         .state = 0,
-        .inc = __bor_i64(__lshift_i64(inc, 1), 1),
+        .inc = __bor_u64(__lshift_u64(inc, 1), 1),
     })
-    let mut rng2 = rand32_u32(rng).0
+    let mut rng2 = rand32_i32(rng).0
     rng2$.state = rng2$.state + seed
-    let rng3 = rand32_u32(rng2).0
+    let rng3 = rand32_i32(rng2).0
     rng3
 end
 
-fn rand32_new(seed I64) Rand32 =
+fn rand32_new(seed U64) Rand32 =
     rand32_new_inc(seed, RAND32_DEFAULT_INC)
 end
 
-fn rand32_u32(rand Rand32) {Rand32, I32} =
+fn rand32_i32(rand Rand32) {Rand32, I32} =
     let oldstate = rand$.state
     let mut newrng = rand
     newrng$.state = oldstate * RAND32_MULTIPLIER + rand$.inc
     -- ok maybe binary op operators are an ok idea
-    let xorshifted = __i64_to_i32( __rshift_i64( __bxor_i64( __rshift_i64(oldstate, 18),  oldstate),  27))
-    let rot = __i64_to_i32(__rshift_i64(oldstate, 59))
-    {newrng, __ror_i32(xorshifted, rot)} 
+    let xorshifted = __u64_to_u32( __rshift_u64( __bxor_u64( __rshift_u64(oldstate, 18),  oldstate),  27))
+    let rot = __u64_to_i32(__rshift_u64(oldstate, 59))
+    {newrng, __u32_to_i32(__ror_u32(xorshifted, rot))} 
+    --{newrng, 3}
 end
 
 

          
@@ 54,11 63,10 @@ end
 fn main() {} =
     let mut rng = rand32_new(54321);
     let mut i I32 = 0
-    loop
-        if i == 3 then break end
+    while i < 10 do
         i = i + 1
         
-        let res = rand32_u32(rng)
+        let res = rand32_i32(rng)
         rng = res.0
         let out I32 = res.1
         __println(out)

          
A => tests/programs/test_unsigned1.gt +18 -0
@@ 0,0 1,18 @@ 
+-- Format:
+--   status: success
+-- Compile:
+--   status: success
+-- Run:
+--   status: success
+--   stdout:
+--     15
+
+
+fn foo(x U64) U64 =
+    x + 3
+end
+
+fn main() {} =
+    let x U64 = 12
+    __println_u64(foo(x))
+end

          
A => tests/programs/test_unsigned2.gt +18 -0
@@ 0,0 1,18 @@ 
+-- Format:
+--   status: success
+-- Compile:
+--   status: error
+--   stderr:
+--     ...
+--     ...Conflict between I64 and U64...
+--     ...
+
+
+fn foo(x I64) U64 =
+    x + 3
+end
+
+fn main() {} =
+    let x U64 = 12
+    __println_u64(foo(x))
+end