Interface based streams. Fix for initializing with a force unwrap inside. Allow $define to take a list. Allow $define to return error on argument type mismatch in call. Fixed broken bit operations on boolean vectors.

This commit is contained in:
Christoffer Lerno
2023-10-31 00:55:20 +01:00
committed by Christoffer Lerno
parent 1aa038c92f
commit cd7a03c2cf
9 changed files with 139 additions and 136 deletions

View File

@@ -0,0 +1,19 @@
module std::collections::maybe(<Type>);
struct Maybe
{
Type value;
bool has_value;
}
fn Maybe value(Type val)
{
return { .value = val, .has_value = true };
}
const Maybe EMPTY = { };
macro Type! Maybe.get(self)
{
return self.has_value ? self.value : SearchResult.MISSING?;
}

View File

@@ -0,0 +1,16 @@
module std::collections::tuple(<Type1, Type2>);
struct Tuple
{
Type1 first;
Type2 second;
}
module std::collections::triple(<Type1, Type2, Type3>);
struct Triple
{
Type1 first;
Type2 second;
Type3 third;
}

View File

@@ -368,14 +368,14 @@ macro bool equals(a, b, isz len = -1, usz $align = 0)
return true;
}
macro @clone(&value, Allocator *using = mem::heap()) @builtin
macro @clone(value, Allocator *using = mem::heap()) @builtin
{
$typeof(value)* x = malloc($typeof(value), .using = using);
*x = value;
return x;
}
macro @tclone(&value) @builtin => @clone(value, mem::temp());
macro @tclone(value) @builtin => @clone(value, mem::temp());
macro type_alloc_must_be_aligned($Type)
{
@@ -601,16 +601,17 @@ macro void @pool(TempAllocator* #other_temp = null; @body) @builtin
tlocal Allocator* thread_allocator @private = &allocator::LIBC_ALLOCATOR;
tlocal TempAllocator* thread_temp_allocator @private = null;
tlocal TempAllocator*[2] temp_allocator_pair @private;
Allocator* temp_base_allocator @private = &allocator::LIBC_ALLOCATOR;
macro TempAllocator* create_default_sized_temp_allocator() @local
macro TempAllocator* create_default_sized_temp_allocator(Allocator* allocator) @local
{
$switch (env::MEMORY_ENV)
$case NORMAL:
return allocator::new_temp(1024 * 256, thread_allocator)!!;
return allocator::new_temp(1024 * 256, allocator)!!;
$case SMALL:
return allocator::new_temp(1024 * 16, thread_allocator)!!;
return allocator::new_temp(1024 * 16, allocator)!!;
$case TINY:
return allocator::new_temp(1024 * 2, thread_allocator)!!;
return allocator::new_temp(1024 * 2, allocator)!!;
$case NONE:
unreachable("Temp allocator must explicitly created when memory-env is set to 'none'.");
$endswitch
@@ -631,8 +632,8 @@ import libc;
fn void init_default_temp_allocators() @private
{
temp_allocator_pair[0] = create_default_sized_temp_allocator();
temp_allocator_pair[1] = create_default_sized_temp_allocator();
temp_allocator_pair[0] = create_default_sized_temp_allocator(temp_base_allocator);
temp_allocator_pair[1] = create_default_sized_temp_allocator(temp_base_allocator);
thread_temp_allocator = temp_allocator_pair[0];
}
@@ -661,5 +662,6 @@ fn void initialize_wasm_mem() @init(1) @private
uptr start = (uptr)&__heap_base;
if (start > mem::DEFAULT_MEM_ALIGNMENT) allocator::wasm_memory.use = start;
wasm_allocator.init(fn (x) => allocator::wasm_memory.allocate_block(x));
temp_base_allocator = &wasm_allocator;
thread_allocator = &wasm_allocator;
}

View File

@@ -11,30 +11,47 @@ interface Random
fn void next_bytes(char[] buffer);
}
macro Random.seed(&self, seed)
macro bool is_random(random) => $assignable(random, Random*);
/**
* @require is_random(random)
**/
macro void seed(random, seed)
{
self.set_seed(@as_char_view(seed));
random.set_seed(@as_char_view(seed));
}
fn int Random.next(&random, int max)
/**
* @require is_random(random)
**/
macro int next(random, int max)
{
return (int)(random.next_double() * max);
return (int)(next_double(random) * max);
}
fn bool Random.next_bool(&self)
/**
* @require is_random(random)
**/
macro void next_bool(random)
{
return self.next_byte() & 1 == 0;
return random.next_byte() & 1;
}
fn float Random.next_float(&self)
/**
* @require is_random(random)
**/
macro float next_float(random)
{
uint val = self.next_int() & (1 << 24 - 1);
uint val = random.next_int() & (1 << 24 - 1);
return val / (float)(1 << 24);
}
fn double Random.next_double(&self)
/**
* @require is_random(random)
**/
macro double next_double(random)
{
ulong val = self.next_long() & (1UL << 53 - 1);
ulong val = random.next_long() & (1UL << 53 - 1);
return val * 0x1.0p-53;
}

View File

@@ -3,6 +3,7 @@
## 0.5.0 Change List
### Changes / improvements
- `$defined` can take a list of expressions.
- `$and` compile time "and" which does not check expressions after the first is an error.
- `$is_const` returns true if an expression is compile time const.
- `$assignable` returns true is an expression may be implicitly cast to a type.
@@ -129,6 +130,7 @@
- `assert` may now take varargs for formatting.
### Stdlib changes
- Tuple and Maybe types.
- `.as_str()` replaced by `.str_view()`
- Added `math::log(x , base)` and `math::ln(x)`.
- Hashmap keys implicitly copied if copy/free are defined.
@@ -179,6 +181,7 @@
- Added posix socket functions.
### Fixes
- Structs returned from macros and then indexed into directly could previously be miscompiled.
- Naked functions now correctly handles `asm`.
- Indexing into arrays would not always widen the index safely.
- Macros with implicit return didn't correctly deduct the return type.

View File

@@ -20,7 +20,7 @@ fn void! very_long_name_bench() @benchmark
return std::thread::sleep_ms(10);
}
static initialize
fn void initialize() @init
{
set_benchmark_warmup_iterations(5);
set_benchmark_max_iterations(1000);

View File

@@ -1,52 +1,16 @@
module test;
import libc;
import std::io;
import std::collections::maybe;
struct Doc { Head *head; }
struct Head { DString* title; }
def MaybeString = Maybe(<String>);
def MaybeHead = Maybe(<Head>);
def new_head_val = maybe::value(<Head>);
def new_string_val = maybe::value(<String>);
struct Summary
fault TitleResult
{
DString* title;
bool ok;
}
struct StringData @private
{
Allocator* allocator;
usz len;
usz capacity;
char[*] chars;
}
fn void Summary.print(Summary *s, File* out)
{
String title = s.title ? s.title.str_view() : "missing";
(void)io::fprintf(out, "Summary({ .title = %s, .ok = %s})", title, s.ok);
}
fn bool contains(String haystack, String needle)
{
usz len = haystack.len;
usz needle_len = needle.len;
if (len < needle_len) return false;
if (!needle_len) return true;
len -= needle_len - 1;
for (usz i = 0; i < len; i++)
{
if (mem::equals(haystack[i..], needle))
{
return true;
}
}
return false;
}
macro @dupe(value)
{
$typeof(&value) temp = malloc_checked($typeof(value))!;
*temp = value;
return temp;
TITLE_MISSING
}
fault ReadError
@@ -54,58 +18,52 @@ fault ReadError
BAD_READ,
}
fn Doc! readDoc(String url)
struct Doc { MaybeHead head; }
struct Head { MaybeString title; }
struct Summary
{
if (contains(url, "fail")) return ReadError.BAD_READ?;
if (contains(url, "head-missing")) return { .head = null };
if (contains(url, "title-missing")) return { @dupe(Head { .title = null }) };
if (contains(url, "title-empty")) return { @dupe(Head { .title = @dupe((DString)null) }) };
DString str;
str.printf("Title of %s", url);
return { @dupe(Head { .title = @dupe(str) }) };
MaybeString title;
bool ok;
}
fn Summary buildSummary(Doc doc)
fn void! Summary.print(Summary *s, OutStream* out)
{
io::fprintf(out, "Summary({ .title = %s, .ok = %s})", s.title.get() ?? "missing", s.ok)!;
}
fn Doc! read_doc(String url)
{
if (url.contains("fail")) return ReadError.BAD_READ?;
if (url.contains("head-missing")) return { };
if (url.contains("title-missing")) return { .head = new_head_val({}) };
if (url.contains("title-empty")) return { .head = new_head_val({ .title = new_string_val("")}) };
return { .head = new_head_val({ .title = new_string_val(string::printf("Title of %s", url)) }) };
}
fn Summary build_summary(Doc doc)
{
return Summary {
.title = doc.head ? doc.head.title : null,
.title = new_string_val(doc.head.get().title.get()) ?? MaybeString {},
.ok = true,
};
}
fn Summary readAndBuildSummary(String url)
fn Summary read_and_build_summary(String url)
{
return buildSummary(readDoc(url)) ?? Summary { .title = null, .ok = false };
/*
// or
Summary summary = buildSummary(readDoc(url));
if (catch summary) return Summary { .title = null, .ok = false };
return summary;
// or
Summary summary = buildSummary(readDoc(url));
if (try summary) return summary;
return Summary { .title = null, .ok = false };
*/
return build_summary(read_doc(url)) ?? Summary {};
}
fault TitleResult
fn bool! is_title_non_empty(Doc doc)
{
TITLE_MISSING
String! title = doc.head.get().title.get();
if (catch title) return TitleResult.TITLE_MISSING?;
return title.len > 0;
}
fn bool! isTitleNonEmpty(Doc doc)
fn bool! read_whether_title_non_empty(String url)
{
if (!doc.head) return TitleResult.TITLE_MISSING?;
DString* head = doc.head.title;
if (!head) return TitleResult.TITLE_MISSING?;
return head.len() > 0;
}
fn bool! readWhetherTitleNonEmpty(String url)
{
return isTitleNonEmpty(readDoc(url));
return is_title_non_empty(read_doc(url));
}
fn String bool_to_string(bool b)
@@ -113,27 +71,26 @@ fn String bool_to_string(bool b)
return b ? "true" : "false";
}
fn void main()
{
const String[] URLS = { "good", "title-empty", "title-missing", "head-missing", "fail" };
DynamicArenaAllocator dynamic_arena;
dynamic_arena.init(1024);
OutStream* out = io::stdout();
foreach (String url : URLS)
{
mem::@scoped(&dynamic_arena)
{
io::printf(`Checking "https://%s/":` "\n", url);
Summary summary = readAndBuildSummary(url);
io::printf(" Summary: ");
summary.print(io::stdout());
io::printn("");
String title_sure = summary.title ? summary.title.str_view() : "";
io::printf(" Title: %s\n", title_sure);
bool! has_title = readWhetherTitleNonEmpty(url);
Summary summary = read_and_build_summary(url);
io::fprintf(out, " Summary: ")!!;
summary.print(out)!!;
io::fprintn(out, "")!!;
io::fprintf(out, " Title: %s\n", summary.title.get() ?? "")!!;
bool! has_title = read_whether_title_non_empty(url);
// This looks a bit less than elegant, but as you see it's mostly due to having to
// use printf here.
io::printf(" Has title: %s vs %s\n", bool_to_string(has_title) ?? (@catch(has_title)).nameof, has_title ?? false);
io::fprintf(out, " Has title: %s vs %s\n", bool_to_string(has_title) ?? (@catch(has_title)).nameof, has_title ?? false)!!;
};
dynamic_arena.reset();
}

View File

@@ -1,8 +1,7 @@
module guess_number;
import std::io;
import libc;
extern fn isz getline(char** linep, usz* linecapp, CFile stream);
import std::math;
import std::time;
struct Game
{
@@ -20,33 +19,21 @@ fault InputResult
int err_count = 0;
fn int! askGuess(int high)
fn int! ask_guess(int high)
{
libc::printf("Guess a number between 1 and %d: ", high);
String text = readLine()!;
char* end = null;
int value = (int)libc::strtol(text.ptr, &end, 10);
if (end && end[0] >= ' ') return InputResult.NOT_AN_INT?;
return value;
io::printf("Guess a number between 1 and %d: ", high);
String text = io::readline() ?? InputResult.FAILED_TO_READ?!;
return text.to_int() ?? InputResult.NOT_AN_INT?;
}
fn String! readLine()
{
char* chars = tmalloc(1024);
isz loaded = getline(&chars, &&(usz)1023, libc::stdin());
if (loaded < 0) return InputResult.FAILED_TO_READ?;
chars[loaded] = 0;
return (String)chars[0..(loaded - 1)];
}
fn int! askGuessMulti(int high)
fn int! ask_guess_multi(int high)
{
while (true)
{
int! result = askGuess(high);
int! result = ask_guess(high);
if (@catch(result) == InputResult.NOT_AN_INT)
{
libc::printf("I didn't understand that.\n");
io::printn("I didn't understand that.");
err_count++;
continue;
}
@@ -58,20 +45,20 @@ fn void! Game.play(Game *game)
{
while (!game.done)
{
int guess = askGuessMulti(game.high)!;
int guess = ask_guess_multi(game.high)!;
game.report(guess);
game.update(guess);
}
}
fn void Game.report(Game *game, int guess)
fn void Game.report(Game* game, int guess)
{
String desc = {|
if (guess < game.answer) return "too low";
if (guess > game.answer) return "too high";
return "the answer";
|};
libc::printf("%d is %.*s.\n", guess, (int)desc.len, desc.ptr);
io::printfn("%d is %s.\n", guess, desc);
}
fn void Game.update(Game *game, int guess)
@@ -82,11 +69,12 @@ fn void Game.update(Game *game, int guess)
fn void! main()
{
libc::srand((int)libc::clock());
Lcg128Random rand;
random::seed(&rand, clock::now());
int high = 100;
int answer = libc::rand() % high + 1;
int answer = random::next(&rand, high) + 1;
Game game = { .answer = answer, .high = high };
(void)game.play();
libc::printf("Finished in %d guesses.\n", game.guesses);
libc::printf("Total input errors: %d.\n", err_count);
io::printfn("Finished in %d guesses.", game.guesses);
io::printfn("Total input errors: %d.", err_count);
}

View File

@@ -768,6 +768,7 @@ static void llvm_emit_member_addr(GenContext *c, BEValue *value, Decl *parent, D
llvm_value_bitcast(c, value, found->type);
break;
case TYPE_STRUCT:
llvm_value_addr(c, value);
llvm_value_struct_gep(c, value, value, (unsigned)index);
break;
default: