Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ jobs:
bait test -b c tests/auto_str
# bait test -b c tests/cast
bait test -b c tests/comptime
# bait test -b c tests/float
bait test -b c tests/float
bait test -b c tests/for
# bait test -b c tests/generics
# bait test -b c tests/ident
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ All notable changes are documented in this file.

### C Backend
- Implement hashmaps
- Implement floats
- Arrays
- Implement comparison
- Implement auto-string conversion
Expand Down
6 changes: 2 additions & 4 deletions docs/docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,10 @@ To get a different type, see [`as` casting](#casting).
These implicit type promotions are also supported:
```text
u8 → u16 → u32 → u64
↘ ↘ ↘
i8 → i16 → i32 → i64
↘ ↘
i8 → i16 → i32 → i64 → f32 → f64
```

> floats are still TODO


#### Base Prefixes
It's also possible to define numbers with a different base by using a prefix:
Expand Down
12 changes: 10 additions & 2 deletions lib/bait/ast/types.bt
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ pub fun (t Type) is_int() bool {
return t >= I8_TYPE and t <= U64_TYPE
}

pub fun (t Type) is_signed_int() bool {
return t >= I8_TYPE and t <= I64_TYPE
}

pub fun (t Type) is_unsigned_int() bool {
return t >= U8_TYPE and t <= U64_TYPE
}

const BUILTIN_STRUCT_TYPES := [
STRING_TYPE,
ARRAY_TYPE,
Expand Down Expand Up @@ -131,8 +139,8 @@ pub fun (t Table) register_builtins(){
_ = t.register_num('u16')
_ = t.register_num('u32')
_ = t.register_num('u64')
_ = t.register_sym(TypeSymbol{ mix_name = 'f32' })
_ = t.register_sym(TypeSymbol{ mix_name = 'f64' })
_ = t.register_num('f32')
_ = t.register_num('f64')
_ = t.register_sym(TypeSymbol{ mix_name = 'bool' })
_ = t.register_sym(TypeSymbol{
mix_name = 'string'
Expand Down
49 changes: 32 additions & 17 deletions lib/bait/checker/type.bt
Original file line number Diff line number Diff line change
Expand Up @@ -81,26 +81,41 @@ fun (c Checker) check_types(got ast.Type, expected ast.Type) bool {
}

fun (c Checker) can_promote(got ast.Type, expected ast.Type) bool {
// Unsigned targets
if expected == ast.U64_TYPE {
return got == ast.U32_TYPE or got == ast.U16_TYPE or got == ast.U8_TYPE
}
if expected == ast.U32_TYPE {
return got == ast.U16_TYPE or got == ast.U8_TYPE
}
if expected == ast.U16_TYPE {
return got == ast.U8_TYPE
// Unsigned to unsigned (u8 to u64)
if expected.is_unsigned_int() {
return got.is_unsigned_int() and got < expected
}

// Signed targets
if expected == ast.I64_TYPE {
return got == ast.I32_TYPE or got == ast.I16_TYPE or got == ast.I8_TYPE or got == ast.U32_TYPE or got == ast.U16_TYPE or got == ast.U8_TYPE
}
if expected == ast.I32_TYPE {
return got == ast.I16_TYPE or got == ast.I8_TYPE or got == ast.U16_TYPE or got == ast.U8_TYPE
// To signed
if expected.is_signed_int() {
// From smaller signed
if got.is_signed_int() {
return got < expected
}

// From unsigned
if got == ast.U8_TYPE {
return expected >= ast.I16_TYPE
}
if got == ast.U16_TYPE {
return expected >= ast.I32_TYPE
}
if got == ast.U32_TYPE {
return expected == ast.I64_TYPE
}
}
if expected == ast.I16_TYPE {
return got == ast.I8_TYPE or got == ast.U8_TYPE

// To float
if expected == ast.F32_TYPE or expected == ast.F64_TYPE {
// From int
if got.is_int() {
return true
}

// f32 to f64
if expected == ast.F64_TYPE and got == ast.F32_TYPE {
return true
}
}

return false
Expand Down
6 changes: 5 additions & 1 deletion lib/bait/gen/c/expr.bt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ fun (mut g Gen) expr(expr ast.Expr) {
ast.CharLiteral { g.char_literal(expr) }
ast.ComptimeVar { g.comptime_var(expr) }
ast.EnumVal { g.enum_val(expr) }
ast.FloatLiteral { panic('floats not implemented') } // TODO
ast.FloatLiteral { g.float_literal(expr) }
ast.HashExpr { g.hash_expr(expr) }
ast.Ident { g.ident(expr) }
ast.IfMatch { g.if_match(expr) }
Expand Down Expand Up @@ -131,6 +131,10 @@ fun (mut g Gen) enum_val(node ast.EnumVal) {
g.write(c_esc(node.val))
}

fun (mut g Gen) float_literal(node ast.FloatLiteral) {
g.write(node.val)
}

fun (mut g Gen) hash_expr(node ast.HashExpr) {
g.write(node.val)
}
Expand Down
17 changes: 17 additions & 0 deletions lib/builtin/number.c.bt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ type u16 := #C.uint16_t
type u32 := #C.uint32_t
type u64 := #C.uint64_t

type f32 := #C.float
type f64 := #C.double

pub fun (n i8) str() string {
return (n as i64).str()
}
Expand Down Expand Up @@ -49,3 +52,17 @@ pub fun (n u64) str() string {
_ = #C.snprintf(buf, length + 1, "%llu".str, n)
return from_c_string(buf)
}

pub fun (n f32) str() string {
length := #C.snprintf(#C.'NULL' as &u8, 0, "%g".str, n as f64)
buf := #C.malloc(length + 1)
_ = #C.snprintf(buf, length + 1, "%g".str, n as f64)
return from_c_string(buf)
Comment thread
serkonda7 marked this conversation as resolved.
}

pub fun (n f64) str() string {
length := #C.snprintf(#C.'NULL' as &u8, 0, "%g".str, n)
buf := #C.malloc(length + 1)
_ = #C.snprintf(buf, length + 1, "%g".str, n)
Comment thread
serkonda7 marked this conversation as resolved.
Outdated
Comment thread
serkonda7 marked this conversation as resolved.
Outdated
return from_c_string(buf)
}
8 changes: 8 additions & 0 deletions lib/builtin/number.js.bt
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ pub fun u64(n #JS.Any) u64 {
return #JS.'BigInt.asUintN(64, n)' as u64
}

pub fun f32(n #JS.Any) f32 {
return #JS.'Math.fround(n)' as f32
}

pub fun f64(n #JS.Any) f64 {
return #JS.'Number(n)' as f64
}

pub fun (n i8) str() string {
return from_js_string(#JS.'n.toString()' as #JS.String)
}
Expand Down
2 changes: 1 addition & 1 deletion tests/float/float_test.bt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Lukas Neubert <lukas.neubert@proton.me>
// SPDX-FileCopyrightText: Lukas Neubert
// SPDX-License-Identifier: MIT

fun test_floats() {
Expand Down
30 changes: 30 additions & 0 deletions tests/float/promotion_test.bt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// SPDX-FileCopyrightText: Lukas Neubert
// SPDX-License-Identifier: MIT

fun test_float_promotion() {
f32v := 1.5 as f32
f64v := 2.5 as f64

assert f32v + f64v == 4.0
assert f64v + f32v == 4.0
}

fun test_int_to_float_promotion() {
u8v := 1 as u8
i16v := 2 as i16
f32v := 3.5 as f32
f64v := 4.5 as f64

assert f32v + u8v == 4.5
assert u8v + f32v == 4.5
assert f64v + i16v == 6.5
assert i16v + f64v == 6.5
}

fun test_float_math_assign() {
u16v := 2 as u16

mut x := 3.5 as f64
x += u16v
assert x == 5.5
}