mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
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:
committed by
Christoffer Lerno
parent
1aa038c92f
commit
cd7a03c2cf
19
lib/std/collections/maybe.c3
Normal file
19
lib/std/collections/maybe.c3
Normal 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?;
|
||||
}
|
||||
16
lib/std/collections/tuple.c3
Normal file
16
lib/std/collections/tuple.c3
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user