Fix conversion if (int x = foo()). Initial stream api. Extended enumset.

This commit is contained in:
Christoffer Lerno
2023-02-22 17:06:06 +01:00
parent 8f5676b488
commit b175b9318a
11 changed files with 541 additions and 35 deletions

View File

@@ -1,83 +1,144 @@
// TODO: ensure the type is an enum first.
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
/**
* @require $Type.kindof == TypeKind.ENUM "Only enums maybe be used with an enumset"
**/
module std::collections::enumset<Enum>;
$assert(Enum.elements < 64, "Maximum number of elements for an enum used as enum set is 63");
$switch ($$C_INT_SIZE):
$case 64:
typedef EnumSetType @private = ulong;
$case 32:
$if (Enum.elements < 32):
typedef EnumSetType @private = uint;
$else:
$if (Enum.elements > 128):
typedef EnumSetType @private = char[(Enum.elements + 7) / 8];
const IS_CHAR_ARRAY = true;
$elif (Enum.elements > 64):
typedef EnumSetType @private = uint128;
const IS_CHAR_ARRAY = false;
$elif (Enum.elements > 32 || $$C_INT_SIZE > 32):
typedef EnumSetType @private = ulong;
$endif;
$default:
$if (Enum.elements < 16):
const IS_CHAR_ARRAY = false;
$elif (Enum.elements > 16 || $$C_INT_SIZE > 16):
typedef EnumSetType @private = uint;
const IS_CHAR_ARRAY = false;
$elif (Enum.elements > 8 || $$C_INT_SIZE > 8):
typedef EnumSetType @private = ushort;
$elif (Enum.elements < 31):
typedef EnumSetType @private = uint;
$else:
typedef EnumSetType @private = ulong;
$endif;
$endswitch;
const IS_CHAR_ARRAY = false;
$else:
typedef EnumSetType @private = char;
const IS_CHAR_ARRAY = false;
$endif;
typedef EnumSet = distinct EnumSetType;
fn void EnumSet.add(EnumSet* this, Enum v)
{
$if (IS_CHAR_ARRAY):
(*this)[v / 8] |= (char)(1u << (v % 8));
$else:
*this = (EnumSet)((EnumSetType)*this | 1u << (EnumSetType)v);
$endif;
}
fn void EnumSet.clear(EnumSet* this)
{
$if (IS_CHAR_ARRAY):
*this = {};
$else:
*this = 0;
$endif;
}
fn bool EnumSet.remove(EnumSet* this, Enum v)
{
$if (IS_CHAR_ARRAY):
if (!this.has(v) @inline) return false;
(*this)[v / 8] &= (char)~(1 << (v % 8));
return true;
$else:
EnumSetType old = (EnumSetType)*this;
EnumSetType new = old & ~(1u << (EnumSetType)v);
*this = (EnumSet)new;
return old != new;
$endif;
}
fn bool EnumSet.has(EnumSet* this, Enum v)
{
$if (IS_CHAR_ARRAY):
return (bool)(((*this)[v / 8] << (v % 8)) & 0x01);
$else:
return ((EnumSetType)*this & (1u << (EnumSetType)v)) != 0;
$endif;
}
fn void EnumSet.add_all(EnumSet* this, EnumSet s)
{
$if (IS_CHAR_ARRAY):
foreach (i, c : s) (*this)[i] |= c;
$else:
*this = (EnumSet)((EnumSetType)*this | (EnumSetType)s);
$endif;
}
fn void EnumSet.retain_all(EnumSet* this, EnumSet s)
{
$if (IS_CHAR_ARRAY):
foreach (i, c : s) (*this)[i] &= c;
$else:
*this = (EnumSet)((EnumSetType)*this & (EnumSetType)s);
$endif;
}
fn void EnumSet.remove_all(EnumSet* this, EnumSet s)
{
$if (IS_CHAR_ARRAY):
foreach (i, c : s) (*this)[i] &= ~c;
$else:
*this = (EnumSet)((EnumSetType)*this & ~(EnumSetType)s);
$endif;
}
fn EnumSet EnumSet.and_of(EnumSet* this, EnumSet s)
{
$if (IS_CHAR_ARRAY):
EnumSet copy = *this;
copy.retain_all(s);
return copy;
$else:
return (EnumSet)((EnumSetType)*this & (EnumSetType)s);
$endif;
}
fn EnumSet EnumSet.or_of(EnumSet* this, EnumSet s)
{
$if (IS_CHAR_ARRAY):
EnumSet copy = *this;
copy.add_all(s);
return copy;
$else:
return (EnumSet)((EnumSetType)*this | (EnumSetType)s);
$endif;
}
fn EnumSet EnumSet.diff_of(EnumSet* this, EnumSet s)
{
$if (IS_CHAR_ARRAY):
EnumSet copy = *this;
copy.remove_all(s);
return copy;
$else:
return (EnumSet)((EnumSetType)*this & ~(EnumSetType)s);
$endif;
}
fn EnumSet EnumSet.xor_of(EnumSet* this, EnumSet s)
{
$if (IS_CHAR_ARRAY):
EnumSet copy = *this;
foreach (i, c : s) copy[i] ^= c;
return copy;
$else:
return (EnumSet)((EnumSetType)*this ^ (EnumSetType)s);
$endif;
}

