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