View File

@@ -19,19 +19,22 @@ enum Seek
fault IoError
{
FILE_NOT_FOUND,
FILE_NOT_SEEKABLE,
FILE_NOT_VALID,
FILE_INVALID_POSITION,
INVALID_POSITION,
FILE_OVERFLOW,
FILE_IS_PIPE,
FILE_EOF,
FILE_INCOMPLETE_WRITE,
INCOMPLETE_WRITE,
FILE_NOT_DIR,
NO_PERMISSION,
INVALID_PUSHBACK,
EOF,
NOT_SEEKABLE,
NAME_TOO_LONG,
INTERRUPTED,
GENERAL_ERROR,
UNKNOWN_ERROR,
UNSUPPORTED_OPERATION,
}
fn void putchar(char c) @inline

View File

@@ -51,19 +51,21 @@ fn void! File.memopen(File* file, char[] data, String mode)
/**
* @require file.file != null
**/
fn void! File.seek(File *file, long offset, Seek seekMode = Seek.SET)
fn usz! File.seek(File *file, isz offset, Seek seekMode = Seek.SET)
{
if (libc::fseek(file.file, (SeekIndex)(offset), (int)(seekMode)))
{
switch (libc::errno())
{
case errno::EBADF: return IoError.FILE_NOT_SEEKABLE!;
case errno::EINVAL: return IoError.FILE_INVALID_POSITION!;
case errno::EBADF: return IoError.NOT_SEEKABLE!;
case errno::EINVAL: return IoError.INVALID_POSITION!;
case errno::EOVERFLOW: return IoError.FILE_OVERFLOW!;
case errno::ESPIPE: return IoError.FILE_IS_PIPE!;
default: return IoError.UNKNOWN_ERROR!;
}
}
if (seekMode == Seek.SET) return offset;
return libc::ftell(file.file);
}
/**
@@ -93,7 +95,7 @@ fn void! File.close(File *file) @inline
case errno::ENETDOWN:
case errno::ENETUNREACH:
case errno::ENOSPC:
case errno::EIO: return IoError.FILE_INCOMPLETE_WRITE!;
case errno::EIO: return IoError.INCOMPLETE_WRITE!;
default: return IoError.UNKNOWN_ERROR!;
}
}
@@ -109,24 +111,21 @@ fn bool File.eof(File* file) @inline
}
/**
* @require file && file.file
* @param [in] buffer
*/
fn usz File.read(File* file, void* buffer, usz items, usz element_size = 1)
fn usz File.read(File* file, char[] buffer)
{
return libc::fread(buffer, element_size, items, file.file);
return libc::fread(buffer.ptr, 1, buffer.len, file.file);
}
/**
* @param [&in] file
* @param [&out] buffer
* @param items
* @param element_size
* @require file.file `File must be initialized`
* @require element_size > 1
*/
fn usz File.write(File* file, void* buffer, usz items, usz element_size = 1)
fn usz File.write(File* file, char[] buffer)
{
return libc::fwrite(buffer, element_size, items, file.file);
return libc::fwrite(buffer.ptr, 1, buffer.len, file.file);
}
/**

205
lib/std/io/io_stream.c3 Normal file
View File

@@ -0,0 +1,205 @@
module std::io;
typedef CloseStreamFn = fn void!(Stream*);
typedef FlushStreamFn = fn void!(Stream*);
typedef SeekStreamFn = fn usz!(Stream*, isz offset, Seek seek);
typedef LenStreamFn = fn usz(Stream*);
typedef AvailableStreamFn = fn usz(Stream*);
typedef ReadStreamFn = fn usz!(Stream*, char[] bytes);
typedef ReadFromStreamFn = fn usz!(Stream*, Stream*);
typedef ReadByteStreamFn = fn char!(Stream*);
typedef PushbackByteStreamFn = fn void!(Stream*);
typedef WriteStreamFn = fn usz!(Stream*, char[] bytes);
typedef WriteToStreamFn = fn usz!(Stream*, Stream* out);
typedef WriteByteStreamFn = fn void!(Stream*, char c);
typedef DestroyStreamFn = fn void!(Stream*);
struct StreamInterface
{
CloseStreamFn close_fn;
FlushStreamFn flush_fn;
SeekStreamFn seek_fn;
LenStreamFn len_fn;
AvailableStreamFn available_fn;
ReadStreamFn read_fn;
ReadFromStreamFn read_stream_fn;
ReadByteStreamFn read_byte_fn;
PushbackByteStreamFn pushback_byte_fn;
WriteStreamFn write_fn;
WriteToStreamFn write_stream_fn;
WriteByteStreamFn write_byte_fn;
DestroyStreamFn destroy_fn;
}
struct Stream
{
StreamInterface *fns;
void* data;
}
fn bool Stream.supports_seek(Stream* s) @inline => (bool)s.fns.seek_fn;
fn bool Stream.supports_available(Stream* s) @inline => s.fns.available_fn || s.fns.seek_fn;
fn bool Stream.supports_len(Stream* s) @inline => s.fns.len_fn || s.fns.seek_fn;
fn bool Stream.supports_read(Stream* s) @inline => s.fns.read_fn || s.fns.read_byte_fn;
fn bool Stream.supports_read_from(Stream* s) @inline => (bool)s.fns.read_stream_fn;
fn bool Stream.supports_write_to(Stream* s) @inline => (bool)s.fns.write_stream_fn;
fn bool Stream.supports_pushback_byte(Stream* s) @inline => s.fns.pushback_byte_fn || s.fns.seek_fn;
fn bool Stream.supports_write(Stream* s) @inline => s.fns.write_fn || s.fns.write_byte_fn;
fn void! Stream.destroy(Stream* s) @inline @maydiscard
{
if (s.fns.destroy_fn) return s.fns.destroy_fn(s);
return s.close();
}
fn void! Stream.close(Stream* s) @inline @maydiscard
{
if (CloseStreamFn func = s.fns.close_fn) return func(s);
}
fn usz! Stream.seek(Stream* s, isz offset, Seek seek) @inline
{
if (SeekStreamFn func = s.fns.seek_fn) return func(s, offset, seek);
return IoError.NOT_SEEKABLE!;
}
fn usz! Stream.available(Stream* s) @inline
{
if (AvailableStreamFn func = s.fns.available_fn) return func(s);
if (SeekStreamFn func = s.fns.seek_fn)
{
usz curr = func(s, 0, Seek.CURSOR)?;
usz len = func(s, 0, Seek.END)?;
func(s, curr, Seek.SET)?;
return len - curr;
}
return IoError.NOT_SEEKABLE!;
}
fn usz! Stream.read(Stream* s, char[] buffer)
{
if (ReadStreamFn func = s.fns.read_fn) return func(s, buffer);
if (ReadByteStreamFn func = s.fns.read_byte_fn)
{
usz len = 0;
foreach (&cptr : buffer)
{
char! c = func(s);
if (catch err = c)
{
case IoError.EOF: return len;
default: return err!;
}
*cptr = c;
len++;
}
}
return IoError.UNSUPPORTED_OPERATION!;
}
fn char! Stream.read_byte(Stream* s) @inline
{
if (ReadByteStreamFn func = s.fns.read_byte_fn) return func(s);
return IoError.UNSUPPORTED_OPERATION!;
}
fn usz! Stream.write(Stream* s, char[] bytes) @inline
{
if (WriteStreamFn func = s.fns.write_fn) return func(s, bytes);
if (WriteByteStreamFn func = s.fns.write_byte_fn)
{
foreach (c : bytes) func(s, c)?;
return bytes.len;
}
return IoError.UNSUPPORTED_OPERATION!;
}
fn void! Stream.write_byte(Stream* s, char b) @inline
{
if (WriteByteStreamFn func = s.fns.write_byte_fn) return func(s, b);
return IoError.UNSUPPORTED_OPERATION!;
}
fn usz! Stream.write_to(Stream* s, Stream* to) @inline
{
if (WriteToStreamFn func = s.fns.write_stream_fn) return func(s, to);
return IoError.UNSUPPORTED_OPERATION!;
}
fn usz! Stream.read_from(Stream* s, Stream* from) @inline
{
if (ReadFromStreamFn func = s.fns.read_stream_fn) return func(s, from);
return IoError.UNSUPPORTED_OPERATION!;
}
fn void! Stream.flush(Stream* s) @inline @maydiscard
{
if (FlushStreamFn func = s.fns.flush_fn) return func(s);
return IoError.UNSUPPORTED_OPERATION!;
}
fn usz! Stream.len(Stream* s) @inline
{
if (LenStreamFn func = s.fns.len_fn) return func(s);
if (SeekStreamFn func = s.fns.seek_fn)
{
usz curr = func(s, 0, Seek.CURSOR)?;
usz len = func(s, 0, Seek.END)?;
func(s, curr, Seek.SET)?;
return len;
}
return IoError.NOT_SEEKABLE!;
}
fn void! Stream.pushback_byte(Stream* s) @inline
{
if (PushbackByteStreamFn func = s.fns.pushback_byte_fn) return func(s);
if (SeekStreamFn func = s.fns.seek_fn)
{
func(s, -1, CURSOR)?;
return;
}
return IoError.UNSUPPORTED_OPERATION!;
}
fn void! Stream.write_string(Stream* s, String str) @inline => (void)(s.write((char[])str)?);
fn usz! Stream.copy_to(Stream* s, Stream* dst, char[] buffer = {})
{
if (buffer.len) return copy_through_buffer(s, dst, buffer);
if (WriteToStreamFn func = s.fns.write_stream_fn) return func(s, dst);
if (ReadFromStreamFn func = dst.fns.read_stream_fn) return func(dst, s);
$switch (env::MEMORY_ENV):
$case NORMAL:
@pool()
{
return copy_through_buffer(s, dst, array::talloc(char, 4096));
};
$case SMALL:
@pool()
{
return copy_through_buffer(s, dst, array::talloc(char, 1024));
};
$case TINY:
$case NONE:
return copy_through_buffer(s, dst, &&(char[256]{}));
$endswitch;
}
macro usz! copy_through_buffer(Stream* s, Stream* dst, char[] buffer) @local
{
usz total_copied;
while (true)
{
usz! len = s.read(buffer);
if (catch err = len)
{
case IoError.EOF: return total_copied;
default: return err!;
}
if (!len) return total_copied;
usz written = dst.write(buffer[:len])?;
total_copied += len;
if (written != len) return IoError.INCOMPLETE_WRITE!;
}
}

View File

@@ -0,0 +1,80 @@
module std::io;
import std::math;
struct ByteReader
{
char[] bytes;
usz index;
}
fn void ByteReader.init(ByteReader* reader, char[] bytes)
{
*reader = { .bytes = bytes };
}
fn Stream ByteReader.as_stream(ByteReader* reader)
{
return { .fns = &bytereader_interface, .data = reader };
}
fn usz! ByteReader.read(ByteReader* reader, char[] bytes)
{
if (reader.index >= reader.bytes.len) return IoError.EOF!;
usz len = math::min(reader.bytes.len - reader.index, bytes.len);
if (len == 0) return 0;
mem::copy(bytes.ptr, &reader.bytes[reader.index], len);
reader.index += len;
return len;
}
fn char! ByteReader.read_byte(ByteReader* reader)
{
if (reader.index >= reader.bytes.len) return IoError.EOF!;
return reader.bytes[reader.index++];
}
fn void! ByteReader.pushback_byte(ByteReader* reader)
{
if (!reader.index) return IoError.INVALID_PUSHBACK!;
reader.index--;
}
fn usz! ByteReader.seek(ByteReader* reader, isz offset, Seek seek)
{
isz new_index;
switch (seek)
{
case SET: new_index = offset;
case CURSOR: new_index = reader.index + offset;
case END: new_index = reader.bytes.len + offset;
}
if (new_index < 0) return IoError.INVALID_POSITION!;
reader.index = new_index;
return new_index;
}
fn usz! ByteReader.write_stream(ByteReader* reader, Stream* writer)
{
if (reader.index >= reader.bytes.len) return 0;
usz written = writer.write(reader.bytes[reader.index..])?;
reader.index += written;
assert(reader.index <= reader.bytes.len);
return written;
}
fn usz ByteReader.available(ByteReader* reader)
{
return math::max((isz)0, (isz)reader.bytes.len - reader.index);
}
StreamInterface bytereader_interface = {
.len_fn = fn (s) => ((ByteReader*)s.data).bytes.len,
.read_fn = fn (s, char[] bytes) => ((ByteReader*)s.data).read(bytes) @inline,
.read_byte_fn = fn (s) => ((ByteReader*)s.data).read_byte() @inline,
.pushback_byte_fn = fn (s) => ((ByteReader*)s.data).pushback_byte() @inline,
.seek_fn = fn (s, offset, seek) => ((ByteReader*)s.data).seek(offset, seek) @inline,
.write_stream_fn = fn (s, writer) => ((ByteReader*)s.data).write_stream(writer) @inline,
.available_fn = fn (s) => ((ByteReader*)s.data).available() @inline,
};

View File

@@ -0,0 +1,114 @@
module std::io;
struct ByteWriter
{
char[] bytes;
usz index;
Allocator* allocator;
}
/**
* @param [&inout] writer
* @param [&in] allocator
* @require writer.bytes.len == 0 "Init may not run on on already initialized data"
* @ensure allocator != null, index == 0
**/
fn void ByteWriter.init(ByteWriter* writer, Allocator* allocator = mem::current_allocator())
{
*writer = { .bytes = {}, .allocator = allocator };
}
/**
* @param [&inout] writer
* @require writer.bytes.len == 0 "Init may not run on on already initialized data"
* @ensure allocator != null, index == 0
**/
fn void ByteWriter.tinit(ByteWriter* writer)
{
*writer = { .bytes = {}, .allocator = mem::temp_allocator() };
}
fn Stream ByteWriter.as_stream(ByteWriter* writer)
{
return { .fns = &bytewriter_interface, .data = writer };
}
fn void ByteWriter.destroy(ByteWriter* writer)
{
if (!writer.allocator) return;
if (void* ptr = writer.bytes.ptr) writer.allocator.free(ptr)!!;
*writer = { };
}
fn String ByteWriter.as_str(ByteWriter* writer)
{
return writer.bytes[:writer.index];
}
fn void! ByteWriter.ensure_capacity(ByteWriter* writer, usz len) @inline
{
if (writer.bytes.len > len) return;
if (len < 16) len = 16;
usz new_capacity = math::next_power_of_2(len);
char* new_ptr = writer.allocator.realloc(writer.bytes.ptr, new_capacity)?;
writer.bytes = new_ptr[:new_capacity];
}
fn usz! ByteWriter.write(ByteWriter* writer, char[] bytes)
{
writer.ensure_capacity(writer.index + bytes.len)?;
mem::copy(&writer.bytes[writer.index], bytes.ptr, bytes.len);
writer.index += bytes.len;
return bytes.len;
}
fn void! ByteWriter.write_byte(ByteWriter* writer, char c)
{
writer.ensure_capacity(writer.index + 1)?;
writer.bytes[writer.index++] = c;
}
/**
* @param [&inout] writer
* @param [&inout] reader
**/
fn usz! ByteWriter.read_from(ByteWriter* writer, Stream* reader)
{
if (reader.supports_available())
{
usz total_read = 0;
while (usz available = reader.available()?)
{
writer.ensure_capacity(writer.index + available)?;
usz len = reader.read(writer.bytes[writer.index..])?;
total_read += len;
writer.index += len;
}
return total_read;
}
usz total_read = 0;
while (true)
{
// See how much we can read.
usz len_to_read = writer.bytes.len - writer.index;
// Less than 16 bytes? Double the capacity
if (len_to_read < 16)
{
writer.ensure_capacity(writer.bytes.len * 2)?;
}
// Read into the rest of the buffer
usz read = reader.read(writer.bytes[writer.index..])?;
writer.index += read;
// Ok, we reached the end.
if (read < len_to_read) return total_read;
// Otherwise go another round
}
}
StreamInterface bytewriter_interface = {
.destroy_fn = fn (s) => ((ByteWriter*)s.data).destroy(),
.len_fn = fn (s) => ((ByteWriter*)s.data).bytes.len,
.write_fn = fn (s, char[] bytes) => ((ByteWriter*)s.data).write(bytes),
.write_byte_fn = fn (s, char c) => ((ByteWriter*)s.data).write_byte(c),
.read_stream_fn = fn (s, reader) => ((ByteWriter*)s.data).read_from(reader),
};

View File

@@ -0,0 +1,18 @@
module std::io;
fn Stream File.as_stream(File* file)
{
return { .fns = &filestream_interface, .data = file };
}
StreamInterface filestream_interface = {
.close_fn = fn (s) => ((File*)s.data).close(),
.seek_fn = fn (s, offset, seek) => ((File*)s.data).seek(offset, seek) @inline,
.read_fn = fn (s, char[] bytes) => ((File*)s.data).read(bytes) @inline,
.write_fn = fn (s, char[] bytes) => ((File*)s.data).write(bytes) @inline,
.write_byte_fn = fn (s, char c) => ((File*)s.data).putc(c) @inline,
.read_byte_fn = fn (s) => ((File*)s.data).getc() @inline,
.flush_fn = fn (s) => ((File*)s.data).flush() @inline,
};

View File

@@ -152,7 +152,7 @@ static void llvm_emit_decl_expr_list(GenContext *context, BEValue *be_value, Exp
if (type->type_kind != TYPE_BOOL)
{
CastKind cast = cast_to_bool_kind(type);
llvm_emit_cast(context, cast, last, be_value, type, type_bool);
llvm_emit_cast(context, cast, last, be_value, type_bool, type);
}
}
}

View File

@@ -324,6 +324,7 @@ RESOLVE_LAMBDA:;
}
FOREACH_END();
if (found_lambda) goto RESOLVE_LAMBDA;
halt_on_error();
if (active_target.strip_unused && !active_target.testing)
{

View File

@@ -1 +1 @@
#define COMPILER_VERSION "0.4.80"
#define COMPILER_VERSION "0.4.81"

View File

@@ -0,0 +1,25 @@
module std::io @test;
fn void! bytestream()
{
ByteReader r;
r.init("abc");
Stream s = r.as_stream();
assert(s.len()? == 3);
char[5] buffer;
assert('a' == s.read_byte()?);
s.pushback_byte()?;
usz len = s.read(&buffer)?;
assert(buffer[:len] == "abc");
ByteWriter w;
w.init();
Stream ws = w.as_stream();
ws.write("helloworld")?;
assert(w.as_str() == "helloworld");
s.seek(0, SET)?;
ws.read_from(&s)?;
s.seek(1, SET)?;
s.write_to(&ws)?;
assert(w.as_str() == "helloworldabcbc");
}