mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Updated grammar. Removal of elif. Removal of ':' ';' in some ct statements. Empty faults is now an error. Remove "define" for types. Remove "private". Better errors on incorrect bitstruct syntax. Introduction of wildcard type rather than optional wildcard. Removal of scaled vector type. mkdir and rmdir. Disallow define @Foo() = { @inline }. Add handling for @optreturn and change it to @return!. Restrict interface style functions. Updated x64 ABI. stdlib updates to string. Removed deprecated functions. Update how variadics are implemented. Extended error messages. x86 ABI fixes. Shift check fixes. '!' and '?' are flipped. No trailing ',' allowed in functions. Fix to string parsing. Allow l suffix. Simplifying flatpath. any replaces variant, anyfault replaces anyerr. Allow getting the underlying type of anyfault. De-duplicate string constants. Fix of readme. Extended list. Fix of "(MyEnum)x + 1". Clock and DateTime types. Fixes to array concat.
This commit is contained in:
committed by
Christoffer Lerno
parent
d14e778232
commit
809321e20c
30
.github/workflows/main.yml
vendored
30
.github/workflows/main.yml
vendored
@@ -34,6 +34,7 @@ jobs:
|
||||
run: |
|
||||
cd resources
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\hello_world_many.c3
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\time.c3
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\fannkuch-redux.c3
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\contextfree\boolerr.c3
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\load_world.c3
|
||||
@@ -101,6 +102,7 @@ jobs:
|
||||
run: |
|
||||
cd resources
|
||||
../build/c3c compile-run examples/hello_world_many.c3
|
||||
../build/c3c compile-run examples/time.c3
|
||||
../build/c3c compile-run examples/fannkuch-redux.c3
|
||||
../build/c3c compile-run examples/contextfree/boolerr.c3
|
||||
../build/c3c compile-run examples/load_world.c3
|
||||
@@ -127,7 +129,7 @@ jobs:
|
||||
|
||||
build-msys2-clang:
|
||||
runs-on: windows-latest
|
||||
if: ${{ false }}
|
||||
#if: ${{ false }}
|
||||
strategy:
|
||||
# Don't abort runners if a single one fails
|
||||
fail-fast: false
|
||||
@@ -155,6 +157,7 @@ jobs:
|
||||
run: |
|
||||
cd resources
|
||||
../build/c3c compile-run examples/hello_world_many.c3
|
||||
../build/c3c compile-run examples/time.c3
|
||||
../build/c3c compile-run examples/fannkuch-redux.c3
|
||||
../build/c3c compile-run examples/contextfree/boolerr.c3
|
||||
../build/c3c compile-run examples/load_world.c3
|
||||
@@ -181,7 +184,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build_type: [Release, Debug]
|
||||
llvm_version: [15, 16, 17]
|
||||
llvm_version: [16, 17]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -218,7 +221,28 @@ jobs:
|
||||
- name: Compile and run some examples
|
||||
run: |
|
||||
cd resources
|
||||
../build/c3c compile examples/base64.c3
|
||||
../build/c3c compile examples/binarydigits.c3
|
||||
../build/c3c compile examples/brainfk.c3
|
||||
../build/c3c compile examples/factorial_macro.c3
|
||||
../build/c3c compile examples/fasta.c3
|
||||
../build/c3c compile examples/gameoflife.c3
|
||||
../build/c3c compile examples/hash.c3
|
||||
../build/c3c compile examples/levenshtein.c3
|
||||
../build/c3c compile examples/load_world.c3
|
||||
../build/c3c compile examples/map.c3
|
||||
../build/c3c compile examples/mandelbrot.c3
|
||||
../build/c3c compile examples/plus_minus.c3
|
||||
../build/c3c compile examples/nbodies.c3
|
||||
../build/c3c compile examples/spectralnorm.c3
|
||||
../build/c3c compile examples/swap.c3
|
||||
../build/c3c compile examples/contextfree/boolerr.c3
|
||||
../build/c3c compile examples/contextfree/dynscope.c3
|
||||
../build/c3c compile examples/contextfree/guess_number.c3
|
||||
../build/c3c compile examples/contextfree/multi.c3
|
||||
../build/c3c compile examples/contextfree/cleanup.c3
|
||||
../build/c3c compile-run examples/hello_world_many.c3
|
||||
../build/c3c compile-run examples/time.c3
|
||||
../build/c3c compile-run examples/fannkuch-redux.c3
|
||||
../build/c3c compile-run examples/contextfree/boolerr.c3
|
||||
../build/c3c compile-run examples/load_world.c3
|
||||
@@ -272,7 +296,6 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Download LLVM
|
||||
run: |
|
||||
brew update
|
||||
brew install llvm@${{ matrix.llvm_version }} ninja curl
|
||||
echo "/usr/local/opt/llvm@${{ matrix.llvm_version }}/bin" >> $GITHUB_PATH
|
||||
TMP_PATH=$(xcrun --show-sdk-path)/user/include
|
||||
@@ -291,6 +314,7 @@ jobs:
|
||||
run: |
|
||||
cd resources
|
||||
../build/c3c compile-run examples/hello_world_many.c3
|
||||
../build/c3c compile-run examples/time.c3
|
||||
../build/c3c compile-run examples/fannkuch-redux.c3
|
||||
../build/c3c compile-run examples/contextfree/boolerr.c3
|
||||
../build/c3c compile-run examples/load_world.c3
|
||||
|
||||
@@ -171,12 +171,8 @@ if (${LLVM_PACKAGE_VERSION} VERSION_GREATER_EQUAL 16)
|
||||
${LLD_WASM}
|
||||
${LLD_MINGW}
|
||||
${LLD_ELF}
|
||||
${LLD_DRIVER}
|
||||
${LLD_READER_WRITER}
|
||||
${LLD_LOONG}
|
||||
${LLD_MACHO}
|
||||
${LLD_YAML}
|
||||
${LLD_CORE}
|
||||
)
|
||||
else()
|
||||
set(lld_libs
|
||||
@@ -185,11 +181,7 @@ else()
|
||||
${LLD_WASM}
|
||||
${LLD_MINGW}
|
||||
${LLD_ELF}
|
||||
${LLD_DRIVER}
|
||||
${LLD_READER_WRITER}
|
||||
${LLD_MACHO}
|
||||
${LLD_YAML}
|
||||
${LLD_CORE}
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@ fn void Stack.push(Stack* this, Type element)
|
||||
if (this.capacity == this.size)
|
||||
{
|
||||
this.capacity *= 2;
|
||||
if (this.capacity < 16) this.capacity = 16;
|
||||
this.elems = mem::realloc(this.elems, Type.sizeof * this.capacity);
|
||||
}
|
||||
this.elems[this.size++] = element;
|
||||
@@ -78,9 +79,9 @@ import stack;
|
||||
|
||||
// Define our new types, the first will implicitly create
|
||||
// a complete copy of the entire Stack module with "Type" set to "int"
|
||||
define IntStack = Stack<int>;
|
||||
typedef IntStack = Stack<int>;
|
||||
// The second creates another copy with "Type" set to "double"
|
||||
define DoubleStack = Stack<double>;
|
||||
typedef DoubleStack = Stack<double>;
|
||||
|
||||
// If we had added "define IntStack2 = Stack<int>"
|
||||
// no additional copy would have been made (since we already
|
||||
|
||||
@@ -87,13 +87,13 @@ fn Type! peek_last(LinkedList* list) => list.last() @inline;
|
||||
|
||||
fn Type! LinkedList.first(LinkedList *list)
|
||||
{
|
||||
if (!list._first) return IteratorResult.NO_MORE_ELEMENT!;
|
||||
if (!list._first) return IteratorResult.NO_MORE_ELEMENT?;
|
||||
return list._first.value;
|
||||
}
|
||||
|
||||
fn Type! LinkedList.last(LinkedList* list)
|
||||
{
|
||||
if (!list._last) return IteratorResult.NO_MORE_ELEMENT!;
|
||||
if (!list._last) return IteratorResult.NO_MORE_ELEMENT?;
|
||||
return list._last.value;
|
||||
}
|
||||
|
||||
@@ -240,7 +240,7 @@ fn bool LinkedList.remove_last_value(LinkedList* list, Type t)
|
||||
**/
|
||||
fn Type! LinkedList.pop(LinkedList* list)
|
||||
{
|
||||
if (!list._first) return IteratorResult.NO_MORE_ELEMENT!;
|
||||
if (!list._first) return IteratorResult.NO_MORE_ELEMENT?;
|
||||
defer list.unlink_first();
|
||||
return list._first.value;
|
||||
}
|
||||
@@ -250,7 +250,7 @@ fn Type! LinkedList.pop(LinkedList* list)
|
||||
**/
|
||||
fn void! LinkedList.remove_last(LinkedList* list)
|
||||
{
|
||||
if (!list._first) return IteratorResult.NO_MORE_ELEMENT!;
|
||||
if (!list._first) return IteratorResult.NO_MORE_ELEMENT?;
|
||||
list.unlink_last();
|
||||
}
|
||||
|
||||
@@ -259,7 +259,7 @@ fn void! LinkedList.remove_last(LinkedList* list)
|
||||
**/
|
||||
fn void! LinkedList.remove_first(LinkedList* list)
|
||||
{
|
||||
if (!list._first) return IteratorResult.NO_MORE_ELEMENT!;
|
||||
if (!list._first) return IteratorResult.NO_MORE_ELEMENT?;
|
||||
list.unlink_first();
|
||||
}
|
||||
|
||||
|
||||
@@ -79,6 +79,50 @@ fn void List.remove_at(List* list, usz index)
|
||||
list.size--;
|
||||
}
|
||||
|
||||
fn void List.add_all(List* list, List* other_list)
|
||||
{
|
||||
if (!other_list.size) return;
|
||||
list.reserve(other_list.size);
|
||||
foreach (&value : other_list)
|
||||
{
|
||||
list.entries[list.size++] = *value;
|
||||
}
|
||||
}
|
||||
|
||||
fn Type[] List.to_array(List* list, Allocator* using = mem::heap())
|
||||
{
|
||||
if (!list.size) return Type[] {};
|
||||
Type[] result = malloc(Type, list.size, .using = using);
|
||||
result[..] = list.entries[:list.size];
|
||||
return result;
|
||||
}
|
||||
|
||||
fn void List.reverse(List* list)
|
||||
{
|
||||
if (list.size < 2) return;
|
||||
usz half = list.size / 2U;
|
||||
usz end = list.size - 1;
|
||||
for (usz i = 0; i < half; i++)
|
||||
{
|
||||
@swap(list.entries[i], list.entries[end - i]);
|
||||
}
|
||||
}
|
||||
|
||||
fn Type[] List.array_view(List* list)
|
||||
{
|
||||
return list.entries[:list.size];
|
||||
}
|
||||
|
||||
fn void List.add_array(List* list, Type[] array)
|
||||
{
|
||||
if (!array.len) return;
|
||||
list.reserve(array.len);
|
||||
foreach (&value : array)
|
||||
{
|
||||
list.entries[list.size++] = *value;
|
||||
}
|
||||
}
|
||||
|
||||
fn void List.push_front(List* list, Type type) @inline
|
||||
{
|
||||
list.insert_at(0, type);
|
||||
@@ -175,11 +219,45 @@ fn Type* List.get_ref(List* list, usz index) @operator(&[]) @inline
|
||||
return &list.entries[index];
|
||||
}
|
||||
|
||||
$if (types::is_equatable_type(Type))
|
||||
|
||||
fn void List.ensure_capacity(List* list) @inline @private
|
||||
fn usz! List.index_of(List* list, Type type)
|
||||
{
|
||||
if (list.capacity == list.size)
|
||||
foreach (i, v : list)
|
||||
{
|
||||
list.reserve(list.capacity ? 2 * list.capacity : 16);
|
||||
if (v == type) return i;
|
||||
}
|
||||
return SearchResult.MISSING?;
|
||||
}
|
||||
|
||||
fn bool List.equals(List* list, List other_list)
|
||||
{
|
||||
if (list.size != other_list.size) return false;
|
||||
foreach (i, v : list)
|
||||
{
|
||||
if (v != other_list.entries[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
fn bool List.contains(List* list, Type type)
|
||||
{
|
||||
foreach (i, v : list)
|
||||
{
|
||||
if (v == type) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
$endif
|
||||
|
||||
fn void List.ensure_capacity(List* list, usz added = 1) @inline @private
|
||||
{
|
||||
usz new_size = list.size + added;
|
||||
if (list.capacity > new_size) return;
|
||||
|
||||
assert(new_size < usz.max / 2U);
|
||||
usz new_capacity = list.capacity ? 2U * list.capacity : 16U;
|
||||
while (new_size >= new_capacity) new_capacity *= 2U;
|
||||
list.reserve(new_capacity);
|
||||
}
|
||||
|
||||
@@ -63,24 +63,24 @@ fn bool HashMap.is_empty(HashMap* map) @inline
|
||||
|
||||
fn Value*! HashMap.get_ref(HashMap* map, Key key)
|
||||
{
|
||||
if (!map.count) return SearchResult.MISSING!;
|
||||
if (!map.count) return SearchResult.MISSING?;
|
||||
uint hash = rehash(key.hash());
|
||||
for (Entry *e = map.table[index_for(hash, map.table.len)]; e != null; e = e.next)
|
||||
{
|
||||
if (e.hash == hash && equals(key, e.key)) return &e.value;
|
||||
}
|
||||
return SearchResult.MISSING!;
|
||||
return SearchResult.MISSING?;
|
||||
}
|
||||
|
||||
fn Entry*! HashMap.get_entry(HashMap* map, Key key)
|
||||
{
|
||||
if (!map.count) return SearchResult.MISSING!;
|
||||
if (!map.count) return SearchResult.MISSING?;
|
||||
uint hash = rehash(key.hash());
|
||||
for (Entry *e = map.table[index_for(hash, map.table.len)]; e != null; e = e.next)
|
||||
{
|
||||
if (e.hash == hash && equals(key, e.key)) return e;
|
||||
}
|
||||
return SearchResult.MISSING!;
|
||||
return SearchResult.MISSING?;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,7 +112,7 @@ fn Value! HashMap.get(HashMap* map, Key key) @operator([])
|
||||
|
||||
fn bool HashMap.has_key(HashMap* map, Key key)
|
||||
{
|
||||
return try? map.get_ref(key);
|
||||
return @ok(map.get_ref(key));
|
||||
}
|
||||
|
||||
fn bool HashMap.set(HashMap* map, Key key, Value value) @operator([]=)
|
||||
@@ -138,7 +138,7 @@ fn bool HashMap.set(HashMap* map, Key key, Value value) @operator([]=)
|
||||
|
||||
fn void! HashMap.remove(HashMap* map, Key key) @maydiscard
|
||||
{
|
||||
if (!map.remove_entry_for_key(key)) return SearchResult.MISSING!;
|
||||
if (!map.remove_entry_for_key(key)) return SearchResult.MISSING?;
|
||||
}
|
||||
|
||||
fn void HashMap.clear(HashMap* map)
|
||||
|
||||
@@ -36,46 +36,46 @@ fn void! Object.to_format(Object* o, Formatter* formatter)
|
||||
switch (o.type)
|
||||
{
|
||||
case void:
|
||||
formatter.printf("{}")?;
|
||||
formatter.printf("{}")!;
|
||||
case void*:
|
||||
formatter.printf("null")?;
|
||||
formatter.printf("null")!;
|
||||
case String:
|
||||
formatter.printf(`"%s"`, o.s)?;
|
||||
formatter.printf(`"%s"`, o.s)!;
|
||||
case bool:
|
||||
formatter.printf(o.b ? "true" : "false")?;
|
||||
formatter.printf(o.b ? "true" : "false")!;
|
||||
case ObjectInternalList:
|
||||
formatter.printf("[")?;
|
||||
formatter.printf("[")!;
|
||||
foreach (i, ol : o.array)
|
||||
{
|
||||
formatter.printf(i == 0 ? " " : ", ")?;
|
||||
ol.to_format(formatter)?;
|
||||
formatter.printf(i == 0 ? " " : ", ")!;
|
||||
ol.to_format(formatter)!;
|
||||
}
|
||||
formatter.printf(" ]")?;
|
||||
formatter.printf(" ]")!;
|
||||
case ObjectInternalMap:
|
||||
formatter.printf("{")?;
|
||||
formatter.printf("{")!;
|
||||
@pool()
|
||||
{
|
||||
foreach (i, key : o.map.key_tlist())
|
||||
{
|
||||
formatter.printf(i == 0 ? " " : ", ")?;
|
||||
formatter.printf(`"%s": `, key)?;
|
||||
o.map.get(key).to_format(formatter)?;
|
||||
formatter.printf(i == 0 ? " " : ", ")!;
|
||||
formatter.printf(`"%s": `, key)!;
|
||||
o.map.get(key).to_format(formatter)!;
|
||||
}
|
||||
};
|
||||
formatter.printf(" }")?;
|
||||
formatter.printf(" }")!;
|
||||
default:
|
||||
switch (o.type.kindof)
|
||||
{
|
||||
case SIGNED_INT:
|
||||
formatter.printf("%d", o.i)?;
|
||||
formatter.printf("%d", o.i)!;
|
||||
case UNSIGNED_INT:
|
||||
formatter.printf("%d", (uint128)o.i)?;
|
||||
formatter.printf("%d", (uint128)o.i)!;
|
||||
case FLOAT:
|
||||
formatter.printf("%d", o.f)?;
|
||||
formatter.printf("%d", o.f)!;
|
||||
case ENUM:
|
||||
formatter.printf("%d", o.i)?;
|
||||
formatter.printf("%d", o.i)!;
|
||||
default:
|
||||
formatter.printf("<>")?;
|
||||
formatter.printf("<>")!;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -266,7 +266,7 @@ macro Object* Object.append(Object* o, value)
|
||||
/**
|
||||
* @require o.is_keyable()
|
||||
**/
|
||||
fn Object*! Object.get(Object* o, String key) => o.is_empty() ? SearchResult.MISSING! : o.map.get(key);
|
||||
fn Object*! Object.get(Object* o, String key) => o.is_empty() ? SearchResult.MISSING? : o.map.get(key);
|
||||
|
||||
|
||||
fn bool Object.has_key(Object* o, String key) => o.is_map() && o.map.has_key(key);
|
||||
@@ -316,12 +316,12 @@ macro get_integer_value(Object* value, $Type)
|
||||
if (value.is_string())
|
||||
{
|
||||
$if ($Type.kindof == TypeKind.SIGNED_INT)
|
||||
return ($Type)str::to_int128(value.s);
|
||||
return ($Type)value.s.to_int128();
|
||||
$else
|
||||
return ($Type)str::to_uint128(value.s);
|
||||
return ($Type)value.s.to_uint128();
|
||||
$endif
|
||||
}
|
||||
if (!value.is_int()) return NumberConversion.MALFORMED_INTEGER!;
|
||||
if (!value.is_int()) return NumberConversion.MALFORMED_INTEGER?;
|
||||
return ($Type)value.i;
|
||||
}
|
||||
|
||||
@@ -370,7 +370,7 @@ fn uint128! Object.get_uint128_at(Object* o, usz index) => o.get_integer_at(uint
|
||||
**/
|
||||
fn String! Object.get_string(Object* o, String key)
|
||||
{
|
||||
Object* value = o.get(key)?;
|
||||
Object* value = o.get(key)!;
|
||||
assert(value.is_string());
|
||||
return value.s;
|
||||
}
|
||||
@@ -390,7 +390,7 @@ fn String Object.get_string_at(Object* o, usz index)
|
||||
**/
|
||||
macro String! Object.get_enum(Object* o, $EnumType, String key)
|
||||
{
|
||||
Object value = o.get(key)?;
|
||||
Object value = o.get(key)!;
|
||||
assert($EnumType.typeid == value.type);
|
||||
return ($EnumType)value.i;
|
||||
}
|
||||
@@ -410,7 +410,7 @@ macro String Object.get_enum_at(Object* o, $EnumType, usz index)
|
||||
**/
|
||||
fn bool! Object.get_bool(Object* o, String key)
|
||||
{
|
||||
Object* value = o.get(key)?;
|
||||
Object* value = o.get(key)!;
|
||||
assert(value.is_bool());
|
||||
return value.b;
|
||||
}
|
||||
@@ -431,7 +431,7 @@ fn bool Object.get_bool_at(Object* o, usz index)
|
||||
**/
|
||||
fn double! Object.get_float(Object* o, String key)
|
||||
{
|
||||
Object* value = o.get(key)?;
|
||||
Object* value = o.get(key)!;
|
||||
switch (value.type.kindof)
|
||||
{
|
||||
case SIGNED_INT:
|
||||
|
||||
@@ -55,7 +55,7 @@ fn Type! PriorityQueue.pop(PriorityQueue* pq)
|
||||
{
|
||||
usz i = 0;
|
||||
usz len = pq.heap.len() @inline;
|
||||
if (!len) return IteratorResult.NO_MORE_ELEMENT!;
|
||||
if (!len) return IteratorResult.NO_MORE_ELEMENT?;
|
||||
usz newCount = len - 1;
|
||||
pq.heap.swap(0, newCount);
|
||||
while ((2 * i + 1) < newCount)
|
||||
@@ -84,7 +84,7 @@ fn Type! PriorityQueue.pop(PriorityQueue* pq)
|
||||
*/
|
||||
fn Type! PriorityQueue.peek(PriorityQueue* pq)
|
||||
{
|
||||
if (!pq.len()) return IteratorResult.NO_MORE_ELEMENT!;
|
||||
if (!pq.len()) return IteratorResult.NO_MORE_ELEMENT?;
|
||||
return pq.heap.get(0);
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ fn void*! arena_allocator_function(Allocator* data, usz size, usz alignment, usz
|
||||
assert(!old_pointer, "Unexpected old pointer for alloc.");
|
||||
if (!size) return null;
|
||||
alignment = alignment_for_allocation(alignment);
|
||||
void* mem = arena._alloc(size, alignment, offset)?;
|
||||
void* mem = arena._alloc(size, alignment, offset)!;
|
||||
if (clear) mem::clear(mem, size, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
return mem;
|
||||
case ALIGNED_REALLOC:
|
||||
@@ -62,7 +62,7 @@ fn void*! arena_allocator_function(Allocator* data, usz size, usz alignment, usz
|
||||
if (!size) nextcase FREE;
|
||||
if (!old_pointer) nextcase ALLOC;
|
||||
alignment = alignment_for_allocation(alignment);
|
||||
return arena._realloc(old_pointer, size, alignment, offset)?;
|
||||
return arena._realloc(old_pointer, size, alignment, offset)!;
|
||||
case ALIGNED_FREE:
|
||||
case FREE:
|
||||
if (!old_pointer) return null;
|
||||
@@ -96,12 +96,12 @@ fn void*! arena_allocator_function(Allocator* data, usz size, usz alignment, usz
|
||||
fn void*! ArenaAllocator._alloc(ArenaAllocator* this, usz size, usz alignment, usz offset) @private
|
||||
{
|
||||
usz total_len = this.data.len;
|
||||
if (size > total_len) return AllocationFailure.CHUNK_TOO_LARGE!;
|
||||
if (size > total_len) return AllocationFailure.CHUNK_TOO_LARGE?;
|
||||
void* start_mem = this.data.ptr;
|
||||
void* unaligned_pointer_to_offset = start_mem + this.used + ArenaAllocatorHeader.sizeof + offset;
|
||||
void* aligned_pointer_to_offset = mem::aligned_pointer(unaligned_pointer_to_offset, alignment);
|
||||
usz end = (usz)(aligned_pointer_to_offset - this.data.ptr) + size - offset;
|
||||
if (end > total_len) return AllocationFailure.OUT_OF_MEMORY!;
|
||||
if (end > total_len) return AllocationFailure.OUT_OF_MEMORY?;
|
||||
this.used = end;
|
||||
void *mem = aligned_pointer_to_offset - offset;
|
||||
ArenaAllocatorHeader* header = mem - ArenaAllocatorHeader.sizeof;
|
||||
@@ -123,7 +123,7 @@ fn void*! ArenaAllocator._realloc(ArenaAllocator* this, void *old_pointer, usz s
|
||||
{
|
||||
assert(old_pointer >= this.data.ptr, "Pointer originates from a different allocator.");
|
||||
usz total_len = this.data.len;
|
||||
if (size > total_len) return AllocationFailure.CHUNK_TOO_LARGE!;
|
||||
if (size > total_len) return AllocationFailure.CHUNK_TOO_LARGE?;
|
||||
ArenaAllocatorHeader* header = old_pointer - ArenaAllocatorHeader.sizeof;
|
||||
usz old_size = header.size;
|
||||
// Do last allocation and alignment match?
|
||||
@@ -136,14 +136,14 @@ fn void*! ArenaAllocator._realloc(ArenaAllocator* this, void *old_pointer, usz s
|
||||
else
|
||||
{
|
||||
usz new_used = this.used + size - old_size;
|
||||
if (new_used > total_len) return AllocationFailure.OUT_OF_MEMORY!;
|
||||
if (new_used > total_len) return AllocationFailure.OUT_OF_MEMORY?;
|
||||
this.used = new_used;
|
||||
}
|
||||
header.size = size;
|
||||
return old_pointer;
|
||||
}
|
||||
// Otherwise just allocate new memory.
|
||||
void* mem = this._alloc(size, alignment, offset)?;
|
||||
void* mem = this._alloc(size, alignment, offset)!;
|
||||
mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
return mem;
|
||||
}
|
||||
@@ -106,7 +106,7 @@ fn void*! DynamicArenaAllocator._realloc(DynamicArenaAllocator* this, void* old_
|
||||
current_page.used += add_size;
|
||||
return old_pointer;
|
||||
}
|
||||
void* new_mem = this._alloc(size, alignment, offset)?;
|
||||
void* new_mem = this._alloc(size, alignment, offset)!;
|
||||
mem::copy(new_mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
return new_mem;
|
||||
}
|
||||
@@ -137,12 +137,12 @@ fn void*! DynamicArenaAllocator._alloc_new(DynamicArenaAllocator* this, usz size
|
||||
usz page_size = max(this.page_size, mem::aligned_offset(size + DynamicArenaChunk.sizeof + offset, alignment) - offset);
|
||||
|
||||
// Grab the page without alignment (we do it ourselves)
|
||||
void* mem = this.backing_allocator.alloc(page_size)?;
|
||||
void* mem = this.backing_allocator.alloc(page_size)!;
|
||||
DynamicArenaPage*! page = malloc(DynamicArenaPage, .using = this.backing_allocator);
|
||||
if (catch err = page)
|
||||
{
|
||||
free(mem, .using = this.backing_allocator);
|
||||
return err!;
|
||||
return err?;
|
||||
}
|
||||
page.memory = mem;
|
||||
void* mem_start = mem::aligned_pointer(mem + offset + DynamicArenaChunk.sizeof, alignment) - offset;
|
||||
@@ -213,7 +213,7 @@ fn void*! dynamic_arena_allocator_function(Allocator* data, usz size, usz alignm
|
||||
case ALIGNED_CALLOC:
|
||||
assert(!old_pointer, "Unexpected no old pointer for calloc.");
|
||||
if (!size) return null;
|
||||
void* mem = allocator._alloc(size, alignment, offset)?;
|
||||
void* mem = allocator._alloc(size, alignment, offset)!;
|
||||
mem::clear(mem, size, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
return mem;
|
||||
case ALLOC:
|
||||
@@ -230,7 +230,7 @@ fn void*! dynamic_arena_allocator_function(Allocator* data, usz size, usz alignm
|
||||
return null;
|
||||
}
|
||||
if (!old_pointer) return allocator._alloc(size, alignment, offset);
|
||||
void* mem = allocator._realloc(old_pointer, size, alignment, offset)?;
|
||||
void* mem = allocator._realloc(old_pointer, size, alignment, offset)!;
|
||||
return mem;
|
||||
case ALIGNED_FREE:
|
||||
case FREE:
|
||||
|
||||
@@ -51,9 +51,9 @@ fn void*! simple_heap_allocator_function(Allocator* this, usz size, usz alignmen
|
||||
if (!old_pointer) nextcase CALLOC;
|
||||
return heap._realloc(old_pointer, size);
|
||||
case RESET:
|
||||
return AllocationFailure.UNSUPPORTED_OPERATION!;
|
||||
return AllocationFailure.UNSUPPORTED_OPERATION?;
|
||||
case ALIGNED_FREE:
|
||||
@aligned_free(heap._free, old_pointer)?;
|
||||
@aligned_free(heap._free, old_pointer)!;
|
||||
return null;
|
||||
case FREE:
|
||||
heap._free(old_pointer);
|
||||
@@ -71,7 +71,7 @@ fn void*! SimpleHeapAllocator._realloc(SimpleHeapAllocator* this, void* old_poin
|
||||
// Find the block header.
|
||||
Header* block = (Header*)old_pointer - 1;
|
||||
if (block.size >= bytes) return old_pointer;
|
||||
void* new = this._alloc(bytes)?;
|
||||
void* new = this._alloc(bytes)!;
|
||||
usz max_to_copy = math::min(block.size, bytes);
|
||||
mem::copy(new, old_pointer, max_to_copy);
|
||||
this._free(old_pointer);
|
||||
@@ -80,7 +80,7 @@ fn void*! SimpleHeapAllocator._realloc(SimpleHeapAllocator* this, void* old_poin
|
||||
|
||||
fn void*! SimpleHeapAllocator._calloc(SimpleHeapAllocator* this, usz bytes) @local
|
||||
{
|
||||
void* data = this._alloc(bytes)?;
|
||||
void* data = this._alloc(bytes)!;
|
||||
mem::clear(data, bytes, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
return data;
|
||||
}
|
||||
@@ -90,7 +90,7 @@ fn void*! SimpleHeapAllocator._alloc(SimpleHeapAllocator* this, usz bytes) @loca
|
||||
usz aligned_bytes = mem::aligned_offset(bytes, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
if (!this.free_list)
|
||||
{
|
||||
this.add_block(aligned_bytes)?;
|
||||
this.add_block(aligned_bytes)!;
|
||||
}
|
||||
|
||||
Header* current = this.free_list;
|
||||
@@ -130,14 +130,14 @@ fn void*! SimpleHeapAllocator._alloc(SimpleHeapAllocator* this, usz bytes) @loca
|
||||
current = current.next;
|
||||
}
|
||||
}
|
||||
this.add_block(aligned_bytes)?;
|
||||
this.add_block(aligned_bytes)!;
|
||||
return this.alloc(aligned_bytes);
|
||||
}
|
||||
|
||||
fn void! SimpleHeapAllocator.add_block(SimpleHeapAllocator* this, usz aligned_bytes) @local
|
||||
{
|
||||
assert(mem::aligned_offset(aligned_bytes, mem::DEFAULT_MEM_ALIGNMENT) == aligned_bytes);
|
||||
char[] result = this.alloc_fn(aligned_bytes + Header.sizeof)?;
|
||||
char[] result = this.alloc_fn(aligned_bytes + Header.sizeof)!;
|
||||
Header* new_block = (Header*)result.ptr;
|
||||
new_block.size = result.len - Header.sizeof;
|
||||
new_block.next = null;
|
||||
|
||||
@@ -18,7 +18,7 @@ fn void*! null_allocator_fn(Allocator* this, usz bytes, usz alignment, usz offse
|
||||
case ALIGNED_ALLOC:
|
||||
case ALIGNED_REALLOC:
|
||||
case ALIGNED_CALLOC:
|
||||
return AllocationFailure.OUT_OF_MEMORY!;
|
||||
return AllocationFailure.OUT_OF_MEMORY?;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@@ -37,8 +37,8 @@ struct AlignedBlock
|
||||
macro void*! @aligned_alloc(#alloc_fn, usz bytes, usz alignment, usz offset)
|
||||
{
|
||||
usz header = mem::aligned_offset(AlignedBlock.sizeof + offset, alignment) - offset;
|
||||
$if ($checks(#alloc_fn(bytes)?))
|
||||
void* data = #alloc_fn(header + bytes)?;
|
||||
$if ($checks(#alloc_fn(bytes)!))
|
||||
void* data = #alloc_fn(header + bytes)!;
|
||||
$else
|
||||
void* data = #alloc_fn(header + bytes);
|
||||
$endif
|
||||
@@ -56,8 +56,8 @@ macro void*! @aligned_alloc(#alloc_fn, usz bytes, usz alignment, usz offset)
|
||||
macro void*! @aligned_calloc(#calloc_fn, usz bytes, usz alignment, usz offset)
|
||||
{
|
||||
usz header = mem::aligned_offset(AlignedBlock.sizeof + offset, alignment) - offset;
|
||||
$if ($checks(#calloc_fn(bytes)?))
|
||||
void* data = #calloc_fn(header + bytes)?;
|
||||
$if ($checks(#calloc_fn(bytes)!))
|
||||
void* data = #calloc_fn(header + bytes)!;
|
||||
$else
|
||||
void* data = #calloc_fn(header + bytes);
|
||||
$endif
|
||||
@@ -76,10 +76,10 @@ macro void*! @aligned_realloc(#calloc_fn, #free_fn, void* old_pointer, usz bytes
|
||||
{
|
||||
AlignedBlock* desc = (AlignedBlock*)old_pointer - 1;
|
||||
void* data_start = desc.start;
|
||||
void* new_data = @aligned_calloc(#calloc_fn, bytes, alignment, offset)?;
|
||||
void* new_data = @aligned_calloc(#calloc_fn, bytes, alignment, offset)!;
|
||||
mem::copy(new_data, old_pointer, desc.len > bytes ? desc.len : bytes, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
$if ($checks(#free_fn(data_start)?))
|
||||
#free_fn(data_start)?;
|
||||
$if ($checks(#free_fn(data_start)!))
|
||||
#free_fn(data_start)!;
|
||||
$else
|
||||
#free_fn(data_start);
|
||||
$endif
|
||||
@@ -89,8 +89,8 @@ macro void*! @aligned_realloc(#calloc_fn, #free_fn, void* old_pointer, usz bytes
|
||||
macro void! @aligned_free(#free_fn, void* old_pointer)
|
||||
{
|
||||
AlignedBlock* desc = (AlignedBlock*)old_pointer - 1;
|
||||
$if ($checks(#free_fn(desc.start)?))
|
||||
#free_fn(desc.start)?;
|
||||
$if ($checks(#free_fn(desc.start)!))
|
||||
#free_fn(desc.start)!;
|
||||
$else
|
||||
#free_fn(desc.start);
|
||||
$endif
|
||||
@@ -121,7 +121,7 @@ fn void*! libc_allocator_fn(Allocator* unused, usz bytes, usz alignment, usz off
|
||||
if (!old_pointer) nextcase CALLOC;
|
||||
data = libc::realloc(old_pointer, bytes);
|
||||
case RESET:
|
||||
return AllocationFailure.UNSUPPORTED_OPERATION!;
|
||||
return AllocationFailure.UNSUPPORTED_OPERATION?;
|
||||
case ALIGNED_FREE:
|
||||
@aligned_free(libc::free, old_pointer)!!;
|
||||
return null;
|
||||
@@ -131,6 +131,6 @@ fn void*! libc_allocator_fn(Allocator* unused, usz bytes, usz alignment, usz off
|
||||
default:
|
||||
unreachable();
|
||||
}
|
||||
if (!data) return AllocationFailure.OUT_OF_MEMORY!;
|
||||
if (!data) return AllocationFailure.OUT_OF_MEMORY?;
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -111,16 +111,16 @@ fn void*! on_stack_allocator_function(Allocator* data, usz size, usz alignment,
|
||||
on_stack_allocator_remove_chunk(allocator, old_pointer);
|
||||
if (kind == AllocationKind.ALIGNED_FREE)
|
||||
{
|
||||
allocator.backing_allocator.free_aligned(old_pointer)?;
|
||||
allocator.backing_allocator.free_aligned(old_pointer)!;
|
||||
}
|
||||
else
|
||||
{
|
||||
allocator.backing_allocator.free(old_pointer)?;
|
||||
allocator.backing_allocator.free(old_pointer)!;
|
||||
}
|
||||
return null;
|
||||
case MARK:
|
||||
case RESET:
|
||||
return AllocationFailure.UNSUPPORTED_OPERATION!;
|
||||
return AllocationFailure.UNSUPPORTED_OPERATION?;
|
||||
}
|
||||
unreachable();
|
||||
}
|
||||
@@ -175,14 +175,14 @@ fn void*! on_stack_allocator_realloc(OnStackAllocator* a, void* old_pointer, usz
|
||||
assert(chunk, "Tried to realloc pointer not belonging to the allocator");
|
||||
if (aligned)
|
||||
{
|
||||
return chunk.data = a.backing_allocator.realloc_aligned(old_pointer, size, alignment, offset)?;
|
||||
return chunk.data = a.backing_allocator.realloc_aligned(old_pointer, size, alignment, offset)!;
|
||||
}
|
||||
return chunk.data = a.backing_allocator.realloc(old_pointer, size)?;
|
||||
return chunk.data = a.backing_allocator.realloc(old_pointer, size)!;
|
||||
}
|
||||
|
||||
OnStackAllocatorHeader* header = old_pointer - OnStackAllocatorHeader.sizeof;
|
||||
usz old_size = header.size;
|
||||
void* mem = on_stack_allocator_alloc(a, size, alignment, offset, true, aligned)?;
|
||||
void* mem = on_stack_allocator_alloc(a, size, alignment, offset, true, aligned)!;
|
||||
mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
return mem;
|
||||
}
|
||||
@@ -209,7 +209,7 @@ fn void*! on_stack_allocator_alloc(OnStackAllocator* a, usz size, usz alignment,
|
||||
|
||||
if (end > total_len)
|
||||
{
|
||||
OnStackAllocatorExtraChunk* chunk = backing_allocator.alloc(OnStackAllocatorExtraChunk.sizeof)?;
|
||||
OnStackAllocatorExtraChunk* chunk = backing_allocator.alloc(OnStackAllocatorExtraChunk.sizeof)!;
|
||||
defer catch backing_allocator.free(chunk)!!;
|
||||
defer try a.chunk = chunk;
|
||||
*chunk = { .prev = a.chunk, .is_aligned = aligned };
|
||||
@@ -217,13 +217,13 @@ fn void*! on_stack_allocator_alloc(OnStackAllocator* a, usz size, usz alignment,
|
||||
switch
|
||||
{
|
||||
case !aligned && !clear:
|
||||
data = backing_allocator.alloc(size)?;
|
||||
data = backing_allocator.alloc(size)!;
|
||||
case aligned && !clear:
|
||||
data = backing_allocator.alloc_aligned(size, alignment, offset)?;
|
||||
data = backing_allocator.alloc_aligned(size, alignment, offset)!;
|
||||
case !aligned && clear:
|
||||
data = backing_allocator.calloc(size)?;
|
||||
data = backing_allocator.calloc(size)!;
|
||||
case aligned && clear:
|
||||
data = backing_allocator.calloc_aligned(size, alignment, offset)?;
|
||||
data = backing_allocator.calloc_aligned(size, alignment, offset)!;
|
||||
}
|
||||
return chunk.data = data;
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ macro bool TempAllocatorPage.is_aligned(TempAllocatorPage* page) => page.size &
|
||||
**/
|
||||
fn TempAllocator*! new_temp(usz size, Allocator* backing_allocator)
|
||||
{
|
||||
TempAllocator* allocator = malloc_checked(TempAllocator, .using = backing_allocator, .end_padding = size)?;
|
||||
TempAllocator* allocator = malloc_checked(TempAllocator, .using = backing_allocator, .end_padding = size)!;
|
||||
allocator.last_page = null;
|
||||
allocator.function = &temp_allocator_function;
|
||||
allocator.backing_allocator = backing_allocator;
|
||||
@@ -75,12 +75,12 @@ fn void*! temp_allocator_function(Allocator* data, usz size, usz alignment, usz
|
||||
case FREE:
|
||||
case ALIGNED_FREE:
|
||||
if (!old_pointer) return null;
|
||||
arena._free(old_pointer)?;
|
||||
arena._free(old_pointer)!;
|
||||
return null;
|
||||
case MARK:
|
||||
return (void*)(uptr)arena.used;
|
||||
case RESET:
|
||||
arena._reset(size)?;
|
||||
arena._reset(size)!;
|
||||
return null;
|
||||
}
|
||||
unreachable();
|
||||
@@ -103,7 +103,7 @@ fn void! TempAllocator._reset(TempAllocator* this, usz mark) @local
|
||||
{
|
||||
TempAllocatorPage *to_free = last_page;
|
||||
last_page = last_page.prev_page;
|
||||
this._free_page(to_free)?;
|
||||
this._free_page(to_free)!;
|
||||
}
|
||||
this.last_page = last_page;
|
||||
this.used = mark;
|
||||
@@ -131,15 +131,15 @@ fn void*! TempAllocator._realloc_page(TempAllocator* this, TempAllocatorPage* pa
|
||||
*pointer_to_prev = page.prev_page;
|
||||
usz page_size = page.pagesize();
|
||||
// Clear on size > original size.
|
||||
void* data = this._alloc(size, alignment, offset, false)?;
|
||||
void* data = this._alloc(size, alignment, offset, false)!;
|
||||
mem::copy(data, &page.data[0], page_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
if (page.is_aligned())
|
||||
{
|
||||
this.backing_allocator.free_aligned(real_pointer)?;
|
||||
this.backing_allocator.free_aligned(real_pointer)!;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.backing_allocator.free(real_pointer)?;
|
||||
this.backing_allocator.free(real_pointer)!;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
@@ -156,7 +156,7 @@ fn void*! TempAllocator._realloc(TempAllocator* this, void* pointer, usz size, u
|
||||
}
|
||||
|
||||
// TODO optimize last allocation
|
||||
TempAllocatorChunk* data = this._alloc(size, alignment, offset, size > chunk.size)?;
|
||||
TempAllocatorChunk* data = this._alloc(size, alignment, offset, size > chunk.size)!;
|
||||
mem::copy(data, pointer, chunk.size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
|
||||
return data;
|
||||
@@ -200,11 +200,11 @@ fn void*! TempAllocator._alloc(TempAllocator* this, usz size, usz alignment, usz
|
||||
usz total_alloc_size = TempAllocatorPage.sizeof + size;
|
||||
if (clear)
|
||||
{
|
||||
page = this.backing_allocator.calloc_aligned(total_alloc_size, alignment, TempAllocatorPage.sizeof + offset)?;
|
||||
page = this.backing_allocator.calloc_aligned(total_alloc_size, alignment, TempAllocatorPage.sizeof + offset)!;
|
||||
}
|
||||
else
|
||||
{
|
||||
page = this.backing_allocator.alloc_aligned(total_alloc_size, alignment, TempAllocatorPage.sizeof + offset)?;
|
||||
page = this.backing_allocator.alloc_aligned(total_alloc_size, alignment, TempAllocatorPage.sizeof + offset)!;
|
||||
}
|
||||
page.start = page;
|
||||
page.size = size | PAGE_IS_ALIGNED;
|
||||
@@ -214,7 +214,7 @@ fn void*! TempAllocator._alloc(TempAllocator* this, usz size, usz alignment, usz
|
||||
// Here we might need to pad
|
||||
usz padded_header_size = mem::aligned_offset(TempAllocatorPage.sizeof, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
usz total_alloc_size = padded_header_size + size;
|
||||
void* alloc = (clear ? this.backing_allocator.calloc(total_alloc_size) : this.backing_allocator.alloc(total_alloc_size))?;
|
||||
void* alloc = (clear ? this.backing_allocator.calloc(total_alloc_size) : this.backing_allocator.alloc(total_alloc_size))!;
|
||||
|
||||
// Find the page.
|
||||
page = alloc + padded_header_size - TempAllocatorPage.sizeof;
|
||||
|
||||
@@ -43,7 +43,7 @@ fn void TrackingAllocator.free(TrackingAllocator* this)
|
||||
fn void*! tracking_allocator_fn(Allocator* data, usz size, usz alignment, usz offset, void* old_pointer, AllocationKind kind) @private
|
||||
{
|
||||
TrackingAllocator* this = (TrackingAllocator*)data;
|
||||
void* result = this.inner_allocator.function(this.inner_allocator, size, alignment, offset, old_pointer, kind)?;
|
||||
void* result = this.inner_allocator.function(this.inner_allocator, size, alignment, offset, old_pointer, kind)!;
|
||||
switch (kind)
|
||||
{
|
||||
case CALLOC:
|
||||
|
||||
@@ -1,33 +1,34 @@
|
||||
module std::core::array;
|
||||
|
||||
macro tconcat(arr1, arr2)
|
||||
{
|
||||
var $Type = $typeof(arr1[0]);
|
||||
$Type[] result = array::talloc($Type, arr1.len + arr2.len);
|
||||
if (arr1.len > 0)
|
||||
{
|
||||
mem::copy(result.ptr, &arr1[0], arr1.len * $Type.sizeof, $Type.alignof, $Type.alignof);
|
||||
}
|
||||
if (arr2.len > 0)
|
||||
{
|
||||
mem::copy(&result[arr1.len], &arr2[0], arr2.len * $Type.sizeof, $Type.alignof, $Type.alignof);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [in] array
|
||||
* @param [in] element
|
||||
* @return "the index of the element"
|
||||
* @return! SearchResult.MISSING
|
||||
**/
|
||||
macro index_of(array, element)
|
||||
{
|
||||
foreach (i, &e : array)
|
||||
{
|
||||
if (*e == element) return i;
|
||||
}
|
||||
return SearchResult.MISSING!;
|
||||
return SearchResult.MISSING?;
|
||||
}
|
||||
|
||||
macro concat(arr1, arr2)
|
||||
/**
|
||||
* Concatenate two arrays or subarrays, returning a subarray containing the concatenation of them.
|
||||
*
|
||||
* @param [in] arr1
|
||||
* @param [in] arr2
|
||||
* @require @typekind(arr1) == SUBARRAY || @typekind(arr1) == ARRAY
|
||||
* @require @typekind(arr2) == SUBARRAY || @typekind(arr2) == ARRAY
|
||||
* @require @typeis(arr1[0], $typeof(arr2[0])) "Arrays must have the same type"
|
||||
* @ensure result.len == arr1.len + arr2.len
|
||||
**/
|
||||
macro concat(arr1, arr2, Allocator* using = mem::heap())
|
||||
{
|
||||
var $Type = $typeof(arr1[0]);
|
||||
$Type[] result = array::alloc($Type, arr1.len + arr2.len);
|
||||
$Type[] result = malloc($Type, arr1.len + arr2.len, .using = using);
|
||||
if (arr1.len > 0)
|
||||
{
|
||||
mem::copy(result.ptr, &arr1[0], arr1.len * $Type.sizeof, $Type.alignof, $Type.alignof);
|
||||
@@ -38,3 +39,5 @@ macro concat(arr1, arr2)
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
macro tconcat(arr1, arr2) => concat(arr1, arr2, mem::temp());
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
// Copyright (c) 2023 Christoffer Lerno and contributors. 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.
|
||||
module std::core::bitorder;
|
||||
|
||||
// This module contains types of different endianness.
|
||||
// *BE types represent big-endian types
|
||||
// *LE types represent little-endian types.
|
||||
|
||||
bitstruct ShortBE : short @bigendian
|
||||
{
|
||||
short val : 0..15;
|
||||
|
||||
@@ -5,17 +5,26 @@ module std::core::builtin;
|
||||
import libc;
|
||||
import std::hash;
|
||||
|
||||
/**
|
||||
* Use `IteratorResult` when reading the end of an iterator, or accessing a result out of bounds.
|
||||
**/
|
||||
fault IteratorResult
|
||||
{
|
||||
NO_MORE_ELEMENT
|
||||
}
|
||||
|
||||
/**
|
||||
* Use `SearchResult` when trying to return a value from some collection but the element is missing.
|
||||
**/
|
||||
fault SearchResult
|
||||
{
|
||||
MISSING
|
||||
}
|
||||
|
||||
fault VarCastResult
|
||||
/**
|
||||
* Use `CastResult` when an attempt at conversion fails.
|
||||
**/
|
||||
fault CastResult
|
||||
{
|
||||
TYPE_MISMATCH
|
||||
}
|
||||
@@ -33,6 +42,9 @@ macro void @scope(&variable; @body) @builtin
|
||||
@body();
|
||||
}
|
||||
|
||||
/**
|
||||
* Swap two variables
|
||||
**/
|
||||
macro void @swap(&a, &b) @builtin
|
||||
{
|
||||
var temp = a;
|
||||
@@ -41,15 +53,17 @@ macro void @swap(&a, &b) @builtin
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a variant type to a type, returning an failure if there is a type mismatch.
|
||||
* Convert an `any` type to a type, returning an failure if there is a type mismatch.
|
||||
*
|
||||
* @param v `the variant to convert to the given type.`
|
||||
* @param v `the any to convert to the given type.`
|
||||
* @param $Type `the type to convert to`
|
||||
* @return `The variant.ptr converted to its type.`
|
||||
* @return `The any.ptr converted to its type.`
|
||||
* @ensure @typeis(return, $Type*)
|
||||
* @return! CastResult.TYPE_MISMATCH
|
||||
**/
|
||||
macro varcast(variant v, $Type) @builtin
|
||||
macro anycast(any v, $Type) @builtin
|
||||
{
|
||||
if (v.type != $Type.typeid) return VarCastResult.TYPE_MISMATCH!;
|
||||
if (v.type != $Type.typeid) return CastResult.TYPE_MISMATCH?;
|
||||
return ($Type*)v.ptr;
|
||||
}
|
||||
|
||||
@@ -64,22 +78,24 @@ struct CallstackElement
|
||||
fn void default_panic(String message, String file, String function, uint line)
|
||||
{
|
||||
CallstackElement* stack = $$stacktrace();
|
||||
$if ($defined(libc::stderr) && $defined(libc::fprintf))
|
||||
$if ($defined(io::stderr) && $defined(File.printf))
|
||||
|
||||
if (stack) stack = stack.prev;
|
||||
if (stack)
|
||||
{
|
||||
libc::fprintf(libc::stderr(), "\nERROR: '%.*s'\n", (int)message.len, message.ptr);
|
||||
(void)io::stderr().print("\nERROR: '");
|
||||
(void)io::stderr().print(message);
|
||||
(void)io::stderr().printn("'");
|
||||
}
|
||||
else
|
||||
{
|
||||
libc::fprintf(libc::stderr(), "\nERROR: '%.*s', function %.*s (%.*s:%d)\n",
|
||||
(int)message.len, message.ptr, (int)function.len, function.ptr, (int)file.len, file.ptr, line);
|
||||
(void)io::stderr().print("\nERROR: '");
|
||||
(void)io::stderr().print(message);
|
||||
(void)io::stderr().printfn("', in function %s (%s:%d)", function, file, line);
|
||||
}
|
||||
while (stack)
|
||||
{
|
||||
libc::fprintf(libc::stderr(), " at function %.*s (%.*s:%u)\n", (int)stack.function.len, stack.function.ptr,
|
||||
(int)stack.file.len, stack.file.ptr, stack.line);
|
||||
(void)io::stderr().printfn(" in function %s (%s:%d)", stack.function, stack.file, stack.line);
|
||||
if (stack == stack.prev) break;
|
||||
stack = stack.prev;
|
||||
}
|
||||
@@ -92,62 +108,103 @@ typedef PanicFn = fn void(String message, String file, String function, uint lin
|
||||
|
||||
PanicFn panic = &default_panic;
|
||||
|
||||
macro void unreachable($string = "Unreachable statement reached.") @builtin @noreturn
|
||||
fn void panicf(String fmt, String file, String function, uint line, args...)
|
||||
{
|
||||
panic($string, $$FILE, $$FUNC, $$LINE);
|
||||
@stack_mem(512; Allocator* mem)
|
||||
{
|
||||
DString s;
|
||||
s.init(.using = mem);
|
||||
s.printf(fmt, ...args);
|
||||
panic(s.str(), file, function, line);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the path as unreachable. This will panic in safe mode, and in fast will simply be assumed
|
||||
* never happens.
|
||||
* @param [in] string "The panic message"
|
||||
**/
|
||||
macro void unreachable(String string = "Unreachable statement reached.", ...) @builtin @noreturn
|
||||
{
|
||||
panicf(string, $$FILE, $$FUNC, $$LINE, $vasplat());
|
||||
$$unreachable();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param expr "the expression to cast"
|
||||
* @param $Type "the type to cast to"
|
||||
*
|
||||
* @require $sizeof(expr) == $Type.sizeof "Cannot bitcast between types of different size."
|
||||
* @ensure @typeis(result, $Type)
|
||||
**/
|
||||
macro bitcast(expr, $Type) @builtin
|
||||
{
|
||||
var $size = (usz)($sizeof(expr));
|
||||
$assert($size == $Type.sizeof, "Cannot bitcast between types of different size.");
|
||||
usz $size = $sizeof(expr);
|
||||
$Type x @noinit;
|
||||
mem::copy(&x, &expr, $size, $Type.alignof, $alignof(expr));
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require $Type.kindof == TypeKind.ENUM `Only enums may be used`
|
||||
* @param $Type `The type of the enum`
|
||||
* @param [in] enum_name `The name of the enum to search for`
|
||||
* @require $Type.kindof == ENUM `Only enums may be used`
|
||||
* @ensure @typeis(return, $Type)
|
||||
* @return! SearchResult.MISSING
|
||||
**/
|
||||
macro enum_by_name($Type, String enum_name) @builtin
|
||||
{
|
||||
typeid x = $Type.typeid;
|
||||
foreach (i, name : x.names)
|
||||
{
|
||||
if (str::compare(name, enum_name)) return ($Type)i;
|
||||
if (name == enum_name) return ($Type)i;
|
||||
}
|
||||
return SearchResult.MISSING!;
|
||||
return SearchResult.MISSING?;
|
||||
}
|
||||
|
||||
macro bool @likely(bool value, $probability = 1.0) @builtin
|
||||
/**
|
||||
* Mark an expression as likely to be true
|
||||
*
|
||||
* @param #value "expression to be marked likely"
|
||||
* @param $probability "in the range 0 - 1"
|
||||
* @require $probability >= 0 && $probability <= 1.0
|
||||
**/
|
||||
macro bool @likely(bool #value, $probability = 1.0) @builtin
|
||||
{
|
||||
$if ($probability == 1.0)
|
||||
return $$expect(value, true);
|
||||
return $$expect(#value, true);
|
||||
$else
|
||||
return $$expect_with_probability(value, true, $probability);
|
||||
$endif
|
||||
}
|
||||
|
||||
macro bool @unlikely(bool value, $probability = 1.0) @builtin
|
||||
{
|
||||
$if ($probability == 1.0)
|
||||
return $$expect(value, false);
|
||||
$else
|
||||
return $$expect_with_probability(value, false, $probability);
|
||||
return $$expect_with_probability(#value, true, $probability);
|
||||
$endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @require values::@is_int(value) || values::@is_bool(value)
|
||||
* @checked $typeof(value) a = expected
|
||||
* Mark an expression as unlikely to be true
|
||||
*
|
||||
* @param #value "expression to be marked unlikely"
|
||||
* @param $probability "in the range 0 - 1"
|
||||
* @require $probability >= 0 && $probability <= 1.0
|
||||
**/
|
||||
macro @expect(value, expected, $probability = 1.0) @builtin
|
||||
macro bool @unlikely(bool #value, $probability = 1.0) @builtin
|
||||
{
|
||||
$if ($probability == 1.0)
|
||||
return $$expect(value, ($typeof(value))expected);
|
||||
return $$expect(#value, false);
|
||||
$else
|
||||
return $$expect_with_probability(value, expected, $probability);
|
||||
return $$expect_with_probability(#value, false, $probability);
|
||||
$endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @require values::@is_int(#value) || values::@is_bool(#value)
|
||||
* @checked $typeof(#value) a = expected
|
||||
* @require $probability >= 0 && $probability <= 1.0
|
||||
**/
|
||||
macro @expect(#value, expected, $probability = 1.0) @builtin
|
||||
{
|
||||
$if ($probability == 1.0)
|
||||
return $$expect(#value, ($typeof(#value))expected);
|
||||
$else
|
||||
return $$expect_with_probability(#value, expected, $probability);
|
||||
$endif
|
||||
}
|
||||
|
||||
@@ -195,6 +252,18 @@ macro bool @convertible(#expr, $To) @builtin
|
||||
return $checks($To x = #expr);
|
||||
}
|
||||
|
||||
macro anyfault @catchof(#expr) @builtin
|
||||
{
|
||||
if (catch f = #expr) return f;
|
||||
return anyfault {};
|
||||
}
|
||||
|
||||
macro bool @ok(#expr) @builtin
|
||||
{
|
||||
if (catch #expr) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
macro uint int.hash(int i) => i;
|
||||
macro uint uint.hash(uint i) => i;
|
||||
macro uint short.hash(short s) => s;
|
||||
@@ -203,7 +272,9 @@ macro uint char.hash(char c) => c;
|
||||
macro uint ichar.hash(ichar c) => c;
|
||||
macro uint long.hash(long i) => (uint)((i >> 32) ^ i);
|
||||
macro uint ulong.hash(ulong i) => (uint)((i >> 32) ^ i);
|
||||
macro uint int128.hash(int128 i) => (uint)((i >> 96) ^ (i >> 64) ^ (i >> 32) ^ i);
|
||||
macro uint uint128.hash(uint128 i) => (uint)((i >> 96) ^ (i >> 64) ^ (i >> 32) ^ i);
|
||||
macro uint bool.hash(bool b) => (uint)b;
|
||||
macro uint typeid.hash(typeid t) => (uint)(((uptr)t >> 32) ^ (uptr)t);
|
||||
macro uint typeid.hash(typeid t) => ((ulong)(uptr)t).hash();
|
||||
macro uint String.hash(String c) => (uint)fnv32a::encode(c);
|
||||
macro uint char[].hash(char[] c) => (uint)fnv32a::encode(c);
|
||||
@@ -16,25 +16,25 @@ const uint UTF16_SURROGATE_HIGH_VALUE @private = 0xD800;
|
||||
**/
|
||||
fn usz! char32_to_utf8(Char32 c, char* output, usz available)
|
||||
{
|
||||
if (!available) return UnicodeResult.CONVERSION_FAILED!;
|
||||
if (!available) return UnicodeResult.CONVERSION_FAILED?;
|
||||
switch (true)
|
||||
{
|
||||
case c <= 0x7f:
|
||||
output[0] = (char)c;
|
||||
return 1;
|
||||
case c <= 0x7ff:
|
||||
if (available < 2) return UnicodeResult.CONVERSION_FAILED!;
|
||||
if (available < 2) return UnicodeResult.CONVERSION_FAILED?;
|
||||
output[0] = (char)(0xC0 | c >> 6);
|
||||
output[1] = (char)(0x80 | (c & 0x3F));
|
||||
return 2;
|
||||
case c <= 0xffff:
|
||||
if (available < 3) return UnicodeResult.CONVERSION_FAILED!;
|
||||
if (available < 3) return UnicodeResult.CONVERSION_FAILED?;
|
||||
output[0] = (char)(0xE0 | c >> 12);
|
||||
output[1] = (char)(0x80 | (c >> 6 & 0x3F));
|
||||
output[2] = (char)(0x80 | (c & 0x3F));
|
||||
return 3;
|
||||
case c <= 0x10ffff:
|
||||
if (available < 4) return UnicodeResult.CONVERSION_FAILED!;
|
||||
if (available < 4) return UnicodeResult.CONVERSION_FAILED?;
|
||||
output[0] = (char)(0xF0 | c >> 18);
|
||||
output[1] = (char)(0x80 | (c >> 12 & 0x3F));
|
||||
output[2] = (char)(0x80 | (c >> 6 & 0x3F));
|
||||
@@ -42,7 +42,7 @@ fn usz! char32_to_utf8(Char32 c, char* output, usz available)
|
||||
return 4;
|
||||
default:
|
||||
// 0x10FFFF and above is not defined.
|
||||
return UnicodeResult.CONVERSION_FAILED!;
|
||||
return UnicodeResult.CONVERSION_FAILED?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,15 +84,15 @@ fn void! char16_to_utf8_unsafe(Char16 *ptr, usz *available, char** output)
|
||||
return;
|
||||
}
|
||||
// Low surrogate first is an error
|
||||
if (high & UTF16_SURROGATE_MASK != UTF16_SURROGATE_HIGH_VALUE) return UnicodeResult.INVALID_UTF16!;
|
||||
if (high & UTF16_SURROGATE_MASK != UTF16_SURROGATE_HIGH_VALUE) return UnicodeResult.INVALID_UTF16?;
|
||||
|
||||
// Unmatched high surrogate is an error
|
||||
if (*available == 1) return UnicodeResult.INVALID_UTF16!;
|
||||
if (*available == 1) return UnicodeResult.INVALID_UTF16?;
|
||||
|
||||
Char16 low = ptr[1];
|
||||
|
||||
// Unmatched high surrogate, invalid
|
||||
if (low & UTF16_SURROGATE_MASK != UTF16_SURROGATE_LOW_VALUE) return UnicodeResult.INVALID_UTF16!;
|
||||
if (low & UTF16_SURROGATE_MASK != UTF16_SURROGATE_LOW_VALUE) return UnicodeResult.INVALID_UTF16?;
|
||||
|
||||
// The high bits of the codepoint are the value bits of the high surrogate
|
||||
// The low bits of the codepoint are the value bits of the low surrogate
|
||||
@@ -134,7 +134,7 @@ fn void char32_to_utf8_unsafe(Char32 c, char** output)
|
||||
fn Char32! utf8_to_char32(char* ptr, usz* size)
|
||||
{
|
||||
usz max_size = *size;
|
||||
if (max_size < 1) return UnicodeResult.INVALID_UTF8!;
|
||||
if (max_size < 1) return UnicodeResult.INVALID_UTF8?;
|
||||
char c = (ptr++)[0];
|
||||
|
||||
if ((c & 0x80) == 0)
|
||||
@@ -144,40 +144,40 @@ fn Char32! utf8_to_char32(char* ptr, usz* size)
|
||||
}
|
||||
if ((c & 0xE0) == 0xC0)
|
||||
{
|
||||
if (max_size < 2) return UnicodeResult.INVALID_UTF8!;
|
||||
if (max_size < 2) return UnicodeResult.INVALID_UTF8?;
|
||||
*size = 2;
|
||||
Char32 uc = (c & 0x1F) << 6;
|
||||
c = *ptr;
|
||||
// Overlong sequence or invalid second.
|
||||
if (!uc || c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8!;
|
||||
if (!uc || c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8?;
|
||||
return uc + c & 0x3F;
|
||||
}
|
||||
if ((c & 0xF0) == 0xE0)
|
||||
{
|
||||
if (max_size < 3) return UnicodeResult.INVALID_UTF8!;
|
||||
if (max_size < 3) return UnicodeResult.INVALID_UTF8?;
|
||||
*size = 3;
|
||||
Char32 uc = (c & 0x0F) << 12;
|
||||
c = ptr++[0];
|
||||
if (c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8!;
|
||||
if (c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8?;
|
||||
uc += (c & 0x3F) << 6;
|
||||
c = ptr++[0];
|
||||
// Overlong sequence or invalid last
|
||||
if (!uc || c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8!;
|
||||
if (!uc || c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8?;
|
||||
return uc + c & 0x3F;
|
||||
}
|
||||
if (max_size < 4) return UnicodeResult.INVALID_UTF8!;
|
||||
if ((c & 0xF8) != 0xF0) return UnicodeResult.INVALID_UTF8!;
|
||||
if (max_size < 4) return UnicodeResult.INVALID_UTF8?;
|
||||
if ((c & 0xF8) != 0xF0) return UnicodeResult.INVALID_UTF8?;
|
||||
*size = 4;
|
||||
Char32 uc = (c & 0x07) << 18;
|
||||
c = ptr++[0];
|
||||
if (c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8!;
|
||||
if (c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8?;
|
||||
uc += (c & 0x3F) << 12;
|
||||
c = ptr++[0];
|
||||
if (c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8!;
|
||||
if (c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8?;
|
||||
uc += (c & 0x3F) << 6;
|
||||
c = ptr++[0];
|
||||
// Overlong sequence or invalid last
|
||||
if (!uc || c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8!;
|
||||
if (!uc || c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8?;
|
||||
return uc + c & 0x3F;
|
||||
}
|
||||
|
||||
@@ -303,7 +303,7 @@ fn usz! utf32to8(Char32[] utf32, String utf8_buffer)
|
||||
char* ptr = utf8_buffer.ptr;
|
||||
foreach (Char32 uc : utf32)
|
||||
{
|
||||
usz used = char32_to_utf8(uc, ptr, len) @inline?;
|
||||
usz used = char32_to_utf8(uc, ptr, len) @inline!;
|
||||
len -= used;
|
||||
ptr += used;
|
||||
}
|
||||
@@ -327,9 +327,9 @@ fn usz! utf8to32(String utf8, Char32[] utf32_buffer)
|
||||
usz buf_len = utf32_buffer.len;
|
||||
for (usz i = 0; i < len;)
|
||||
{
|
||||
if (len32 == buf_len) return UnicodeResult.CONVERSION_FAILED!;
|
||||
if (len32 == buf_len) return UnicodeResult.CONVERSION_FAILED?;
|
||||
usz width = len - i;
|
||||
Char32 uc = utf8_to_char32(&utf8[i], &width) @inline?;
|
||||
Char32 uc = utf8_to_char32(&utf8[i], &width) @inline!;
|
||||
i += width;
|
||||
ptr[len32++] = uc;
|
||||
}
|
||||
@@ -352,7 +352,7 @@ fn void! utf16to8_unsafe(Char16[] utf16, char* utf8_buffer)
|
||||
for (usz i = 0; i < len16;)
|
||||
{
|
||||
usz available = len16 - i;
|
||||
char16_to_utf8_unsafe(&utf16[i], &available, &utf8_buffer) @inline?;
|
||||
char16_to_utf8_unsafe(&utf16[i], &available, &utf8_buffer) @inline!;
|
||||
i += available;
|
||||
}
|
||||
}
|
||||
@@ -371,7 +371,7 @@ fn void! utf8to32_unsafe(String utf8, Char32* utf32_buffer)
|
||||
for (usz i = 0; i < len;)
|
||||
{
|
||||
usz width = len - i;
|
||||
Char32 uc = utf8_to_char32(&utf8[i], &width) @inline?;
|
||||
Char32 uc = utf8_to_char32(&utf8[i], &width) @inline!;
|
||||
i += width;
|
||||
(utf32_buffer++)[0] = uc;
|
||||
}
|
||||
@@ -391,7 +391,7 @@ fn void! utf8to16_unsafe(String utf8, Char16* utf16_buffer)
|
||||
for (usz i = 0; i < len;)
|
||||
{
|
||||
usz width = len - i;
|
||||
Char32 uc = utf8_to_char32(&utf8[i], &width) @inline?;
|
||||
Char32 uc = utf8_to_char32(&utf8[i], &width) @inline!;
|
||||
char32_to_utf16_unsafe(uc, &utf16_buffer) @inline;
|
||||
i += width;
|
||||
}
|
||||
|
||||
@@ -261,7 +261,7 @@ fn void DString.append_chars(DString* this, String str)
|
||||
|
||||
fn Char32[] DString.copy_utf32(DString* this, Allocator* using = mem::heap())
|
||||
{
|
||||
return str::utf8to32(this.str(), using) @inline!!;
|
||||
return this.str().to_utf32(using) @inline!!;
|
||||
}
|
||||
|
||||
fn void DString.append_string(DString* this, DString str)
|
||||
@@ -325,7 +325,7 @@ fn usz! DString.printfn(DString* str, String format, args...) @maydiscard
|
||||
{
|
||||
Formatter formatter;
|
||||
formatter.init(&out_string_append_fn, str);
|
||||
usz len = formatter.vprintf(format, args)?;
|
||||
usz len = formatter.vprintf(format, args)!;
|
||||
str.append('\n');
|
||||
return len + 1;
|
||||
}
|
||||
@@ -350,7 +350,7 @@ fn DString new_join(String[] s, String joiner, Allocator* using = mem::heap())
|
||||
|
||||
fn void! out_string_append_fn(char c, void* data) @private
|
||||
{
|
||||
DynString* s = data;
|
||||
DString* s = data;
|
||||
s.append_char(c);
|
||||
}
|
||||
|
||||
@@ -380,11 +380,11 @@ fn usz! DString.read_from_stream(DString* string, Stream* reader)
|
||||
if (reader.supports_available())
|
||||
{
|
||||
usz total_read = 0;
|
||||
while (usz available = reader.available()?)
|
||||
while (usz available = reader.available()!)
|
||||
{
|
||||
string.reserve(available);
|
||||
StringData* data = string.data();
|
||||
usz len = reader.read(data.chars[data.len..(data.capacity - 1)])?;
|
||||
usz len = reader.read(data.chars[data.len..(data.capacity - 1)])!;
|
||||
total_read += len;
|
||||
data.len += len;
|
||||
}
|
||||
@@ -397,7 +397,7 @@ fn usz! DString.read_from_stream(DString* string, Stream* reader)
|
||||
string.reserve(16);
|
||||
StringData* data = string.data();
|
||||
// Read into the rest of the buffer
|
||||
usz read = reader.read(data.chars[data.len..(data.capacity - 1)])?;
|
||||
usz read = reader.read(data.chars[data.len..(data.capacity - 1)])!;
|
||||
data.len += read;
|
||||
// Ok, we reached the end.
|
||||
if (read < 16) return total_read;
|
||||
|
||||
@@ -32,7 +32,7 @@ enum OsType
|
||||
KFREEBSD,
|
||||
LINUX,
|
||||
PS3,
|
||||
MACOSX,
|
||||
MACOS,
|
||||
NETBSD,
|
||||
OPENBSD,
|
||||
SOLARIS,
|
||||
@@ -137,7 +137,7 @@ macro bool os_is_darwin()
|
||||
{
|
||||
$switch (OS_TYPE)
|
||||
$case IOS:
|
||||
$case MACOSX:
|
||||
$case MACOS:
|
||||
$case TVOS:
|
||||
$case WATCHOS:
|
||||
return true;
|
||||
@@ -150,7 +150,7 @@ macro bool os_is_posix()
|
||||
{
|
||||
$switch (OS_TYPE)
|
||||
$case IOS:
|
||||
$case MACOSX:
|
||||
$case MACOS:
|
||||
$case NETBSD:
|
||||
$case LINUX:
|
||||
$case KFREEBSD:
|
||||
@@ -181,7 +181,7 @@ $if (COMPILER_LIBC_AVAILABLE && OS_TYPE != OsType.WIN32)
|
||||
@pool()
|
||||
{
|
||||
ZString val = libc::getenv(name.zstr_tcopy());
|
||||
return val ? val.as_str() : SearchResult.MISSING!;
|
||||
return val ? val.as_str() : SearchResult.MISSING?;
|
||||
};
|
||||
$else
|
||||
return "";
|
||||
|
||||
@@ -241,10 +241,6 @@ macro malloc_aligned(..., usz alignment = 0, usz end_padding = 0, Allocator* usi
|
||||
$endif
|
||||
}
|
||||
|
||||
macro alloc($Type) @deprecated => malloc($Type);
|
||||
|
||||
macro char[] alloc_bytes(usz bytes) @deprecated => malloc(char, bytes);
|
||||
|
||||
/**
|
||||
* @require $vacount > 0 && $vacount < 3 "Expected size, type, or type + len"
|
||||
* @require $vacount != 2 || $checks($vatype(0).sizeof) "Expected 'malloc(Foo, 12)'"
|
||||
@@ -329,8 +325,6 @@ macro void @scoped(Allocator* using; @body())
|
||||
@body();
|
||||
}
|
||||
|
||||
macro talloc($Type) @builtin @deprecated => tmalloc($Type);
|
||||
|
||||
/**
|
||||
* @require $vacount > 0 && $vacount < 3 "Expected size, type, or type + len"
|
||||
* @require $vacount != 2 || $checks($vatype(0).sizeof) "Expected 'malloc(Foo, 12)'"
|
||||
|
||||
@@ -83,12 +83,12 @@ fn void*! Allocator.calloc_aligned(Allocator* allocator, usz size, usz alignment
|
||||
|
||||
fn void! Allocator.free(Allocator* allocator, void* old_pointer) @inline
|
||||
{
|
||||
allocator.function(allocator, 0, 0, 0, old_pointer, FREE)?;
|
||||
allocator.function(allocator, 0, 0, 0, old_pointer, FREE)!;
|
||||
}
|
||||
|
||||
fn void! Allocator.free_aligned(Allocator* allocator, void* old_pointer) @inline
|
||||
{
|
||||
allocator.function(allocator, 0, 0, 0, old_pointer, ALIGNED_FREE)?;
|
||||
allocator.function(allocator, 0, 0, 0, old_pointer, ALIGNED_FREE)!;
|
||||
}
|
||||
|
||||
fn void Allocator.reset(Allocator* allocator, usz mark = 0)
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
// Copyright (c) 2021-2023 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.
|
||||
|
||||
module std::core::mem::array;
|
||||
|
||||
/**
|
||||
* @require usz.max / elements > $Type.sizeof
|
||||
**/
|
||||
macro alloc($Type, usz elements) @deprecated => malloc($Type, elements);
|
||||
|
||||
/**
|
||||
* @require usz.max / elements > $Type.sizeof
|
||||
**/
|
||||
macro talloc($Type, usz elements) @deprecated => tmalloc($Type, elements);
|
||||
|
||||
/**
|
||||
* @require (usz.max / elements > $Type.sizeof)
|
||||
**/
|
||||
macro make($Type, usz elements, Allocator* using = mem::heap()) @deprecated
|
||||
{
|
||||
return calloc($Type, elements, .using = using);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require (usz.max / elements > $Type.sizeof)
|
||||
**/
|
||||
macro tmake($Type, usz elements) @deprecated
|
||||
{
|
||||
return tcalloc($Type, elements);
|
||||
}
|
||||
@@ -25,7 +25,7 @@ fn char[]! WasmMemory.allocate_block(WasmMemory* this, usz bytes)
|
||||
}
|
||||
|
||||
usz blocks_required = (bytes_required + WASM_BLOCK_SIZE + 1) / WASM_BLOCK_SIZE;
|
||||
if ($$wasm_memory_grow(0, blocks_required) == -1) return AllocationFailure.OUT_OF_MEMORY!;
|
||||
if ($$wasm_memory_grow(0, blocks_required) == -1) return AllocationFailure.OUT_OF_MEMORY?;
|
||||
this.allocation = $$wasm_memory_size(0) * WASM_BLOCK_SIZE;
|
||||
defer this.use += bytes;
|
||||
return ((char*)this.use)[:bytes];
|
||||
|
||||
@@ -7,7 +7,11 @@ macro usz _strlen(ptr) @private
|
||||
return len;
|
||||
}
|
||||
|
||||
macro int @main_to_err_main(#m, int, char**) => catch? #m() ? 1 : 0;
|
||||
macro int @main_to_err_main(#m, int, char**)
|
||||
{
|
||||
if (catch #m()) return 1;
|
||||
return 0;
|
||||
}
|
||||
macro int @main_to_int_main(#m, int, char**) => #m();
|
||||
macro int @main_to_void_main(#m, int, char**)
|
||||
{
|
||||
@@ -32,7 +36,8 @@ macro int @main_to_err_main_args(#m, int argc, char** argv)
|
||||
{
|
||||
String[] list = args_to_strings(argc, argv);
|
||||
defer free(list.ptr);
|
||||
return catch? #m(list) ? 1 : 0;
|
||||
if (catch #m(list)) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
macro int @main_to_int_main_args(#m, int argc, char** argv)
|
||||
@@ -79,7 +84,11 @@ macro void release_wargs(String[] list) @private
|
||||
free(list.ptr);
|
||||
}
|
||||
|
||||
macro int @win_to_err_main_noargs(#m, void* handle, Char16* cmd_line, int show_cmd) => catch? #m() ? 1 : 0;
|
||||
macro int @win_to_err_main_noargs(#m, void* handle, Char16* cmd_line, int show_cmd)
|
||||
{
|
||||
if (catch #m()) return 1;
|
||||
return 0;
|
||||
}
|
||||
macro int @win_to_int_main_noargs(#m, void* handle, Char16* cmd_line, int show_cmd) => #m();
|
||||
macro int @win_to_void_main_noargs(#m, void* handle, Char16* cmd_line, int show_cmd)
|
||||
{
|
||||
@@ -91,7 +100,8 @@ macro int @win_to_err_main_args(#m, void* handle, Char16* cmd_line, int show_cmd
|
||||
{
|
||||
String[] args = win_command_line_to_strings(cmd_line);
|
||||
defer release_wargs(args);
|
||||
return catch? #m(args) ? 1 : 0;
|
||||
if (catch #m(args)) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
macro int @win_to_int_main_args(#m, void* handle, Char16* cmd_line, int show_cmd)
|
||||
@@ -113,7 +123,8 @@ macro int @win_to_err_main(#m, void* handle, Char16* cmd_line, int show_cmd)
|
||||
{
|
||||
String[] args = win_command_line_to_strings(cmd_line);
|
||||
defer release_wargs(args);
|
||||
return catch? #m(handle, args, show_cmd) ? 1 : 0;
|
||||
if (catch #m(handle, args, show_cmd)) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
macro int @win_to_int_main(#m, void* handle, Char16* cmd_line, int show_cmd)
|
||||
@@ -135,7 +146,8 @@ macro int @wmain_to_err_main_args(#m, int argc, Char16** argv)
|
||||
{
|
||||
String[] args = wargs_strings(argc, argv);
|
||||
defer release_wargs(args);
|
||||
return catch? #m(args) ? 1 : 0;
|
||||
if (catch #m(args)) return 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
macro int @wmain_to_int_main_args(#m, int argc, Char16** argv)
|
||||
|
||||
@@ -9,25 +9,12 @@ struct VirtualAny
|
||||
typeid type_id;
|
||||
}
|
||||
|
||||
struct VirtualContainer
|
||||
{
|
||||
void* ptr;
|
||||
void* impl_ptr;
|
||||
}
|
||||
|
||||
struct SubArrayContainer
|
||||
{
|
||||
void* ptr;
|
||||
usz len;
|
||||
}
|
||||
|
||||
struct VarArrayHeader
|
||||
{
|
||||
usz size;
|
||||
usz capacity;
|
||||
void *allocator;
|
||||
}
|
||||
|
||||
typedef TestFn = fn void!();
|
||||
|
||||
struct TestRunner
|
||||
@@ -48,10 +35,13 @@ fn TestRunner test_runner_create()
|
||||
import libc;
|
||||
|
||||
TestRunner* current_runner @private;
|
||||
|
||||
fn void test_panic(String message, String file, String function, uint line)
|
||||
{
|
||||
io::printn("[error]");
|
||||
io::printfn("\n Error: %s", message);
|
||||
io::print("\n Error: ");
|
||||
io::print(message);
|
||||
io::printn();
|
||||
io::printfn(" - in %s %s:%s.\n", function, file, line);
|
||||
libc::longjmp(¤t_runner.buf, 1);
|
||||
}
|
||||
|
||||
@@ -1,259 +0,0 @@
|
||||
module std::core::str;
|
||||
|
||||
fn VarString join(String[] s, String joiner)
|
||||
{
|
||||
if (!s.len) return (VarString)null;
|
||||
usz total_size = joiner.len * s.len;
|
||||
foreach (String* &str : s)
|
||||
{
|
||||
total_size += str.len;
|
||||
}
|
||||
VarString res = string::new_with_capacity(total_size);
|
||||
res.append(s[0]);
|
||||
foreach (String* &str : s[1..])
|
||||
{
|
||||
res.append(joiner);
|
||||
res.append(*str);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
macro bool char_in_set(char c, String set)
|
||||
{
|
||||
foreach (ch : set)
|
||||
{
|
||||
if (ch == c) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
macro char_is_space_tab(char c) @private
|
||||
{
|
||||
return c == ' ' || c == '\t';
|
||||
}
|
||||
|
||||
macro to_integer($Type, String string) @private
|
||||
{
|
||||
usz len = string.len;
|
||||
usz index = 0;
|
||||
char* ptr = string.ptr;
|
||||
while (index < len && char_is_space_tab(ptr[index])) index++;
|
||||
if (len == index) return NumberConversion.EMPTY_STRING!;
|
||||
bool is_negative;
|
||||
switch (string[index])
|
||||
{
|
||||
case '-':
|
||||
if ($Type.min == 0) return NumberConversion.NEGATIVE_VALUE!;
|
||||
is_negative = true;
|
||||
index++;
|
||||
case '+':
|
||||
index++;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (len == index) return NumberConversion.MALFORMED_INTEGER!;
|
||||
$Type base = 10;
|
||||
if (string[index] == '0')
|
||||
{
|
||||
index++;
|
||||
if (index == len) return ($Type)0;
|
||||
switch (string[index])
|
||||
{
|
||||
case 'x':
|
||||
case 'X':
|
||||
base = 16;
|
||||
index++;
|
||||
case 'b':
|
||||
case 'B':
|
||||
base = 2;
|
||||
index++;
|
||||
case 'o':
|
||||
case 'O':
|
||||
base = 8;
|
||||
index++;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (len == index) return NumberConversion.MALFORMED_INTEGER!;
|
||||
}
|
||||
$Type value = 0;
|
||||
while (index != len)
|
||||
{
|
||||
char c = {|
|
||||
char ch = string[index++];
|
||||
if (base != 16 || ch < 'A') return (char)(ch - '0');
|
||||
if (ch <= 'F') return (char)(ch - 'A');
|
||||
if (ch < 'a') return NumberConversion.MALFORMED_INTEGER!;
|
||||
if (ch > 'f') return NumberConversion.MALFORMED_INTEGER!;
|
||||
return (char)(ch - 'a');
|
||||
|}?;
|
||||
if (c >= base) return NumberConversion.MALFORMED_INTEGER!;
|
||||
value = {|
|
||||
if (is_negative)
|
||||
{
|
||||
$Type new_value = value * base - c;
|
||||
if (new_value > value) return NumberConversion.INTEGER_OVERFLOW!;
|
||||
return new_value;
|
||||
}
|
||||
$Type new_value = value * base + c;
|
||||
if (new_value < value) return NumberConversion.INTEGER_OVERFLOW!;
|
||||
return new_value;
|
||||
|}?;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
fn float! to_float(String string) => floatparse(string, float);
|
||||
fn double! to_double(String string) => floatparse(string, double);
|
||||
fn int128! to_int128(String string) => to_integer(int128, string);
|
||||
fn long! to_long(String string) => to_integer(long, string);
|
||||
fn int! to_int(String string) => to_integer(int, string);
|
||||
fn short! to_short(String string) => to_integer(short, string);
|
||||
fn ichar! to_ichar(String string) => to_integer(ichar, string);
|
||||
|
||||
fn uint128! to_uint128(String str) => to_integer(uint128, str);
|
||||
fn ulong! to_ulong(String str) => to_integer(ulong, str);
|
||||
fn uint! to_uint(String str) => to_integer(uint, str);
|
||||
fn ushort! to_ushort(String str) => to_integer(ushort, str);
|
||||
fn char! to_uchar(String str) => to_integer(char, str);
|
||||
|
||||
fn String trim(String string, String to_trim = "\t\n\r ") @deprecated => string.trim(to_trim);
|
||||
|
||||
fn bool starts_with(String s, String needle) @deprecated => s.starts_with(needle);
|
||||
|
||||
fn String[] tsplit(String s, String needle) @deprecated => s.split(needle, .using = mem::temp()) @inline;
|
||||
fn String[] split(String s, String needle, Allocator* using = mem::heap()) @deprecated => s.split(needle, .using = using);
|
||||
fn usz! rindex_of(String s, String needle) @deprecated => s.rindex_of(needle);
|
||||
fn usz! index_of(String s, String needle) @deprecated => s.index_of(needle);
|
||||
|
||||
fn ZString String.zstrcopy(String s, Allocator* using = mem::heap()) @deprecated => s.zstr_copy(using);
|
||||
fn ZString String.zstrtcopy(String s) @deprecated => s.zstr_tcopy();
|
||||
|
||||
fn ZString copy_zstring(String s, Allocator* using = mem::heap()) @deprecated => s.zstr_copy(using);
|
||||
fn String copyz(String s, Allocator* using = mem::heap()) @deprecated => s.copy(using);
|
||||
fn ZString tcopy_zstring(String s) @deprecated => s.zstr_tcopy();
|
||||
|
||||
fn bool compare(String a, String b)
|
||||
{
|
||||
if (a.len != b.len) return false;
|
||||
foreach (i, c : a)
|
||||
{
|
||||
if (c != b[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
fault UnicodeResult
|
||||
{
|
||||
INVALID_UTF8,
|
||||
INVALID_UTF16,
|
||||
CONVERSION_FAILED,
|
||||
}
|
||||
|
||||
fn usz utf8_codepoints(String utf8)
|
||||
{
|
||||
usz len = 0;
|
||||
foreach (char c : utf8)
|
||||
{
|
||||
if (c & 0xC0 != 0x80) len++;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
fn Char32[]! utf8to32(String utf8, Allocator* using = mem::heap())
|
||||
{
|
||||
usz codepoints = conv::utf8_codepoints(utf8);
|
||||
Char32* data = malloc_checked(Char32, codepoints + 1, .using = using)?;
|
||||
conv::utf8to32_unsafe(utf8, data)?;
|
||||
data[codepoints] = 0;
|
||||
return data[:codepoints];
|
||||
}
|
||||
|
||||
fn String utf32to8(Char32[] utf32, Allocator* using = mem::heap())
|
||||
{
|
||||
usz len = conv::utf8len_for_utf32(utf32);
|
||||
char* data = malloc_checked(len + 1, .using = using)!!;
|
||||
conv::utf32to8_unsafe(utf32, data);
|
||||
data[len] = 0;
|
||||
return (String)data[:len];
|
||||
}
|
||||
|
||||
fn Char16[]! utf8to16(String utf8, Allocator* using = mem::heap())
|
||||
{
|
||||
usz len16 = conv::utf16len_for_utf8(utf8);
|
||||
Char16* data = malloc_checked(Char16, len16 + 1, .using = using)?;
|
||||
conv::utf8to16_unsafe(utf8, data)?;
|
||||
data[len16] = 0;
|
||||
return data[:len16];
|
||||
}
|
||||
|
||||
|
||||
fn String! utf16to8(Char16[] utf16, Allocator* using = mem::heap())
|
||||
{
|
||||
usz len = conv::utf8len_for_utf16(utf16);
|
||||
char* data = malloc_checked(len + 1, .using = using)?;
|
||||
conv::utf16to8_unsafe(utf16, data)?;
|
||||
data[len] = 0;
|
||||
return (String)data[:len];
|
||||
}
|
||||
|
||||
fn String copy(String s, Allocator* using = mem::heap()) @deprecated
|
||||
{
|
||||
usz len = s.len;
|
||||
ZString str_copy = s.zstr_copy(using) @inline;
|
||||
return (String)str_copy[:len];
|
||||
}
|
||||
|
||||
fn String tcopy(String s) @deprecated
|
||||
{
|
||||
usz len = s.len;
|
||||
ZString str_copy = s.zstr_tcopy() @inline;
|
||||
return (String)str_copy[:len];
|
||||
}
|
||||
|
||||
fn String tconcat(String s1, String s2)
|
||||
{
|
||||
usz full_len = s1.len + s2.len;
|
||||
char* str = tmalloc(full_len + 1);
|
||||
usz s1_len = s1.len;
|
||||
mem::copy(str, s1.ptr, s1_len);
|
||||
mem::copy(str + s1_len, s2.ptr, s2.len);
|
||||
str[full_len] = 0;
|
||||
return (String)str[:full_len];
|
||||
}
|
||||
|
||||
fn String concat(String s1, String s2)
|
||||
{
|
||||
usz full_len = s1.len + s2.len;
|
||||
char* str = malloc(full_len + 1);
|
||||
usz s1_len = s1.len;
|
||||
mem::copy(str, s1.ptr, s1_len);
|
||||
mem::copy(str + s1_len, s2.ptr, s2.len);
|
||||
str[full_len] = 0;
|
||||
return (String)str[:full_len];
|
||||
}
|
||||
|
||||
fn String ZString.as_str(ZString str)
|
||||
{
|
||||
return (String)((char*)str)[:str.len()];
|
||||
}
|
||||
|
||||
fn usz ZString.char_len(ZString str)
|
||||
{
|
||||
usz len = 0;
|
||||
char* ptr = (char*)str;
|
||||
while (char c = ptr++[0])
|
||||
{
|
||||
if (c & 0xC0 != 0x80) len++;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
fn usz ZString.len(ZString str)
|
||||
{
|
||||
usz len = 0;
|
||||
char* ptr = (char*)str;
|
||||
while (char c = ptr++[0]) len++;
|
||||
return len;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
module std::core::string;
|
||||
import std::ascii;
|
||||
|
||||
typedef ZString = distinct inline char*;
|
||||
typedef Char32 = uint;
|
||||
typedef Char16 = ushort;
|
||||
|
||||
fault UnicodeResult
|
||||
{
|
||||
INVALID_UTF8,
|
||||
INVALID_UTF16,
|
||||
CONVERSION_FAILED,
|
||||
}
|
||||
|
||||
const uint SURROGATE_OFFSET @private = 0x10000;
|
||||
const uint SURROGATE_GENERIC_MASK @private = 0xF800;
|
||||
const uint SURROGATE_MASK @private = 0xFC00;
|
||||
@@ -47,6 +55,31 @@ macro bool char_in_set(char c, String set)
|
||||
return false;
|
||||
}
|
||||
|
||||
fn String join(String[] s, String joiner, Allocator* using = mem::heap())
|
||||
{
|
||||
if (!s)
|
||||
{
|
||||
return (String)(calloc(char, 2, .using = using)[:0]);
|
||||
}
|
||||
|
||||
usz total_size = joiner.len * s.len;
|
||||
foreach (String* &str : s)
|
||||
{
|
||||
total_size += str.len;
|
||||
}
|
||||
@stack_mem(256; Allocator* mem)
|
||||
{
|
||||
DString res = dstring::new_with_capacity(total_size, .using = mem);
|
||||
res.append(s[0]);
|
||||
foreach (String* &str : s[1..])
|
||||
{
|
||||
res.append(joiner);
|
||||
res.append(*str);
|
||||
}
|
||||
return res.copy_str(using);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [in] string
|
||||
* @param [in] to_trim
|
||||
@@ -128,7 +161,7 @@ fn String[] String.split(String s, String needle, usz max = 0, Allocator* using
|
||||
bool no_more = false;
|
||||
while (!no_more)
|
||||
{
|
||||
usz! index = i == max - 1 ? SearchResult.MISSING! : s.index_of(needle);
|
||||
usz! index = i == max - 1 ? SearchResult.MISSING? : s.index_of(needle);
|
||||
String res @noinit;
|
||||
if (try index)
|
||||
{
|
||||
@@ -163,6 +196,11 @@ fn String[] String.tsplit(String s, String needle, usz max = 0)
|
||||
return s.split(needle, max, mem::temp()) @inline;
|
||||
}
|
||||
|
||||
fn bool String.contains(String s, String needle)
|
||||
{
|
||||
return @ok(s.index_of(needle));
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the index of the first incidence of a string.
|
||||
*
|
||||
@@ -170,7 +208,9 @@ fn String[] String.tsplit(String s, String needle, usz max = 0)
|
||||
* @param [in] needle
|
||||
* @pure
|
||||
* @ensure return < s.len
|
||||
* @require needle.len > 0 "The needle must be len 1 or more"
|
||||
* @require needle.len > 0 : "The needle must be len 1 or more"
|
||||
* @return "the index of the needle"
|
||||
* @return! SearchResult.MISSING "if the needle cannot be found"
|
||||
**/
|
||||
fn usz! String.index_of(String s, String needle)
|
||||
{
|
||||
@@ -194,7 +234,7 @@ fn usz! String.index_of(String s, String needle)
|
||||
search = needle[0];
|
||||
}
|
||||
}
|
||||
return SearchResult.MISSING!;
|
||||
return SearchResult.MISSING?;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -205,6 +245,8 @@ fn usz! String.index_of(String s, String needle)
|
||||
* @pure
|
||||
* @ensure return < s.len
|
||||
* @require needle.len > 0 "The needle must be len 1 or more"
|
||||
* @return "the index of the needle"
|
||||
* @return! SearchResult.MISSING "if the needle cannot be found"
|
||||
**/
|
||||
fn usz! String.rindex_of(String s, String needle)
|
||||
{
|
||||
@@ -228,9 +270,34 @@ fn usz! String.rindex_of(String s, String needle)
|
||||
search = needle[^1];
|
||||
}
|
||||
}
|
||||
return SearchResult.MISSING!;
|
||||
return SearchResult.MISSING?;
|
||||
}
|
||||
|
||||
fn String ZString.as_str(ZString str)
|
||||
{
|
||||
return (String)((char*)str)[:str.len()];
|
||||
}
|
||||
|
||||
fn usz ZString.char_len(ZString str)
|
||||
{
|
||||
usz len = 0;
|
||||
char* ptr = (char*)str;
|
||||
while (char c = ptr++[0])
|
||||
{
|
||||
if (c & 0xC0 != 0x80) len++;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
fn usz ZString.len(ZString str)
|
||||
{
|
||||
usz len = 0;
|
||||
char* ptr = (char*)str;
|
||||
while (char c = ptr++[0]) len++;
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
fn ZString String.zstr_copy(String s, Allocator* using = mem::heap())
|
||||
{
|
||||
usz len = s.len;
|
||||
@@ -270,23 +337,154 @@ fn String String.tcopy(String s) => s.copy(mem::temp()) @inline;
|
||||
fn String ZString.copy(ZString z, Allocator* using = mem::heap()) => z.as_str().copy(using) @inline;
|
||||
fn String ZString.tcopy(ZString z) => z.as_str().copy(mem::temp()) @inline;
|
||||
|
||||
/**
|
||||
* Convert an UTF-8 string to UTF-16
|
||||
* @return "The UTF-16 string as a slice, allocated using the given allocator"
|
||||
* @return! UnicodeResult.INVALID_UTF8 "If the string contained an invalid UTF-8 sequence"
|
||||
* @return! AllocationFailure "If allocation of the string fails"
|
||||
**/
|
||||
fn Char16[]! String.to_utf16(String s, Allocator* using = mem::heap())
|
||||
{
|
||||
usz len16 = conv::utf16len_for_utf8(s);
|
||||
Char16* data = malloc_checked(Char16, len16 + 1, .using = using)?;
|
||||
conv::utf8to16_unsafe(s, data)?;
|
||||
Char16* data = malloc_checked(Char16, len16 + 1, .using = using)!;
|
||||
conv::utf8to16_unsafe(s, data)!;
|
||||
data[len16] = 0;
|
||||
return data[:len16];
|
||||
}
|
||||
|
||||
fn Char32[]! String.to_utf32(String s, Allocator* using = mem::heap())
|
||||
{
|
||||
usz codepoints = conv::utf8_codepoints(s);
|
||||
Char32* data = malloc_checked(Char32, codepoints + 1, .using = using)!;
|
||||
conv::utf8to32_unsafe(s, data)!;
|
||||
data[codepoints] = 0;
|
||||
return data[:codepoints];
|
||||
}
|
||||
|
||||
fn String! from_utf32(Char32[] utf32, Allocator* using = mem::heap())
|
||||
{
|
||||
usz len = conv::utf8len_for_utf32(utf32);
|
||||
char* data = malloc_checked(len + 1, .using = using)!;
|
||||
defer catch free(data, .using = using);
|
||||
conv::utf32to8_unsafe(utf32, data);
|
||||
data[len] = 0;
|
||||
return (String)data[:len];
|
||||
}
|
||||
|
||||
fn String! from_utf16(Char16[] utf16, Allocator* using = mem::heap())
|
||||
{
|
||||
usz len = conv::utf8len_for_utf16(utf16);
|
||||
char* data = malloc_checked(len + 1, .using = using)?;
|
||||
conv::utf16to8_unsafe(utf16, data)?;
|
||||
char* data = malloc_checked(len + 1, .using = using)!;
|
||||
defer catch free(data, .using = using);
|
||||
conv::utf16to8_unsafe(utf16, data)!;
|
||||
data[len] = 0;
|
||||
return (String)data[:len];
|
||||
}
|
||||
|
||||
fn String! from_zutf16(Char16* utf16_pointer, Allocator* using = mem::heap())
|
||||
{
|
||||
usz utf16_len;
|
||||
while (utf16_pointer[utf16_len] != 0) utf16_len++;
|
||||
Char16[] utf16 = utf16_pointer[:utf16_len];
|
||||
return from_utf16(utf16, using);
|
||||
}
|
||||
|
||||
fn usz String.utf8_codepoints(String s)
|
||||
{
|
||||
usz len = 0;
|
||||
foreach (char c : s)
|
||||
{
|
||||
if (c & 0xC0 != 0x80) len++;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
macro String.to_integer(String string, $Type)
|
||||
{
|
||||
usz len = string.len;
|
||||
usz index = 0;
|
||||
char* ptr = string.ptr;
|
||||
while (index < len && ascii::is_blank_m(ptr[index])) index++;
|
||||
if (len == index) return NumberConversion.EMPTY_STRING?;
|
||||
bool is_negative;
|
||||
switch (string[index])
|
||||
{
|
||||
case '-':
|
||||
if ($Type.min == 0) return NumberConversion.NEGATIVE_VALUE?;
|
||||
is_negative = true;
|
||||
index++;
|
||||
case '+':
|
||||
index++;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (len == index) return NumberConversion.MALFORMED_INTEGER?;
|
||||
$Type base = 10;
|
||||
if (string[index] == '0')
|
||||
{
|
||||
index++;
|
||||
if (index == len) return ($Type)0;
|
||||
switch (string[index])
|
||||
{
|
||||
case 'x':
|
||||
case 'X':
|
||||
base = 16;
|
||||
index++;
|
||||
case 'b':
|
||||
case 'B':
|
||||
base = 2;
|
||||
index++;
|
||||
case 'o':
|
||||
case 'O':
|
||||
base = 8;
|
||||
index++;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (len == index) return NumberConversion.MALFORMED_INTEGER?;
|
||||
}
|
||||
$Type value = 0;
|
||||
while (index != len)
|
||||
{
|
||||
char c = {|
|
||||
char ch = string[index++];
|
||||
if (base != 16 || ch < 'A') return (char)(ch - '0');
|
||||
if (ch <= 'F') return (char)(ch - 'A');
|
||||
if (ch < 'a') return NumberConversion.MALFORMED_INTEGER?;
|
||||
if (ch > 'f') return NumberConversion.MALFORMED_INTEGER?;
|
||||
return (char)(ch - 'a');
|
||||
|}!;
|
||||
if (c >= base) return NumberConversion.MALFORMED_INTEGER?;
|
||||
value = {|
|
||||
if (is_negative)
|
||||
{
|
||||
$Type new_value = value * base - c;
|
||||
if (new_value > value) return NumberConversion.INTEGER_OVERFLOW?;
|
||||
return new_value;
|
||||
}
|
||||
$Type new_value = value * base + c;
|
||||
if (new_value < value) return NumberConversion.INTEGER_OVERFLOW?;
|
||||
return new_value;
|
||||
|}!;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
fn Char16[]! String.to_temp_utf16(String s) => s.to_utf16(mem::temp());
|
||||
|
||||
fn int128! String.to_int128(String s) => s.to_integer(int128);
|
||||
fn long! String.to_long(String s) => s.to_integer(long);
|
||||
fn int! String.to_int(String s) => s.to_integer(int);
|
||||
fn short! String.to_short(String s) => s.to_integer(short);
|
||||
fn ichar! String.to_ichar(String s) => s.to_integer(ichar);
|
||||
|
||||
fn uint128! String.to_uint128(String s) => s.to_integer(uint128);
|
||||
fn ulong! String.to_ulong(String s) => s.to_integer(ulong);
|
||||
fn uint! String.to_uint(String s) => s.to_integer(uint);
|
||||
fn ushort! String.to_ushort(String s) => s.to_integer(ushort);
|
||||
fn char! String.to_uchar(String s) => s.to_integer(char);
|
||||
|
||||
fn double! String.to_double(String s) => s.to_real(double);
|
||||
fn float! String.to_float(String s) => s.to_real(float);
|
||||
|
||||
@@ -15,9 +15,9 @@ fn Char32! StringIterator.next(StringIterator* this)
|
||||
{
|
||||
usz len = this.utf8.len;
|
||||
usz current = this.current;
|
||||
if (current >= len) return IteratorResult.NO_MORE_ELEMENT!;
|
||||
if (current >= len) return IteratorResult.NO_MORE_ELEMENT?;
|
||||
usz read = (len - current < 4 ? len - current : 4);
|
||||
Char32 res = conv::utf8_to_char32(&this.utf8[current], &read)?;
|
||||
Char32 res = conv::utf8_to_char32(&this.utf8[current], &read)!;
|
||||
this.current += read;
|
||||
return res;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
module std::core::str;
|
||||
module std::core::string;
|
||||
import std::math;
|
||||
|
||||
// Float parsing based on code in Musl floatscan.c by Rich Felker.
|
||||
@@ -64,7 +64,7 @@ macro double! decfloat(char[] chars, int $bits, int $emin, int sign)
|
||||
got_rad = true;
|
||||
if (index == last_char)
|
||||
{
|
||||
if (!got_digit) return NumberConversion.MALFORMED_FLOAT!;
|
||||
if (!got_digit) return NumberConversion.MALFORMED_FLOAT?;
|
||||
return sign * 0.0;
|
||||
}
|
||||
if (index != last_char && (c = chars[++index]) == '0')
|
||||
@@ -83,7 +83,7 @@ macro double! decfloat(char[] chars, int $bits, int $emin, int sign)
|
||||
switch
|
||||
{
|
||||
case c == '.':
|
||||
if (got_rad) return NumberConversion.MALFORMED_FLOAT!;
|
||||
if (got_rad) return NumberConversion.MALFORMED_FLOAT?;
|
||||
got_rad = true;
|
||||
lrp = dc;
|
||||
case k < KMAX - 3:
|
||||
@@ -113,24 +113,24 @@ macro double! decfloat(char[] chars, int $bits, int $emin, int sign)
|
||||
c = chars[++index];
|
||||
}
|
||||
if (!got_rad) lrp = dc;
|
||||
if (!got_digit) return NumberConversion.MALFORMED_FLOAT!;
|
||||
if (!got_digit) return NumberConversion.MALFORMED_FLOAT?;
|
||||
if ((c | 32) == 'e')
|
||||
{
|
||||
if (last_char == index) return NumberConversion.MALFORMED_FLOAT!;
|
||||
long e10 = str::to_long((String)chars[index + 1..]) ?? NumberConversion.MALFORMED_FLOAT!?;
|
||||
if (last_char == index) return NumberConversion.MALFORMED_FLOAT?;
|
||||
long e10 = String.to_long((String)chars[index + 1..]) ?? NumberConversion.MALFORMED_FLOAT?!;
|
||||
lrp += e10;
|
||||
}
|
||||
else if (index != last_char)
|
||||
{
|
||||
return NumberConversion.MALFORMED_FLOAT!;
|
||||
return NumberConversion.MALFORMED_FLOAT?;
|
||||
}
|
||||
// Handle zero specially to avoid nasty special cases later
|
||||
if (!x[0]) return sign * 0.0;
|
||||
|
||||
// Optimize small integers (w/no exponent) and over/under-flow
|
||||
if (lrp == dc && dc < 10 && ($bits > 30 || (ulong)x[0] >> $bits == 0)) return sign * (double)x[0];
|
||||
if (lrp > - $emin / 2) return NumberConversion.FLOAT_OUT_OF_RANGE!;
|
||||
if (lrp < $emin - 2 * math::DOUBLE_MANT_DIG) return NumberConversion.FLOAT_OUT_OF_RANGE!;
|
||||
if (lrp > - $emin / 2) return NumberConversion.FLOAT_OUT_OF_RANGE?;
|
||||
if (lrp < $emin - 2 * math::DOUBLE_MANT_DIG) return NumberConversion.FLOAT_OUT_OF_RANGE?;
|
||||
|
||||
// Align incomplete final B1B digit
|
||||
if (j)
|
||||
@@ -320,7 +320,7 @@ macro double! decfloat(char[] chars, int $bits, int $emin, int sign)
|
||||
y *= 0.5;
|
||||
e2++;
|
||||
}
|
||||
if (e2 + math::DOUBLE_MANT_DIG > emax || (denormal && frac)) return NumberConversion.MALFORMED_FLOAT!;
|
||||
if (e2 + math::DOUBLE_MANT_DIG > emax || (denormal && frac)) return NumberConversion.MALFORMED_FLOAT?;
|
||||
}
|
||||
return math::scalbn(y, e2);
|
||||
}
|
||||
@@ -351,7 +351,7 @@ macro double! hexfloat(char[] chars, int $bits, int $emin, int sign)
|
||||
got_rad = true;
|
||||
if (index == last_char)
|
||||
{
|
||||
if (!got_digit) return NumberConversion.MALFORMED_FLOAT!;
|
||||
if (!got_digit) return NumberConversion.MALFORMED_FLOAT?;
|
||||
return sign * 0.0;
|
||||
}
|
||||
if (index != last_char && (c = chars[++index]) == '0')
|
||||
@@ -369,7 +369,7 @@ macro double! hexfloat(char[] chars, int $bits, int $emin, int sign)
|
||||
{
|
||||
if (c == '.')
|
||||
{
|
||||
if (got_rad) return NumberConversion.MALFORMED_FLOAT!;
|
||||
if (got_rad) return NumberConversion.MALFORMED_FLOAT?;
|
||||
got_rad = true;
|
||||
rp = dc;
|
||||
}
|
||||
@@ -396,20 +396,20 @@ macro double! hexfloat(char[] chars, int $bits, int $emin, int sign)
|
||||
if (index == last_char) break;
|
||||
c = chars[++index];
|
||||
}
|
||||
if (!got_digit) return NumberConversion.MALFORMED_FLOAT!;
|
||||
if (!got_digit) return NumberConversion.MALFORMED_FLOAT?;
|
||||
if (!got_rad) rp = dc;
|
||||
for (; dc < 8; dc++) x *= 16;
|
||||
|
||||
long e2;
|
||||
if ((c | 32) == 'p')
|
||||
{
|
||||
long e2val = str::to_long((String)chars[index + 1..]) ?? NumberConversion.MALFORMED_FLOAT!?;
|
||||
long e2val = String.to_long((String)chars[index + 1..]) ?? (NumberConversion.MALFORMED_FLOAT?)!;
|
||||
e2 = e2val;
|
||||
}
|
||||
e2 += 4 * rp - 32;
|
||||
if (!x) return sign * 0.0;
|
||||
if (e2 > -$emin) return NumberConversion.FLOAT_OUT_OF_RANGE!;
|
||||
if (e2 < $emin - 2 * math::DOUBLE_MANT_DIG) return NumberConversion.FLOAT_OUT_OF_RANGE!;
|
||||
if (e2 > -$emin) return NumberConversion.FLOAT_OUT_OF_RANGE?;
|
||||
if (e2 < $emin - 2 * math::DOUBLE_MANT_DIG) return NumberConversion.FLOAT_OUT_OF_RANGE?;
|
||||
|
||||
while (x < 0x80000000)
|
||||
{
|
||||
@@ -444,12 +444,12 @@ macro double! hexfloat(char[] chars, int $bits, int $emin, int sign)
|
||||
}
|
||||
y = bias + sign * (double)x + sign * y;
|
||||
y -= bias;
|
||||
if (!y) return NumberConversion.FLOAT_OUT_OF_RANGE!;
|
||||
if (!y) return NumberConversion.FLOAT_OUT_OF_RANGE?;
|
||||
|
||||
return math::scalbn(y, (int)e2);
|
||||
}
|
||||
|
||||
macro floatparse(String chars, $Type) @private
|
||||
macro String.to_real(String chars, $Type) @private
|
||||
{
|
||||
int sign = 1;
|
||||
$switch ($Type)
|
||||
@@ -466,7 +466,7 @@ macro floatparse(String chars, $Type) @private
|
||||
$endswitch
|
||||
|
||||
while (chars.len && chars[0] == ' ') chars = chars[1..];
|
||||
if (!chars.len) return NumberConversion.MALFORMED_FLOAT!;
|
||||
if (!chars.len) return NumberConversion.MALFORMED_FLOAT?;
|
||||
switch (chars[0])
|
||||
{
|
||||
case '-':
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
module std::core::types;
|
||||
import libc;
|
||||
|
||||
@@ -10,63 +11,63 @@ fault ConversionResult
|
||||
/**
|
||||
* @require $Type.kindof.is_int() || $Type.kindof == TypeKind.ENUM "Argument was not an integer"
|
||||
**/
|
||||
macro variant_to_int(variant v, $Type)
|
||||
macro any_to_int(any v, $Type)
|
||||
{
|
||||
typeid variant_type = v.type;
|
||||
TypeKind kind = variant_type.kindof;
|
||||
typeid any_type = v.type;
|
||||
TypeKind kind = any_type.kindof;
|
||||
if (kind == TypeKind.ENUM)
|
||||
{
|
||||
variant_type = variant_type.inner;
|
||||
kind = variant_type.kindof;
|
||||
any_type = any_type.inner;
|
||||
kind = any_type.kindof;
|
||||
}
|
||||
bool is_mixed_signed = $Type.kindof != variant_type.kindof;
|
||||
bool is_mixed_signed = $Type.kindof != any_type.kindof;
|
||||
$Type max = $Type.max;
|
||||
$Type min = $Type.min;
|
||||
switch (variant_type)
|
||||
switch (any_type)
|
||||
{
|
||||
case ichar:
|
||||
ichar c = *(char*)v.ptr;
|
||||
if (is_mixed_signed && c < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE!;
|
||||
if (is_mixed_signed && c < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE?;
|
||||
return ($Type)c;
|
||||
case short:
|
||||
short s = *(short*)v.ptr;
|
||||
if (is_mixed_signed && s < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE!;
|
||||
if (s > max || s < min) return ConversionResult.VALUE_OUT_OF_RANGE!;
|
||||
if (is_mixed_signed && s < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE?;
|
||||
if (s > max || s < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
|
||||
return ($Type)s;
|
||||
case int:
|
||||
int i = *(int*)v.ptr;
|
||||
if (is_mixed_signed && i < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE!;
|
||||
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE!;
|
||||
if (is_mixed_signed && i < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE?;
|
||||
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
|
||||
return ($Type)i;
|
||||
case long:
|
||||
long l = *(long*)v.ptr;
|
||||
if (is_mixed_signed && l < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE!;
|
||||
if (l > max || l < min) return ConversionResult.VALUE_OUT_OF_RANGE!;
|
||||
if (is_mixed_signed && l < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE?;
|
||||
if (l > max || l < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
|
||||
return ($Type)l;
|
||||
case int128:
|
||||
int128 i = *(int128*)v.ptr;
|
||||
if (is_mixed_signed && i < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE!;
|
||||
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE!;
|
||||
if (is_mixed_signed && i < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE?;
|
||||
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
|
||||
return ($Type)i;
|
||||
case char:
|
||||
char c = *(char*)v.ptr;
|
||||
if (c > max) return ConversionResult.VALUE_OUT_OF_RANGE!;
|
||||
if (c > max) return ConversionResult.VALUE_OUT_OF_RANGE?;
|
||||
return ($Type)c;
|
||||
case ushort:
|
||||
ushort s = *(ushort*)v.ptr;
|
||||
if (s > max || s < min) return ConversionResult.VALUE_OUT_OF_RANGE!;
|
||||
if (s > max || s < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
|
||||
return ($Type)s;
|
||||
case uint:
|
||||
uint i = *(uint*)v.ptr;
|
||||
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE!;
|
||||
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
|
||||
return ($Type)i;
|
||||
case ulong:
|
||||
ulong l = *(ulong*)v.ptr;
|
||||
if (l > max || l < min) return ConversionResult.VALUE_OUT_OF_RANGE!;
|
||||
if (l > max || l < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
|
||||
return ($Type)l;
|
||||
case uint128:
|
||||
uint128 i = *(uint128*)v.ptr;
|
||||
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE!;
|
||||
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
|
||||
return ($Type)i;
|
||||
default:
|
||||
unreachable();
|
||||
@@ -219,15 +220,20 @@ macro bool is_same_vector_type($Type1, $Type2)
|
||||
$endif
|
||||
}
|
||||
|
||||
macro bool is_equatable_value(value)
|
||||
macro bool is_equatable_type($Type)
|
||||
{
|
||||
$if ($defined(value.less) || $defined(value.compare_to) || $defined(value.equals))
|
||||
$if ($defined($Type.less) || $defined($Type.compare_to) || $defined($Type.equals))
|
||||
return true;
|
||||
$else
|
||||
return is_equatable($typeof(value));
|
||||
return is_equatable($Type);
|
||||
$endif
|
||||
}
|
||||
|
||||
macro bool is_equatable_value(value)
|
||||
{
|
||||
return is_equatable_type($typeof(value));
|
||||
}
|
||||
|
||||
macro bool is_comparable_value(value)
|
||||
{
|
||||
$if ($defined(value.less) || $defined(value.compare_to))
|
||||
@@ -245,8 +251,8 @@ enum TypeKind : char
|
||||
UNSIGNED_INT,
|
||||
FLOAT,
|
||||
TYPEID,
|
||||
ANYERR,
|
||||
VARIANT,
|
||||
ANYFAULT,
|
||||
ANY,
|
||||
ENUM,
|
||||
FAULT,
|
||||
STRUCT,
|
||||
|
||||
@@ -20,3 +20,4 @@ macro promote_int(x)
|
||||
}
|
||||
|
||||
macro TypeKind @inner_kind(#value) => types::inner_kind($typeof(#value));
|
||||
|
||||
|
||||
@@ -1,327 +0,0 @@
|
||||
module std::core::string;
|
||||
import libc;
|
||||
|
||||
typedef VarString = distinct void*;
|
||||
typedef DynStr = VarString;
|
||||
typedef DynString = VarString;
|
||||
typedef VString = VarString;
|
||||
typedef Text = VarString;
|
||||
|
||||
const usz MIN_CAPACITY = 16;
|
||||
|
||||
fn VarString new_with_capacity(usz capacity, Allocator* allocator = mem::heap())
|
||||
{
|
||||
if (capacity < MIN_CAPACITY) capacity = MIN_CAPACITY;
|
||||
StringData* data = malloc(StringData, 1, .using = allocator, .end_padding = capacity);
|
||||
data.allocator = allocator;
|
||||
data.len = 0;
|
||||
data.capacity = capacity;
|
||||
return (VarString)data;
|
||||
}
|
||||
|
||||
fn VarString new(String c)
|
||||
{
|
||||
usz len = c.len;
|
||||
VarString str = new_with_capacity(len);
|
||||
StringData* data = str.data();
|
||||
if (len)
|
||||
{
|
||||
data.len = len;
|
||||
mem::copy(&data.chars, c.ptr, len);
|
||||
}
|
||||
return (VarString)data;
|
||||
}
|
||||
|
||||
fn ZString VarString.zstr(VarString str)
|
||||
{
|
||||
StringData* data = str.data();
|
||||
if (!data) return (ZString)"";
|
||||
if (data.capacity == data.len)
|
||||
{
|
||||
str.reserve(1);
|
||||
data.chars[data.len] = 0;
|
||||
}
|
||||
else if (data.chars[data.len] != 0)
|
||||
{
|
||||
data.chars[data.len] = 0;
|
||||
}
|
||||
return (ZString)&data.chars[0];
|
||||
}
|
||||
|
||||
fn usz VarString.capacity(VarString this)
|
||||
{
|
||||
if (!this) return 0;
|
||||
return this.data().capacity;
|
||||
}
|
||||
|
||||
fn usz VarString.len(VarString this)
|
||||
{
|
||||
if (!this) return 0;
|
||||
return this.data().len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require new_size <= this.len()
|
||||
*/
|
||||
fn void VarString.chop(VarString this, usz new_size)
|
||||
{
|
||||
if (!this) return;
|
||||
this.data().len = new_size;
|
||||
}
|
||||
|
||||
fn String VarString.str(VarString str)
|
||||
{
|
||||
StringData* data = (StringData*)str;
|
||||
if (!data) return "";
|
||||
return (String)data.chars[:data.len];
|
||||
}
|
||||
|
||||
fn void VarString.append_utf32(VarString* str, Char32[] chars)
|
||||
{
|
||||
str.reserve(chars.len);
|
||||
foreach (Char32 c : chars)
|
||||
{
|
||||
str.append_char32(c);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @require index < str.len()
|
||||
**/
|
||||
fn void VarString.set(VarString str, usz index, char c)
|
||||
{
|
||||
str.data().chars[index] = c;
|
||||
}
|
||||
|
||||
fn void VarString.append_repeat(VarString* str, char c, usz times)
|
||||
{
|
||||
if (times == 0) return;
|
||||
str.reserve(times);
|
||||
StringData* data = str.data();
|
||||
for (usz i = 0; i < times; i++)
|
||||
{
|
||||
data.chars[data.len++] = c;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @require c <= 0x10ffff
|
||||
*/
|
||||
fn void VarString.append_char32(VarString* str, Char32 c)
|
||||
{
|
||||
if (c < 0x7f)
|
||||
{
|
||||
str.reserve(1);
|
||||
StringData* data = str.data();
|
||||
data.chars[data.len++] = (char)c;
|
||||
return;
|
||||
}
|
||||
if (c < 0x7ff)
|
||||
{
|
||||
str.reserve(2);
|
||||
StringData* data = str.data();
|
||||
data.chars[data.len++] = (char)(0xC0 | c >> 6);
|
||||
data.chars[data.len++] = (char)(0x80 | (c & 0x3F));
|
||||
return;
|
||||
}
|
||||
if (c < 0xffff)
|
||||
{
|
||||
str.reserve(3);
|
||||
StringData* data = str.data();
|
||||
data.chars[data.len++] = (char)(0xE0 | c >> 12);
|
||||
data.chars[data.len++] = (char)(0x80 | (c >> 6 & 0x3F));
|
||||
data.chars[data.len++] = (char)(0x80 | (c & 0x3F));
|
||||
return;
|
||||
}
|
||||
str.reserve(4);
|
||||
StringData* data = str.data();
|
||||
data.chars[data.len++] = (char)(0xF0 | c >> 18);
|
||||
data.chars[data.len++] = (char)(0x80 | (c >> 12 & 0x3F));
|
||||
data.chars[data.len++] = (char)(0x80 | (c >> 6 & 0x3F));
|
||||
data.chars[data.len++] = (char)(0x80 | (c & 0x3F));
|
||||
}
|
||||
|
||||
fn VarString VarString.tcopy(VarString* str) => str.copy(mem::temp());
|
||||
|
||||
fn VarString VarString.copy(VarString* str, Allocator* allocator = null)
|
||||
{
|
||||
if (!str)
|
||||
{
|
||||
if (allocator) return new_with_capacity(0, allocator);
|
||||
return (VarString)null;
|
||||
}
|
||||
if (!allocator) allocator = mem::heap();
|
||||
StringData* data = str.data();
|
||||
VarString new_string = new_with_capacity(data.capacity, allocator);
|
||||
mem::copy((char*)new_string.data(), (char*)data, StringData.sizeof + data.len);
|
||||
return new_string;
|
||||
}
|
||||
|
||||
fn ZString VarString.copy_zstr(VarString* str, Allocator* allocator = mem::heap())
|
||||
{
|
||||
usz str_len = str.len();
|
||||
if (!str_len)
|
||||
{
|
||||
return (ZString)calloc(1, .using = allocator);
|
||||
}
|
||||
char* zstr = malloc(str_len + 1, .using = allocator);
|
||||
StringData* data = str.data();
|
||||
mem::copy(zstr, &data.chars, str_len);
|
||||
zstr[str_len] = 0;
|
||||
return (ZString)zstr;
|
||||
}
|
||||
|
||||
fn String VarString.copy_str(VarString* str, Allocator* allocator = mem::heap())
|
||||
{
|
||||
return (String)str.copy_zstr(allocator)[:str.len()];
|
||||
}
|
||||
|
||||
fn String VarString.tcopy_str(VarString* str) => str.copy_str(mem::temp()) @inline;
|
||||
|
||||
fn bool VarString.equals(VarString str, VarString other_string)
|
||||
{
|
||||
StringData *str1 = str.data();
|
||||
StringData *str2 = other_string.data();
|
||||
if (str1 == str2) return true;
|
||||
if (!str1) return str2.len == 0;
|
||||
if (!str2) return str1.len == 0;
|
||||
usz str1_len = str1.len;
|
||||
if (str1_len != str2.len) return false;
|
||||
for (int i = 0; i < str1_len; i++)
|
||||
{
|
||||
if (str1.chars[i] != str2.chars[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
fn void VarString.destroy(VarString* str)
|
||||
{
|
||||
if (!*str) return;
|
||||
StringData* data = str.data();
|
||||
if (!data) return;
|
||||
free(data, .using = data.allocator);
|
||||
*str = (VarString)null;
|
||||
}
|
||||
|
||||
fn bool VarString.less(VarString str, VarString other_string)
|
||||
{
|
||||
StringData* str1 = str.data();
|
||||
StringData* str2 = other_string.data();
|
||||
if (str1 == str2) return false;
|
||||
if (!str1) return str2.len != 0;
|
||||
if (!str2) return str1.len == 0;
|
||||
usz str1_len = str1.len;
|
||||
usz str2_len = str2.len;
|
||||
if (str1_len != str2_len) return str1_len < str2_len;
|
||||
for (int i = 0; i < str1_len; i++)
|
||||
{
|
||||
if (str1.chars[i] >= str2.chars[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
fn void VarString.append_chars(VarString* this, String str)
|
||||
{
|
||||
usz other_len = str.len;
|
||||
if (!other_len) return;
|
||||
if (!*this)
|
||||
{
|
||||
*this = new(str);
|
||||
return;
|
||||
}
|
||||
this.reserve(other_len);
|
||||
StringData* data = (StringData*)*this;
|
||||
mem::copy(&data.chars[data.len], str.ptr, other_len);
|
||||
data.len += other_len;
|
||||
}
|
||||
|
||||
fn Char32[] VarString.copy_utf32(VarString* this, Allocator* allocator = mem::heap())
|
||||
{
|
||||
return str::utf8to32(this.str(), allocator) @inline!!;
|
||||
}
|
||||
|
||||
fn void VarString.append_string(VarString* this, VarString str)
|
||||
{
|
||||
StringData* other = (StringData*)str;
|
||||
if (!other) return;
|
||||
this.append(str.str());
|
||||
}
|
||||
|
||||
fn void VarString.clear(VarString* str)
|
||||
{
|
||||
str.data().len = 0;
|
||||
}
|
||||
|
||||
fn void VarString.append_char(VarString* str, char c)
|
||||
{
|
||||
if (!*str)
|
||||
{
|
||||
*str = new_with_capacity(MIN_CAPACITY);
|
||||
}
|
||||
str.reserve(1);
|
||||
StringData* data = (StringData*)*str;
|
||||
data.chars[data.len++] = c;
|
||||
}
|
||||
|
||||
|
||||
macro void VarString.append(VarString* str, value)
|
||||
{
|
||||
var $Type = $typeof(value);
|
||||
$switch ($Type)
|
||||
$case char:
|
||||
$case ichar:
|
||||
str.append_char(value);
|
||||
$case VarString:
|
||||
str.append_string(value);
|
||||
$case String:
|
||||
str.append_chars(value);
|
||||
$case Char32:
|
||||
str.append_char32(value);
|
||||
$default:
|
||||
$switch
|
||||
$case @convertible(value, Char32):
|
||||
str.append_char32(value);
|
||||
$case @convertible(value, String):
|
||||
str.append_chars(value);
|
||||
$default:
|
||||
$assert(false, "Unsupported type for append – use printf instead.");
|
||||
$endswitch
|
||||
$endswitch
|
||||
}
|
||||
|
||||
|
||||
fn StringData* VarString.data(VarString str) @inline @private
|
||||
{
|
||||
return (StringData*)str;
|
||||
}
|
||||
|
||||
fn void VarString.reserve(VarString* str, usz addition) @private
|
||||
{
|
||||
StringData* data = str.data();
|
||||
if (!data)
|
||||
{
|
||||
*str = string::new_with_capacity(addition);
|
||||
return;
|
||||
}
|
||||
usz len = data.len + addition;
|
||||
if (data.capacity >= len) return;
|
||||
usz new_capacity = data.capacity *= 2;
|
||||
if (new_capacity < MIN_CAPACITY) new_capacity = MIN_CAPACITY;
|
||||
*str = (VarString)realloc(data, StringData.sizeof + new_capacity, .using = data.allocator);
|
||||
}
|
||||
|
||||
fn VarString VarString.new_concat(VarString a, VarString b, Allocator* allocator = mem::heap())
|
||||
{
|
||||
VarString string = new_with_capacity(a.len() + b.len(), allocator);
|
||||
string.append(a);
|
||||
string.append(b);
|
||||
return string;
|
||||
}
|
||||
|
||||
struct StringData @private
|
||||
{
|
||||
Allocator* allocator;
|
||||
usz len;
|
||||
usz capacity;
|
||||
char[*] chars;
|
||||
}
|
||||
@@ -32,7 +32,7 @@ struct JsonParser
|
||||
DString last_string;
|
||||
double last_number;
|
||||
char current;
|
||||
anyerr current_err;
|
||||
anyfault current_err;
|
||||
bool skip_comments;
|
||||
bool reached_end;
|
||||
}
|
||||
@@ -61,13 +61,13 @@ fn Object*! JsonParser.parse_from_token(JsonParser* this, JsonTokenType token)
|
||||
case COMMA:
|
||||
case RBRACE:
|
||||
case RBRACKET:
|
||||
case COLON: return JsonParsingError.UNEXPECTED_CHARACTER!;
|
||||
case COLON: return JsonParsingError.UNEXPECTED_CHARACTER?;
|
||||
case STRING: return object::new_string(this.last_string.str(), this.allocator);
|
||||
case NUMBER: return object::new_float(this.last_number, this.allocator);
|
||||
case TRUE: return object::new_bool(true);
|
||||
case FALSE: return object::new_bool(false);
|
||||
case NULL: return object::new_null();
|
||||
case EOF: return JsonParsingError.EOF!;
|
||||
case EOF: return JsonParsingError.EOF?;
|
||||
}
|
||||
unreachable();
|
||||
}
|
||||
@@ -85,17 +85,17 @@ fn JsonTokenType! JsonParser.lex_number(JsonParser* this, char c)
|
||||
if (negate)
|
||||
{
|
||||
t.append(c);
|
||||
c = this.read_next()?;
|
||||
c = this.read_next()!;
|
||||
}
|
||||
while (c >= '0' && c <= '9')
|
||||
{
|
||||
t.append(c);
|
||||
c = this.read_next()?;
|
||||
c = this.read_next()!;
|
||||
}
|
||||
if (c == '.')
|
||||
{
|
||||
t.append(c);
|
||||
while (c = this.read_next()?, c >= '0' && c <= '9')
|
||||
while (c = this.read_next()!, c >= '0' && c <= '9')
|
||||
{
|
||||
t.append(c);
|
||||
}
|
||||
@@ -103,24 +103,24 @@ fn JsonTokenType! JsonParser.lex_number(JsonParser* this, char c)
|
||||
if ((c | 32) == 'e')
|
||||
{
|
||||
t.append(c);
|
||||
c = this.read_next()?;
|
||||
c = this.read_next()!;
|
||||
switch (c)
|
||||
{
|
||||
case '-':
|
||||
case '+':
|
||||
t.append(c);
|
||||
c = this.read_next()?;
|
||||
c = this.read_next()!;
|
||||
}
|
||||
if (c < '0' || c > '9') return JsonParsingError.INVALID_NUMBER!;
|
||||
if (c < '0' || c > '9') return JsonParsingError.INVALID_NUMBER?;
|
||||
while (c >= '0' && c <= '9')
|
||||
{
|
||||
t.append(c);
|
||||
c = this.read_next()?;
|
||||
c = this.read_next()!;
|
||||
}
|
||||
}
|
||||
this.pushback();
|
||||
double! d = str::to_double(t.str()) ?? JsonParsingError.INVALID_NUMBER!;
|
||||
this.last_number = d?;
|
||||
double! d = t.str().to_double() ?? JsonParsingError.INVALID_NUMBER?;
|
||||
this.last_number = d!;
|
||||
return NUMBER;
|
||||
};
|
||||
}
|
||||
@@ -128,30 +128,30 @@ fn JsonTokenType! JsonParser.lex_number(JsonParser* this, char c)
|
||||
fn Object*! JsonParser.parse_map(JsonParser* this)
|
||||
{
|
||||
Object* map = object::new_obj(this.allocator);
|
||||
JsonTokenType token = this.advance()?;
|
||||
JsonTokenType token = this.advance()!;
|
||||
defer catch map.free();
|
||||
|
||||
DString temp_key = dstring::new_with_capacity(32, this.allocator);
|
||||
defer temp_key.free();
|
||||
while (token != JsonTokenType.RBRACE)
|
||||
{
|
||||
if (token != JsonTokenType.STRING) return JsonParsingError.UNEXPECTED_CHARACTER!;
|
||||
if (token != JsonTokenType.STRING) return JsonParsingError.UNEXPECTED_CHARACTER?;
|
||||
DString string = this.last_string;
|
||||
if (map.has_key(string.str())) return JsonParsingError.DUPLICATE_MEMBERS!;
|
||||
if (map.has_key(string.str())) return JsonParsingError.DUPLICATE_MEMBERS?;
|
||||
// Copy the key to our temp holder. We do this to work around the issue
|
||||
// if the temp allocator should be used as the default allocator.
|
||||
temp_key.clear();
|
||||
temp_key.append(string);
|
||||
this.parse_expected(COLON)?;
|
||||
Object* element = this.parse_any()?;
|
||||
this.parse_expected(COLON)!;
|
||||
Object* element = this.parse_any()!;
|
||||
map.set(temp_key.str(), element);
|
||||
token = this.advance()?;
|
||||
token = this.advance()!;
|
||||
if (token == JsonTokenType.COMMA)
|
||||
{
|
||||
token = this.advance()?;
|
||||
token = this.advance()!;
|
||||
continue;
|
||||
}
|
||||
if (token != JsonTokenType.RBRACE) return JsonParsingError.UNEXPECTED_CHARACTER!;
|
||||
if (token != JsonTokenType.RBRACE) return JsonParsingError.UNEXPECTED_CHARACTER?;
|
||||
}
|
||||
return map;
|
||||
}
|
||||
@@ -160,18 +160,18 @@ fn Object*! JsonParser.parse_array(JsonParser* this)
|
||||
{
|
||||
Object* list = object::new_obj(this.allocator);
|
||||
defer catch list.free();
|
||||
JsonTokenType token = this.advance()?;
|
||||
JsonTokenType token = this.advance()!;
|
||||
while (token != JsonTokenType.RBRACKET)
|
||||
{
|
||||
Object* element = this.parse_from_token(token)?;
|
||||
Object* element = this.parse_from_token(token)!;
|
||||
list.append(element);
|
||||
token = this.advance()?;
|
||||
token = this.advance()!;
|
||||
if (token == JsonTokenType.COMMA)
|
||||
{
|
||||
token = this.advance()?;
|
||||
token = this.advance()!;
|
||||
continue;
|
||||
}
|
||||
if (token != JsonTokenType.RBRACKET) return JsonParsingError.UNEXPECTED_CHARACTER!;
|
||||
if (token != JsonTokenType.RBRACKET) return JsonParsingError.UNEXPECTED_CHARACTER?;
|
||||
}
|
||||
return list;
|
||||
}
|
||||
@@ -191,7 +191,7 @@ fn char! JsonParser.read_next(JsonParser* this)
|
||||
this.reached_end = true;
|
||||
return '\0';
|
||||
default:
|
||||
return err!;
|
||||
return err?;
|
||||
}
|
||||
if (c == 0)
|
||||
{
|
||||
@@ -204,7 +204,7 @@ fn JsonTokenType! JsonParser.advance(JsonParser* this)
|
||||
{
|
||||
char c;
|
||||
// Skip whitespace
|
||||
while WS: (c = this.read_next()?)
|
||||
while WS: (c = this.read_next()!)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
@@ -218,7 +218,7 @@ fn JsonTokenType! JsonParser.advance(JsonParser* this)
|
||||
continue;
|
||||
case '/':
|
||||
if (!this.skip_comments) break;
|
||||
c = this.read_next()?;
|
||||
c = this.read_next()!;
|
||||
if (c != '*')
|
||||
{
|
||||
this.pushback();
|
||||
@@ -227,12 +227,12 @@ fn JsonTokenType! JsonParser.advance(JsonParser* this)
|
||||
while COMMENT: (1)
|
||||
{
|
||||
// Skip to */
|
||||
while (c = this.read_next()?)
|
||||
while (c = this.read_next()!)
|
||||
{
|
||||
if (c == '\n') this.line++;
|
||||
if (c != '*') continue;
|
||||
// Skip through all the '*'
|
||||
while (c = this.read_next()?)
|
||||
while (c = this.read_next()!)
|
||||
{
|
||||
if (c == '\n') this.line++;
|
||||
if (c != '*') break;
|
||||
@@ -248,7 +248,7 @@ fn JsonTokenType! JsonParser.advance(JsonParser* this)
|
||||
switch (c)
|
||||
{
|
||||
case '\0':
|
||||
return IoError.EOF!;
|
||||
return IoError.EOF?;
|
||||
case '{':
|
||||
return LBRACE;
|
||||
case '}':
|
||||
@@ -267,16 +267,16 @@ fn JsonTokenType! JsonParser.advance(JsonParser* this)
|
||||
case '0'..'9':
|
||||
return this.lex_number(c);
|
||||
case 't':
|
||||
this.match("rue")?;
|
||||
this.match("rue")!;
|
||||
return TRUE;
|
||||
case 'f':
|
||||
this.match("alse")?;
|
||||
this.match("alse")!;
|
||||
return FALSE;
|
||||
case 'n':
|
||||
this.match("ull")?;
|
||||
this.match("ull")!;
|
||||
return NULL;
|
||||
default:
|
||||
return JsonParsingError.UNEXPECTED_CHARACTER!;
|
||||
return JsonParsingError.UNEXPECTED_CHARACTER?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,14 +284,14 @@ fn void! JsonParser.match(JsonParser* this, String str)
|
||||
{
|
||||
foreach (c : str)
|
||||
{
|
||||
char l = this.read_next()?;
|
||||
if (l != c) return JsonParsingError.UNEXPECTED_CHARACTER!;
|
||||
char l = this.read_next()!;
|
||||
if (l != c) return JsonParsingError.UNEXPECTED_CHARACTER?;
|
||||
}
|
||||
}
|
||||
|
||||
fn void! JsonParser.parse_expected(JsonParser* this, JsonTokenType token) @local
|
||||
{
|
||||
if (this.advance()? != token) return JsonParsingError.UNEXPECTED_CHARACTER!;
|
||||
if (this.advance()! != token) return JsonParsingError.UNEXPECTED_CHARACTER?;
|
||||
}
|
||||
|
||||
fn JsonTokenType! JsonParser.lex_string(JsonParser *this)
|
||||
@@ -299,13 +299,13 @@ fn JsonTokenType! JsonParser.lex_string(JsonParser *this)
|
||||
this.last_string.clear();
|
||||
while LOOP: (1)
|
||||
{
|
||||
char c = this.read_next()?;
|
||||
char c = this.read_next()!;
|
||||
switch (c)
|
||||
{
|
||||
case '\0':
|
||||
return JsonParsingError.EOF!;
|
||||
return JsonParsingError.EOF?;
|
||||
case 1..31:
|
||||
return JsonParsingError.UNEXPECTED_CHARACTER!;
|
||||
return JsonParsingError.UNEXPECTED_CHARACTER?;
|
||||
case '"':
|
||||
break LOOP;
|
||||
case '\\':
|
||||
@@ -314,13 +314,13 @@ fn JsonTokenType! JsonParser.lex_string(JsonParser *this)
|
||||
this.last_string.append(c);
|
||||
continue;
|
||||
}
|
||||
c = this.read_next()?;
|
||||
c = this.read_next()!;
|
||||
switch (c)
|
||||
{
|
||||
case '\0':
|
||||
return JsonParsingError.EOF!;
|
||||
return JsonParsingError.EOF?;
|
||||
case 1..31:
|
||||
return JsonParsingError.UNEXPECTED_CHARACTER!;
|
||||
return JsonParsingError.UNEXPECTED_CHARACTER?;
|
||||
case '"':
|
||||
case '\\':
|
||||
case '/':
|
||||
@@ -339,14 +339,14 @@ fn JsonTokenType! JsonParser.lex_string(JsonParser *this)
|
||||
uint val;
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
c = this.read_next()?;
|
||||
if (!c.is_xdigit()) return JsonParsingError.INVALID_ESCAPE_SEQUENCE!;
|
||||
c = this.read_next()!;
|
||||
if (!c.is_xdigit()) return JsonParsingError.INVALID_ESCAPE_SEQUENCE?;
|
||||
val = val << 4 + (c > '9' ? (c | 32) - 'a' + 10 : c - '0');
|
||||
}
|
||||
this.last_string.append_char32(val);
|
||||
continue;
|
||||
default:
|
||||
return JsonParsingError.INVALID_ESCAPE_SEQUENCE!;
|
||||
return JsonParsingError.INVALID_ESCAPE_SEQUENCE?;
|
||||
}
|
||||
}
|
||||
return STRING;
|
||||
|
||||
@@ -25,6 +25,7 @@ fault IoError
|
||||
FILE_IS_PIPE,
|
||||
FILE_EOF,
|
||||
INCOMPLETE_WRITE,
|
||||
BUSY,
|
||||
NO_PERMISSION,
|
||||
OUT_OF_SPACE,
|
||||
INVALID_PUSHBACK,
|
||||
@@ -39,6 +40,7 @@ fault IoError
|
||||
NOT_SEEKABLE,
|
||||
NAME_TOO_LONG,
|
||||
WOULD_BLOCK,
|
||||
DIR_NOT_EMPTY,
|
||||
INTERRUPTED,
|
||||
GENERAL_ERROR,
|
||||
UNKNOWN_ERROR,
|
||||
@@ -55,18 +57,16 @@ macro void print(x)
|
||||
var $Type = $typeof(x);
|
||||
$switch ($Type)
|
||||
$case String:
|
||||
catch? stdout().print(x);
|
||||
(void)stdout().print(x);
|
||||
$case ZString:
|
||||
catch? stdout().print(x.as_str());
|
||||
(void)stdout().print(x.as_str());
|
||||
$case DString:
|
||||
catch? stdout().print(x.str());
|
||||
$case VarString:
|
||||
catch? stdout().print(x.str());
|
||||
(void)stdout().print(x.str());
|
||||
$default:
|
||||
$if (@convertible(x, String))
|
||||
catch? stdout().print((String)x);
|
||||
(void)stdout().print((String)x);
|
||||
$else
|
||||
catch? stdout().printf("%s", x);
|
||||
(void)stdout().printf("%s", x);
|
||||
$endif
|
||||
$endswitch
|
||||
}
|
||||
@@ -76,24 +76,20 @@ macro void printn(x = "")
|
||||
var $Type = $typeof(x);
|
||||
$switch ($Type)
|
||||
$case String:
|
||||
catch? stdout().printn(x);
|
||||
(void)stdout().printn(x);
|
||||
$case ZString:
|
||||
catch? stdout().printn(x.as_str());
|
||||
(void)stdout().printn(x.as_str());
|
||||
$case DString:
|
||||
catch? stdout().printn(x.str());
|
||||
$case VarString:
|
||||
catch? stdout().printn(x.str());
|
||||
(void)stdout().printn(x.str());
|
||||
$default:
|
||||
$if (@convertible(x, String))
|
||||
catch? stdout().printn((String)x);
|
||||
(void)stdout().printn((String)x);
|
||||
$else
|
||||
catch? stdout().printfn("%s", x);
|
||||
(void)stdout().printfn("%s", x);
|
||||
$endif
|
||||
$endswitch
|
||||
}
|
||||
|
||||
macro void println(x = "") @deprecated => printn(x);
|
||||
|
||||
fn File stdout()
|
||||
{
|
||||
return { libc::stdout() };
|
||||
|
||||
@@ -6,9 +6,9 @@ fn File! open(String filename, String mode)
|
||||
return { .file = os::native_fopen(filename, mode) };
|
||||
}
|
||||
|
||||
fn void! File.open(File* file, String filename, String mode) @deprecated
|
||||
fn File! open_path(Path path, String mode)
|
||||
{
|
||||
file.file = os::native_fopen(filename, mode)?;
|
||||
return { .file = os::native_fopen(path.as_str(), mode) };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -16,7 +16,7 @@ fn void! File.open(File* file, String filename, String mode) @deprecated
|
||||
**/
|
||||
fn void! File.reopen(File* file, String filename, String mode)
|
||||
{
|
||||
file.file = os::native_freopen(file.file, filename, mode)?;
|
||||
file.file = os::native_freopen(file.file, filename, mode)!;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -24,7 +24,7 @@ fn void! File.reopen(File* file, String filename, String mode)
|
||||
**/
|
||||
fn usz! File.seek(File file, isz offset, Seek seek_mode = Seek.SET)
|
||||
{
|
||||
os::native_fseek(file.file, offset, seek_mode)?;
|
||||
os::native_fseek(file.file, offset, seek_mode)!;
|
||||
return os::native_ftell(file.file);
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ fn void! File.memopen(File* file, char[] data, String mode)
|
||||
*/
|
||||
fn void! File.putc(File *file, char c)
|
||||
{
|
||||
if (!libc::fputc(c, file.file)) return IoError.FILE_EOF!;
|
||||
if (!libc::fputc(c, file.file)) return IoError.FILE_EOF?;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,8 +63,8 @@ fn void! File.close(File *file) @inline
|
||||
switch (libc::errno())
|
||||
{
|
||||
case errno::ECONNRESET:
|
||||
case errno::EBADF: return IoError.FILE_NOT_VALID!;
|
||||
case errno::EINTR: return IoError.INTERRUPTED!;
|
||||
case errno::EBADF: return IoError.FILE_NOT_VALID?;
|
||||
case errno::EINTR: return IoError.INTERRUPTED?;
|
||||
case errno::EDQUOT:
|
||||
case errno::EFAULT:
|
||||
case errno::EAGAIN:
|
||||
@@ -72,8 +72,8 @@ fn void! File.close(File *file) @inline
|
||||
case errno::ENETDOWN:
|
||||
case errno::ENETUNREACH:
|
||||
case errno::ENOSPC:
|
||||
case errno::EIO: return IoError.INCOMPLETE_WRITE!;
|
||||
default: return IoError.UNKNOWN_ERROR!;
|
||||
case errno::EIO: return IoError.INCOMPLETE_WRITE?;
|
||||
default: return IoError.UNKNOWN_ERROR?;
|
||||
}
|
||||
}
|
||||
file.file = null;
|
||||
@@ -111,8 +111,8 @@ fn usz! File.write(File file, char[] buffer)
|
||||
*/
|
||||
fn usz! File.printn(File file, String string)
|
||||
{
|
||||
usz len = file.print(string)?;
|
||||
if (!libc::putc('\n', file.file)) return IoError.UNKNOWN_ERROR!;
|
||||
usz len = file.print(string)!;
|
||||
if (!libc::putc('\n', file.file)) return IoError.UNKNOWN_ERROR?;
|
||||
return len + 1;
|
||||
}
|
||||
|
||||
@@ -123,12 +123,10 @@ fn usz! File.printn(File file, String string)
|
||||
fn usz! File.print(File file, String string)
|
||||
{
|
||||
usz len = string.len;
|
||||
if (len != file.write((char[])string)?) return IoError.UNKNOWN_ERROR!;
|
||||
if (len != file.write((char[])string)!) return IoError.UNKNOWN_ERROR?;
|
||||
return len;
|
||||
}
|
||||
|
||||
fn usz! File.println(File file, String string) @deprecated => file.printn(string);
|
||||
|
||||
/**
|
||||
* @param [&in] file
|
||||
* @require file.file `File must be initialized`
|
||||
@@ -159,7 +157,7 @@ fn String File.tgetline(File* file)
|
||||
fn char! File.getc(File* file)
|
||||
{
|
||||
int c = libc::fgetc(file.file);
|
||||
if (c == -1) return IoError.FILE_EOF!;
|
||||
if (c == -1) return IoError.FILE_EOF?;
|
||||
return (char)c;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,17 +6,17 @@ const char[16] XDIGITS_L = "0123456789abcdef";
|
||||
fn void! Formatter.left_adjust(Formatter* this, usz len) @local
|
||||
{
|
||||
if (!this.flags.left) return;
|
||||
for (usz l = len; l < this.width; l++) this.out(' ')?;
|
||||
for (usz l = len; l < this.width; l++) this.out(' ')!;
|
||||
}
|
||||
|
||||
fn void! Formatter.right_adjust(Formatter* this, usz len) @local
|
||||
{
|
||||
if (this.flags.left) return;
|
||||
for (usz l = len; l < this.width; l++) this.out(' ')?;
|
||||
for (usz l = len; l < this.width; l++) this.out(' ')!;
|
||||
}
|
||||
|
||||
|
||||
fn uint128! int_from_variant(variant arg, bool *is_neg) @private
|
||||
fn uint128! int_from_any(any arg, bool *is_neg) @private
|
||||
{
|
||||
*is_neg = false;
|
||||
if (arg.type.kindof == TypeKind.POINTER)
|
||||
@@ -25,7 +25,7 @@ fn uint128! int_from_variant(variant arg, bool *is_neg) @private
|
||||
}
|
||||
if (arg.type.kindof == TypeKind.DISTINCT)
|
||||
{
|
||||
return int_from_variant(variant { arg.ptr, arg.type.inner }, is_neg);
|
||||
return int_from_any(any { arg.ptr, arg.type.inner }, is_neg);
|
||||
}
|
||||
switch (arg)
|
||||
{
|
||||
@@ -63,11 +63,11 @@ fn uint128! int_from_variant(variant arg, bool *is_neg) @private
|
||||
double d = *arg;
|
||||
return (uint128)((*is_neg = d < 0) ? -d : d);
|
||||
default:
|
||||
return PrintFault.INVALID_ARGUMENT_TYPE!;
|
||||
return PrintFault.INVALID_ARGUMENT_TYPE?;
|
||||
}
|
||||
}
|
||||
|
||||
fn FloatType! float_from_variant(variant arg) @private
|
||||
fn FloatType! float_from_any(any arg) @private
|
||||
{
|
||||
$if (env::F128_SUPPORT)
|
||||
if (arg.type == float128.typeid) return (FloatType)*((float128*)arg.ptr);
|
||||
@@ -77,7 +77,7 @@ fn FloatType! float_from_variant(variant arg) @private
|
||||
$endif
|
||||
if (arg.type.kindof == TypeKind.DISTINCT)
|
||||
{
|
||||
return float_from_variant(variant { arg.ptr, arg.type.inner });
|
||||
return float_from_any(any { arg.ptr, arg.type.inner });
|
||||
}
|
||||
switch (arg)
|
||||
{
|
||||
@@ -108,7 +108,7 @@ fn FloatType! float_from_variant(variant arg) @private
|
||||
case double:
|
||||
return (FloatType)*arg;
|
||||
default:
|
||||
return PrintFault.INVALID_ARGUMENT_TYPE!;
|
||||
return PrintFault.INVALID_ARGUMENT_TYPE?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ fn void! Formatter.out_substr(Formatter *this, String str) @private
|
||||
usz l = conv::utf8_codepoints(str);
|
||||
uint prec = this.prec;
|
||||
if (this.flags.precision && l < prec) l = prec;
|
||||
this.right_adjust(' ')?;
|
||||
this.right_adjust(' ')!;
|
||||
usz index = 0;
|
||||
usz chars = str.len;
|
||||
char* ptr = str.ptr;
|
||||
@@ -150,7 +150,7 @@ fn void! Formatter.out_substr(Formatter *this, String str) @private
|
||||
char c = ptr[index];
|
||||
// Break if we have precision set and we ran out...
|
||||
if (c & 0xC0 != 0x80 && this.flags.precision && !prec--) break;
|
||||
this.out(c)?;
|
||||
this.out(c)!;
|
||||
index++;
|
||||
}
|
||||
return this.left_adjust(l);
|
||||
@@ -158,7 +158,7 @@ fn void! Formatter.out_substr(Formatter *this, String str) @private
|
||||
|
||||
fn void! Formatter.pad(Formatter* this, char c, isz width, isz len) @inline
|
||||
{
|
||||
for (isz i = len; i < width; i++) this.out(c)?;
|
||||
for (isz i = len; i < width; i++) this.out(c)!;
|
||||
}
|
||||
|
||||
fn char* fmt_u(uint128 x, char* s)
|
||||
@@ -170,7 +170,7 @@ fn char* fmt_u(uint128 x, char* s)
|
||||
|
||||
fn void! Formatter.out_chars(Formatter* this, char[] s)
|
||||
{
|
||||
foreach (c : s) this.out(c)?;
|
||||
foreach (c : s) this.out(c)!;
|
||||
}
|
||||
|
||||
enum FloatFormatting
|
||||
@@ -203,12 +203,12 @@ fn void! Formatter.floatformat(Formatter* this, FloatFormatting formatting, doub
|
||||
if (!math::is_finite(y))
|
||||
{
|
||||
// Add padding
|
||||
if (!this.flags.left) this.pad(' ', this.width, 3 + pl)?;
|
||||
if (!this.flags.left) this.pad(' ', this.width, 3 + pl)!;
|
||||
String s = this.flags.uppercase ? "INF" : "inf";
|
||||
if (y != y) this.flags.uppercase ? "NAN" : "nan";
|
||||
if (pl) this.out(is_neg ? '-' : '+')?;
|
||||
this.out_chars(s)?;
|
||||
if (this.flags.left) this.pad(' ', this.width, 3 + pl)?;
|
||||
if (pl) this.out(is_neg ? '-' : '+')!;
|
||||
this.out_chars(s)!;
|
||||
if (this.flags.left) this.pad(' ', this.width, 3 + pl)!;
|
||||
return;
|
||||
}
|
||||
// Rescale
|
||||
@@ -260,18 +260,18 @@ fn void! Formatter.floatformat(Formatter* this, FloatFormatting formatting, doub
|
||||
} while (y);
|
||||
isz outlen = s - buf;
|
||||
isz explen = ebuf - estr;
|
||||
if (p > int.max - 2 - explen - pl) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
if (p > int.max - 2 - explen - pl) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
|
||||
usz l = p && outlen - 2 < p
|
||||
? p + 2 + explen
|
||||
: outlen + explen;
|
||||
if (!this.flags.left && !this.flags.zeropad) this.pad(' ', this.width, pl + l)?;
|
||||
if (is_neg || this.flags.plus) this.out(is_neg ? '-' : '+')?;
|
||||
this.out_chars(this.flags.uppercase ? "0X" : "0x")?;
|
||||
if (this.flags.zeropad) this.pad('0', this.width, pl + l)?;
|
||||
this.out_chars(buf[:outlen])?;
|
||||
this.pad('0', l - outlen - explen, 0)?;
|
||||
this.out_chars(estr[:explen])?;
|
||||
if (this.flags.left) this.pad(' ', this.width, pl + l)?;
|
||||
if (!this.flags.left && !this.flags.zeropad) this.pad(' ', this.width, pl + l)!;
|
||||
if (is_neg || this.flags.plus) this.out(is_neg ? '-' : '+')!;
|
||||
this.out_chars(this.flags.uppercase ? "0X" : "0x")!;
|
||||
if (this.flags.zeropad) this.pad('0', this.width, pl + l)!;
|
||||
this.out_chars(buf[:outlen])!;
|
||||
this.pad('0', l - outlen - explen, 0)!;
|
||||
this.out_chars(estr[:explen])!;
|
||||
if (this.flags.left) this.pad(' ', this.width, pl + l)!;
|
||||
return;
|
||||
}
|
||||
if (p < 0) p = 6;
|
||||
@@ -427,12 +427,12 @@ fn void! Formatter.floatformat(Formatter* this, FloatFormatting formatting, doub
|
||||
}
|
||||
}
|
||||
}
|
||||
if (p > int.max - 1 - (isz)(p || this.flags.hash)) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
if (p > int.max - 1 - (isz)(p || this.flags.hash)) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
|
||||
int l = (int)(1 + p + (isz)(p || this.flags.hash));
|
||||
char* estr @noinit;
|
||||
if (formatting == FLOAT)
|
||||
{
|
||||
if (e > int.max - l) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
if (e > int.max - l) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
|
||||
if (e > 0) l += e;
|
||||
}
|
||||
else
|
||||
@@ -441,13 +441,13 @@ fn void! Formatter.floatformat(Formatter* this, FloatFormatting formatting, doub
|
||||
while (ebuf - estr < 2) (--estr)[0] = '0';
|
||||
*--estr = (e < 0 ? '-' : '+');
|
||||
*--estr = this.flags.uppercase ? 'E' : 'e';
|
||||
if (ebuf - estr > (isz)int.max - l) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
if (ebuf - estr > (isz)int.max - l) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
|
||||
l += (int)(ebuf - estr);
|
||||
}
|
||||
if (l > int.max - pl) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
if (!this.flags.left && !this.flags.zeropad) this.pad(' ', this.width, pl + l)?;
|
||||
if (is_neg || this.flags.plus) this.out(is_neg ? '-' : '+')?;
|
||||
if (this.flags.zeropad) this.pad('0', this.width, pl + l)?;
|
||||
if (l > int.max - pl) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
|
||||
if (!this.flags.left && !this.flags.zeropad) this.pad(' ', this.width, pl + l)!;
|
||||
if (is_neg || this.flags.plus) this.out(is_neg ? '-' : '+')!;
|
||||
if (this.flags.zeropad) this.pad('0', this.width, pl + l)!;
|
||||
if (formatting == FLOAT)
|
||||
{
|
||||
if (a > r) a = r;
|
||||
@@ -462,16 +462,16 @@ fn void! Formatter.floatformat(Formatter* this, FloatFormatting formatting, doub
|
||||
case s == buf + 9:
|
||||
*--s = '0';
|
||||
}
|
||||
this.out_chars(s[:buf + 9 - s])?;
|
||||
this.out_chars(s[:buf + 9 - s])!;
|
||||
}
|
||||
if (p || this.flags.hash) this.out('.')?;
|
||||
if (p || this.flags.hash) this.out('.')!;
|
||||
for (; d < z && p > 0; d++, p -= 9)
|
||||
{
|
||||
char* s = fmt_u(*d, buf + 9);
|
||||
while (s > buf) *--s = '0';
|
||||
this.out_chars(s[:math::min((isz)9, p)])?;
|
||||
this.out_chars(s[:math::min((isz)9, p)])!;
|
||||
}
|
||||
this.pad('0', p + 9, 9)?;
|
||||
this.pad('0', p + 9, 9)!;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -486,17 +486,17 @@ fn void! Formatter.floatformat(Formatter* this, FloatFormatting formatting, doub
|
||||
}
|
||||
else
|
||||
{
|
||||
this.out(s++[0])?;
|
||||
if (p > 0 || this.flags.hash) this.out('.')?;
|
||||
this.out(s++[0])!;
|
||||
if (p > 0 || this.flags.hash) this.out('.')!;
|
||||
}
|
||||
this.out_chars(s[:math::min(buf + 9 - s, p)])?;
|
||||
this.out_chars(s[:math::min(buf + 9 - s, p)])!;
|
||||
p -= buf + 9 - s;
|
||||
}
|
||||
this.pad('0', p + 18, 18)?;
|
||||
this.out_chars(estr[:ebuf - estr])?;
|
||||
this.pad('0', p + 18, 18)!;
|
||||
this.out_chars(estr[:ebuf - estr])!;
|
||||
}
|
||||
|
||||
if (this.flags.left) this.pad(' ', this.width, pl + l)?;
|
||||
if (this.flags.left) this.pad(' ', this.width, pl + l)!;
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -515,7 +515,7 @@ fn void! Formatter.ntoa(Formatter* this, uint128 value, bool negative, uint base
|
||||
char past_10 = (this.flags.uppercase ? 'A' : 'a') - 10;
|
||||
do
|
||||
{
|
||||
if (len >= PRINTF_NTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
if (len >= PRINTF_NTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
|
||||
char digit = (char)(value % base);
|
||||
buf[len++] = digit + (digit < 10 ? '0' : past_10);
|
||||
value /= base;
|
||||
@@ -533,12 +533,12 @@ fn void! Formatter.ntoa_format(Formatter* this, String buf, usz len, bool negati
|
||||
if (this.width && this.flags.zeropad && (negative || this.flags.plus || this.flags.space)) this.width--;
|
||||
while (len < this.prec)
|
||||
{
|
||||
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
|
||||
buf[len++] = '0';
|
||||
}
|
||||
while (this.flags.zeropad && len < this.width)
|
||||
{
|
||||
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
|
||||
buf[len++] = '0';
|
||||
}
|
||||
}
|
||||
@@ -553,7 +553,7 @@ fn void! Formatter.ntoa_format(Formatter* this, String buf, usz len, bool negati
|
||||
}
|
||||
if (base != 10)
|
||||
{
|
||||
if (len + 1 >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
if (len + 1 >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
|
||||
switch (base)
|
||||
{
|
||||
case 16:
|
||||
@@ -572,13 +572,13 @@ fn void! Formatter.ntoa_format(Formatter* this, String buf, usz len, bool negati
|
||||
switch (true)
|
||||
{
|
||||
case negative:
|
||||
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
|
||||
buf[len++] = '-';
|
||||
case this.flags.plus:
|
||||
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
|
||||
buf[len++] = '+';
|
||||
case this.flags.space:
|
||||
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
|
||||
buf[len++] = ' ';
|
||||
}
|
||||
if (!len) return;
|
||||
@@ -586,36 +586,36 @@ fn void! Formatter.ntoa_format(Formatter* this, String buf, usz len, bool negati
|
||||
}
|
||||
|
||||
|
||||
fn void! Formatter.ntoa_variant(Formatter* this, variant arg, uint base) @private
|
||||
fn void! Formatter.ntoa_any(Formatter* this, any arg, uint base) @private
|
||||
{
|
||||
bool is_neg;
|
||||
uint128 val = int_from_variant(arg, &is_neg)!!;
|
||||
uint128 val = int_from_any(arg, &is_neg)!!;
|
||||
return this.ntoa(val, is_neg, base) @inline;
|
||||
}
|
||||
|
||||
fn void! Formatter.out_char(Formatter* this, variant arg) @private
|
||||
fn void! Formatter.out_char(Formatter* this, any arg) @private
|
||||
{
|
||||
uint l = 1;
|
||||
// pre padding
|
||||
this.right_adjust(l)?;
|
||||
this.right_adjust(l)!;
|
||||
// char output
|
||||
Char32 c = types::variant_to_int(arg, uint) ?? 0xFFFD;
|
||||
Char32 c = types::any_to_int(arg, uint) ?? 0xFFFD;
|
||||
switch (true)
|
||||
{
|
||||
case c < 0x7f:
|
||||
this.out((char)c)?;
|
||||
this.out((char)c)!;
|
||||
case c < 0x7ff:
|
||||
this.out((char)(0xC0 | c >> 6))?;
|
||||
this.out((char)(0x80 | (c & 0x3F)))?;
|
||||
this.out((char)(0xC0 | c >> 6))!;
|
||||
this.out((char)(0x80 | (c & 0x3F)))!;
|
||||
case c < 0xffff:
|
||||
this.out((char)(0xE0 | c >> 12))?;
|
||||
this.out((char)(0x80 | (c >> 6 & 0x3F)))?;
|
||||
this.out((char)(0x80 | (c & 0x3F)))?;
|
||||
this.out((char)(0xE0 | c >> 12))!;
|
||||
this.out((char)(0x80 | (c >> 6 & 0x3F)))!;
|
||||
this.out((char)(0x80 | (c & 0x3F)))!;
|
||||
default:
|
||||
this.out((char)(0xF0 | c >> 18))?;
|
||||
this.out((char)(0x80 | (c >> 12 & 0x3F)))?;
|
||||
this.out((char)(0x80 | (c >> 6 & 0x3F)))?;
|
||||
this.out((char)(0x80 | (c & 0x3F)))?;
|
||||
this.out((char)(0xF0 | c >> 18))!;
|
||||
this.out((char)(0x80 | (c >> 12 & 0x3F)))!;
|
||||
this.out((char)(0x80 | (c >> 6 & 0x3F)))!;
|
||||
this.out((char)(0x80 | (c & 0x3F)))!;
|
||||
}
|
||||
return this.left_adjust(l);
|
||||
}
|
||||
@@ -630,11 +630,11 @@ fn void! Formatter.out_reverse(Formatter* this, char[] buf) @private
|
||||
{
|
||||
for (usz i = len; i < this.width; i++)
|
||||
{
|
||||
this.out(' ')?;
|
||||
this.out(' ')!;
|
||||
}
|
||||
}
|
||||
// reverse string
|
||||
while (len) this.out(buf[--len])?;
|
||||
while (len) this.out(buf[--len])!;
|
||||
|
||||
// append pad spaces up to given width
|
||||
return this.left_adjust(this.idx - buffer_start_idx);
|
||||
@@ -643,25 +643,25 @@ fn void! Formatter.out_reverse(Formatter* this, char[] buf) @private
|
||||
fn void! printf_advance_format(usz format_len, usz *index_ptr) @inline @private
|
||||
{
|
||||
usz val = ++(*index_ptr);
|
||||
if (val >= format_len) return FormattingFault.UNTERMINATED_FORMAT!;
|
||||
if (val >= format_len) return FormattingFault.UNTERMINATED_FORMAT?;
|
||||
}
|
||||
|
||||
fn variant! next_variant(variant* args_ptr, usz args_len, usz* arg_index_ptr) @inline @private
|
||||
fn any! next_any(any* args_ptr, usz args_len, usz* arg_index_ptr) @inline @private
|
||||
{
|
||||
if (*arg_index_ptr >= args_len) return FormattingFault.MISSING_ARG!;
|
||||
if (*arg_index_ptr >= args_len) return FormattingFault.MISSING_ARG?;
|
||||
return args_ptr[(*arg_index_ptr)++];
|
||||
}
|
||||
|
||||
fn int! printf_parse_format_field(
|
||||
variant* args_ptr, usz args_len, usz* args_index_ptr,
|
||||
any* args_ptr, usz args_len, usz* args_index_ptr,
|
||||
char* format_ptr, usz format_len, usz* index_ptr) @inline @private
|
||||
{
|
||||
char c = format_ptr[*index_ptr];
|
||||
if (c >= '0' && c <= '9') return simple_atoi(format_ptr, format_len, index_ptr);
|
||||
if (c != '*') return 0;
|
||||
printf_advance_format(format_len, index_ptr)?;
|
||||
variant val = next_variant(args_ptr, args_len, args_index_ptr)?;
|
||||
if (!val.type.kindof.is_int()) return FormattingFault.INVALID_WIDTH_ARG!;
|
||||
uint! intval = types::variant_to_int(val, int);
|
||||
return intval ?? FormattingFault.INVALID_WIDTH_ARG!;
|
||||
printf_advance_format(format_len, index_ptr)!;
|
||||
any val = next_any(args_ptr, args_len, args_index_ptr)!;
|
||||
if (!val.type.kindof.is_int()) return FormattingFault.INVALID_WIDTH_ARG?;
|
||||
uint! intval = types::any_to_int(val, int);
|
||||
return intval ?? FormattingFault.INVALID_WIDTH_ARG?;
|
||||
}
|
||||
|
||||
@@ -33,39 +33,21 @@ fn usz! printf(String format, args...) @maydiscard
|
||||
return formatter.vprintf(format, args);
|
||||
}
|
||||
|
||||
define printfln = printfn;
|
||||
|
||||
fn usz! printfn(String format, args...) @maydiscard
|
||||
{
|
||||
Formatter formatter;
|
||||
formatter.init(&out_putchar_fn);
|
||||
usz len = formatter.vprintf(format, args)?;
|
||||
usz len = formatter.vprintf(format, args)!;
|
||||
putchar('\n');
|
||||
return len + 1;
|
||||
}
|
||||
|
||||
fn usz! VarString.printf(VarString* str, String format, args...) @maydiscard
|
||||
{
|
||||
Formatter formatter;
|
||||
formatter.init(&out_string_append_fn, str);
|
||||
return formatter.vprintf(format, args);
|
||||
}
|
||||
|
||||
fn usz! VarString.printfn(VarString* str, String format, args...) @maydiscard
|
||||
{
|
||||
Formatter formatter;
|
||||
formatter.init(&out_string_append_fn, str);
|
||||
usz len = formatter.vprintf(format, args)?;
|
||||
str.append('\n');
|
||||
return len + 1;
|
||||
}
|
||||
|
||||
fn char[]! bprintf(char[] buffer, String format, args...) @maydiscard
|
||||
{
|
||||
Formatter formatter;
|
||||
BufferData data = { .buffer = buffer };
|
||||
formatter.init(&out_buffer_fn, &data);
|
||||
usz size = formatter.vprintf(format, args)?;
|
||||
usz size = formatter.vprintf(format, args)!;
|
||||
return buffer[:data.written];
|
||||
}
|
||||
|
||||
@@ -73,15 +55,15 @@ fn usz! File.printf(File file, String format, args...) @maydiscard
|
||||
{
|
||||
Formatter formatter;
|
||||
formatter.init(&out_fputchar_fn, &file);
|
||||
return formatter.vprintf(format, args)?;
|
||||
return formatter.vprintf(format, args)!;
|
||||
}
|
||||
|
||||
fn usz! File.printfn(File file, String format, args...) @maydiscard
|
||||
{
|
||||
Formatter formatter;
|
||||
formatter.init(&out_fputchar_fn, &file);
|
||||
usz len = formatter.vprintf(format, args)?;
|
||||
file.putc('\n')?;
|
||||
usz len = formatter.vprintf(format, args)!;
|
||||
file.putc('\n')!;
|
||||
file.flush();
|
||||
return len + 1;
|
||||
}
|
||||
@@ -157,10 +139,10 @@ static initialize @priority(101)
|
||||
|
||||
fn void! Formatter.out(Formatter* this, char c) @private
|
||||
{
|
||||
this.out_fn(c, this.data)?;
|
||||
this.out_fn(c, this.data)!;
|
||||
}
|
||||
|
||||
macro bool! Formatter.print_with_function(Formatter* this, variant arg)
|
||||
macro bool! Formatter.print_with_function(Formatter* this, any arg)
|
||||
{
|
||||
if (try to_format = toformat_functions.get(arg.type))
|
||||
{
|
||||
@@ -173,7 +155,7 @@ macro bool! Formatter.print_with_function(Formatter* this, variant arg)
|
||||
this.width = old_width;
|
||||
this.prec = old_prec;
|
||||
}
|
||||
to_format(arg.ptr, this)?;
|
||||
to_format(arg.ptr, this)!;
|
||||
return true;
|
||||
}
|
||||
if (try to_string = tostring_functions.get(arg.type))
|
||||
@@ -189,96 +171,92 @@ macro bool! Formatter.print_with_function(Formatter* this, variant arg)
|
||||
}
|
||||
@pool()
|
||||
{
|
||||
this.out_substr(to_string(arg.ptr, mem::temp()))?;
|
||||
this.out_substr(to_string(arg.ptr, mem::temp()))!;
|
||||
return true;
|
||||
};
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fn void! Formatter.out_str(Formatter* this, variant arg) @private
|
||||
fn void! Formatter.out_str(Formatter* this, any arg) @private
|
||||
{
|
||||
switch (arg.type.kindof)
|
||||
{
|
||||
case TYPEID:
|
||||
return this.out_substr("<typeid>");
|
||||
return this.out_substr("typeid");
|
||||
case VOID:
|
||||
return this.out_substr("void");
|
||||
case ANYERR:
|
||||
case ANYFAULT:
|
||||
case FAULT:
|
||||
return this.out_substr((*(anyerr*)arg.ptr).nameof);
|
||||
case VARIANT:
|
||||
return this.out_str(*(variant*)arg);
|
||||
return this.out_substr((*(anyfault*)arg.ptr).nameof);
|
||||
case ANY:
|
||||
return this.out_str(*(any*)arg);
|
||||
case ENUM:
|
||||
if (this.print_with_function(arg)?) return;
|
||||
return this.out_substr(arg.type.names[types::variant_to_int(arg, usz)!!]);
|
||||
if (this.print_with_function(arg)!) return;
|
||||
return this.out_substr(arg.type.names[types::any_to_int(arg, usz)!!]);
|
||||
case STRUCT:
|
||||
if (this.print_with_function(arg)?) return;
|
||||
if (this.print_with_function(arg)!) return;
|
||||
return this.out_substr("<struct>");
|
||||
case UNION:
|
||||
if (this.print_with_function(arg)?) return;
|
||||
if (this.print_with_function(arg)!) return;
|
||||
return this.out_substr("<union>");
|
||||
case BITSTRUCT:
|
||||
if (this.print_with_function(arg)?) return;
|
||||
if (this.print_with_function(arg)!) return;
|
||||
return this.out_substr("<bitstruct>");
|
||||
case FUNC:
|
||||
if (this.print_with_function(arg)?) return;
|
||||
if (this.print_with_function(arg)!) return;
|
||||
return this.out_substr("<function>");
|
||||
case OPTIONAL:
|
||||
unreachable();
|
||||
case DISTINCT:
|
||||
if (this.print_with_function(arg)?) return;
|
||||
if (arg.type == VarString.typeid)
|
||||
{
|
||||
return this.out_substr(((VarString*)arg).str());
|
||||
}
|
||||
if (this.print_with_function(arg)!) return;
|
||||
if (arg.type == DString.typeid)
|
||||
{
|
||||
return this.out_substr(((DString*)arg).str());
|
||||
}
|
||||
return this.out_str(variant { arg.ptr, arg.type.inner });
|
||||
return this.out_str(any { arg.ptr, arg.type.inner });
|
||||
case POINTER:
|
||||
if (this.print_with_function(arg)?) return;
|
||||
return this.ntoa_variant(arg, 16);
|
||||
if (this.print_with_function(arg)!) return;
|
||||
return this.ntoa_any(arg, 16);
|
||||
case SIGNED_INT:
|
||||
case UNSIGNED_INT:
|
||||
return this.ntoa_variant(arg, 10);
|
||||
return this.ntoa_any(arg, 10);
|
||||
case FLOAT:
|
||||
return this.ftoa(float_from_variant(arg)!!);
|
||||
return this.ftoa(float_from_any(arg)!!);
|
||||
case ARRAY:
|
||||
if (this.print_with_function(arg)?) return;
|
||||
if (this.print_with_function(arg)!) return;
|
||||
// this is SomeType[*] so grab the "SomeType"
|
||||
typeid inner = arg.type.inner;
|
||||
usz size = inner.sizeof;
|
||||
usz len = arg.type.len;
|
||||
// Pretend this is a String
|
||||
void* ptr = (void*)arg.ptr;
|
||||
this.out('[')?;
|
||||
this.out('[')!;
|
||||
for (usz i = 0; i < len; i++)
|
||||
{
|
||||
if (i != 0) this.out_substr(", ")?;
|
||||
this.out_str(variant { ptr, inner })?;
|
||||
if (i != 0) this.out_substr(", ")!;
|
||||
this.out_str(any { ptr, inner })!;
|
||||
ptr += size;
|
||||
}
|
||||
return this.out(']');
|
||||
case VECTOR:
|
||||
if (this.print_with_function(arg)?) return;
|
||||
if (this.print_with_function(arg)!) return;
|
||||
// this is SomeType[*] so grab the "SomeType"
|
||||
typeid inner = arg.type.inner;
|
||||
usz size = inner.sizeof;
|
||||
usz len = arg.type.len;
|
||||
// Pretend this is a String
|
||||
void* ptr = (void*)arg.ptr;
|
||||
this.out_substr("[<")?;
|
||||
this.out_substr("[<")!;
|
||||
for (usz i = 0; i < len; i++)
|
||||
{
|
||||
if (i != 0) this.out_substr(", ")?;
|
||||
this.out_str(variant { ptr, inner })?;
|
||||
if (i != 0) this.out_substr(", ")!;
|
||||
this.out_str(any { ptr, inner })!;
|
||||
ptr += size;
|
||||
}
|
||||
return this.out_substr(">]");
|
||||
case SUBARRAY:
|
||||
if (this.print_with_function(arg)?) return;
|
||||
if (this.print_with_function(arg)!) return;
|
||||
// this is SomeType[] so grab the "SomeType"
|
||||
typeid inner = arg.type.inner;
|
||||
if (inner == char.typeid)
|
||||
@@ -290,14 +268,14 @@ fn void! Formatter.out_str(Formatter* this, variant arg) @private
|
||||
String* temp = (void*)arg.ptr;
|
||||
void* ptr = (void*)temp.ptr;
|
||||
usz len = temp.len;
|
||||
this.out('[')?;
|
||||
this.out('[')!;
|
||||
for (usz i = 0; i < len; i++)
|
||||
{
|
||||
if (i != 0) this.out_substr(", ")?;
|
||||
this.out_str(variant { ptr, inner })?;
|
||||
if (i != 0) this.out_substr(", ")!;
|
||||
this.out_str(any { ptr, inner })!;
|
||||
ptr += size;
|
||||
}
|
||||
this.out(']')?;
|
||||
this.out(']')!;
|
||||
case BOOL:
|
||||
if (*(bool*)arg.ptr)
|
||||
{
|
||||
@@ -308,7 +286,13 @@ fn void! Formatter.out_str(Formatter* this, variant arg) @private
|
||||
return this.out_substr("false");
|
||||
}
|
||||
default:
|
||||
if (this.print_with_function(arg)?) return;
|
||||
switch (arg)
|
||||
{
|
||||
case Object:
|
||||
arg.to_format(this)!;
|
||||
return;
|
||||
}
|
||||
if (this.print_with_function(arg)!) return;
|
||||
return this.out_substr("Invalid type");
|
||||
}
|
||||
}
|
||||
@@ -318,7 +302,7 @@ fn void! Formatter.out_str(Formatter* this, variant arg) @private
|
||||
fn void! out_buffer_fn(char c, void *data) @private
|
||||
{
|
||||
BufferData *buffer_data = data;
|
||||
if (buffer_data.written >= buffer_data.buffer.len) return PrintFault.BUFFER_EXCEEDED!;
|
||||
if (buffer_data.written >= buffer_data.buffer.len) return PrintFault.BUFFER_EXCEEDED?;
|
||||
buffer_data.buffer[buffer_data.written++] = c;
|
||||
}
|
||||
|
||||
@@ -334,16 +318,9 @@ fn void! out_putchar_fn(char c, void* data @unused) @private
|
||||
fn void! out_fputchar_fn(char c, void* data) @private
|
||||
{
|
||||
File* f = data;
|
||||
f.putc(c)?;
|
||||
f.putc(c)!;
|
||||
}
|
||||
|
||||
fn void! out_string_append_fn(char c, void* data) @private
|
||||
{
|
||||
VarString* s = data;
|
||||
s.append_char(c);
|
||||
}
|
||||
|
||||
|
||||
struct BufferData @private
|
||||
{
|
||||
char[] buffer;
|
||||
@@ -351,7 +328,7 @@ struct BufferData @private
|
||||
}
|
||||
|
||||
|
||||
fn usz! Formatter.vprintf(Formatter* this, String format, variant[] variants)
|
||||
fn usz! Formatter.vprintf(Formatter* this, String format, any[] anys)
|
||||
{
|
||||
if (!this.out_fn)
|
||||
{
|
||||
@@ -367,15 +344,15 @@ fn usz! Formatter.vprintf(Formatter* this, String format, variant[] variants)
|
||||
if (c != '%')
|
||||
{
|
||||
// no
|
||||
this.out(c)?;
|
||||
this.out(c)!;
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
if (i >= format_len) return PrintFault.INVALID_FORMAT_STRING!;
|
||||
if (i >= format_len) return PrintFault.INVALID_FORMAT_STRING?;
|
||||
c = format[i];
|
||||
if (c == '%')
|
||||
{
|
||||
this.out(c)?;
|
||||
this.out(c)!;
|
||||
continue;
|
||||
}
|
||||
// evaluate flags
|
||||
@@ -391,11 +368,11 @@ fn usz! Formatter.vprintf(Formatter* this, String format, variant[] variants)
|
||||
case '#': this.flags.hash = true;
|
||||
default: break FLAG_EVAL;
|
||||
}
|
||||
if (++i >= format_len) return PrintFault.INVALID_FORMAT_STRING!;
|
||||
if (++i >= format_len) return PrintFault.INVALID_FORMAT_STRING?;
|
||||
c = format[i];
|
||||
}
|
||||
// evaluate width field
|
||||
int w = printf_parse_format_field(variants.ptr, variants.len, &variant_index, format.ptr, format.len, &i)?;
|
||||
int w = printf_parse_format_field(anys.ptr, anys.len, &variant_index, format.ptr, format.len, &i)!;
|
||||
c = format[i];
|
||||
if (w < 0)
|
||||
{
|
||||
@@ -408,16 +385,16 @@ fn usz! Formatter.vprintf(Formatter* this, String format, variant[] variants)
|
||||
if (c == '.')
|
||||
{
|
||||
this.flags.precision = true;
|
||||
if (++i >= format_len) return PrintFault.INVALID_FORMAT_STRING!;
|
||||
int prec = printf_parse_format_field(variants.ptr, variants.len, &variant_index, format.ptr, format.len, &i)?;
|
||||
if (++i >= format_len) return PrintFault.INVALID_FORMAT_STRING?;
|
||||
int prec = printf_parse_format_field(anys.ptr, anys.len, &variant_index, format.ptr, format.len, &i)!;
|
||||
this.prec = prec < 0 ? 0 : prec;
|
||||
c = format[i];
|
||||
}
|
||||
|
||||
// evaluate specifier
|
||||
uint base = 0;
|
||||
if (variant_index >= variants.len) return PrintFault.MISSING_ARG!;
|
||||
variant current = variants[variant_index++];
|
||||
if (variant_index >= anys.len) return PrintFault.MISSING_ARG?;
|
||||
any current = anys[variant_index++];
|
||||
switch (c)
|
||||
{
|
||||
case 'd':
|
||||
@@ -442,38 +419,38 @@ fn usz! Formatter.vprintf(Formatter* this, String format, variant[] variants)
|
||||
this.flags.uppercase = true;
|
||||
nextcase;
|
||||
case 'a':
|
||||
this.atoa(float_from_variant(current)!!)?;
|
||||
this.atoa(float_from_any(current)!!)!;
|
||||
continue;
|
||||
case 'F' :
|
||||
this.flags.uppercase = true;
|
||||
nextcase;
|
||||
case 'f':
|
||||
this.ftoa(float_from_variant(current)!!)?;
|
||||
this.ftoa(float_from_any(current)!!)!;
|
||||
continue;
|
||||
case 'E':
|
||||
this.flags.uppercase = true;
|
||||
nextcase;
|
||||
case 'e':
|
||||
this.etoa(float_from_variant(current)!!)?;
|
||||
this.etoa(float_from_any(current)!!)!;
|
||||
continue;
|
||||
case 'G':
|
||||
this.flags.uppercase = true;
|
||||
nextcase;
|
||||
case 'g':
|
||||
this.gtoa(float_from_variant(current)!!)?;
|
||||
this.gtoa(float_from_any(current)!!)!;
|
||||
continue;
|
||||
case 'c':
|
||||
this.out_char(current)?;
|
||||
this.out_char(current)!;
|
||||
continue;
|
||||
case 's':
|
||||
this.out_str(current)?;
|
||||
this.out_str(current)!;
|
||||
continue;
|
||||
case 'p':
|
||||
this.flags.zeropad = true;
|
||||
this.flags.hash = true;
|
||||
base = 16;
|
||||
default:
|
||||
return PrintFault.INVALID_FORMAT_STRING!;
|
||||
return PrintFault.INVALID_FORMAT_STRING?;
|
||||
}
|
||||
if (base != 10)
|
||||
{
|
||||
@@ -484,9 +461,9 @@ fn usz! Formatter.vprintf(Formatter* this, String format, variant[] variants)
|
||||
if (this.flags.precision) this.flags.zeropad = false;
|
||||
|
||||
bool is_neg;
|
||||
uint128 v = int_from_variant(current, &is_neg)!!;
|
||||
uint128 v = int_from_any(current, &is_neg)!!;
|
||||
|
||||
this.ntoa(v, is_neg, base)?;
|
||||
this.ntoa(v, is_neg, base)!;
|
||||
}
|
||||
// termination
|
||||
// out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);
|
||||
|
||||
@@ -60,7 +60,7 @@ fn void! Stream.close(Stream* s) @inline @maydiscard
|
||||
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!;
|
||||
return IoError.NOT_SEEKABLE?;
|
||||
}
|
||||
|
||||
fn usz! Stream.available(Stream* s) @inline
|
||||
@@ -68,12 +68,12 @@ 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)?;
|
||||
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!;
|
||||
return IoError.NOT_SEEKABLE?;
|
||||
}
|
||||
|
||||
fn usz! Stream.read(Stream* s, char[] buffer)
|
||||
@@ -88,19 +88,19 @@ fn usz! Stream.read(Stream* s, char[] buffer)
|
||||
if (catch err = c)
|
||||
{
|
||||
case IoError.EOF: return len;
|
||||
default: return err!;
|
||||
default: return err?;
|
||||
}
|
||||
*cptr = c;
|
||||
len++;
|
||||
}
|
||||
}
|
||||
return IoError.UNSUPPORTED_OPERATION!;
|
||||
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!;
|
||||
return IoError.UNSUPPORTED_OPERATION?;
|
||||
}
|
||||
|
||||
fn usz! Stream.write(Stream* s, char[] bytes) @inline
|
||||
@@ -108,34 +108,34 @@ 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)?;
|
||||
foreach (c : bytes) func(s, c)!;
|
||||
return bytes.len;
|
||||
}
|
||||
return IoError.UNSUPPORTED_OPERATION!;
|
||||
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!;
|
||||
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!;
|
||||
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!;
|
||||
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!;
|
||||
return IoError.UNSUPPORTED_OPERATION?;
|
||||
}
|
||||
|
||||
fn usz! Stream.len(Stream* s) @inline
|
||||
@@ -143,12 +143,12 @@ 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)?;
|
||||
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!;
|
||||
return IoError.NOT_SEEKABLE?;
|
||||
}
|
||||
|
||||
fn void! Stream.pushback_byte(Stream* s) @inline
|
||||
@@ -156,13 +156,13 @@ 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)?;
|
||||
func(s, -1, CURSOR)!;
|
||||
return;
|
||||
}
|
||||
return IoError.UNSUPPORTED_OPERATION!;
|
||||
return IoError.UNSUPPORTED_OPERATION?;
|
||||
}
|
||||
|
||||
fn void! Stream.write_string(Stream* s, String str) @inline => (void)(s.write((char[])str)?);
|
||||
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 = {})
|
||||
{
|
||||
@@ -195,11 +195,11 @@ macro usz! copy_through_buffer(Stream* s, Stream* dst, char[] buffer) @local
|
||||
if (catch err = len)
|
||||
{
|
||||
case IoError.EOF: return total_copied;
|
||||
default: return err!;
|
||||
default: return err?;
|
||||
}
|
||||
if (!len) return total_copied;
|
||||
usz written = dst.write(buffer[:len])?;
|
||||
usz written = dst.write(buffer[:len])!;
|
||||
total_copied += len;
|
||||
if (written != len) return IoError.INCOMPLETE_WRITE!;
|
||||
if (written != len) return IoError.INCOMPLETE_WRITE?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,41 +3,42 @@ import libc;
|
||||
|
||||
|
||||
$switch
|
||||
$case !env::COMPILER_LIBC_AVAILABLE:
|
||||
|
||||
$case env::COMPILER_LIBC_AVAILABLE && env::os_is_posix():
|
||||
|
||||
macro void! native_chdir(Path p)
|
||||
{
|
||||
if (posix::chdir(p.as_zstr()))
|
||||
{
|
||||
switch (libc::errno())
|
||||
{
|
||||
case errno::EACCES: return IoError.NO_PERMISSION?;
|
||||
case errno::ENAMETOOLONG: return IoError.NAME_TOO_LONG?;
|
||||
case errno::ENOTDIR: return IoError.FILE_NOT_DIR?;
|
||||
case errno::ENOENT: return IoError.FILE_NOT_FOUND?;
|
||||
case errno::ELOOP: return IoError.SYMLINK_FAILED?;
|
||||
default: return IoError.GENERAL_ERROR?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$case env::COMPILER_LIBC_AVAILABLE && env::os_is_win32():
|
||||
|
||||
macro void! native_chdir(Path path)
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
// TODO improve with better error handling.
|
||||
if (win32::win32_SetCurrentDirectoryW(path.as_str().to_temp_utf16()!!)) return;
|
||||
};
|
||||
return IoError.GENERAL_ERROR?;
|
||||
}
|
||||
|
||||
$default:
|
||||
|
||||
fn void! native_chdir(Path path)
|
||||
{
|
||||
unreachable("'getcwd' not available");
|
||||
}
|
||||
|
||||
$case env::os_is_win32():
|
||||
|
||||
macro void! native_chdir(Path path)
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
if (files::win32_SetCurrentDirectoryW(path.as_str().to_temp_utf16()!!)) return;
|
||||
};
|
||||
return IoError.GENERAL_ERROR!;
|
||||
}
|
||||
|
||||
$default:
|
||||
|
||||
extern fn int _chdir(ZString) @extern("chdir");
|
||||
macro void! native_chdir(Path p)
|
||||
{
|
||||
if (_chdir((ZString)p.as_str()))
|
||||
{
|
||||
switch (libc::errno())
|
||||
{
|
||||
case errno::EACCES: return IoError.NO_PERMISSION!;
|
||||
case errno::ENAMETOOLONG: return IoError.NAME_TOO_LONG!;
|
||||
case errno::ENOTDIR: return IoError.FILE_NOT_DIR!;
|
||||
case errno::ENOENT: return IoError.FILE_NOT_FOUND!;
|
||||
case errno::ELOOP: return IoError.SYMLINK_FAILED!;
|
||||
default: return IoError.GENERAL_ERROR!;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$endswitch
|
||||
@@ -45,11 +45,11 @@ $else
|
||||
@pool()
|
||||
{
|
||||
$if (env::os_is_win32())
|
||||
void* file = (CFile)_wfopen(filename.to_temp_utf16(), filename.to_temp_utf16())?;
|
||||
void* file = (CFile)_wfopen(filename.to_temp_utf16(), filename.to_temp_utf16())!;
|
||||
$else
|
||||
void* file = libc::fopen(filename.zstr_tcopy(), mode.zstr_tcopy());
|
||||
$endif
|
||||
return file ?: file_open_errno()!;
|
||||
return file ?: file_open_errno()?;
|
||||
};
|
||||
$endif
|
||||
}
|
||||
@@ -67,11 +67,11 @@ $else
|
||||
@pool()
|
||||
{
|
||||
$if (env::os_is_win32())
|
||||
file = _wfreopen(filename.to_temp_utf16(), mode.to_temp_utf16(), file)?;
|
||||
file = _wfreopen(filename.to_temp_utf16(), mode.to_temp_utf16(), file)!;
|
||||
$else
|
||||
file = libc::freopen(filename.zstr_tcopy(), mode.zstr_tcopy(), file);
|
||||
$endif
|
||||
return file ?: file_open_errno()!;
|
||||
return file ?: file_open_errno()?;
|
||||
};
|
||||
$endif
|
||||
}
|
||||
@@ -87,7 +87,7 @@ $else
|
||||
$else
|
||||
bool success = libc::fseek(file, (SeekIndex)offset, (CInt)seek_mode) == 0;
|
||||
$endif
|
||||
if (!success) return file_seek_errno()!;
|
||||
if (!success) return file_seek_errno()?;
|
||||
$endif
|
||||
}
|
||||
|
||||
@@ -99,10 +99,10 @@ $if (!env::COMPILER_LIBC_AVAILABLE)
|
||||
$else
|
||||
$if (env::os_is_win32())
|
||||
long index = _ftelli64(file);
|
||||
return index >= 0 ? index : file_seek_errno()!;
|
||||
return index >= 0 ? index : file_seek_errno()?;
|
||||
$else
|
||||
SeekIndex index = libc::ftell(file);
|
||||
return index >= 0 ? index : file_seek_errno()!;
|
||||
return index >= 0 ? index : file_seek_errno()?;
|
||||
$endif
|
||||
$endif
|
||||
}
|
||||
@@ -127,7 +127,7 @@ $else
|
||||
$endif
|
||||
}
|
||||
|
||||
macro anyerr file_open_errno() @local
|
||||
macro anyfault file_open_errno() @local
|
||||
{
|
||||
switch (libc::errno())
|
||||
{
|
||||
@@ -155,7 +155,7 @@ macro anyerr file_open_errno() @local
|
||||
}
|
||||
}
|
||||
|
||||
macro anyerr file_seek_errno() @local
|
||||
macro anyfault file_seek_errno() @local
|
||||
{
|
||||
switch (libc::errno())
|
||||
{
|
||||
|
||||
@@ -44,26 +44,26 @@ const S_IFSOCK = 0o140000; // socket
|
||||
fn usz! native_file_size(String path)
|
||||
{
|
||||
Darwin64Stat stat;
|
||||
read_stat(&stat, path)?;
|
||||
read_stat(&stat, path)!;
|
||||
return stat.st_size;
|
||||
}
|
||||
|
||||
fn bool native_file_or_dir_exists(String path)
|
||||
{
|
||||
Darwin64Stat stat;
|
||||
return try? read_stat(&stat, path);
|
||||
return @ok(read_stat(&stat, path));
|
||||
}
|
||||
|
||||
fn bool native_is_file(String path)
|
||||
{
|
||||
Darwin64Stat stat;
|
||||
return try? read_stat(&stat, path) && (stat.st_mode & S_IFREG);
|
||||
return @ok(read_stat(&stat, path)) && stat.st_mode & S_IFREG;
|
||||
}
|
||||
|
||||
fn bool native_is_dir(String path)
|
||||
{
|
||||
Darwin64Stat stat;
|
||||
return try? read_stat(&stat, path) && (stat.st_mode & S_IFDIR);
|
||||
return @ok(read_stat(&stat, path)) && stat.st_mode & S_IFDIR;
|
||||
}
|
||||
|
||||
fn void! read_stat(Darwin64Stat* stat, String path) @local
|
||||
@@ -76,25 +76,25 @@ fn void! read_stat(Darwin64Stat* stat, String path) @local
|
||||
switch (libc::errno())
|
||||
{
|
||||
case errno::EBADF:
|
||||
return IoError.FILE_NOT_VALID!;
|
||||
return IoError.FILE_NOT_VALID?;
|
||||
case errno::EFAULT:
|
||||
unreachable("Invalid stat");
|
||||
case errno::EIO:
|
||||
return IoError.GENERAL_ERROR!;
|
||||
return IoError.GENERAL_ERROR?;
|
||||
case errno::EACCES:
|
||||
return IoError.NO_PERMISSION!;
|
||||
return IoError.NO_PERMISSION?;
|
||||
case errno::ELOOP:
|
||||
return IoError.NO_PERMISSION!;
|
||||
return IoError.NO_PERMISSION?;
|
||||
case errno::ENAMETOOLONG:
|
||||
return IoError.NAME_TOO_LONG!;
|
||||
return IoError.NAME_TOO_LONG?;
|
||||
case errno::ENOENT:
|
||||
return IoError.FILE_NOT_FOUND!;
|
||||
return IoError.FILE_NOT_FOUND?;
|
||||
case errno::ENOTDIR:
|
||||
return IoError.FILE_NOT_DIR!;
|
||||
return IoError.FILE_NOT_DIR?;
|
||||
case errno::EOVERFLOW:
|
||||
return IoError.GENERAL_ERROR!;
|
||||
return IoError.GENERAL_ERROR?;
|
||||
default:
|
||||
return IoError.UNKNOWN_ERROR!;
|
||||
return IoError.UNKNOWN_ERROR?;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -17,6 +17,7 @@ $if (env::COMPILER_LIBC_AVAILABLE)
|
||||
|
||||
extern fn void* opendir(ZString);
|
||||
extern fn void closedir(void*);
|
||||
extern fn int remove(ZString);
|
||||
|
||||
const DT_UNKNOWN = 0;
|
||||
const DT_FIFO = 1;
|
||||
@@ -34,7 +35,7 @@ fn PathList! native_readdir(Path dir, bool no_dirs, bool no_symlinks, String mas
|
||||
list.init(.using = using);
|
||||
void* directory = opendir(dir.as_str() ? dir.as_zstr() : (ZString)".");
|
||||
defer if (directory) closedir(directory);
|
||||
if (!directory) return (path::is_dir(dir) ? IoError.CANNOT_READ_DIR : IoError.FILE_NOT_DIR)!;
|
||||
if (!directory) return (path::is_dir(dir) ? IoError.CANNOT_READ_DIR : IoError.FILE_NOT_DIR)?;
|
||||
NativeDirentry* entry;
|
||||
while ((entry = readdir(directory)))
|
||||
{
|
||||
@@ -48,6 +49,37 @@ fn PathList! native_readdir(Path dir, bool no_dirs, bool no_symlinks, String mas
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require dir.as_str()
|
||||
**/
|
||||
fn void! native_rmtree(Path dir)
|
||||
{
|
||||
void* directory = opendir(dir.as_zstr());
|
||||
defer if (directory) closedir(directory);
|
||||
if (!directory) return path::is_dir(dir) ? IoError.CANNOT_READ_DIR? : IoError.FILE_NOT_DIR?;
|
||||
NativeDirentry* entry;
|
||||
while ((entry = readdir(directory)))
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
String name = ((ZString)&entry.name).as_str();
|
||||
if (!name || name == "." || name == "..") continue;
|
||||
Path new_path = dir.tappend(name)!;
|
||||
if (entry.type == DT_DIR)
|
||||
{
|
||||
native_rmtree(new_path)!;
|
||||
continue;
|
||||
}
|
||||
if (remove(new_path.as_zstr()))
|
||||
{
|
||||
// TODO improve
|
||||
return IoError.GENERAL_ERROR?;
|
||||
}
|
||||
};
|
||||
}
|
||||
os::native_rmdir(dir)!;
|
||||
}
|
||||
|
||||
$endif
|
||||
|
||||
$endif
|
||||
@@ -56,9 +88,9 @@ $if (!env::os_is_darwin() && !env::os_is_win32())
|
||||
|
||||
fn usz! native_file_size(String path)
|
||||
{
|
||||
File f = file::open(path, "r")?;
|
||||
File f = file::open(path, "r")!;
|
||||
defer (void)f.close();
|
||||
return f.seek(0, Seek.END)?;
|
||||
return f.seek(0, Seek.END)!;
|
||||
}
|
||||
|
||||
$if (env::os_is_posix() && env::COMPILER_LIBC_AVAILABLE)
|
||||
@@ -75,7 +107,7 @@ fn bool native_is_file(String path)
|
||||
{
|
||||
File! f = file::open(path, "r");
|
||||
defer (void)f.close();
|
||||
return try? f;
|
||||
return @ok(f);
|
||||
}
|
||||
|
||||
fn bool native_is_dir(String path)
|
||||
@@ -106,7 +138,7 @@ $endif
|
||||
|
||||
$switch (env::OS_TYPE)
|
||||
$case IOS:
|
||||
$case MACOSX:
|
||||
$case MACOS:
|
||||
$case TVOS:
|
||||
$case WATCHOS:
|
||||
|
||||
|
||||
@@ -1,15 +1,38 @@
|
||||
module std::io::file::os;
|
||||
import std::os::win32::files;
|
||||
import std::os::win32;
|
||||
|
||||
$if (env::os_is_win32())
|
||||
|
||||
const Win32_DWORD FILE_ATTRIBUTE_READONLY = 0x01;
|
||||
const Win32_DWORD FILE_ATTRIBUTE_HIDDEN = 0x02;
|
||||
const Win32_DWORD FILE_ATTRIBUTE_SYSTEM = 0x04;
|
||||
const Win32_DWORD FILE_ATTRIBUTE_DIRECTORY = 0x10;
|
||||
const Win32_DWORD FILE_ATTRIBUTE_ARCHIVE = 0x20;
|
||||
const Win32_DWORD FILE_ATTRIBUTE_DEVICE = 0x40;
|
||||
const Win32_DWORD FILE_ATTRIBUTE_NORMAL = 0x80;
|
||||
const Win32_DWORD FILE_ATTRIBUTE_TEMPORARY = 0x100;
|
||||
const Win32_DWORD FILE_ATTRIBUTE_SPARSE_FILE = 0x200;
|
||||
const Win32_DWORD FILE_ATTRIBUTE_REPARSE_POINT = 0x400;
|
||||
const Win32_DWORD FILE_ATTRIBUTE_COMPRESSED = 0x800;
|
||||
const Win32_DWORD FILE_ATTRIBUTE_OFFLINE = 0x1000;
|
||||
const Win32_DWORD FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x2000;
|
||||
const Win32_DWORD FILE_ATTRIBUTE_ENCRYPTED = 0x4000;
|
||||
const Win32_DWORD FILE_ATTRIBUTE_INTEGRITY_STREAM = 0x8000;
|
||||
const Win32_DWORD FILE_ATTRIBUTE_VIRTUAL = 0x10000;
|
||||
const Win32_DWORD FILE_ATTRIBUTE_NO_SCRUB_DATA = 0x20000;
|
||||
const Win32_DWORD FILE_ATTRIBUTE_EA = 0x40000;
|
||||
const Win32_DWORD FILE_ATTRIBUTE_PINNED = 0x80000;
|
||||
const Win32_DWORD FILE_ATTRIBUTE_UNPINNED = 0x100000;
|
||||
const Win32_DWORD FILE_ATTRIBUTE_RECALL_ON_OPEN = 0x40000;
|
||||
const Win32_DWORD FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS = 0x400000;
|
||||
|
||||
fn usz! native_file_size(String path)
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
Char16[] path16 = path.to_temp_utf16()?;
|
||||
Char16[] path16 = path.to_temp_utf16()!;
|
||||
Win32_FILE_ATTRIBUTE_DATA data;
|
||||
files::win32_GetFileAttributesExW(path16, Win32_GET_FILEEX_INFO_LEVELS.STANDARD, &data);
|
||||
win32::win32_GetFileAttributesExW(path16, Win32_GET_FILEEX_INFO_LEVELS.STANDARD, &data);
|
||||
Win32_LARGE_INTEGER size;
|
||||
size.lowPart = data.nFileSizeLow;
|
||||
size.highPart = data.nFileSizeHigh;
|
||||
@@ -21,7 +44,7 @@ fn bool native_file_or_dir_exists(String path)
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
return (bool)files::win32_PathFileExistsW(path.to_temp_utf16()) ?? false;
|
||||
return (bool)win32::win32_PathFileExistsW(path.to_temp_utf16()) ?? false;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -30,7 +53,7 @@ fn bool native_is_file(String path)
|
||||
{
|
||||
File! f = file::open(path, "r");
|
||||
defer (void)f.close();
|
||||
return try? f;
|
||||
return @ok(f);
|
||||
}
|
||||
|
||||
fn bool native_is_dir(String path)
|
||||
@@ -38,14 +61,41 @@ fn bool native_is_dir(String path)
|
||||
return native_file_or_dir_exists(path) && !native_is_file(path);
|
||||
}
|
||||
|
||||
fn void! native_rmtree(Path path)
|
||||
{
|
||||
Win32_WIN32_FIND_DATAW find_data;
|
||||
|
||||
String s = path.as_str().tconcat("\\*");
|
||||
Win32_HANDLE find = win32::win32_FindFirstFileW(s.to_utf16(mem::temp()), &find_data)!;
|
||||
|
||||
if (find == win32::INVALID_HANDLE_VALUE) return IoError.CANNOT_READ_DIR?;
|
||||
|
||||
defer win32::win32_FindClose(find);
|
||||
do
|
||||
{
|
||||
String filename = string::from_zutf16(&find_data.cFileName, mem::temp())!;
|
||||
if (filename == "." || filename == "..") continue;
|
||||
Path file_path = path.tappend(filename)!;
|
||||
if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
{
|
||||
native_rmtree(file_path)!;
|
||||
}
|
||||
else
|
||||
{
|
||||
win32::win32_DeleteFileW(file_path.as_str().to_utf16(mem::temp()));
|
||||
}
|
||||
} while (win32::win32_FindNextFileW(find, &find_data) != 0);
|
||||
os::native_rmdir(path)!;
|
||||
}
|
||||
|
||||
fn Path! native_temp_directory(Allocator* using = mem::heap())
|
||||
{
|
||||
@stack_mem(256; Allocator* mem)
|
||||
{
|
||||
Win32_DWORD len = files::win32_GetTempPathW(0, null);
|
||||
if (!len) return IoError.GENERAL_ERROR!;
|
||||
Win32_DWORD len = win32::win32_GetTempPathW(0, null);
|
||||
if (!len) return IoError.GENERAL_ERROR?;
|
||||
Char16[] buff = malloc(Char16, len + 1, .using = mem);
|
||||
if (!files::win32_GetTempPathW(len, buff)) return IoError.GENERAL_ERROR!;
|
||||
if (!win32::win32_GetTempPathW(len, buff)) return IoError.GENERAL_ERROR?;
|
||||
return path::new(string::from_utf16(buff[:len], .using = mem), using);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,53 +2,50 @@ module std::io::os;
|
||||
import libc;
|
||||
|
||||
$switch
|
||||
$case !env::COMPILER_LIBC_AVAILABLE:
|
||||
|
||||
fn String! getcwd(Allocator* using = mem::heap())
|
||||
{
|
||||
unreachable("'getcwd' not available");
|
||||
}
|
||||
|
||||
$case env::os_is_win32():
|
||||
|
||||
extern fn Char16* _wgetcwd(Char16* buffer, int maxlen);
|
||||
extern fn usz wcslen(Char16* str);
|
||||
$case env::COMPILER_LIBC_AVAILABLE && env::os_is_win32():
|
||||
|
||||
macro String! getcwd(Allocator* using = mem::heap())
|
||||
{
|
||||
const DEFAULT_BUFFER = 256;
|
||||
Char16[DEFAULT_BUFFER] buffer;
|
||||
Char16 *res = _wgetcwd(&buffer, DEFAULT_BUFFER);
|
||||
Char16 *res = win32::_wgetcwd(&buffer, DEFAULT_BUFFER);
|
||||
bool free = false;
|
||||
defer if (free) libc::free(res);
|
||||
if (!res)
|
||||
{
|
||||
if (libc::errno() != errno::ERANGE) return IoError.GENERAL_ERROR!;
|
||||
res = _wgetcwd(null, 0);
|
||||
if (libc::errno() != errno::ERANGE) return IoError.GENERAL_ERROR?;
|
||||
res = win32::_wgetcwd(null, 0);
|
||||
free = true;
|
||||
}
|
||||
Char16[] str16 = res[:wcslen(res)];
|
||||
return str::utf16to8(str16, using);
|
||||
Char16[] str16 = res[:win32::wcslen(res)];
|
||||
return string::from_utf16(str16, using);
|
||||
}
|
||||
|
||||
$default:
|
||||
$case env::COMPILER_LIBC_AVAILABLE && env::os_is_posix():
|
||||
|
||||
extern fn ZString _getcwd(char* pwd, usz len) @extern("getcwd");
|
||||
macro String! getcwd(Allocator* using = mem::heap())
|
||||
{
|
||||
const usz DEFAULT_BUFFER = 256;
|
||||
char[DEFAULT_BUFFER] buffer;
|
||||
ZString res = _getcwd(&buffer, DEFAULT_BUFFER);
|
||||
ZString res = posix::getcwd(&buffer, DEFAULT_BUFFER);
|
||||
bool free = false;
|
||||
if (!res)
|
||||
{
|
||||
// Improve error
|
||||
if (libc::errno() != errno::ERANGE) return IoError.GENERAL_ERROR!;
|
||||
res = _getcwd(null, 0);
|
||||
if (libc::errno() != errno::ERANGE) return IoError.GENERAL_ERROR?;
|
||||
res = posix::getcwd(null, 0);
|
||||
free = true;
|
||||
}
|
||||
defer if (free) libc::free((void*)res);
|
||||
return res.copy(using);
|
||||
}
|
||||
|
||||
$default:
|
||||
|
||||
fn String! getcwd(Allocator* using = mem::heap())
|
||||
{
|
||||
unreachable("'getcwd' not available");
|
||||
}
|
||||
|
||||
$endswitch
|
||||
63
lib/std/io/os/mkdir.c3
Normal file
63
lib/std/io/os/mkdir.c3
Normal file
@@ -0,0 +1,63 @@
|
||||
module std::io::os;
|
||||
import libc;
|
||||
import std::io::path;
|
||||
import std::os::win32;
|
||||
import std::os::posix;
|
||||
|
||||
$switch
|
||||
$case env::COMPILER_LIBC_AVAILABLE && env::os_is_posix():
|
||||
|
||||
macro bool! native_mkdir(Path path, MkdirPermissions permissions)
|
||||
{
|
||||
if (!posix::mkdir(path.as_zstr(), permissions == NORMAL ? 0o777 : 0o700)) return true;
|
||||
switch (libc::errno())
|
||||
{
|
||||
case errno::EACCES:
|
||||
case errno::EPERM:
|
||||
case errno::EROFS:
|
||||
case errno::EFAULT: return IoError.NO_PERMISSION?;
|
||||
case errno::ENAMETOOLONG: return IoError.NAME_TOO_LONG?;
|
||||
case errno::EDQUOT:
|
||||
case errno::ENOSPC: return IoError.OUT_OF_SPACE?;
|
||||
case errno::EISDIR:
|
||||
case errno::EEXIST: return false;
|
||||
case errno::ELOOP: return IoError.SYMLINK_FAILED?;
|
||||
case errno::ENOTDIR: return IoError.FILE_NOT_FOUND?;
|
||||
default: return IoError.GENERAL_ERROR?;
|
||||
}
|
||||
}
|
||||
|
||||
$case env::COMPILER_LIBC_AVAILABLE && env::os_is_win32():
|
||||
|
||||
macro bool! native_mkdir(Path path, MkdirPermissions permissions)
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
// TODO security attributes
|
||||
if (win32::win32_CreateDirectoryW(path.as_str().to_temp_utf16()!!, null)) return true;
|
||||
switch (win32::win32_GetLastError())
|
||||
{
|
||||
case win32::ERROR_ACCESS_DENIED:
|
||||
return IoError.NO_PERMISSION?;
|
||||
case win32::ERROR_DISK_FULL:
|
||||
return IoError.OUT_OF_SPACE?;
|
||||
case win32::ERROR_ALREADY_EXISTS:
|
||||
return false;
|
||||
case win32::ERROR_PATH_NOT_FOUND:
|
||||
return IoError.FILE_NOT_FOUND?;
|
||||
default:
|
||||
return IoError.GENERAL_ERROR?;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
$default:
|
||||
|
||||
fn bool! native_mkdir(Path path, MkdirPermissions permissions)
|
||||
{
|
||||
unreachable("'mkdir' not available");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
$endswitch
|
||||
61
lib/std/io/os/rmdir.c3
Normal file
61
lib/std/io/os/rmdir.c3
Normal file
@@ -0,0 +1,61 @@
|
||||
module std::io::os;
|
||||
import libc;
|
||||
import std::io::path;
|
||||
import std::os::win32;
|
||||
import std::os::posix;
|
||||
|
||||
$switch
|
||||
|
||||
$case env::COMPILER_LIBC_AVAILABLE && env::os_is_posix():
|
||||
|
||||
macro bool! native_rmdir(Path path)
|
||||
{
|
||||
if (!posix::rmdir(path.as_zstr())) return true;
|
||||
switch (libc::errno())
|
||||
{
|
||||
case errno::EBUSY: return IoError.BUSY?;
|
||||
case errno::EACCES:
|
||||
case errno::EPERM:
|
||||
case errno::EROFS:
|
||||
case errno::EFAULT: return IoError.NO_PERMISSION?;
|
||||
case errno::ENAMETOOLONG: return IoError.NAME_TOO_LONG?;
|
||||
case errno::ENOTDIR:
|
||||
case errno::ENOENT: return false;
|
||||
case errno::ENOTEMPTY: return IoError.DIR_NOT_EMPTY?;
|
||||
case errno::ELOOP: return IoError.SYMLINK_FAILED?;
|
||||
default: return IoError.GENERAL_ERROR?;
|
||||
}
|
||||
}
|
||||
|
||||
$case env::COMPILER_LIBC_AVAILABLE && env::os_is_win32():
|
||||
|
||||
macro bool! native_rmdir(Path path)
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
if (win32::win32_RemoveDirectoryW(path.as_str().to_temp_utf16()!!)) return true;
|
||||
switch (win32::win32_GetLastError())
|
||||
{
|
||||
case win32::ERROR_ACCESS_DENIED:
|
||||
return IoError.NO_PERMISSION?;
|
||||
case win32::ERROR_CURRENT_DIRECTORY:
|
||||
return IoError.BUSY?;
|
||||
case win32::ERROR_DIR_NOT_EMPTY:
|
||||
return IoError.DIR_NOT_EMPTY?;
|
||||
case win32::ERROR_DIRECTORY:
|
||||
case win32::ERROR_PATH_NOT_FOUND:
|
||||
return false;
|
||||
default:
|
||||
return IoError.GENERAL_ERROR?;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
$default:
|
||||
|
||||
fn bool! native_rmdir(Path path)
|
||||
{
|
||||
unreachable("'rmdir' not available");
|
||||
}
|
||||
|
||||
$endswitch
|
||||
@@ -58,6 +58,51 @@ macro bool is_win32_separator(char c)
|
||||
return c == '/' || c == '\\';
|
||||
}
|
||||
|
||||
fn Path[]! ls(Path path)
|
||||
{
|
||||
unreachable();
|
||||
}
|
||||
|
||||
enum MkdirPermissions
|
||||
{
|
||||
NORMAL,
|
||||
USER_ONLY,
|
||||
USER_AND_ADMIN
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn bool! mkdir(Path path, bool recursive = false, MkdirPermissions permissions = NORMAL)
|
||||
{
|
||||
if (!path.path_string.len) return PathResult.INVALID_PATH?;
|
||||
if (is_dir(path)) return false;
|
||||
if (exists(path)) return IoError.FILE_NOT_DIR?;
|
||||
|
||||
if (recursive)
|
||||
{
|
||||
if (try parent = path.parent()) mkdir(parent, true, permissions)!;
|
||||
}
|
||||
if (!is_dir(path.parent()) ?? false) return IoError.CANNOT_READ_DIR?;
|
||||
|
||||
return os::native_mkdir(path, permissions);
|
||||
}
|
||||
|
||||
fn bool! rmdir(Path path)
|
||||
{
|
||||
if (!path.path_string.len) return PathResult.INVALID_PATH?;
|
||||
return os::native_rmdir(path);
|
||||
}
|
||||
|
||||
fn void! rmtree(Path path)
|
||||
{
|
||||
if (!path.path_string.len) return PathResult.INVALID_PATH?;
|
||||
$if ($defined(os::native_rmtree))
|
||||
return os::native_rmtree(path);
|
||||
$else
|
||||
assert(false, "rmtree is not available");
|
||||
$endif
|
||||
}
|
||||
|
||||
fn Path! new(String path, Allocator* using = mem::heap(), PathEnv path_env = DEFAULT_PATH_ENV)
|
||||
{
|
||||
return { normalize(path.copy(using), path_env), path_env };
|
||||
@@ -87,7 +132,7 @@ fn bool Path.equals(Path p1, Path p2)
|
||||
**/
|
||||
fn Path! Path.append(Path path, String filename, Allocator* using = mem::heap())
|
||||
{
|
||||
if (!path.path_string.len) return new(filename, using, path.env)?;
|
||||
if (!path.path_string.len) return new(filename, using, path.env)!;
|
||||
assert(!is_separator(path.path_string[^1], path.env));
|
||||
|
||||
@stack_mem(256; Allocator* mem)
|
||||
@@ -134,9 +179,9 @@ fn String Path.dirname(Path path)
|
||||
fn String! Path.extension(Path path)
|
||||
{
|
||||
String basename = path.basename();
|
||||
usz index = basename.rindex_of(".")?;
|
||||
usz index = basename.rindex_of(".")!;
|
||||
// Plain ".foo" does not have an
|
||||
if (index == 0) return SearchResult.MISSING!;
|
||||
if (index == 0) return SearchResult.MISSING?;
|
||||
if (index == basename.len) return "";
|
||||
return basename[index + 1..];
|
||||
}
|
||||
@@ -166,9 +211,9 @@ fn usz! volume_name_len(String path, PathEnv path_env) @local
|
||||
{
|
||||
char c = path[i];
|
||||
if (is_win32_separator(c)) return i;
|
||||
if (is_reserved_win32_path_char(c)) return PathResult.INVALID_PATH!;
|
||||
if (is_reserved_win32_path_char(c)) return PathResult.INVALID_PATH?;
|
||||
}
|
||||
return PathResult.INVALID_PATH!;
|
||||
return PathResult.INVALID_PATH?;
|
||||
case 'A'..'Z':
|
||||
case 'a'..'z':
|
||||
return path[1] == ':' ? 2 : 0;
|
||||
@@ -179,7 +224,7 @@ fn usz! volume_name_len(String path, PathEnv path_env) @local
|
||||
|
||||
fn Path! Path.parent(Path path)
|
||||
{
|
||||
if (path.path_string.len == 1 && is_separator(path.path_string[0], path.env)) return PathResult.NO_PARENT!;
|
||||
if (path.path_string.len == 1 && is_separator(path.path_string[0], path.env)) return PathResult.NO_PARENT?;
|
||||
foreach_r(i, c : path.path_string)
|
||||
{
|
||||
if (is_separator(c, path.env))
|
||||
@@ -187,13 +232,13 @@ fn Path! Path.parent(Path path)
|
||||
return { path.path_string[:i], path.env };
|
||||
}
|
||||
}
|
||||
return PathResult.NO_PARENT!;
|
||||
return PathResult.NO_PARENT?;
|
||||
}
|
||||
|
||||
fn String! normalize(String path_str, PathEnv path_env = DEFAULT_PATH_ENV)
|
||||
{
|
||||
if (!path_str.len) return path_str;
|
||||
usz path_start = volume_name_len(path_str, path_env)?;
|
||||
usz path_start = volume_name_len(path_str, path_env)!;
|
||||
usz path_len = path_str.len;
|
||||
if (path_start == path_len) return path_str;
|
||||
char path_separator = path_env == PathEnv.WIN32 ? PREFERRED_SEPARATOR_WIN32 : PREFERRED_SEPARATOR_POSIX;
|
||||
@@ -225,7 +270,7 @@ fn String! normalize(String path_str, PathEnv path_env = DEFAULT_PATH_ENV)
|
||||
|
||||
// The rest are names of the path elements, so check that the
|
||||
// characters are valid.
|
||||
if (is_reserved_path_char(c, path_env)) return PathResult.INVALID_PATH!;
|
||||
if (is_reserved_path_char(c, path_env)) return PathResult.INVALID_PATH?;
|
||||
|
||||
// If we have '.' after a separator
|
||||
if (c == '.' && previous_was_separator)
|
||||
@@ -251,7 +296,7 @@ fn String! normalize(String path_str, PathEnv path_env = DEFAULT_PATH_ENV)
|
||||
continue;
|
||||
case 2:
|
||||
// This is an error: /a/../..
|
||||
if (len == path_start && has_root) return PathResult.INVALID_PATH!;
|
||||
if (len == path_start && has_root) return PathResult.INVALID_PATH?;
|
||||
|
||||
// If this .. at the start, or after ../? If so, we just copy ..
|
||||
if (len == path_start ||
|
||||
|
||||
@@ -19,7 +19,7 @@ fn Stream ByteReader.as_stream(ByteReader* reader)
|
||||
|
||||
fn usz! ByteReader.read(ByteReader* reader, char[] bytes)
|
||||
{
|
||||
if (reader.index >= reader.bytes.len) return IoError.EOF!;
|
||||
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);
|
||||
@@ -29,13 +29,13 @@ fn usz! ByteReader.read(ByteReader* reader, char[] bytes)
|
||||
|
||||
fn char! ByteReader.read_byte(ByteReader* reader)
|
||||
{
|
||||
if (reader.index >= reader.bytes.len) return IoError.EOF!;
|
||||
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!;
|
||||
if (!reader.index) return IoError.INVALID_PUSHBACK?;
|
||||
reader.index--;
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ fn usz! ByteReader.seek(ByteReader* reader, isz offset, Seek seek)
|
||||
case CURSOR: new_index = reader.index + offset;
|
||||
case END: new_index = reader.bytes.len + offset;
|
||||
}
|
||||
if (new_index < 0) return IoError.INVALID_POSITION!;
|
||||
if (new_index < 0) return IoError.INVALID_POSITION?;
|
||||
reader.index = new_index;
|
||||
return new_index;
|
||||
}
|
||||
@@ -56,7 +56,7 @@ fn usz! ByteReader.seek(ByteReader* reader, isz offset, Seek seek)
|
||||
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..])?;
|
||||
usz written = writer.write(reader.bytes[reader.index..])!;
|
||||
reader.index += written;
|
||||
assert(reader.index <= reader.bytes.len);
|
||||
return written;
|
||||
|
||||
@@ -52,16 +52,16 @@ fn String ByteWriter.as_str(ByteWriter* writer)
|
||||
fn void! ByteWriter.ensure_capacity(ByteWriter* writer, usz len) @inline
|
||||
{
|
||||
if (writer.bytes.len > len) return;
|
||||
if (!writer.allocator) return IoError.OUT_OF_SPACE!;
|
||||
if (!writer.allocator) return IoError.OUT_OF_SPACE?;
|
||||
if (len < 16) len = 16;
|
||||
usz new_capacity = math::next_power_of_2(len);
|
||||
char* new_ptr = realloc_checked(writer.bytes.ptr, new_capacity, .using = writer.allocator)?;
|
||||
char* new_ptr = realloc_checked(writer.bytes.ptr, new_capacity, .using = writer.allocator)!;
|
||||
writer.bytes = new_ptr[:new_capacity];
|
||||
}
|
||||
|
||||
fn usz! ByteWriter.write(ByteWriter* writer, char[] bytes)
|
||||
{
|
||||
writer.ensure_capacity(writer.index + bytes.len)?;
|
||||
writer.ensure_capacity(writer.index + bytes.len)!;
|
||||
mem::copy(&writer.bytes[writer.index], bytes.ptr, bytes.len);
|
||||
writer.index += bytes.len;
|
||||
return bytes.len;
|
||||
@@ -69,7 +69,7 @@ fn usz! ByteWriter.write(ByteWriter* writer, char[] bytes)
|
||||
|
||||
fn void! ByteWriter.write_byte(ByteWriter* writer, char c)
|
||||
{
|
||||
writer.ensure_capacity(writer.index + 1)?;
|
||||
writer.ensure_capacity(writer.index + 1)!;
|
||||
writer.bytes[writer.index++] = c;
|
||||
}
|
||||
|
||||
@@ -82,10 +82,10 @@ fn usz! ByteWriter.read_from(ByteWriter* writer, Stream* reader)
|
||||
if (reader.supports_available())
|
||||
{
|
||||
usz total_read = 0;
|
||||
while (usz available = reader.available()?)
|
||||
while (usz available = reader.available()!)
|
||||
{
|
||||
writer.ensure_capacity(writer.index + available)?;
|
||||
usz len = reader.read(writer.bytes[writer.index..])?;
|
||||
writer.ensure_capacity(writer.index + available)!;
|
||||
usz len = reader.read(writer.bytes[writer.index..])!;
|
||||
total_read += len;
|
||||
writer.index += len;
|
||||
}
|
||||
@@ -99,10 +99,10 @@ fn usz! ByteWriter.read_from(ByteWriter* writer, Stream* reader)
|
||||
// Less than 16 bytes? Double the capacity
|
||||
if (len_to_read < 16)
|
||||
{
|
||||
writer.ensure_capacity(writer.bytes.len * 2)?;
|
||||
writer.ensure_capacity(writer.bytes.len * 2)!;
|
||||
}
|
||||
// Read into the rest of the buffer
|
||||
usz read = reader.read(writer.bytes[writer.index..])?;
|
||||
usz read = reader.read(writer.bytes[writer.index..])!;
|
||||
writer.index += read;
|
||||
// Ok, we reached the end.
|
||||
if (read < len_to_read) return total_read;
|
||||
|
||||
@@ -162,7 +162,7 @@ $case env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == OsType.LINUX:
|
||||
macro CFile stdin() { return __stdin; }
|
||||
macro CFile stdout() { return __stdout; }
|
||||
macro CFile stderr() { return __stderr; }
|
||||
$case env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == OsType.MACOSX:
|
||||
$case env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == OsType.MACOS:
|
||||
extern CFile __stdinp;
|
||||
extern CFile __stdoutp;
|
||||
extern CFile __stderrp;
|
||||
@@ -187,7 +187,7 @@ $endswitch
|
||||
const HAS_MALLOC_SIZE =
|
||||
env::OS_TYPE == OsType.LINUX
|
||||
|| env::OS_TYPE == OsType.WIN32
|
||||
|| env::OS_TYPE == OsType.MACOSX;
|
||||
|| env::OS_TYPE == OsType.MACOS;
|
||||
|
||||
// The following needs to be set per arch+os
|
||||
// For now I have simply pulled the defaults from MacOS
|
||||
@@ -320,9 +320,7 @@ $endif
|
||||
|
||||
// time.h
|
||||
|
||||
typedef TimeOffset = CLong;
|
||||
|
||||
struct Tm
|
||||
struct TmCommon @private
|
||||
{
|
||||
int tm_sec; /* seconds after the minute [0-60] */
|
||||
int tm_min; /* minutes after the hour [0-59] */
|
||||
@@ -333,18 +331,70 @@ struct Tm
|
||||
int tm_wday; /* days since Sunday [0-6] */
|
||||
int tm_yday; /* days since January 1 [0-365] */
|
||||
int tm_isdst; /* Daylight Savings Time flag */
|
||||
}
|
||||
|
||||
|
||||
$switch (env::OS_TYPE)
|
||||
|
||||
$case WIN32:
|
||||
|
||||
typedef Tm = TmCommon;
|
||||
|
||||
$case WASI:
|
||||
|
||||
typedef TimeOffset = int;
|
||||
struct Tm
|
||||
{
|
||||
inline TmCommon common;
|
||||
TimeOffset tm_gmtoff; /* offset from UTC in seconds */
|
||||
char *tm_zone; /* timezone abbreviation */
|
||||
int tm_nsec;
|
||||
}
|
||||
|
||||
$case MACOS:
|
||||
$case IOS:
|
||||
$case TVOS:
|
||||
$case WATCHOS:
|
||||
$case OPENBSD:
|
||||
$case FREEBSD:
|
||||
$default:
|
||||
|
||||
typedef TimeOffset = CLong;
|
||||
struct Tm
|
||||
{
|
||||
inline TmCommon common;
|
||||
TimeOffset tm_gmtoff; /* offset from UTC in seconds */
|
||||
char *tm_zone; /* timezone abbreviation */
|
||||
}
|
||||
|
||||
$endswitch
|
||||
|
||||
|
||||
$if (env::os_is_win32())
|
||||
|
||||
struct TimeSpec
|
||||
{
|
||||
Time s;
|
||||
Time_t s;
|
||||
ulong ns;
|
||||
}
|
||||
|
||||
const int TIME_UTC = 1;
|
||||
typedef Time_t = long;
|
||||
typedef Clock_t = ulong;
|
||||
|
||||
$else
|
||||
|
||||
struct TimeSpec
|
||||
{
|
||||
Time_t s;
|
||||
CLong ns;
|
||||
}
|
||||
|
||||
typedef Time_t = CLong;
|
||||
typedef Clock_t = CULong;
|
||||
|
||||
$endif
|
||||
|
||||
const int TIME_UTC = 1;
|
||||
|
||||
extern fn int timespec_get(TimeSpec* ts, int base);
|
||||
extern fn int nanosleep(TimeSpec* req, TimeSpec* remaining);
|
||||
@@ -352,19 +402,34 @@ extern fn int nanosleep(TimeSpec* req, TimeSpec* remaining);
|
||||
// Likely wrong, must be per platform.
|
||||
const CLOCKS_PER_SEC = 1000000;
|
||||
|
||||
// Time also needs to be per platform
|
||||
typedef Time = long;
|
||||
typedef Clock = ulong;
|
||||
|
||||
extern fn ZString asctime(Tm *timeptr);
|
||||
extern fn Clock clock();
|
||||
extern fn ZString ctime(Time *timer);
|
||||
extern fn double difftime(Time time1, Time time2);
|
||||
extern fn Tm* gmtime(Time *timer);
|
||||
extern fn Tm* localtime(Time *timer);
|
||||
extern fn Time mktime(Tm *timeptr);
|
||||
extern fn Clock_t clock();
|
||||
extern fn ZString ctime(Time_t *timer);
|
||||
extern fn double difftime(Time_t time1, Time_t time2);
|
||||
extern fn Tm* gmtime(Time_t *timer);
|
||||
|
||||
extern fn Tm* localtime(Time_t *timer);
|
||||
|
||||
$if (env::os_is_win32())
|
||||
extern fn Tm* _gmtime64_s(Tm* buf, Time_t *timer);
|
||||
extern fn Tm* _localtime64_s(Tm* buf, Time_t *timer);
|
||||
extern fn void _get_timezone(CLong *timezone);
|
||||
|
||||
macro Tm* gmtime_r(Time_t *timer, Tm* buf) => _gmtime64_s(buf, timer);
|
||||
macro Tm* localtime_r(Time_t *timer, Tm* buf) => _localtime64_s(buf, timer);
|
||||
|
||||
extern fn Time_t mktime(Tm *timeptr) @extern("_mktime64");
|
||||
extern fn Time_t timegm(Tm *timeptr) @extern("_mkgmtime64");
|
||||
$else
|
||||
extern fn Tm* gmtime_r(Time_t *timer, Tm* buf);
|
||||
extern fn Tm* localtime_r(Time_t *timer, Tm* buf);
|
||||
extern fn Time_t mktime(Tm *timeptr);
|
||||
extern fn Time_t timegm(Tm *timeptr);
|
||||
$endif
|
||||
|
||||
extern fn usz strftime(char* str, usz maxsize, char* format, Tm *timeptr);
|
||||
extern fn Time time(Time *timer);
|
||||
extern fn Time_t time(Time_t *timer);
|
||||
|
||||
// signal
|
||||
typedef SignalFunction = fn void(int);
|
||||
@@ -386,7 +451,7 @@ const Errno ENOEXEC = 8; // Exec format error
|
||||
const Errno EBADF = 9; // Bad file number
|
||||
const Errno ECHILD = 10; // No child processes
|
||||
|
||||
$if (env::OS_TYPE == MACOSX)
|
||||
$if (env::OS_TYPE == MACOS)
|
||||
const Errno EAGAIN = 35; // Try again Macos
|
||||
$else
|
||||
const Errno EAGAIN = 11; // Try again
|
||||
@@ -418,7 +483,7 @@ const Errno ERANGE = 34; // Math result not representable
|
||||
|
||||
$switch (env::OS_TYPE)
|
||||
|
||||
$case MACOSX:
|
||||
$case MACOS:
|
||||
const Errno EDEADLK = 11; // Resource deadlock would occur MacOS
|
||||
const Errno ENAMETOOLONG = 63; // File name too long MacOS
|
||||
const Errno ELOOP = 62; // Too many symbolic links encountered
|
||||
@@ -428,6 +493,7 @@ const Errno ENETDOWN = 50; // Network is down MacOS
|
||||
const Errno ENETUNREACH = 51; // Network is unreachable MacOS
|
||||
const Errno ENETRESET = 52; // Network dropped connection because of reset MacOS
|
||||
const Errno EOPNOTSUPP = 45; // Operation not supported on transport endpoint
|
||||
const Errno ENOTEMPTY = 66; // Directory not empty
|
||||
|
||||
$case WIN32:
|
||||
const Errno EDEADLK = 36; // Resource deadlock would occur Win32
|
||||
@@ -439,6 +505,7 @@ const Errno ECONNRESET = 108; // Connection reset by peer
|
||||
const Errno ENETUNREACH = 118; // Network is unreachable
|
||||
const Errno ENETRESET = 117; // Network dropped connection because of reset
|
||||
const Errno EOPNOTSUPP = 130; // Operation not supported on transport endpoint
|
||||
const Errno ENOTEMPTY = 41; // Directory not empty
|
||||
|
||||
$default:
|
||||
const Errno EDEADLK = 35; // Resource deadlock would occur Linux (others?)
|
||||
@@ -450,6 +517,7 @@ const Errno ECONNRESET = 104; // Connection reset by peer
|
||||
const Errno ENETUNREACH = 101; // Network is unreachable
|
||||
const Errno ENETRESET = 102; // Network dropped connection because of reset
|
||||
const Errno EOPNOTSUPP = 95; // Operation not supported on transport endpoint
|
||||
const Errno ENOTEMPTY = 39; // Directory not empty
|
||||
|
||||
$endswitch
|
||||
|
||||
@@ -458,7 +526,6 @@ $endswitch
|
||||
/*
|
||||
const Errno ENOLCK = 37; /* No record locks available */
|
||||
const Errno ENOSYS = 38; /* Function not implemented */
|
||||
const Errno ENOTEMPTY = 39; /* Directory not empty */
|
||||
|
||||
const Errno ENOMSG = 42; /* No message of desired type */
|
||||
const Errno EIDRM = 43; /* Identifier removed */
|
||||
@@ -529,7 +596,7 @@ const Errno EHOSTUNREACH = 113; /* No route to host */
|
||||
|
||||
$switch (env::OS_TYPE)
|
||||
|
||||
$case MACOSX:
|
||||
$case MACOS:
|
||||
const Errno ETIMEDOUT = 60; // Connection timed out
|
||||
const Errno EINPROGRESS = 36; // Operation now in progress MacOS
|
||||
const Errno EALREADY = 37; // Operation already in progress MacOS
|
||||
|
||||
@@ -9,7 +9,7 @@ extern fn int* __errno_location();
|
||||
macro int errno() => *__errno_location();
|
||||
macro void errno_set(int err) => *(__errno_location()) = err;
|
||||
|
||||
$case env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == MACOSX:
|
||||
$case env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == MACOS:
|
||||
|
||||
extern fn int* __error();
|
||||
macro int errno() => *__error();
|
||||
|
||||
@@ -441,7 +441,7 @@ macro is_nan(x)
|
||||
$case float16:
|
||||
return bitcast((float)x, uint) & 0x7fffffff > 0x7f800000;
|
||||
$default:
|
||||
return bitcast((double)x, ulong) & (~0u64 >> 1) > 0x7ff << 52;
|
||||
return bitcast((double)x, ulong) & (~0u64 >> 1) > 0x7ffu64 << 52;
|
||||
$endswitch
|
||||
}
|
||||
|
||||
@@ -455,7 +455,7 @@ macro is_inf(x)
|
||||
$case float16:
|
||||
return bitcast((float)x, uint) & 0x7fffffff == 0x7f800000;
|
||||
$default:
|
||||
return bitcast((double)x, ulong) & (~0u64 >> 1) == 0x7ff << 52;
|
||||
return bitcast((double)x, ulong) & (~0u64 >> 1) == 0x7ffu64 << 52;
|
||||
$endswitch
|
||||
}
|
||||
|
||||
|
||||
@@ -276,7 +276,7 @@ fn Matrix4x4 Matrix4x4.adjoint(Matrix4x4* mat)
|
||||
fn Matrix2x2! Matrix2x2.inverse(Matrix2x2* m)
|
||||
{
|
||||
Real det = m.determinant();
|
||||
if (det == 0) return MatrixError.MATRIX_INVERSE_DOESNT_EXIST!;
|
||||
if (det == 0) return MatrixError.MATRIX_INVERSE_DOESNT_EXIST?;
|
||||
Matrix2x2 adj = m.adjoint();
|
||||
return adj.component_mul(1 / det).transpose();
|
||||
}
|
||||
@@ -284,7 +284,7 @@ fn Matrix2x2! Matrix2x2.inverse(Matrix2x2* m)
|
||||
fn Matrix3x3! Matrix3x3.inverse(Matrix3x3* m)
|
||||
{
|
||||
Real det = m.determinant();
|
||||
if (det == 0) return MatrixError.MATRIX_INVERSE_DOESNT_EXIST!;
|
||||
if (det == 0) return MatrixError.MATRIX_INVERSE_DOESNT_EXIST?;
|
||||
Matrix3x3 adj = m.adjoint();
|
||||
return adj.component_mul(1 / det).transpose();
|
||||
}
|
||||
@@ -292,7 +292,7 @@ fn Matrix3x3! Matrix3x3.inverse(Matrix3x3* m)
|
||||
fn Matrix4x4! Matrix4x4.inverse(Matrix4x4* m)
|
||||
{
|
||||
Real det = m.determinant();
|
||||
if (det == 0) return MatrixError.MATRIX_INVERSE_DOESNT_EXIST!;
|
||||
if (det == 0) return MatrixError.MATRIX_INVERSE_DOESNT_EXIST?;
|
||||
Matrix4x4 adj = m.adjoint();
|
||||
return adj.component_mul(1 / det).transpose();
|
||||
}
|
||||
|
||||
@@ -138,8 +138,9 @@ macro cross3(v1, v2) @private
|
||||
|
||||
macro transform2(v, mat) @private
|
||||
{
|
||||
return $typeof(v) { mat.m00 * v[0] + mat.m10 * v[1] + mat.30,
|
||||
mat.m01 * v[0] + mar.m11 * v[1] + mat.31 };
|
||||
return $typeof(v) {
|
||||
mat.m00 * v[0] + mat.m10 * v[1] + mat.m30 ,
|
||||
mat.m01 * v[0] + mat.m11 * v[1] + mat.m31 };
|
||||
}
|
||||
|
||||
macro transform3(v, mat) @private
|
||||
|
||||
@@ -48,10 +48,10 @@ fn void! InetAddress.to_format(InetAddress* addr, Formatter* formatter)
|
||||
{
|
||||
formatter.printf("%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x",
|
||||
addr.ipv6.a, addr.ipv6.b, addr.ipv6.c, addr.ipv6.d,
|
||||
addr.ipv6.e, addr.ipv6.f, addr.ipv6.g, addr.ipv6.h)?;
|
||||
addr.ipv6.e, addr.ipv6.f, addr.ipv6.g, addr.ipv6.h)!;
|
||||
return;
|
||||
}
|
||||
formatter.printf("%d.%d.%d.%d", addr.ipv4.a, addr.ipv4.b, addr.ipv4.c, addr.ipv4.d)?;
|
||||
formatter.printf("%d.%d.%d.%d", addr.ipv4.a, addr.ipv4.b, addr.ipv4.c, addr.ipv4.d)!;
|
||||
}
|
||||
|
||||
fn String! InetAddress.to_string(InetAddress* addr, Allocator* using = mem::heap())
|
||||
@@ -61,22 +61,22 @@ fn String! InetAddress.to_string(InetAddress* addr, Allocator* using = mem::heap
|
||||
char[8 * 5 + 1] buffer;
|
||||
String res = (String)io::bprintf(&buffer, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x",
|
||||
addr.ipv6.a, addr.ipv6.b, addr.ipv6.c, addr.ipv6.d,
|
||||
addr.ipv6.e, addr.ipv6.f, addr.ipv6.g, addr.ipv6.h)?;
|
||||
addr.ipv6.e, addr.ipv6.f, addr.ipv6.g, addr.ipv6.h)!;
|
||||
return res.copy(using);
|
||||
}
|
||||
char[3 * 4 + 3 + 1] buffer;
|
||||
String res = (String)io::bprintf(&buffer, "%d.%d.%d.%d", addr.ipv4.a, addr.ipv4.b, addr.ipv4.c, addr.ipv4.d)?;
|
||||
String res = (String)io::bprintf(&buffer, "%d.%d.%d.%d", addr.ipv4.a, addr.ipv4.b, addr.ipv4.c, addr.ipv4.d)!;
|
||||
return res.copy(using);
|
||||
}
|
||||
|
||||
fn InetAddress! ipv6_from_str(String s)
|
||||
{
|
||||
uint sections = 0;
|
||||
if (s.len < 2) return NetError.INVALID_IP_STRING!;
|
||||
if (s.len < 2) return NetError.INVALID_IP_STRING?;
|
||||
foreach (c : s) if (c == ':') sections++;
|
||||
int zero_segment_len = s[0] == ':' || s[^1] == ':' ? 9 - sections : 8 - sections;
|
||||
if (zero_segment_len == 7 && s.len == 2) return { .is_ipv6 = true };
|
||||
if (zero_segment_len > 7) return NetError.INVALID_IP_STRING!;
|
||||
if (zero_segment_len > 7) return NetError.INVALID_IP_STRING?;
|
||||
usz index = 0;
|
||||
bool last_was_colon, found_zero;
|
||||
int current = -1;
|
||||
@@ -92,7 +92,7 @@ fn InetAddress! ipv6_from_str(String s)
|
||||
last_was_colon = true;
|
||||
continue;
|
||||
}
|
||||
if (current < 0 || current > 65535) return NetError.INVALID_IP_STRING!;
|
||||
if (current < 0 || current > 65535) return NetError.INVALID_IP_STRING?;
|
||||
addr.ipv6arr[index++].val = (ushort)current;
|
||||
current = -1;
|
||||
last_was_colon = true;
|
||||
@@ -100,9 +100,9 @@ fn InetAddress! ipv6_from_str(String s)
|
||||
}
|
||||
assert(current == -1);
|
||||
// Check that this is the first ::
|
||||
if (found_zero) return NetError.INVALID_IP_STRING!;
|
||||
if (found_zero) return NetError.INVALID_IP_STRING?;
|
||||
// Also check that the zeroed section is at least 2
|
||||
if (zero_segment_len < 2) return NetError.INVALID_IP_STRING!;
|
||||
if (zero_segment_len < 2) return NetError.INVALID_IP_STRING?;
|
||||
// Skip (will be zero by default
|
||||
index += zero_segment_len;
|
||||
found_zero = true;
|
||||
@@ -110,7 +110,7 @@ fn InetAddress! ipv6_from_str(String s)
|
||||
continue;
|
||||
}
|
||||
last_was_colon = false;
|
||||
if (index > 7 || !c.is_xdigit()) return NetError.INVALID_IP_STRING!;
|
||||
if (index > 7 || !c.is_xdigit()) return NetError.INVALID_IP_STRING?;
|
||||
if (current < 0) current = 0;
|
||||
current = current * 16 + (c <= '9' ? c - '0' : (c | 32) - 'a' + 10);
|
||||
}
|
||||
@@ -118,7 +118,7 @@ fn InetAddress! ipv6_from_str(String s)
|
||||
if (index == 8 && current == -1) return addr;
|
||||
|
||||
// Ends with number
|
||||
if (index != 7 || current < 0 || current > 65535) return NetError.INVALID_IP_STRING!;
|
||||
if (index != 7 || current < 0 || current > 65535) return NetError.INVALID_IP_STRING?;
|
||||
addr.ipv6arr[7].val = (ushort)current;
|
||||
return addr;
|
||||
}
|
||||
@@ -132,20 +132,20 @@ fn InetAddress! ipv4_from_str(String s)
|
||||
{
|
||||
if (c == '.')
|
||||
{
|
||||
if (current < 0) return NetError.INVALID_IP_STRING!;
|
||||
if (current > 255) return NetError.INVALID_IP_STRING!;
|
||||
if (current < 0) return NetError.INVALID_IP_STRING?;
|
||||
if (current > 255) return NetError.INVALID_IP_STRING?;
|
||||
switch (element)
|
||||
{
|
||||
case 0: addr.ipv4.a = (char)current;
|
||||
case 1: addr.ipv4.b = (char)current;
|
||||
case 2: addr.ipv4.c = (char)current;
|
||||
default: return NetError.INVALID_IP_STRING!;
|
||||
default: return NetError.INVALID_IP_STRING?;
|
||||
}
|
||||
current = -1;
|
||||
element++;
|
||||
continue;
|
||||
}
|
||||
if (element > 3 || c < '0' || c > '9') return NetError.INVALID_IP_STRING!;
|
||||
if (element > 3 || c < '0' || c > '9') return NetError.INVALID_IP_STRING?;
|
||||
if (current < 0)
|
||||
{
|
||||
current = c - '0';
|
||||
@@ -153,7 +153,7 @@ fn InetAddress! ipv4_from_str(String s)
|
||||
}
|
||||
current = current * 10 + c - '0';
|
||||
}
|
||||
if (element != 3 || current < 0 || current > 255) return NetError.INVALID_IP_STRING!;
|
||||
if (element != 3 || current < 0 || current > 255) return NetError.INVALID_IP_STRING?;
|
||||
addr.ipv4.d = (char)current;
|
||||
return addr;
|
||||
}
|
||||
|
||||
@@ -19,13 +19,13 @@ fn uint! ipv4toint(String s)
|
||||
{
|
||||
if (c == '.')
|
||||
{
|
||||
if (current < 0) return NetError.INVALID_IP_STRING!;
|
||||
if (current < 0) return NetError.INVALID_IP_STRING?;
|
||||
out = out << 8 + current;
|
||||
current = -1;
|
||||
element++;
|
||||
continue;
|
||||
}
|
||||
if (element > 3 || c < '0' || c > '9') return NetError.INVALID_IP_STRING!;
|
||||
if (element > 3 || c < '0' || c > '9') return NetError.INVALID_IP_STRING?;
|
||||
if (current < 0)
|
||||
{
|
||||
current = c - '0';
|
||||
@@ -33,7 +33,7 @@ fn uint! ipv4toint(String s)
|
||||
}
|
||||
current = current * 10 + c - '0';
|
||||
}
|
||||
if (element != 3 || current < 0) return NetError.INVALID_IP_STRING!;
|
||||
if (element != 3 || current < 0) return NetError.INVALID_IP_STRING?;
|
||||
out = out << 8 + current;
|
||||
return out;
|
||||
}
|
||||
@@ -41,6 +41,6 @@ fn uint! ipv4toint(String s)
|
||||
fn String! inttoipv4(uint val, Allocator* using = mem::heap())
|
||||
{
|
||||
char[3 * 4 + 3 + 1] buffer;
|
||||
String res = (String)io::bprintf(&buffer, "%d.%d.%d.%d", val >> 24, (val >> 16) & 0xFF, (val >> 8) & 0xFF, val & 0xFF)?;
|
||||
String res = (String)io::bprintf(&buffer, "%d.%d.%d.%d", val >> 24, (val >> 16) & 0xFF, (val >> 8) & 0xFF, val & 0xFF)!;
|
||||
return res.copy(using);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
module std::net::os;
|
||||
import libc;
|
||||
|
||||
$if (env::OS_TYPE == OsType.MACOSX)
|
||||
$if (env::OS_TYPE == OsType.MACOS)
|
||||
|
||||
const AI_NUMERICSERV = 0x1000;
|
||||
const AI_ALL = 0x100;
|
||||
|
||||
@@ -19,8 +19,8 @@ macro void! NativeSocket.close(NativeSocket this)
|
||||
{
|
||||
if (close(this))
|
||||
{
|
||||
if (libc::errno() == errno::EBADF) return NetError.INVALID_SOCKET!;
|
||||
return NetError.GENERAL_ERROR!;
|
||||
if (libc::errno() == errno::EBADF) return NetError.INVALID_SOCKET?;
|
||||
return NetError.GENERAL_ERROR?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,8 +29,8 @@ macro void! NativeSocket.set_non_blocking(NativeSocket this)
|
||||
int flags = fcntl(this, F_GETFL, 0);
|
||||
if (fcntl(this, F_SETFL, flags | O_NONBLOCK) == -1)
|
||||
{
|
||||
if (libc::errno() == errno::EBADF) return NetError.INVALID_SOCKET!;
|
||||
return NetError.GENERAL_ERROR!;
|
||||
if (libc::errno() == errno::EBADF) return NetError.INVALID_SOCKET?;
|
||||
return NetError.GENERAL_ERROR?;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module std::os::macos::cf;
|
||||
|
||||
$if (env::OS_TYPE == OsType.MACOSX)
|
||||
$if (env::OS_TYPE == OsType.MACOS)
|
||||
|
||||
typedef CFAllocatorRef = distinct void*;
|
||||
typedef CFAllocatorContextRef = distinct void*;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module std::os::macos::cf;
|
||||
|
||||
$if (env::OS_TYPE == OsType.MACOSX)
|
||||
$if (env::OS_TYPE == OsType.MACOS)
|
||||
|
||||
typedef CFArrayRef = distinct void*;
|
||||
typedef CFArrayCallBacksRef = distinct void*;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module std::os::macos::cf;
|
||||
|
||||
$if (env::OS_TYPE == OsType.MACOSX)
|
||||
$if (env::OS_TYPE == OsType.MACOS)
|
||||
|
||||
typedef CFTypeRef = distinct void*;
|
||||
typedef CFIndex = isz;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module std::os::macos::objc;
|
||||
|
||||
$if (env::OS_TYPE == OsType.MACOSX)
|
||||
$if (env::OS_TYPE == OsType.MACOS)
|
||||
|
||||
typedef Class = distinct void*;
|
||||
typedef Method = distinct void*;
|
||||
@@ -24,7 +24,7 @@ macro Selector selector_register(char* c) => _macos_sel_registerName(c);
|
||||
macro Class! class_by_name(char* c)
|
||||
{
|
||||
Class cls = _macos_objc_lookUpClass(c);
|
||||
if (!cls) return ObjcFailure.CLASS_NOT_FOUND!;
|
||||
if (!cls) return ObjcFailure.CLASS_NOT_FOUND?;
|
||||
return cls;
|
||||
}
|
||||
|
||||
|
||||
10
lib/std/os/posix/files.c3
Normal file
10
lib/std/os/posix/files.c3
Normal file
@@ -0,0 +1,10 @@
|
||||
module std::os::posix;
|
||||
|
||||
$if (env::os_is_posix() && env::COMPILER_LIBC_AVAILABLE)
|
||||
|
||||
extern fn int rmdir(ZString);
|
||||
extern fn int mkdir(ZString, ushort mode_t);
|
||||
extern fn int chdir(ZString);
|
||||
extern fn ZString getcwd(char* pwd, usz len);
|
||||
|
||||
$endif
|
||||
@@ -1,29 +0,0 @@
|
||||
module std::os::win32::wsa;
|
||||
|
||||
$if (env::OS_TYPE == OsType.WIN32)
|
||||
extern fn int get_last_error() @extern("WSAGetLastError");
|
||||
extern fn void set_last_error(int error) @extern("WSASetLastError");
|
||||
|
||||
const int INVALID_HANDLE = 6;
|
||||
const int NOT_ENOUGHT_MEMORY = 8;
|
||||
const int INVALID_PARAMETER = 87;
|
||||
const int OPERATION_ABORTED = 995;
|
||||
const int IO_INCOMPLETE = 996;
|
||||
const int IO_PENDING = 997;
|
||||
const int EINTR = 10004;
|
||||
const int EBADF = 10009;
|
||||
const int ACCESS = 10013;
|
||||
const int EFAULT = 10014;
|
||||
const int EINVAL = 10022;
|
||||
const int EMFILE = 10024;
|
||||
const int EWOULDBLOCK = 10035;
|
||||
const int EINPROGRESS = 10036;
|
||||
const int EALREADY = 10037;
|
||||
const int ENOTSOCK = 10038;
|
||||
const int EDESTADDRREQ = 10039;
|
||||
const int EMSGSIZE = 10040;
|
||||
const int EPROTOTYPE = 10041;
|
||||
const int ENOPROTOOPT = 10042;
|
||||
const int EPROTONOSUPPORT = 10043;
|
||||
const int ESOCKTNOSUPPORT = 10044;
|
||||
$endif
|
||||
@@ -1,4 +1,4 @@
|
||||
module std::os::win32::files;
|
||||
module std::os::win32;
|
||||
|
||||
$if (env::os_is_win32())
|
||||
|
||||
@@ -8,11 +8,6 @@ enum Win32_GET_FILEEX_INFO_LEVELS
|
||||
MAX,
|
||||
}
|
||||
|
||||
struct Win32_FILETIME
|
||||
{
|
||||
Win32_DWORD dwLowDateTime;
|
||||
Win32_DWORD dwHighDateTime;
|
||||
}
|
||||
|
||||
struct Win32_FILE_ATTRIBUTE_DATA
|
||||
{
|
||||
@@ -24,18 +19,47 @@ struct Win32_FILE_ATTRIBUTE_DATA
|
||||
Win32_DWORD nFileSizeLow;
|
||||
}
|
||||
|
||||
|
||||
const MAX_PATH = 260;
|
||||
|
||||
struct Win32_WIN32_FIND_DATAW
|
||||
{
|
||||
Win32_DWORD dwFileAttributes;
|
||||
Win32_FILETIME ftCreationTime;
|
||||
Win32_FILETIME ftLastAccessTime;
|
||||
Win32_FILETIME ftLastWriteTime;
|
||||
Win32_DWORD nFileSizeHigh;
|
||||
Win32_DWORD nFileSizeLow;
|
||||
Win32_DWORD dwReserved0;
|
||||
Win32_DWORD dwReserved1;
|
||||
Win32_WCHAR[260] cFileName;
|
||||
Win32_WCHAR[14] cAlternateFileName;
|
||||
Win32_DWORD dwFileType; // Obsolete. Do not use.
|
||||
Win32_DWORD dwCreatorType; // Obsolete. Do not use
|
||||
Win32_WORD wFinderFlags; // Obsolete. Do not use
|
||||
}
|
||||
|
||||
typedef Win32_LPWIN32_FIND_DATAW = Win32_WIN32_FIND_DATAW*;
|
||||
|
||||
extern fn Win32_BOOL win32_CloseHandle(Win32_HANDLE) @extern("CloseHandle");
|
||||
extern fn Win32_BOOL win32_CreatePipe(Win32_PHANDLE hReadPipe, Win32_PHANDLE hWritePipe, Win32_LPSECURITY_ATTRIBUTES lpPipeAttributes, Win32_DWORD nSize) @extern("CreatePipe");
|
||||
extern fn Win32_BOOL win32_GetFileAttributesExW(Win32_LPCWSTR, Win32_GET_FILEEX_INFO_LEVELS, Win32_LPVOID) @extern("GetFileAttributesExW");
|
||||
extern fn Win32_BOOL win32_PathFileExistsW(Win32_LPCWSTR) @extern("PathFileExistsW");
|
||||
extern fn Win32_DWORD win32_GetTempPathW(Win32_DWORD nBufferLength, Win32_LPWSTR lpBuffer) @extern("GetTempPathW");
|
||||
extern fn Win32_BOOL win32_SetCurrentDirectoryW(Win32_LPCTSTR buffer) @extern("SetCurrentDirectoryW");
|
||||
extern fn Win32_BOOL win32_RemoveDirectoryW(Win32_LPCWSTR lpPathName) @extern("RemoveDirectoryW");
|
||||
extern fn Win32_BOOL win32_CreateDirectoryW(Win32_LPCWSTR lpPathName, Win32_LPSECURITY_ATTRIBUTES lpPipeAttributes) @extern("CreateDirectoryW");
|
||||
extern fn Win32_BOOL win32_DeleteFileW(Win32_LPCWSTR lpFileName) @extern("DeleteFileW");
|
||||
extern fn Win32_HANDLE win32_FindFirstFileW(Win32_LPCWSTR lpFileName, Win32_LPWIN32_FIND_DATAW lpFindFileData) @extern("FindFirstFileW");
|
||||
extern fn Win32_BOOL win32_FindNextFileW(Win32_HANDLE hFindFile, Win32_LPWIN32_FIND_DATAW lpFindFileData) @extern("FindNextFileW");
|
||||
extern fn Win32_BOOL win32_FindClose(Win32_HANDLE hFindFile) @extern("FindClose");
|
||||
|
||||
extern fn Char16* _wgetcwd(Char16* buffer, int maxlen);
|
||||
extern fn usz wcslen(Char16* str);
|
||||
|
||||
/*
|
||||
extern ulong _win32_GetCurrentDirectoryW(ulong, Char16* buffer) @extern("GetCurrentDirectoryW");
|
||||
extern bool _win32_CreateSymbolicLinkW(Char16* symlink_file, Char16* target_file, ulong flags) @extern("CreateSymbolicLinkW");
|
||||
extern bool _win32_CreateDirectoryW(Char16* path_name, void* security_attributes) @extern("CreateDirectoryW");
|
||||
extern bool _win32_DeleteFileW(Char16* file) @extern("DeleteFileW");
|
||||
extern bool _win32_CopyFileW(Char16* from_file, Char16* to_file, bool no_overwrite) @extern("CopyFileW");
|
||||
extern ulong _win32_GetFullPathNameW(Char16* file_name, ulong buffer_len, Char16* buffer, Char16** file_part) @extern("GetFullPathNameW");
|
||||
*/
|
||||
|
||||
227
lib/std/os/win32/general.c3
Normal file
227
lib/std/os/win32/general.c3
Normal file
@@ -0,0 +1,227 @@
|
||||
module std::os::win32;
|
||||
|
||||
$if (env::os_is_win32())
|
||||
|
||||
extern fn Win32_DWORD win32_GetLastError() @extern("GetLastError");
|
||||
|
||||
const Win32_DWORD ERROR_INVALID_FUNCTION = 0x1;
|
||||
const Win32_DWORD ERROR_FILE_NOT_FOUND = 0x2;
|
||||
const Win32_DWORD ERROR_PATH_NOT_FOUND = 0x3;
|
||||
const Win32_DWORD ERROR_TOO_MANY_OPEN_FILES = 0x4;
|
||||
const Win32_DWORD ERROR_ACCESS_DENIED = 0x5;
|
||||
const Win32_DWORD ERROR_INVALID_HANDLE = 0x6;
|
||||
const Win32_DWORD ERROR_ARENA_TRASHED = 0x7;
|
||||
const Win32_DWORD ERROR_NOT_ENOUGH_MEMORY = 0x8;
|
||||
const Win32_DWORD ERROR_INVALID_BLOCK = 0x9;
|
||||
const Win32_DWORD ERROR_BAD_ENVIRONMENT = 0xA;
|
||||
const Win32_DWORD ERROR_BAD_FORMAT = 0xB;
|
||||
const Win32_DWORD ERROR_INVALID_ACCESS = 0xC;
|
||||
const Win32_DWORD ERROR_INVALID_DATA = 0xD;
|
||||
const Win32_DWORD ERROR_OUTOFMEMORY = 0xE;
|
||||
const Win32_DWORD ERROR_INVALID_DRIVE = 0xF;
|
||||
const Win32_DWORD ERROR_CURRENT_DIRECTORY = 0x10;
|
||||
const Win32_DWORD ERROR_NOT_SAME_DEVICE = 0x11;
|
||||
const Win32_DWORD ERROR_NO_MORE_FILES = 0x12;
|
||||
const Win32_DWORD ERROR_WRITE_PROTECT = 0x13;
|
||||
const Win32_DWORD ERROR_BAD_UNIT = 0x14;
|
||||
const Win32_DWORD ERROR_NOT_READY = 0x15;
|
||||
const Win32_DWORD ERROR_BAD_COMMAND = 0x16;
|
||||
const Win32_DWORD ERROR_CRC = 0x17;
|
||||
const Win32_DWORD ERROR_BAD_LENGTH = 0x18;
|
||||
const Win32_DWORD ERROR_SEEK = 0x19;
|
||||
const Win32_DWORD ERROR_NOT_DOS_DISK = 0x1A;
|
||||
const Win32_DWORD ERROR_SECTOR_NOT_FOUND = 0x1B;
|
||||
const Win32_DWORD ERROR_OUT_OF_PAPER = 0x1C;
|
||||
const Win32_DWORD ERROR_WRITE_FAULT = 0x1D;
|
||||
const Win32_DWORD ERROR_READ_FAULT = 0x1E;
|
||||
const Win32_DWORD ERROR_GEN_FAILURE = 0x1F;
|
||||
const Win32_DWORD ERROR_SHARING_VIOLATION = 0x20;
|
||||
const Win32_DWORD ERROR_LOCK_VIOLATION = 0x21;
|
||||
const Win32_DWORD ERROR_WRONG_DISK = 0x22;
|
||||
const Win32_DWORD ERROR_SHARING_BUFFER_EXCEEDED = 0x24;
|
||||
const Win32_DWORD ERROR_HANDLE_EOF = 0x26;
|
||||
const Win32_DWORD ERROR_HANDLE_DISK_FULL = 0x27;
|
||||
const Win32_DWORD ERROR_NOT_SUPPORTED = 0x32;
|
||||
const Win32_DWORD ERROR_REM_NOT_LIST = 0x33;
|
||||
const Win32_DWORD ERROR_DUP_NAME = 0x34;
|
||||
const Win32_DWORD ERROR_BAD_NETPATH = 0x35;
|
||||
const Win32_DWORD ERROR_NETWORK_BUSY = 0x36;
|
||||
const Win32_DWORD ERROR_DEV_NOT_EXIST = 0x37;
|
||||
const Win32_DWORD ERROR_TOO_MANY_CMDS = 0x38;
|
||||
const Win32_DWORD ERROR_ADAP_HDW_ERR = 0x39;
|
||||
const Win32_DWORD ERROR_BAD_NET_RESP = 0x3A;
|
||||
const Win32_DWORD ERROR_UNEXP_NET_ERR = 0x3B;
|
||||
const Win32_DWORD ERROR_BAD_REM_ADAP = 0x3C;
|
||||
const Win32_DWORD ERROR_PRINTQ_FULL = 0x3D;
|
||||
const Win32_DWORD ERROR_NO_SPOOL_SPACE = 0x3E;
|
||||
const Win32_DWORD ERROR_PRINT_CANCELLED = 0x3F;
|
||||
const Win32_DWORD ERROR_NETNAME_DELETED = 0x40;
|
||||
const Win32_DWORD ERROR_NETWORK_ACCESS_DENIED = 0x41;
|
||||
const Win32_DWORD ERROR_BAD_DEV_TYPE = 0x42;
|
||||
const Win32_DWORD ERROR_BAD_NET_NAME = 0x43;
|
||||
const Win32_DWORD ERROR_TOO_MANY_NAMES = 0x44;
|
||||
const Win32_DWORD ERROR_TOO_MANY_SESS = 0x45;
|
||||
const Win32_DWORD ERROR_SHARING_PAUSED = 0x46;
|
||||
const Win32_DWORD ERROR_REQ_NOT_ACCEP = 0x47;
|
||||
const Win32_DWORD ERROR_REDIR_PAUSED = 0x48;
|
||||
const Win32_DWORD ERROR_FILE_EXISTS = 0x50;
|
||||
const Win32_DWORD ERROR_CANNOT_MAKE = 0x52;
|
||||
const Win32_DWORD ERROR_FAIL_I24 = 0x53;
|
||||
const Win32_DWORD ERROR_OUT_OF_STRUCTURES = 0x54;
|
||||
const Win32_DWORD ERROR_ALREADY_ASSIGNED = 0x55;
|
||||
const Win32_DWORD ERROR_INVALID_PASSWORD = 0x56;
|
||||
const Win32_DWORD ERROR_INVALID_PARAMETER = 0x57;
|
||||
const Win32_DWORD ERROR_NET_WRITE_FAULT = 0x58;
|
||||
const Win32_DWORD ERROR_NO_PROC_SLOTS = 0x59;
|
||||
const Win32_DWORD ERROR_TOO_MANY_SEMAPHORES = 0x64;
|
||||
const Win32_DWORD ERROR_EXCL_SEM_ALREADY_OWNED = 0x65;
|
||||
const Win32_DWORD ERROR_SEM_IS_SET = 0x66;
|
||||
const Win32_DWORD ERROR_TOO_MANY_SEM_REQUESTS = 0x67;
|
||||
const Win32_DWORD ERROR_INVALID_AT_INTERRUPT_TIME = 0x68;
|
||||
const Win32_DWORD ERROR_SEM_OWNER_DIED = 0x69;
|
||||
const Win32_DWORD ERROR_SEM_USER_LIMIT = 0x6A;
|
||||
const Win32_DWORD ERROR_DISK_CHANGE = 0x6B;
|
||||
const Win32_DWORD ERROR_DRIVE_LOCKED = 0x6C;
|
||||
const Win32_DWORD ERROR_BROKEN_PIPE = 0x6D;
|
||||
const Win32_DWORD ERROR_OPEN_FAILED = 0x6E;
|
||||
const Win32_DWORD ERROR_BUFFER_OVERFLOW = 0x6F;
|
||||
const Win32_DWORD ERROR_DISK_FULL = 0x70;
|
||||
const Win32_DWORD ERROR_NO_MORE_SEARCH_HANDLES = 0x71;
|
||||
const Win32_DWORD ERROR_INVALID_TARGET_HANDLE = 0x72;
|
||||
const Win32_DWORD ERROR_INVALID_CATEGORY = 0x75;
|
||||
const Win32_DWORD ERROR_INVALID_VERIFY_SWITCH = 0x76;
|
||||
const Win32_DWORD ERROR_BAD_DRIVER_LEVEL = 0x77;
|
||||
const Win32_DWORD ERROR_CALL_NOT_IMPLEMENTED = 0x78;
|
||||
const Win32_DWORD ERROR_SEM_TIMEOUT = 0x79;
|
||||
const Win32_DWORD ERROR_INSUFFICIENT_BUFFER = 0x7A;
|
||||
const Win32_DWORD ERROR_INVALID_NAME = 0x7B;
|
||||
const Win32_DWORD ERROR_INVALID_LEVEL = 0x7C;
|
||||
const Win32_DWORD ERROR_NO_VOLUME_LABEL = 0x7D;
|
||||
const Win32_DWORD ERROR_MOD_NOT_FOUND = 0x7E;
|
||||
const Win32_DWORD ERROR_PROC_NOT_FOUND = 0x7F;
|
||||
const Win32_DWORD ERROR_WAIT_NO_CHILDREN = 0x80;
|
||||
const Win32_DWORD ERROR_CHILD_NOT_COMPLETE = 0x81;
|
||||
const Win32_DWORD ERROR_DIRECT_ACCESS_HANDLE = 0x82;
|
||||
const Win32_DWORD ERROR_NEGATIVE_SEEK = 0x83;
|
||||
const Win32_DWORD ERROR_SEEK_ON_DEVICE = 0x84;
|
||||
const Win32_DWORD ERROR_IS_JOIN_TARGET = 0x85;
|
||||
const Win32_DWORD ERROR_IS_JOINED = 0x86;
|
||||
const Win32_DWORD ERROR_IS_SUBSTED = 0x87;
|
||||
const Win32_DWORD ERROR_NOT_JOINED = 0x88;
|
||||
const Win32_DWORD ERROR_NOT_SUBSTED = 0x89;
|
||||
const Win32_DWORD ERROR_JOIN_TO_JOIN = 0x8A;
|
||||
const Win32_DWORD ERROR_SUBST_TO_SUBST = 0x8B;
|
||||
const Win32_DWORD ERROR_JOIN_TO_SUBST = 0x8C;
|
||||
const Win32_DWORD ERROR_SUBST_TO_JOIN = 0x8D;
|
||||
const Win32_DWORD ERROR_BUSY_DRIVE = 0x8E;
|
||||
const Win32_DWORD ERROR_SAME_DRIVE = 0x8F;
|
||||
const Win32_DWORD ERROR_DIR_NOT_ROOT = 0x90;
|
||||
const Win32_DWORD ERROR_DIR_NOT_EMPTY = 0x91;
|
||||
const Win32_DWORD ERROR_IS_SUBST_PATH = 0x92;
|
||||
const Win32_DWORD ERROR_IS_JOIN_PATH = 0x93;
|
||||
const Win32_DWORD ERROR_PATH_BUSY = 0x94;
|
||||
const Win32_DWORD ERROR_IS_SUBST_TARGET = 0x95;
|
||||
const Win32_DWORD ERROR_SYSTEM_TRACE = 0x96;
|
||||
const Win32_DWORD ERROR_INVALID_EVENT_COUNT = 0x97;
|
||||
const Win32_DWORD ERROR_TOO_MANY_MUXWAITERS = 0x98;
|
||||
const Win32_DWORD ERROR_INVALID_LIST_FORMAT = 0x99;
|
||||
const Win32_DWORD ERROR_LABEL_TOO_LONG = 0x9A;
|
||||
const Win32_DWORD ERROR_TOO_MANY_TCBS = 0x9B;
|
||||
const Win32_DWORD ERROR_SIGNAL_REFUSED = 0x9C;
|
||||
const Win32_DWORD ERROR_DISCARDED = 0x9D;
|
||||
const Win32_DWORD ERROR_NOT_LOCKED = 0x9E;
|
||||
const Win32_DWORD ERROR_BAD_THREADID_ADDR = 0x9F;
|
||||
const Win32_DWORD ERROR_BAD_ARGUMENTS = 0xA0;
|
||||
const Win32_DWORD ERROR_BAD_PATHNAME = 0xA1;
|
||||
const Win32_DWORD ERROR_SIGNAL_PENDING = 0xA2;
|
||||
const Win32_DWORD ERROR_MAX_THRDS_REACHED = 0xA4;
|
||||
const Win32_DWORD ERROR_LOCK_FAILED = 0xA7;
|
||||
const Win32_DWORD ERROR_BUSY = 0xAA;
|
||||
const Win32_DWORD ERROR_DEVICE_SUPPORT_IN_PROGRESS = 0xAB;
|
||||
const Win32_DWORD ERROR_CANCEL_VIOLATION = 0xAD;
|
||||
const Win32_DWORD ERROR_ATOMIC_LOCKS_NOT_SUPPORTED = 0xAE;
|
||||
const Win32_DWORD ERROR_INVALID_SEGMENT_NUMBER = 0xB4;
|
||||
const Win32_DWORD ERROR_INVALID_ORDINAL = 0xB6;
|
||||
const Win32_DWORD ERROR_ALREADY_EXISTS = 0xB7;
|
||||
const Win32_DWORD ERROR_INVALID_FLAG_NUMBER = 0xBA;
|
||||
const Win32_DWORD ERROR_SEM_NOT_FOUND = 0xBB;
|
||||
const Win32_DWORD ERROR_INVALID_STARTING_CODESEG = 0xBC;
|
||||
const Win32_DWORD ERROR_INVALID_STACKSEG = 0xBD;
|
||||
const Win32_DWORD ERROR_INVALID_MODULETYPE = 0xBE;
|
||||
const Win32_DWORD ERROR_INVALID_EXE_SIGNATURE = 0xBF;
|
||||
|
||||
const Win32_DWORD ERROR_EXE_MARKED_INVALID = 0xC0;
|
||||
const Win32_DWORD ERROR_BAD_EXE_FORMAT = 0xC1;
|
||||
const Win32_DWORD ERROR_ITERATED_DATA_EXCEEDS_64K = 0xC2;
|
||||
const Win32_DWORD ERROR_INVALID_MINALLOCSIZE = 0xC3;
|
||||
const Win32_DWORD ERROR_DYNLINK_FROM_INVALID_RING = 0xC4;
|
||||
const Win32_DWORD ERROR_IOPL_NOT_ENABLED = 0xC5;
|
||||
const Win32_DWORD ERROR_INVALID_SEGDPL = 0xC6;
|
||||
const Win32_DWORD ERROR_AUTODATASEG_EXCEEDS_64K = 0xC7;
|
||||
const Win32_DWORD ERROR_RING2SEG_MUST_BE_MOVABLE = 0xC8;
|
||||
const Win32_DWORD ERROR_RELOC_CHAIN_XEEDS_SEGLIM = 0xC9;
|
||||
const Win32_DWORD ERROR_INFLOOP_IN_RELOC_CHAIN = 0xCA;
|
||||
const Win32_DWORD ERROR_ENVVAR_NOT_FOUND = 0xCB;
|
||||
const Win32_DWORD ERROR_NO_SIGNAL_SENT = 0xCD;
|
||||
const Win32_DWORD ERROR_FILENAME_EXCED_RANGE = 0xCE;
|
||||
const Win32_DWORD ERROR_RING2_STACK_IN_USE = 0xCF;
|
||||
const Win32_DWORD ERROR_META_EXPANSION_TOO_LONG = 0xD0;
|
||||
const Win32_DWORD ERROR_INVALID_SIGNAL_NUMBER = 0xD1;
|
||||
const Win32_DWORD ERROR_THREAD_1_INACTIVE = 0xD2;
|
||||
const Win32_DWORD ERROR_LOCKED = 0xD4;
|
||||
const Win32_DWORD ERROR_TOO_MANY_MODULES = 0xD6;
|
||||
const Win32_DWORD ERROR_NESTING_NOT_ALLOWED = 0xD7;
|
||||
const Win32_DWORD ERROR_EXE_MACHINE_TYPE_MISMATCH = 0xD8;
|
||||
const Win32_DWORD ERROR_EXE_CANNOT_MODIFY_SIGNED_BINARY = 0xD9;
|
||||
const Win32_DWORD ERROR_EXE_CANNOT_MODIFY_STRONG_SIGNED_BINARY = 0xDA;
|
||||
const Win32_DWORD ERROR_FILE_CHECKED_OUT = 0xDC;
|
||||
const Win32_DWORD ERROR_CHECKOUT_REQUIRED = 0xDD;
|
||||
const Win32_DWORD ERROR_BAD_FILE_TYPE = 0xDE;
|
||||
const Win32_DWORD ERROR_FILE_TOO_LARGE = 0xDF;
|
||||
const Win32_DWORD ERROR_FORMS_AUTH_REQUIRED = 0xE0;
|
||||
const Win32_DWORD ERROR_VIRUS_INFECTED = 0xE1;
|
||||
const Win32_DWORD ERROR_VIRUS_DELETED = 0xE2;
|
||||
const Win32_DWORD ERROR_PIPE_LOCAL = 0xE5;
|
||||
const Win32_DWORD ERROR_BAD_PIPE = 0xE6;
|
||||
const Win32_DWORD ERROR_PIPE_BUSY = 0xE7;
|
||||
const Win32_DWORD ERROR_NO_DATA = 0xE8;
|
||||
const Win32_DWORD ERROR_PIPE_NOT_CONNECTED = 0xE9;
|
||||
const Win32_DWORD ERROR_MORE_DATA = 0xEA;
|
||||
const Win32_DWORD ERROR_VC_DISCONNECTED = 0xF0;
|
||||
const Win32_DWORD ERROR_INVALID_EA_NAME = 0xFE;
|
||||
const Win32_DWORD ERROR_EA_LIST_INCONSISTENT = 0xFF;
|
||||
const Win32_DWORD WAIT_TIMEOUT = 0x102;
|
||||
const Win32_DWORD ERROR_NO_MORE_ITEMS = 0x103;
|
||||
const Win32_DWORD ERROR_CANNOT_COPY = 0x10A;
|
||||
const Win32_DWORD ERROR_DIRECTORY = 0x10B;
|
||||
|
||||
const Win32_DWORD ERROR_EAS_DIDNT_FIT = 0x113;
|
||||
const Win32_DWORD ERROR_EA_FILE_CORRUPT = 0x114;
|
||||
const Win32_DWORD ERROR_EA_TABLE_FULL = 0x115;
|
||||
const Win32_DWORD ERROR_INVALID_EA_HANDLE = 0x116;
|
||||
const Win32_DWORD ERROR_EAS_NOT_SUPPORTED = 0x11A;
|
||||
const Win32_DWORD ERROR_NOT_OWNER = 0x120;
|
||||
const Win32_DWORD ERROR_TOO_MANY_POSTS = 0x12A;
|
||||
const Win32_DWORD ERROR_PARTIAL_COPY = 0x12A;
|
||||
const Win32_DWORD ERROR_OPLOCK_NOT_GRANTED = 0x12C;
|
||||
const Win32_DWORD ERROR_INVALID_OPLOCK_PROTOCOL = 0x12D;
|
||||
const Win32_DWORD ERROR_DISK_TOO_FRAGMENTED = 0x12E;
|
||||
const Win32_DWORD ERROR_DELETE_PENDING = 0x12F;
|
||||
const Win32_DWORD ERROR_INCOMPATIBLE_WITH_GLOBAL_SHORT_NAME_REGISTRY_SETTING = 0x130;
|
||||
const Win32_DWORD ERROR_SHORT_NAMES_NOT_ENABLED_ON_VOLUME = 0x131;
|
||||
const Win32_DWORD ERROR_SECURITY_STREAM_IS_INCONSISTENT = 0x132;
|
||||
const Win32_DWORD ERROR_INVALID_LOCK_RANGE = 0x133;
|
||||
const Win32_DWORD ERROR_IMAGE_SUBSYSTEM_NOT_PRESENT = 0x134;
|
||||
const Win32_DWORD ERROR_NOTIFICATION_GUID_ALREADY_DEFINED = 0x135;
|
||||
const Win32_DWORD ERROR_INVALID_EXCEPTION_HANDLER = 0x136;
|
||||
const Win32_DWORD ERROR_DUPLICATE_PRIVILEGES = 0x137;
|
||||
const Win32_DWORD ERROR_NO_RANGES_PROCESSED = 0x138;
|
||||
const Win32_DWORD ERROR_NOT_ALLOWED_ON_SYSTEM_FILE = 0x139;
|
||||
const Win32_DWORD ERROR_DISK_RESOURCES_EXHAUSTED = 0x13A;
|
||||
const Win32_DWORD ERROR_INVALID_TOKEN = 0x13B;
|
||||
const Win32_DWORD ERROR_DEVICE_FEATURE_NOT_SUPPORTED = 0x13C;
|
||||
const Win32_DWORD ERROR_MR_MID_NOT_FOUND = 0x13D;
|
||||
const Win32_DWORD ERROR_SCOPE_NOT_FOUND = 0x13E;
|
||||
const Win32_DWORD ERROR_UNDEFINED_SCOPE = 0x13F;
|
||||
|
||||
$endif
|
||||
@@ -1,4 +1,4 @@
|
||||
module std::os::win32::process;
|
||||
module std::os::win32;
|
||||
|
||||
$if (env::os_is_win32())
|
||||
|
||||
@@ -13,6 +13,6 @@ extern fn bool win32_CreateProcessW(
|
||||
Win32_LPCWSTR lpCurrentDirectory,
|
||||
Win32_LPSTARTUPINFOW lpStartupInfo,
|
||||
Win32_LPPROCESS_INFORMATION lpProcessInformation
|
||||
);
|
||||
) @extern("CreateProcessW");
|
||||
|
||||
$endif
|
||||
@@ -168,6 +168,8 @@ typedef Win32_WCHAR = Char16;
|
||||
typedef Win32_WORD = ushort;
|
||||
typedef Win32_WPARAM = Win32_UINT_PTR;
|
||||
|
||||
const INVALID_HANDLE_VALUE = (Win32_HANDLE)(uptr)-1;
|
||||
|
||||
struct Win32_UNICODE_STRING
|
||||
{
|
||||
Win32_USHORT length;
|
||||
@@ -230,6 +232,12 @@ struct Win32_STARTUPINFOEXW
|
||||
typedef Win32_LPPROC_THREAD_ATTRIBUTE_LIST = void*;
|
||||
typedef Win32_LPSTARTUPINFOEXW = Win32_STARTUPINFOEXW*;
|
||||
|
||||
struct Win32_FILETIME
|
||||
{
|
||||
Win32_DWORD dwLowDateTime;
|
||||
Win32_DWORD dwHighDateTime;
|
||||
}
|
||||
|
||||
struct Win32_PROCESS_INFORMATION
|
||||
{
|
||||
Win32_HANDLE hProcess;
|
||||
|
||||
36
lib/std/os/win32/wsa.c3
Normal file
36
lib/std/os/win32/wsa.c3
Normal file
@@ -0,0 +1,36 @@
|
||||
module std::os::win32;
|
||||
|
||||
typedef WSAError = distinct int;
|
||||
|
||||
$if (env::os_is_win32())
|
||||
|
||||
extern fn WSAError win32_WSAGetLastError() @extern("WSAGetLastError");
|
||||
extern fn void win32_WSASetLastError(WSAError error) @extern("WSASetLastError");
|
||||
$endif
|
||||
|
||||
module std::os::win32::wsa;
|
||||
|
||||
$if (env::os_is_win32())
|
||||
const WSAError INVALID_HANDLE = 6;
|
||||
const WSAError NOT_ENOUGHT_MEMORY = 8;
|
||||
const WSAError INVALID_PARAMETER = 87;
|
||||
const WSAError OPERATION_ABORTED = 995;
|
||||
const WSAError IO_INCOMPLETE = 996;
|
||||
const WSAError IO_PENDING = 997;
|
||||
const WSAError EINTR = 10004;
|
||||
const WSAError EBADF = 10009;
|
||||
const WSAError ACCESS = 10013;
|
||||
const WSAError EFAULT = 10014;
|
||||
const WSAError EINVAL = 10022;
|
||||
const WSAError EMFILE = 10024;
|
||||
const WSAError EWOULDBLOCK = 10035;
|
||||
const WSAError EINPROGRESS = 10036;
|
||||
const WSAError EALREADY = 10037;
|
||||
const WSAError ENOTSOCK = 10038;
|
||||
const WSAError EDESTADDRREQ = 10039;
|
||||
const WSAError EMSGSIZE = 10040;
|
||||
const WSAError EPROTOTYPE = 10041;
|
||||
const WSAError ENOPROTOOPT = 10042;
|
||||
const WSAError EPROTONOSUPPORT = 10043;
|
||||
const WSAError ESOCKTNOSUPPORT = 10044;
|
||||
$endif
|
||||
@@ -61,23 +61,23 @@ extern fn int sched_yield();
|
||||
fn void! NativeMutex.init(NativeMutex* mutex, MutexType type)
|
||||
{
|
||||
PthreadMutexAttribute attr;
|
||||
if (pthread_mutexattr_init(&attr)) return ThreadFault.INIT_FAILED!;
|
||||
if (pthread_mutexattr_init(&attr)) return ThreadFault.INIT_FAILED?;
|
||||
defer pthread_mutexattr_destroy(&attr);
|
||||
if (type & thread::MUTEX_RECURSIVE)
|
||||
{
|
||||
if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)) return ThreadFault.INIT_FAILED!;
|
||||
if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)) return ThreadFault.INIT_FAILED?;
|
||||
}
|
||||
if (pthread_mutex_init(mutex, &attr)) return ThreadFault.INIT_FAILED!;
|
||||
if (pthread_mutex_init(mutex, &attr)) return ThreadFault.INIT_FAILED?;
|
||||
}
|
||||
|
||||
fn void! NativeMutex.destroy(NativeMutex* mtx)
|
||||
{
|
||||
if (pthread_mutex_destroy(mtx)) return ThreadFault.DESTROY_FAILED!;
|
||||
if (pthread_mutex_destroy(mtx)) return ThreadFault.DESTROY_FAILED?;
|
||||
}
|
||||
|
||||
fn void! NativeMutex.lock(NativeMutex* mtx)
|
||||
{
|
||||
if (pthread_mutex_lock(mtx)) return ThreadFault.LOCK_FAILED!;
|
||||
if (pthread_mutex_lock(mtx)) return ThreadFault.LOCK_FAILED?;
|
||||
}
|
||||
|
||||
fn void! NativeMutex.lock_timeoutout(NativeMutex* mtx, ulong ms)
|
||||
@@ -88,7 +88,7 @@ fn void! NativeMutex.lock_timeoutout(NativeMutex* mtx, ulong ms)
|
||||
{
|
||||
if (!ms) break;
|
||||
ulong sleep = min(5, ms);
|
||||
if (!libc::nanosleep(&& TimeSpec { .s = 0, .ns = sleep * 1000_000 }, null)) return ThreadFault.LOCK_FAILED!;
|
||||
if (!libc::nanosleep(&& TimeSpec { .s = 0, .ns = (CLong)sleep * 1000_000 }, null)) return ThreadFault.LOCK_FAILED?;
|
||||
ms -= sleep;
|
||||
}
|
||||
switch (result)
|
||||
@@ -97,9 +97,9 @@ fn void! NativeMutex.lock_timeoutout(NativeMutex* mtx, ulong ms)
|
||||
return;
|
||||
case errno::EBUSY:
|
||||
case errno::ETIMEDOUT:
|
||||
return ThreadFault.LOCK_TIMEOUT!;
|
||||
return ThreadFault.LOCK_TIMEOUT?;
|
||||
default:
|
||||
return ThreadFault.LOCK_FAILED!;
|
||||
return ThreadFault.LOCK_FAILED?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,48 +110,48 @@ fn bool NativeMutex.try_lock(NativeMutex* mtx)
|
||||
|
||||
fn void! NativeMutex.unlock(NativeMutex* mtx)
|
||||
{
|
||||
if (pthread_mutex_unlock(mtx)) return ThreadFault.UNLOCK_FAILED!;
|
||||
if (pthread_mutex_unlock(mtx)) return ThreadFault.UNLOCK_FAILED?;
|
||||
}
|
||||
|
||||
fn void! NativeConditionVariable.init(NativeConditionVariable* cond)
|
||||
{
|
||||
if (pthread_cond_init(cond, null)) return ThreadFault.INIT_FAILED!;
|
||||
if (pthread_cond_init(cond, null)) return ThreadFault.INIT_FAILED?;
|
||||
}
|
||||
|
||||
fn void! NativeConditionVariable.destroy(NativeConditionVariable* cond)
|
||||
{
|
||||
if (pthread_cond_destroy(cond)) return ThreadFault.DESTROY_FAILED!;
|
||||
if (pthread_cond_destroy(cond)) return ThreadFault.DESTROY_FAILED?;
|
||||
}
|
||||
|
||||
fn void! NativeConditionVariable.signal(NativeConditionVariable* cond)
|
||||
{
|
||||
if (pthread_cond_signal(cond)) return ThreadFault.SIGNAL_FAILED!;
|
||||
if (pthread_cond_signal(cond)) return ThreadFault.SIGNAL_FAILED?;
|
||||
}
|
||||
|
||||
fn void! NativeConditionVariable.broadcast(NativeConditionVariable* cond)
|
||||
{
|
||||
if (pthread_cond_broadcast(cond)) return ThreadFault.SIGNAL_FAILED!;
|
||||
if (pthread_cond_broadcast(cond)) return ThreadFault.SIGNAL_FAILED?;
|
||||
}
|
||||
|
||||
fn void! NativeConditionVariable.wait(NativeConditionVariable* cond, NativeMutex* mtx)
|
||||
{
|
||||
if (pthread_cond_wait(cond, mtx)) return ThreadFault.WAIT_FAILED!;
|
||||
if (pthread_cond_wait(cond, mtx)) return ThreadFault.WAIT_FAILED?;
|
||||
}
|
||||
|
||||
fn void! NativeConditionVariable.wait_timeout(NativeConditionVariable* cond, NativeMutex* mtx, ulong ms)
|
||||
{
|
||||
TimeSpec now;
|
||||
if (libc::timespec_get(&now, libc::TIME_UTC) != libc::TIME_UTC) return ThreadFault.WAIT_FAILED!;
|
||||
now.s += ms / 1000;
|
||||
now.ns += (ms % 1000) * 1000_000;
|
||||
if (libc::timespec_get(&now, libc::TIME_UTC) != libc::TIME_UTC) return ThreadFault.WAIT_FAILED?;
|
||||
now.s += (Time_t)(ms / 1000);
|
||||
now.ns += (CLong)((ms % 1000) * 1000_000);
|
||||
switch (pthread_cond_timedwait(cond, mtx, &now))
|
||||
{
|
||||
case errno::ETIMEDOUT:
|
||||
return ThreadFault.WAIT_TIMEOUT!;
|
||||
return ThreadFault.WAIT_TIMEOUT?;
|
||||
case errno::OK:
|
||||
return;
|
||||
default:
|
||||
return ThreadFault.WAIT_FAILED!;
|
||||
return ThreadFault.WAIT_FAILED?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,13 +170,13 @@ fn void! NativeThread.create(NativeThread* thread, ThreadFn thread_fn, void* arg
|
||||
{
|
||||
*thread = null;
|
||||
free(thread_data);
|
||||
return ThreadFault.INIT_FAILED!;
|
||||
return ThreadFault.INIT_FAILED?;
|
||||
}
|
||||
}
|
||||
|
||||
fn void! NativeThread.detach(NativeThread thread)
|
||||
{
|
||||
if (!pthread_detach(thread)) return ThreadFault.DETACH_FAILED!;
|
||||
if (!pthread_detach(thread)) return ThreadFault.DETACH_FAILED?;
|
||||
}
|
||||
|
||||
fn void native_thread_exit(int result)
|
||||
@@ -197,7 +197,7 @@ fn bool NativeThread.equals(NativeThread this, NativeThread other)
|
||||
fn int! NativeThread.join(NativeThread thread)
|
||||
{
|
||||
void *pres;
|
||||
if (pthread_join(thread, &pres)) return ThreadFault.JOIN_FAILED!;
|
||||
if (pthread_join(thread, &pres)) return ThreadFault.JOIN_FAILED?;
|
||||
return (int)(iptr)pres;
|
||||
}
|
||||
|
||||
@@ -219,21 +219,21 @@ struct PosixThreadData @private
|
||||
|
||||
fn void! native_sleep_nano(ulong nano)
|
||||
{
|
||||
TimeSpec to = { .s = 0, .ns = nano };
|
||||
if (libc::nanosleep(&to, null)) return ThreadFault.INTERRUPTED!;
|
||||
TimeSpec to = { .s = 0, .ns = (CLong)nano };
|
||||
if (libc::nanosleep(&to, null)) return ThreadFault.INTERRUPTED?;
|
||||
}
|
||||
|
||||
fn void! native_sleep_ms(ulong ms)
|
||||
{
|
||||
TimeSpec to = { .s = ms / 1000, .ns = (ms % 1000) * 1000_000 };
|
||||
if (libc::nanosleep(&to, null)) return ThreadFault.INTERRUPTED!;
|
||||
TimeSpec to = { .s = (Time_t)(ms / 1000), .ns = (CLong)((ms % 1000) * 1000_000) };
|
||||
if (libc::nanosleep(&to, null)) return ThreadFault.INTERRUPTED?;
|
||||
}
|
||||
|
||||
fn void! native_sleep(double s)
|
||||
{
|
||||
ulong si = (ulong)s;
|
||||
TimeSpec to = { .s = si, .ns = (ulong)((s - si) * 1000_000_000) };
|
||||
if (libc::nanosleep(&to, null)) return ThreadFault.INTERRUPTED!;
|
||||
Time_t si = (Time_t)s;
|
||||
TimeSpec to = { .s = si, .ns = (CLong)((s - si) * 1000_000_000) };
|
||||
if (libc::nanosleep(&to, null)) return ThreadFault.INTERRUPTED?;
|
||||
}
|
||||
|
||||
$endif
|
||||
@@ -76,7 +76,7 @@ fn void! NativeMutex.init(NativeMutex* mtx, MutexType type)
|
||||
win32_InitializeCriticalSection(&(mtx.critical_section));
|
||||
return;
|
||||
}
|
||||
if (!(mtx.handle = win32_CreateMutex(null, false, null))) return ThreadFault.INIT_FAILED!;
|
||||
if (!(mtx.handle = win32_CreateMutex(null, false, null))) return ThreadFault.INIT_FAILED?;
|
||||
}
|
||||
|
||||
fn void! NativeMutex.destroy(NativeMutex* mtx)
|
||||
@@ -86,7 +86,7 @@ fn void! NativeMutex.destroy(NativeMutex* mtx)
|
||||
win32_DeleteCriticalSection(&mtx.critical_section);
|
||||
return;
|
||||
}
|
||||
if (!win32_CloseHandle(mtx.handle)) return ThreadFault.DESTROY_FAILED!;
|
||||
if (!win32_CloseHandle(mtx.handle)) return ThreadFault.DESTROY_FAILED?;
|
||||
}
|
||||
|
||||
fn void! NativeMutex.lock(NativeMutex* mtx)
|
||||
@@ -103,7 +103,7 @@ fn void! NativeMutex.lock(NativeMutex* mtx)
|
||||
break;
|
||||
case WAIT_ABANDONED:
|
||||
default:
|
||||
return ThreadFault.LOCK_FAILED!;
|
||||
return ThreadFault.LOCK_FAILED?;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -125,10 +125,10 @@ fn void! NativeMutex.lock_timeout(NativeMutex* mtx, uint ms)
|
||||
case WAIT_OBJECT_0:
|
||||
break;
|
||||
case WAIT_TIMEOUT:
|
||||
return ThreadFault.LOCK_TIMEOUT!;
|
||||
return ThreadFault.LOCK_TIMEOUT?;
|
||||
case WAIT_ABANDONED:
|
||||
default:
|
||||
return ThreadFault.LOCK_FAILED!;
|
||||
return ThreadFault.LOCK_FAILED?;
|
||||
}
|
||||
if (!mtx.recursive)
|
||||
{
|
||||
@@ -165,7 +165,7 @@ fn void! NativeMutex.unlock(NativeMutex* mtx)
|
||||
win32_LeaveCriticalSection(&mtx.critical_section);
|
||||
return;
|
||||
}
|
||||
if (!win32_ReleaseMutex(mtx.handle)) return ThreadFault.UNLOCK_FAILED!;
|
||||
if (!win32_ReleaseMutex(mtx.handle)) return ThreadFault.UNLOCK_FAILED?;
|
||||
}
|
||||
|
||||
const int CONDITION_EVENT_ONE = 0;
|
||||
@@ -179,14 +179,14 @@ fn void! NativeConditionVariable.init(NativeConditionVariable* cond)
|
||||
if (!cond.event_one)
|
||||
{
|
||||
cond.event_all = (Win32_HANDLE)0;
|
||||
return ThreadFault.INIT_FAILED!;
|
||||
return ThreadFault.INIT_FAILED?;
|
||||
}
|
||||
cond.event_all = win32_CreateEventA(null, true, false, null);
|
||||
if (!cond.event_all)
|
||||
{
|
||||
win32_CloseHandle(cond.event_one);
|
||||
cond.event_one = (Win32_HANDLE)0;
|
||||
return ThreadFault.INIT_FAILED!;
|
||||
return ThreadFault.INIT_FAILED?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,7 +202,7 @@ fn void! NativeConditionVariable.signal(NativeConditionVariable* cond)
|
||||
win32_EnterCriticalSection(&cond.waiters_count_lock);
|
||||
bool have_waiters = cond.waiters_count > 0;
|
||||
win32_LeaveCriticalSection(&cond.waiters_count_lock);
|
||||
if (have_waiters && !win32_SetEvent(cond.event_one)) return ThreadFault.SIGNAL_FAILED!;
|
||||
if (have_waiters && !win32_SetEvent(cond.event_one)) return ThreadFault.SIGNAL_FAILED?;
|
||||
}
|
||||
|
||||
fn void! NativeConditionVariable.broadcast(NativeConditionVariable* cond)
|
||||
@@ -210,7 +210,7 @@ fn void! NativeConditionVariable.broadcast(NativeConditionVariable* cond)
|
||||
win32_EnterCriticalSection(&cond.waiters_count_lock);
|
||||
bool have_waiters = cond.waiters_count > 0;
|
||||
win32_LeaveCriticalSection(&cond.waiters_count_lock);
|
||||
if (have_waiters && !win32_SetEvent(cond.event_all)) return ThreadFault.SIGNAL_FAILED!;
|
||||
if (have_waiters && !win32_SetEvent(cond.event_all)) return ThreadFault.SIGNAL_FAILED?;
|
||||
}
|
||||
|
||||
fn void! timedwait(NativeConditionVariable* cond, NativeMutex* mtx, uint timeout) @private
|
||||
@@ -219,17 +219,17 @@ fn void! timedwait(NativeConditionVariable* cond, NativeMutex* mtx, uint timeout
|
||||
cond.waiters_count++;
|
||||
win32_LeaveCriticalSection(&cond.waiters_count_lock);
|
||||
|
||||
mtx.unlock()?;
|
||||
mtx.unlock()!;
|
||||
|
||||
uint result = win32_WaitForMultipleObjects(2, &cond.events, false, timeout);
|
||||
switch (result)
|
||||
{
|
||||
case WAIT_TIMEOUT:
|
||||
mtx.lock()?;
|
||||
return ThreadFault.WAIT_TIMEOUT!;
|
||||
mtx.lock()!;
|
||||
return ThreadFault.WAIT_TIMEOUT?;
|
||||
case WAIT_FAILED:
|
||||
mtx.lock()?;
|
||||
return ThreadFault.WAIT_FAILED!;
|
||||
mtx.lock()!;
|
||||
return ThreadFault.WAIT_FAILED?;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -244,12 +244,12 @@ fn void! timedwait(NativeConditionVariable* cond, NativeMutex* mtx, uint timeout
|
||||
{
|
||||
if (!win32_ResetEvent(cond.event_all))
|
||||
{
|
||||
mtx.lock()?;
|
||||
return ThreadFault.WAIT_FAILED!;
|
||||
mtx.lock()!;
|
||||
return ThreadFault.WAIT_FAILED?;
|
||||
}
|
||||
}
|
||||
|
||||
mtx.lock()?;
|
||||
mtx.lock()!;
|
||||
}
|
||||
|
||||
fn void! NativeConditionVariable.wait(NativeConditionVariable* cond, NativeMutex* mtx) @inline
|
||||
@@ -264,12 +264,12 @@ fn void! NativeConditionVariable.wait_timeout(NativeConditionVariable* cond, Nat
|
||||
|
||||
fn void! NativeThread.create(NativeThread* thread, ThreadFn func, void* args)
|
||||
{
|
||||
if (!(*thread = win32_CreateThread(null, 0, func, args, 0, null))) return ThreadFault.INIT_FAILED!;
|
||||
if (!(*thread = win32_CreateThread(null, 0, func, args, 0, null))) return ThreadFault.INIT_FAILED?;
|
||||
}
|
||||
|
||||
fn void! NativeThread.detach(NativeThread thread) @inline
|
||||
{
|
||||
if (!win32_CloseHandle(thread)) return ThreadFault.DETACH_FAILED!;
|
||||
if (!win32_CloseHandle(thread)) return ThreadFault.DETACH_FAILED?;
|
||||
}
|
||||
|
||||
|
||||
@@ -313,8 +313,8 @@ fn void NativeOnceFlag.call_once(NativeOnceFlag* flag, OnceFn func)
|
||||
|
||||
fn void! NativeThread.join(NativeThread thread, int *res)
|
||||
{
|
||||
if (win32_WaitForSingleObject(thread, INFINITE) == WAIT_FAILED) return ThreadFault.JOIN_FAILED!;
|
||||
if (!win32_GetExitCodeThread(thread, (uint*)res)) return ThreadFault.JOIN_FAILED!;
|
||||
if (win32_WaitForSingleObject(thread, INFINITE) == WAIT_FAILED) return ThreadFault.JOIN_FAILED?;
|
||||
if (!win32_GetExitCodeThread(thread, (uint*)res)) return ThreadFault.JOIN_FAILED?;
|
||||
defer win32_CloseHandle(thread);
|
||||
}
|
||||
|
||||
@@ -333,7 +333,7 @@ fn bool NativeThread.equals(NativeThread this, NativeThread other)
|
||||
**/
|
||||
fn void! native_sleep_ms(ulong ms)
|
||||
{
|
||||
if (win32_SleepEx((uint)ms, true) == WAIT_IO_COMPLETION) return ThreadFault.INTERRUPTED!;
|
||||
if (win32_SleepEx((uint)ms, true) == WAIT_IO_COMPLETION) return ThreadFault.INTERRUPTED?;
|
||||
}
|
||||
|
||||
fn void! native_sleep(double s)
|
||||
|
||||
23
lib/std/time/clock.c3
Normal file
23
lib/std/time/clock.c3
Normal file
@@ -0,0 +1,23 @@
|
||||
module std::time::clock;
|
||||
|
||||
fn Clock now()
|
||||
{
|
||||
$if ($defined(native_clock))
|
||||
return os::native_clock();
|
||||
$else
|
||||
return 0;
|
||||
$endif
|
||||
}
|
||||
|
||||
fn NanoDuration Clock.mark(Clock* this)
|
||||
{
|
||||
Clock mark = now();
|
||||
NanoDuration diff = (NanoDuration)(mark - *this);
|
||||
*this = mark;
|
||||
return diff;
|
||||
}
|
||||
|
||||
fn NanoDuration Clock.to_now(Clock this)
|
||||
{
|
||||
return (NanoDuration)(now() - this);
|
||||
}
|
||||
175
lib/std/time/datetime.c3
Normal file
175
lib/std/time/datetime.c3
Normal file
@@ -0,0 +1,175 @@
|
||||
module std::time::datetime;
|
||||
import libc;
|
||||
|
||||
fn DateTime now()
|
||||
{
|
||||
return from_time(time::now());
|
||||
}
|
||||
|
||||
/**
|
||||
* @require day >= 1 && day < 32
|
||||
* @require hour >= 0 && hour < 24
|
||||
* @require min >= 0 && min < 60
|
||||
* @require sec >= 0 && sec < 60
|
||||
* @require us >= 0 && us < 999_999
|
||||
**/
|
||||
fn DateTime from_date(int year, Month month = JANUARY, int day = 1, int hour = 0, int min = 0, int sec = 0, int us = 0)
|
||||
{
|
||||
DateTime dt @noinit;
|
||||
dt.set_date(year, month, day, hour, min, sec, us) @inline;
|
||||
return dt;
|
||||
}
|
||||
|
||||
fn TzDateTime DateTime.to_local(DateTime* this)
|
||||
{
|
||||
Tm tm @noinit;
|
||||
Time_t time_t = (Time_t)this.time.to_seconds();
|
||||
libc::localtime_r(&time_t, &tm);
|
||||
TzDateTime dt;
|
||||
dt.usec = (int)((long)this.time % (long)time::MICROSECONDS_PER_SECOND);
|
||||
dt.sec = (char)tm.tm_sec;
|
||||
dt.min = (char)tm.tm_min;
|
||||
dt.hour = (char)tm.tm_hour;
|
||||
dt.day = (char)tm.tm_mday;
|
||||
dt.month = (Month)tm.tm_mon;
|
||||
dt.year = tm.tm_year + 1900;
|
||||
dt.weekday = !tm.tm_wday ? Weekday.SUNDAY : (Weekday)tm.tm_wday + 1;
|
||||
dt.year_day = (ushort)tm.tm_yday;
|
||||
dt.time = this.time;
|
||||
$if ($defined(tm.tm_gmtoff))
|
||||
dt.gmt_offset = (int)tm.tm_gmtoff;
|
||||
$else
|
||||
$assert($defined(libc::_get_timezone));
|
||||
CLong timezone;
|
||||
libc::_get_timezone(&timezone);
|
||||
dt.gmt_offset = (int)-timezone;
|
||||
$endif
|
||||
return dt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require day >= 1 && day < 32
|
||||
* @require hour >= 0 && hour < 24
|
||||
* @require min >= 0 && min <= 60
|
||||
* @require sec >= 0 && sec < 60
|
||||
* @require us >= 0 && us < 999_999
|
||||
**/
|
||||
fn void DateTime.set_date(DateTime *this, int year, Month month = JANUARY, int day = 1, int hour = 0, int min = 0, int sec = 0, int us = 0)
|
||||
{
|
||||
Tm tm;
|
||||
tm.tm_sec = sec;
|
||||
tm.tm_min = min;
|
||||
tm.tm_hour = hour;
|
||||
tm.tm_mon = month.ordinal;
|
||||
tm.tm_mday = day;
|
||||
tm.tm_year = year - 1900;
|
||||
Time_t time = libc::timegm(&tm);
|
||||
this.set_time((Time)(time * (long)time::MICROSECONDS_PER_SECOND + us));
|
||||
}
|
||||
|
||||
fn void DateTime.set_time(DateTime* this, Time time)
|
||||
{
|
||||
Tm tm @noinit;
|
||||
Time_t time_t = (Time_t)time.to_seconds();
|
||||
libc::gmtime_r(&time_t, &tm);
|
||||
this.usec = (int)((long)time % (long)time::MICROSECONDS_PER_SECOND);
|
||||
this.sec = (char)tm.tm_sec;
|
||||
this.min = (char)tm.tm_min;
|
||||
this.hour = (char)tm.tm_hour;
|
||||
this.day = (char)tm.tm_mday;
|
||||
this.month = (Month)tm.tm_mon;
|
||||
this.year = tm.tm_year + 1900;
|
||||
this.weekday = !tm.tm_wday ? Weekday.SUNDAY : (Weekday)tm.tm_wday + 1;
|
||||
this.year_day = (ushort)tm.tm_yday;
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
fn DateTime DateTime.add_seconds(DateTime* date, int seconds) => from_time(date.time.add_seconds(seconds));
|
||||
fn DateTime DateTime.add_minutes(DateTime* date, int minutes) => from_time(date.time.add_minutes(minutes));
|
||||
fn DateTime DateTime.add_hours(DateTime* date, int hours) => from_time(date.time.add_hours(hours));
|
||||
fn DateTime DateTime.add_days(DateTime* date, int days) => from_time(date.time.add_days(days));
|
||||
fn DateTime DateTime.add_weeks(DateTime* date, int weeks) => from_time(date.time.add_weeks(weeks));
|
||||
|
||||
fn DateTime DateTime.add_years(DateTime* date, int years)
|
||||
{
|
||||
if (!years) return *date;
|
||||
return from_date(date.year + years, date.month, date.day, date.hour, date.min, date.sec, date.usec);
|
||||
}
|
||||
|
||||
fn DateTime DateTime.add_months(DateTime* date, int months)
|
||||
{
|
||||
if (months == 0) return *date;
|
||||
int year = date.year;
|
||||
int month = date.month.ordinal;
|
||||
switch
|
||||
{
|
||||
case months % 12 == 0:
|
||||
year += months / 12;
|
||||
case months < 0:
|
||||
month += months % 12;
|
||||
year += months / 12;
|
||||
if (month < 0)
|
||||
{
|
||||
year--;
|
||||
month += 12;
|
||||
}
|
||||
default:
|
||||
month += months;
|
||||
year += month / 12;
|
||||
month %= 12;
|
||||
}
|
||||
return from_date(year, (Month)month, date.day, date.hour, date.min, date.sec, date.usec);
|
||||
}
|
||||
|
||||
fn DateTime from_time(Time time)
|
||||
{
|
||||
DateTime dt @noinit;
|
||||
dt.set_time(time) @inline;
|
||||
return dt;
|
||||
}
|
||||
|
||||
fn Time DateTime.to_time(DateTime* this) @inline
|
||||
{
|
||||
return this.time;
|
||||
}
|
||||
|
||||
fn bool DateTime.after(DateTime* this, DateTime compare)
|
||||
{
|
||||
return this.time > compare.time;
|
||||
}
|
||||
|
||||
fn bool DateTime.before(DateTime* this, DateTime compare)
|
||||
{
|
||||
return this.time < compare.time;
|
||||
}
|
||||
|
||||
fn int DateTime.compare_to(DateTime* this, DateTime compare)
|
||||
{
|
||||
switch
|
||||
{
|
||||
case this.time > compare.time: return 1;
|
||||
case this.time < compare.time: return -1;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
fn int DateTime.diff_years(DateTime *to, DateTime from)
|
||||
{
|
||||
int year_diff = to.year - from.year;
|
||||
switch
|
||||
{
|
||||
case year_diff < 0: return -from.diff_years(*to);
|
||||
case year_diff == 0: return 0;
|
||||
}
|
||||
if (to.year_day < from.year_day) year_diff--;
|
||||
return year_diff;
|
||||
}
|
||||
|
||||
fn double DateTime.diff_sec(DateTime to, DateTime from)
|
||||
{
|
||||
return (double)to.time.diff_us(from.time) / (double)time::MICROSECONDS_PER_SECOND;
|
||||
}
|
||||
fn TimeDuration DateTime.diff_us(DateTime to, DateTime from)
|
||||
{
|
||||
return to.time.diff_us(from.time);
|
||||
}
|
||||
28
lib/std/time/os/time_darwin.c3
Normal file
28
lib/std/time/os/time_darwin.c3
Normal file
@@ -0,0 +1,28 @@
|
||||
module std::time::os;
|
||||
|
||||
$if (env::os_is_darwin())
|
||||
|
||||
struct Darwin_mach_timebase_info
|
||||
{
|
||||
uint numer;
|
||||
uint denom;
|
||||
}
|
||||
|
||||
typedef Darwin_mach_timebase_info_t = Darwin_mach_timebase_info;
|
||||
typedef Darwin_mach_timebase_info_data_t = Darwin_mach_timebase_info;
|
||||
|
||||
extern fn void mach_timebase_info(Darwin_mach_timebase_info_data_t* timebase);
|
||||
extern fn ulong mach_absolute_time();
|
||||
|
||||
fn Clock native_clock()
|
||||
{
|
||||
static Darwin_mach_timebase_info_data_t timebase;
|
||||
if (!timebase.denom)
|
||||
{
|
||||
mach_timebase_info(&timebase);
|
||||
}
|
||||
return (Clock)(mach_absolute_time() * timebase.numer / timebase.denom);
|
||||
}
|
||||
|
||||
|
||||
$endif
|
||||
88
lib/std/time/os/time_posix.c3
Normal file
88
lib/std/time/os/time_posix.c3
Normal file
@@ -0,0 +1,88 @@
|
||||
module std::time::os;
|
||||
import libc;
|
||||
|
||||
$if (env::os_is_posix() && env::COMPILER_LIBC_AVAILABLE)
|
||||
|
||||
$switch (env::OS_TYPE)
|
||||
$case OPENBSD:
|
||||
const CLOCK_REALTIME = 0;
|
||||
const CLOCK_PROCESS_CPUTIME_ID = 2;
|
||||
const CLOCK_MONOTONIC = 3;
|
||||
const CLOCK_THREAD_CPUTIME_ID = 4;
|
||||
const CLOCK_UPTIME = 5;
|
||||
const CLOCK_BOOTTIME = 6;
|
||||
$case FREEBSD:
|
||||
const CLOCK_REALTIME = 0;
|
||||
const CLOCK_VIRTUAL = 1;
|
||||
const CLOCK_PROF = 2;
|
||||
const CLOCK_MONOTONIC = 4;
|
||||
const CLOCK_UPTIME = 5;
|
||||
const CLOCK_UPTIME_PRECISE = 7;
|
||||
const CLOCK_UPTIME_FAST = 8;
|
||||
const CLOCK_REALTIME_PRECISE = 9;
|
||||
const CLOCK_REALTIME_FAST = 10;
|
||||
const CLOCK_MONOTONIC_PRECISE = 11;
|
||||
const CLOCK_MONOTONIC_FAST = 12;
|
||||
const CLOCK_SECOND = 13;
|
||||
const CLOCK_THREAD_CPUTIME_ID = 14;
|
||||
const CLOCK_PROCESS_CPUTIME_ID = 15;
|
||||
const CLOCK_BOOTTIME = CLOCK_UPTIME;
|
||||
const CLOCK_REALTIME_COARSE = CLOCK_REALTIME_FAST;
|
||||
const CLOCK_MONOTONIC_COARSE = CLOCK_MONOTONIC_FAST;
|
||||
$case NETBSD:
|
||||
const CLOCK_REALTIME = 0;
|
||||
const CLOCK_VIRTUAL = 1;
|
||||
const CLOCK_PROF = 2;
|
||||
const CLOCK_MONOTONIC = 3;
|
||||
const CLOCK_THREAD_CPUTIME_ID = 0x20000000;
|
||||
const CLOCK_PROCESS_CPUTIME_ID = 0x40000000;
|
||||
$case WASI:
|
||||
// Not implemented
|
||||
const CLOCK_REALTIME = 0;
|
||||
const CLOCK_MONOTONIC = 0;
|
||||
$case MACOS:
|
||||
$case TVOS:
|
||||
$case IOS:
|
||||
$case WATCHOS:
|
||||
const CLOCK_REALTIME = 0;
|
||||
const CLOCK_MONOTONIC = 6;
|
||||
const CLOCK_MONOTONIC_RAW = 4;
|
||||
const CLOCK_MONOTONIC_RAW_APPROX = 5;
|
||||
const CLOCK_UPTIME_RAW = 8;
|
||||
const CLOCK_UPTIME_RAW_APPROX = 9;
|
||||
const CLOCK_PROCESS_CPUTIME_ID = 12;
|
||||
const CLOCK_THREAD_CPUTIME_ID = 16;
|
||||
$case LINUX:
|
||||
$default:
|
||||
const CLOCK_REALTIME = 0;
|
||||
const CLOCK_MONOTONIC = 1;
|
||||
const CLOCK_PROCESS_CPUTIME_ID = 2;
|
||||
const CLOCK_THREAD_CPUTIME_ID = 3;
|
||||
const CLOCK_MONOTONIC_RAW = 4;
|
||||
const CLOCK_REALTIME_COARSE = 5;
|
||||
const CLOCK_MONOTONIC_COARSE = 6;
|
||||
const CLOCK_BOOTTIME = 7;
|
||||
const CLOCK_REALTIME_ALARM = 8;
|
||||
const CLOCK_BOOTTIME_ALARM = 9;
|
||||
const CLOCK_TAI = 11;
|
||||
$endswitch
|
||||
|
||||
extern fn void clock_gettime(int type, TimeSpec *time);
|
||||
|
||||
fn Time native_timestamp()
|
||||
{
|
||||
TimeSpec ts;
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
return (Time)(ts.s * 1_000_000i64 + ts.ns / 1_000i64);
|
||||
}
|
||||
|
||||
$if (!env::os_is_darwin())
|
||||
fn Clock native_clock()
|
||||
{
|
||||
TimeSpec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return (Clock)((ulong)ts.s * 1_000_000_000u64 + (ulong)ts.ns);
|
||||
}
|
||||
$endif
|
||||
|
||||
$endif
|
||||
33
lib/std/time/os/time_win32.c3
Normal file
33
lib/std/time/os/time_win32.c3
Normal file
@@ -0,0 +1,33 @@
|
||||
module std::time::os;
|
||||
import std::os::win32;
|
||||
|
||||
$if (env::os_is_win32() && env::COMPILER_LIBC_AVAILABLE)
|
||||
|
||||
extern fn void win32_GetSystemTimeAsFileTime(Win32_FILETIME* time) @extern("GetSystemTimeAsFileTime");
|
||||
extern fn Win32_BOOL win32_QueryPerformanceFrequency(Win32_LARGE_INTEGER* lpFrequency) @extern("QueryPerformanceFrequency");
|
||||
extern fn Win32_BOOL win32_QueryPerformanceCounter(Win32_LARGE_INTEGER* lpPerformanceCount) @extern("QueryPerformanceCounter");
|
||||
|
||||
const ulong WINDOWS_TICK_US @local = 10;
|
||||
const ulong WIN_TO_UNIX_EPOCH_US @local = 116444736000000000u64 / WINDOWS_TICK_US;
|
||||
|
||||
fn Clock native_clock()
|
||||
{
|
||||
static Win32_LARGE_INTEGER freq;
|
||||
if (!freq.quadPart)
|
||||
{
|
||||
if (!win32_QueryPerformanceFrequency(&freq)) return 0;
|
||||
}
|
||||
Win32_LARGE_INTEGER counter @noinit;
|
||||
if (!win32_QueryPerformanceCounter(&counter)) return 0;
|
||||
return (Clock)counter.quadPart;
|
||||
}
|
||||
|
||||
fn Time native_timestamp()
|
||||
{
|
||||
Win32_FILETIME ft @noinit;
|
||||
win32_GetSystemTimeAsFileTime(&ft);
|
||||
ulong result = (ulong)ft.dwHighDateTime << 32 | ft.dwLowDateTime;
|
||||
return (Time)(result / WINDOWS_TICK_US - WIN_TO_UNIX_EPOCH_US);
|
||||
}
|
||||
|
||||
$endif
|
||||
87
lib/std/time/time.c3
Normal file
87
lib/std/time/time.c3
Normal file
@@ -0,0 +1,87 @@
|
||||
module std::time;
|
||||
|
||||
typedef Time @inline = distinct long;
|
||||
typedef TimeDuration @inline = distinct long;
|
||||
typedef Clock @inline = distinct ulong;
|
||||
typedef NanoDuration @inline = distinct long;
|
||||
|
||||
const TimeDuration MICROSECONDS_PER_SECOND = 1_000_000;
|
||||
const TimeDuration MICROSECONDS_PER_MINUTE = MICROSECONDS_PER_SECOND * 60;
|
||||
const TimeDuration MICROSECONDS_PER_HOUR = MICROSECONDS_PER_MINUTE * 60;
|
||||
const TimeDuration MICROSECONDS_PER_DAY = MICROSECONDS_PER_HOUR * 24;
|
||||
const TimeDuration MICROSECONDS_PER_WEEK = MICROSECONDS_PER_DAY * 7;
|
||||
|
||||
struct DateTime
|
||||
{
|
||||
int usec;
|
||||
char sec;
|
||||
char min;
|
||||
char hour;
|
||||
char day;
|
||||
Month month;
|
||||
Weekday weekday;
|
||||
int year;
|
||||
ushort year_day;
|
||||
Time time;
|
||||
}
|
||||
|
||||
struct TzDateTime
|
||||
{
|
||||
inline DateTime date_time;
|
||||
int gmt_offset;
|
||||
}
|
||||
|
||||
enum Weekday : char
|
||||
{
|
||||
MONDAY,
|
||||
TUESDAY,
|
||||
WEDNESDAY,
|
||||
THURSDAY,
|
||||
FRIDAY,
|
||||
SATURDAY,
|
||||
SUNDAY,
|
||||
}
|
||||
|
||||
enum Month : char
|
||||
{
|
||||
JANUARY,
|
||||
FEBRUARY,
|
||||
MARCH,
|
||||
APRIL,
|
||||
MAY,
|
||||
JUNE,
|
||||
JULY,
|
||||
AUGUST,
|
||||
SEPTEMBER,
|
||||
OCTOBER,
|
||||
NOVEMBER,
|
||||
DECEMBER
|
||||
}
|
||||
|
||||
|
||||
fn Time now()
|
||||
{
|
||||
$if ($defined(native_timestamp))
|
||||
return os::native_timestamp();
|
||||
$else
|
||||
return 0;
|
||||
$endif
|
||||
}
|
||||
|
||||
fn Time Time.add_seconds(Time time, long seconds) => time + (Time)(seconds * (long)MICROSECONDS_PER_SECOND);
|
||||
fn Time Time.add_minutes(Time time, long minutes) => time + (Time)(minutes * (long)MICROSECONDS_PER_MINUTE);
|
||||
fn Time Time.add_hours(Time time, long hours) => time + (Time)(hours * (long)MICROSECONDS_PER_HOUR);
|
||||
fn Time Time.add_days(Time time, long days) => time + (Time)(days * (long)MICROSECONDS_PER_DAY);
|
||||
fn Time Time.add_weeks(Time time, long weeks) => time + (Time)(weeks * (long)MICROSECONDS_PER_WEEK);
|
||||
fn double Time.to_seconds(Time time) => (long)time / (double)MICROSECONDS_PER_SECOND;
|
||||
fn TimeDuration Time.diff_us(Time time, Time other) => (TimeDuration)(time - other);
|
||||
fn double Time.diff_sec(Time time, Time other) => (long)time.diff_us(other) / (double)MICROSECONDS_PER_SECOND;
|
||||
fn double Time.diff_min(Time time, Time other) => (long)time.diff_us(other) / (double)MICROSECONDS_PER_MINUTE;
|
||||
fn double Time.diff_hour(Time time, Time other) => (long)time.diff_us(other) / (double)MICROSECONDS_PER_HOUR;
|
||||
fn double Time.diff_days(Time time, Time other) => (long)time.diff_us(other) / (double)MICROSECONDS_PER_DAY;
|
||||
fn double Time.diff_weeks(Time time, Time other) => (long)time.diff_us(other) / (double)MICROSECONDS_PER_WEEK;
|
||||
|
||||
fn double NanoDuration.to_sec(NanoDuration nd) => (double)nd / 1_000_000_000.0;
|
||||
fn long NanoDuration.to_ms(NanoDuration nd) => (long)nd / 1_000_000;
|
||||
fn TimeDuration NanoDuration.to_duration(NanoDuration nd) => (TimeDuration)nd / 1_000;
|
||||
fn NanoDuration TimeDuration.to_nano(TimeDuration td) => (NanoDuration)td * 1_000;
|
||||
168
releasenotes.md
168
releasenotes.md
@@ -1,4 +1,170 @@
|
||||
Release Notes
|
||||
# C3C Release Notes
|
||||
|
||||
## 0.5.0 Change List
|
||||
|
||||
### Changes / improvements
|
||||
- Dropped support for LLVM 13-14.
|
||||
- Updated grammar and lexer definition.
|
||||
- Removal of `$elif`.
|
||||
- Empty fault definitions is now an error.
|
||||
- Better errors on incorrect bitstruct syntax.
|
||||
- Internal use wildcard type rather than optional wildcard.
|
||||
- Experimental scaled vector type removed.
|
||||
- Disallow parameterize attributes without parameters eg `define @Foo() = { @inline }`.
|
||||
- Handle `@optreturn` contract, renamed `@return!`.
|
||||
- Restrict interface style functions.
|
||||
- Optional propagation and assignment '!' and '?' are flipped.
|
||||
- Add `l` suffix (alias for i64).
|
||||
- Allow getting the underlying type of anyfault.
|
||||
- De-duplicate string constants.
|
||||
- Change @extname => @extern.
|
||||
- `define Type = int` is replaced by `typedef Type = int`.
|
||||
- LLVM "wrapper" library compilation is exception free.
|
||||
- `private` is replaced by attribute `@private`.
|
||||
- Addition of `@local` for file local visibility.
|
||||
- Addition of `@public` for overriding default visibility.
|
||||
- Default visibility can be overridden per module compile unit. Eg `module foo @private`.
|
||||
- Addition of unary `+`.
|
||||
- Remove the `:` and `;` used in $if, $switch etc.
|
||||
- Faults have an ordinal.
|
||||
- Generic module contracts.
|
||||
- Type inference on enum comparisons, e.g `foo_enum == ABC`.
|
||||
- Allow {} to initialize basic types.
|
||||
- String literals default to `String`.
|
||||
- More const modification detection.
|
||||
- C3L zip support.
|
||||
- Support printing object files.
|
||||
- Downloading of libraries using vendor "fetch".
|
||||
- Structural casts removed.
|
||||
- Added "native" option for vector capability.
|
||||
- `$$shufflevector` replaced with `$$swizzle` and `$$swizzle2`.
|
||||
- Builtin swizzle accessors.
|
||||
- Lambdas, e.g `a = int(x, y) => x + y`.
|
||||
- $$FILEPATH builtin constant.
|
||||
- `variant` renamed `any`.
|
||||
- `anyerr` renamed `anyfault`.
|
||||
- Added `$$wasm_memory_size` and `$$wasm_memory_grow` builtins.
|
||||
- Add "link-args" for project.
|
||||
- Possible to suppress entry points using `--no-entry`.
|
||||
- Added `memory-env` option.
|
||||
- Use the .wasm extension on WASM binaries.
|
||||
- Update precedence clarification rules for ^|&.
|
||||
- Support for casting any expression to `void`.
|
||||
- Win 32-bit processor target removed.
|
||||
- Insert null-check for contracts declaring & params.
|
||||
- Support user defined attributes in generic modules.
|
||||
- `--strip-unused` directive for small binaries.
|
||||
- `$$atomic_store` and `$$atomic_load` added.
|
||||
- `usz`/`isz` replaces `usize` and `isize`.
|
||||
- `@export` attribute to determine what is visible in precompiled libraries.
|
||||
- Disallow obviously wrong code returning a pointer to a stack variable.
|
||||
- Add &^| operations for bitstructs.
|
||||
- `@noinit` replaces `= void` to opt-out of implicit zeroing.
|
||||
- Multiple declarations are now allowed in most places, eg `int a, b;`.
|
||||
- Allow simplified (boolean) bitstruct definitions.
|
||||
- Allow `@test` to be placed on module declarations.
|
||||
- Updated name mangling for non-exports.
|
||||
- `defer catch` and `defer try` statements added.
|
||||
- Better errors from `$assert`.
|
||||
- `@deprecated` attribute added.
|
||||
- Allow complex array length inference, eg `int[*][2][*] a = ...`.
|
||||
- Cleanup of cast code.
|
||||
- Removal of `generic` keyword.
|
||||
- Remove implicit cast enum <-> int.
|
||||
- Allow enums to use a distinct type as the backing type.
|
||||
- Update addition and subtraction on enums.
|
||||
- `@ensure` checks only non-optional results.
|
||||
|
||||
### Stdlib changes
|
||||
- Stdlib updates to string.
|
||||
- Additions to `List`
|
||||
- Added dstringwriter.
|
||||
- Improved printf formatting.
|
||||
- is_finite/is_nam/is_inf added.
|
||||
- OnStack allocator to easily allocate a stack buffer.
|
||||
- File enhancements: mkdir, rmdir, chdir.
|
||||
- Path type for file path handling.
|
||||
- Distinct `String` type.
|
||||
- VarString replaced by DString.
|
||||
- Removal of std::core::str.
|
||||
- JSON parser and general Object type.
|
||||
- Addition of `EnumMap`.
|
||||
- RC4 crypto.
|
||||
- Matrix identity macros.
|
||||
- compare_exchange added.
|
||||
- `printfln` and `println` renamed `printfn` and `printn`.
|
||||
- Support of roundeven.
|
||||
- Added easings.
|
||||
- Updated complex/matrix, added quaternion maths.
|
||||
- Improved support for freestanding.
|
||||
- Improved windows main support, with @winmain annotations.
|
||||
- `SimpleHeapAllocator` added.
|
||||
- Added win32 standard types.
|
||||
- Added `saturated` math.
|
||||
- Added `@expect`, `@unlikely` and `@likely` macros.
|
||||
- Temp allocator uses memory-env to determine starting size.
|
||||
- Temp allocator is now accessed using `mem::temp()`, heap allocator using `mem::heap()`.
|
||||
- Float parsing added.
|
||||
- Additions to std::net, ipv4/ipv6 parsing.
|
||||
- Stream api.
|
||||
- Random api.
|
||||
- Sha1 hash function.
|
||||
- Extended enumset functionality.
|
||||
- Updated malloc/calloc/realloc/free removing old helper functions.
|
||||
- Added TrackingAllocator.
|
||||
- Add checks to prevent incorrect alignment on malloc.
|
||||
- Updated clamp.
|
||||
- Added `Clock` and `DateTime`.
|
||||
|
||||
### Fixes
|
||||
- Fixes to the x64 ABI.
|
||||
- Updates to how variadics are implemented.
|
||||
- Fixes to shift checks.
|
||||
- Fixes to string parsing.
|
||||
- Disallow trailing ',' in function parameter list.
|
||||
- Fixed errors on flexible array slices.
|
||||
- Fix of `readdir` issues on macOS.
|
||||
- Fix to slice assignment of distinct types.
|
||||
- Fix of issue casting subarrays to distinct types.
|
||||
- Fixes to `split`, `rindex_of`.
|
||||
- List no longer uses the temp allocator by default.
|
||||
- Remove test global when not in test mode.
|
||||
- Fix sum/product on floats.
|
||||
- Fix error on void! return of macros.
|
||||
- Removed too permissive casts on subarrays.
|
||||
- Using C files correctly places objects in the build folder.
|
||||
- Fix of overaligned deref.
|
||||
- Fix negating a float vector.
|
||||
- Fix where $typeof(x) { ... } would not be a valid compound literal.
|
||||
- Fix so that using `var` in `if (var x = ...)` works correctly.
|
||||
- Fix int[] -> void* casts.
|
||||
- Fix in utf8to16 conversions.
|
||||
- Updated builtin checking.
|
||||
- Reduce formatter register memory usage.
|
||||
- Fixes to the "any" type.
|
||||
- Fix bug in associated values.
|
||||
- More RISC-V tests and fixes to the ABI.
|
||||
- Fix issue with hex floats assumed being double despite `f` suffix.
|
||||
- Fix of the `tan` function.
|
||||
- Fixes to the aarch64 ABI when passing invalid vectors.
|
||||
- Fix creating typed compile time variables.
|
||||
- Fix bug in !floatval codegen.
|
||||
- Fix of visibility issues for generic methods.
|
||||
- Fixes to `$include`.
|
||||
- Fix of LLVM codegen for optionals in certain cases.
|
||||
- Fix of `$vasplat` when invoked repeatedly.
|
||||
- Fix to `$$DATE`.
|
||||
- Fix of attributes on nested bitstructs.
|
||||
- Fix comparing const values > 64 bits.
|
||||
- Defer now correctly invoked in expressions like `return a > 0 ? Foo.ABC! : 1`.
|
||||
- Fix conversion in `if (int x = foo())`.
|
||||
- Delay C ABI lowering until requested to prevent circular dependencies.
|
||||
- Fix issue with decls accidentally invalidated during `$checked` eval.
|
||||
- Fold optional when casting slice to pointer.
|
||||
- Fixed issue when using named arguments after varargs.
|
||||
- Fix bug initializing nested struct/unions.
|
||||
- Fix of bool -> vector cast.
|
||||
- Correctly widen C style varargs for distinct types and optionals.
|
||||
|
||||
## 0.4.0 Change List
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ fn int! decode(String in, char* out, int* invalid_char_index = null)
|
||||
if (c == ERR)
|
||||
{
|
||||
if (invalid_char_index) *invalid_char_index = i;
|
||||
return DecodingError.INVALID_CHARACTER!;
|
||||
return DecodingError.INVALID_CHARACTER?;
|
||||
}
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ fn void! main()
|
||||
printf("Result: %s\n", &buffer);
|
||||
char *to_decode = "aGVsbG8gd29ybGRcMA==";
|
||||
char[*] result = b64"aGVsbG8gd29ybGRcMA==";
|
||||
decode((String)to_decode[0..19], &buffer)?;
|
||||
decode((String)to_decode[0..19], &buffer)!;
|
||||
printf("Result: %s\n", &buffer);
|
||||
printf("Result direct: %.*s\n", 13, &result);
|
||||
}
|
||||
@@ -12,7 +12,7 @@ fault InterpretError
|
||||
fn void! print_error(usz pos, String err)
|
||||
{
|
||||
io::printfn("Error at %s: %s", pos, err);
|
||||
return InterpretError.INTEPRET_FAILED!;
|
||||
return InterpretError.INTEPRET_FAILED?;
|
||||
}
|
||||
|
||||
fn void! brainf(String program)
|
||||
@@ -81,5 +81,5 @@ fn void! main()
|
||||
>>>+[[-]++++++>>>]<<<[[<++++++++<++>>-]+<.<[>----<-]<]
|
||||
<<[>>>>>[>>>[-]+++++++++<[>-<-]+++++++++>[-[<->-]+[<<<]]<[>+<-]>]<<-]<<-
|
||||
]`;
|
||||
brainf(program)?;
|
||||
brainf(program)!;
|
||||
}
|
||||
@@ -3,11 +3,11 @@ import libc;
|
||||
import std::io;
|
||||
|
||||
struct Doc { Head *head; }
|
||||
struct Head { VarString* title; }
|
||||
struct Head { DString* title; }
|
||||
|
||||
struct Summary
|
||||
{
|
||||
VarString* title;
|
||||
DString* title;
|
||||
bool ok;
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ fn bool contains(String haystack, String needle)
|
||||
|
||||
macro @dupe(value)
|
||||
{
|
||||
$typeof(&value) temp = malloc_checked($typeof(value))?;
|
||||
$typeof(&value) temp = malloc_checked($typeof(value))!;
|
||||
*temp = value;
|
||||
return temp;
|
||||
}
|
||||
@@ -56,11 +56,11 @@ fault ReadError
|
||||
|
||||
fn Doc! readDoc(String url)
|
||||
{
|
||||
if (contains(url, "fail")) return ReadError.BAD_READ!;
|
||||
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((VarString)null) }) };
|
||||
VarString str;
|
||||
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) }) };
|
||||
}
|
||||
@@ -96,9 +96,9 @@ fault TitleResult
|
||||
|
||||
fn bool! isTitleNonEmpty(Doc doc)
|
||||
{
|
||||
if (!doc.head) return TitleResult.TITLE_MISSING!;
|
||||
VarString* head = doc.head.title;
|
||||
if (!head) return TitleResult.TITLE_MISSING!;
|
||||
if (!doc.head) return TitleResult.TITLE_MISSING?;
|
||||
DString* head = doc.head.title;
|
||||
if (!head) return TitleResult.TITLE_MISSING?;
|
||||
return head.len() > 0;
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ fn void main()
|
||||
bool! has_title = readWhetherTitleNonEmpty(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::printf(" Has title: %s vs %s\n", bool_to_string(has_title) ?? (@catchof(has_title)).nameof, has_title ?? false);
|
||||
};
|
||||
dynamic_arena.reset();
|
||||
}
|
||||
|
||||
52
resources/examples/contextfree/cleanup.c3
Normal file
52
resources/examples/contextfree/cleanup.c3
Normal file
@@ -0,0 +1,52 @@
|
||||
import std::io;
|
||||
|
||||
|
||||
struct Resource
|
||||
{
|
||||
String name;
|
||||
}
|
||||
|
||||
fault Error
|
||||
{
|
||||
WELCOME_TO_YOUR_DOOM
|
||||
}
|
||||
|
||||
fn Resource! resource_init(String name)
|
||||
{
|
||||
io::printfn("open %s", name);
|
||||
return { name };
|
||||
}
|
||||
|
||||
fn void Resource.deinit(Resource this) => io::printfn("close %s", this.name);
|
||||
|
||||
macro void! @open_with(String name; @body(Resource resource))
|
||||
{
|
||||
Resource resource = resource_init(name)!;
|
||||
defer {
|
||||
io::printn("Using open_with to close");
|
||||
resource.deinit();
|
||||
}
|
||||
@body(resource);
|
||||
}
|
||||
|
||||
fn Resource! prep_out(String out_name, String[] prep_names)
|
||||
{
|
||||
Resource writer = resource_init(out_name)!; // Rethrow the optional result
|
||||
defer catch writer.deinit();
|
||||
foreach (name : prep_names)
|
||||
{
|
||||
@open_with(name; Resource reader)
|
||||
{
|
||||
io::printfn("use %s", reader.name);
|
||||
// if (true) return Error.WELCOME_TO_YOUR_DOOM?;
|
||||
}!;
|
||||
}
|
||||
return writer;
|
||||
}
|
||||
|
||||
fn void! main()
|
||||
{
|
||||
Resource writer = prep_out("out", String[] { "a", "b"})!;
|
||||
defer writer.deinit();
|
||||
io::printn("use out");
|
||||
}
|
||||
@@ -23,10 +23,10 @@ int err_count = 0;
|
||||
fn int! askGuess(int high)
|
||||
{
|
||||
libc::printf("Guess a number between 1 and %d: ", high);
|
||||
String text = readLine()?;
|
||||
String text = readLine()!;
|
||||
char* end = null;
|
||||
int value = (int)libc::strtol(text.ptr, &end, 10);
|
||||
if (end && end[0] >= ' ') return InputResult.NOT_AN_INT!;
|
||||
if (end && end[0] >= ' ') return InputResult.NOT_AN_INT?;
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ fn String! readLine()
|
||||
{
|
||||
char* chars = tmalloc(1024);
|
||||
isz loaded = getline(&chars, &&(usz)1023, libc::stdin());
|
||||
if (loaded < 0) return InputResult.FAILED_TO_READ!;
|
||||
if (loaded < 0) return InputResult.FAILED_TO_READ?;
|
||||
chars[loaded] = 0;
|
||||
return (String)chars[0..(loaded - 1)];
|
||||
}
|
||||
@@ -44,7 +44,7 @@ fn int! askGuessMulti(int high)
|
||||
while (true)
|
||||
{
|
||||
int! result = askGuess(high);
|
||||
if (catch? result == InputResult.NOT_AN_INT)
|
||||
if (@catchof(result) == InputResult.NOT_AN_INT)
|
||||
{
|
||||
libc::printf("I didn't understand that.\n");
|
||||
err_count++;
|
||||
@@ -59,7 +59,7 @@ fn void! Game.play(Game *game)
|
||||
{
|
||||
while (!game.done)
|
||||
{
|
||||
int guess = askGuessMulti(game.high)?;
|
||||
int guess = askGuessMulti(game.high)!;
|
||||
game.report(guess);
|
||||
game.update(guess);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ module load_world;
|
||||
import std::io;
|
||||
fn void! main()
|
||||
{
|
||||
File f = file::open("examples/hello_world.txt", "rb")?;
|
||||
File f = file::open("examples/hello_world.txt", "rb")!;
|
||||
defer f.close()!!;
|
||||
while (!f.eof())
|
||||
{
|
||||
|
||||
@@ -35,17 +35,17 @@ fn void Map.init(Map *map, uint capacity = 128)
|
||||
|
||||
fn Type! Map.valueForKey(Map *map, Key key)
|
||||
{
|
||||
if (!map.map) return MapResult.KEY_NOT_FOUND!;
|
||||
if (!map.map) return MapResult.KEY_NOT_FOUND?;
|
||||
uint hash = key.hash();
|
||||
usz pos = hash & map.mod;
|
||||
Entry* entry = &map.map[pos];
|
||||
if (!entry) return MapResult.KEY_NOT_FOUND!;
|
||||
if (!entry) return MapResult.KEY_NOT_FOUND?;
|
||||
while (entry)
|
||||
{
|
||||
if (entry.hash == hash && entry.key == key) return entry.value;
|
||||
entry = entry.next;
|
||||
}
|
||||
return MapResult.KEY_NOT_FOUND!;
|
||||
return MapResult.KEY_NOT_FOUND?;
|
||||
}
|
||||
|
||||
fn Type! Map.set(Map *map, Key key, Type value) @maydiscard
|
||||
@@ -67,7 +67,7 @@ fn Type! Map.set(Map *map, Key key, Type value) @maydiscard
|
||||
entry.value = value;
|
||||
entry.hash = hash;
|
||||
entry.key = key;
|
||||
return MapResult.KEY_NOT_FOUND!;
|
||||
return MapResult.KEY_NOT_FOUND?;
|
||||
}
|
||||
if (entry.hash == hash && entry.key == key)
|
||||
{
|
||||
@@ -87,7 +87,7 @@ fn Type! Map.set(Map *map, Key key, Type value) @maydiscard
|
||||
new.next = null;
|
||||
new.used = true;
|
||||
entry.next = new;
|
||||
return MapResult.KEY_NOT_FOUND!;
|
||||
return MapResult.KEY_NOT_FOUND?;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -144,7 +144,7 @@ fn void scale_bodies(Planet[] bodies, double scale)
|
||||
|
||||
fn void main(String[] args)
|
||||
{
|
||||
int n = args.len < 2 ? 50000000 : str::to_int(args[1])!!;
|
||||
int n = args.len < 2 ? 50000000 : args[1].to_int()!!;
|
||||
|
||||
Planet[] bodies = &planet_bodies;
|
||||
offset_momentum(bodies);
|
||||
|
||||
@@ -13,9 +13,9 @@ fault TokenResult
|
||||
fn void main(String[] args)
|
||||
{
|
||||
// Grab a string from stdin
|
||||
VarString s = io::stdin().getline();
|
||||
DString s = io::stdin().getline();
|
||||
// Delete it at scope end [defer]
|
||||
defer s.destroy();
|
||||
defer s.free();
|
||||
|
||||
// Grab the string as a slice.
|
||||
String numbers = s.str();
|
||||
@@ -77,8 +77,8 @@ fn String! read_next(String* remaining)
|
||||
}
|
||||
|
||||
// If it's a zero length token, return an optional result.
|
||||
if (!len) return TokenResult.NO_MORE_TOKENS!;
|
||||
if (!len) return TokenResult.NO_MORE_TOKENS?;
|
||||
|
||||
// Otherwise create a slice from the pointer start and length.
|
||||
return ptr_start[:len];
|
||||
return (String)ptr_start[:len];
|
||||
}
|
||||
|
||||
@@ -9,14 +9,14 @@ fault TestErr
|
||||
fn int! eventually_succeed()
|
||||
{
|
||||
static int i = 0;
|
||||
if (i++ < 3) return TestErr.NOPE!;
|
||||
if (i++ < 3) return TestErr.NOPE?;
|
||||
return i * 3;
|
||||
}
|
||||
|
||||
macro @retry(#function, int retries = 3)
|
||||
{
|
||||
var $Type = $typeof(#function);
|
||||
anyerr e;
|
||||
anyfault e;
|
||||
do
|
||||
{
|
||||
$Type! result = #function;
|
||||
|
||||
@@ -43,7 +43,7 @@ fn void eval_AtA_times_u(double[] u, double[] atau, double[] x)
|
||||
|
||||
fn void main(String[] args)
|
||||
{
|
||||
int n = args.len == 2 ? str::to_int(args[1])!! : 2000;
|
||||
int n = args.len == 2 ? args[1].to_int()!! : 2000;
|
||||
temparr = malloc(double, n);
|
||||
double[] u = malloc(double, n);
|
||||
double[] v = malloc(double, n);
|
||||
|
||||
17
resources/examples/time.c3
Normal file
17
resources/examples/time.c3
Normal file
@@ -0,0 +1,17 @@
|
||||
import std::io;
|
||||
import std::time;
|
||||
import std::math;
|
||||
|
||||
fn void main()
|
||||
{
|
||||
Clock start = clock::now();
|
||||
DateTime d = datetime::now();
|
||||
io::printfn("Today is: %d-%02d-%02d %02d:%02d", d.year, d.month.ordinal + 1, d.day, d.hour, d.min);
|
||||
io::printfn("Epoch timestamp: %d", d.time / 1_000);
|
||||
TzDateTime td = d.to_local();
|
||||
int absolute_offset = math::abs(td.gmt_offset);
|
||||
int offset_hour = absolute_offset / 3600;
|
||||
int offset_min = (absolute_offset / 60) % 60;
|
||||
io::printfn("Local time is: %d-%02d-%02d %02d:%02d:%02d %c%02d:%02d", td.year, td.month.ordinal + 1, td.day, td.hour, td.min, td.sec, td.gmt_offset < 0 ? '-' : '+', offset_hour, offset_min);
|
||||
io::printfn("Executed the above in %d ns", start.to_now());
|
||||
}
|
||||
7
resources/grammar/Makefile
Normal file
7
resources/grammar/Makefile
Normal file
@@ -0,0 +1,7 @@
|
||||
c3yacc: grammar.y c3.l
|
||||
bison -d grammar.y; cc -c grammar.tab.c
|
||||
flex c3.l; gcc -c lex.yy.c
|
||||
cc -o c3yacc lex.yy.o grammar.tab.o
|
||||
|
||||
clean:
|
||||
rm -f c3yacc grammar.output *.o grammar.tab.* lex.yy.c
|
||||
@@ -1,98 +1,196 @@
|
||||
|
||||
D [0-9]
|
||||
DU [0-9_]
|
||||
UN [_]
|
||||
L [a-zA-Z_]
|
||||
AN [a-zA-Z_0-9]
|
||||
H [a-fA-F0-9]
|
||||
HU [a-fA-F0-9_]
|
||||
UA [A-Z_0-9]
|
||||
O [0-7]
|
||||
B [0-1]
|
||||
DC [a-z]
|
||||
UC [A-Z]
|
||||
CONST [_]*{UC}{UA}*
|
||||
TYPE [_]*{UC}{UA}*{DC}{AN}*
|
||||
IDENTIFIER [_]*{DC}{AN}*
|
||||
E [Ee][+-]?{D}+
|
||||
P [Pp][+-]?{D}+
|
||||
B64 [ \t\v\n\f]?[A-Za-z0-9+/][ \t\v\n\fA-Za-z0-9+/=]+
|
||||
HEX [ \t\v\n\f]?[A-Fa-f0-9][ \t\v\n\fA-Fa-f0-9]+
|
||||
INTTYPE [ui](8|16|32|64|128)?
|
||||
REALTYPE [f](8|16|32|64|128)?
|
||||
INT {D}(_*{D})*
|
||||
HINT {H}(_*{H})*
|
||||
OINT {O}(_*{O})*
|
||||
BINT {B}(_*{B})*
|
||||
|
||||
%x COMMENT RAW_STRING
|
||||
|
||||
%{
|
||||
#include <stdio.h>
|
||||
#include "y.tab.h"
|
||||
#include "grammar.tab.h"
|
||||
void count(void);
|
||||
void comment(void);
|
||||
int comment_level = 0;
|
||||
|
||||
%}
|
||||
|
||||
%%
|
||||
"/*" { comment(); }
|
||||
|
||||
|
||||
"$alignof" { count(); return(CT_ALIGNOF); }
|
||||
"$assert" { count(); return(CT_ASSERT); }
|
||||
"$case" { count(); return(CT_CASE); }
|
||||
"$checks" { count(); return(CT_CHECKS); }
|
||||
"$default" { count(); return(CT_DEFAULT); }
|
||||
"$defined" { count(); return(CT_DEFINED); }
|
||||
"$echo" { count(); return(CT_ECHO); }
|
||||
"$else" { count(); return(CT_ELSE); }
|
||||
"$endfor" { count(); return(CT_ENDFOR); }
|
||||
"$endforeach" { count(); return(CT_ENDFOREACH); }
|
||||
"$endif" { count(); return(CT_ENDIF); }
|
||||
"$endswitch" { count(); return(CT_ENDSWITCH); }
|
||||
"$eval" { count(); return(CT_EVAL); }
|
||||
"$evaltype" { count(); return(CT_EVALTYPE); }
|
||||
"$extnameof" { count(); return(CT_EXTNAMEOF); }
|
||||
"$for" { count(); return(CT_FOR); }
|
||||
"$foreach" { count(); return(CT_FOREACH); }
|
||||
"$if" { count(); return(CT_IF); }
|
||||
"$nameof" { count(); return(CT_NAMEOF); }
|
||||
"$offsetof" { count(); return(CT_OFFSETOF); }
|
||||
"$qnameof" { count(); return(CT_QNAMEOF); }
|
||||
"$sizeof" { count(); return(CT_SIZEOF); }
|
||||
"$stringify" { count(); return(CT_STRINGIFY); }
|
||||
"$switch" { count(); return(CT_SWITCH); }
|
||||
"$typefrom" { count(); return(CT_TYPEFROM); }
|
||||
"$typeof" { count(); return(CT_TYPEOF); }
|
||||
"$vaarg" { count(); return(CT_VAARG); }
|
||||
"$vaconst" { count(); return(CT_VACONST); }
|
||||
"$vacount" { count(); return(CT_VACOUNT); }
|
||||
"$vaexpr" { count(); return(CT_VAEXPR); }
|
||||
"$varef" { count(); return(CT_VAREF); }
|
||||
"$vasplat" { count(); return(CT_VASPLAT); }
|
||||
"$vatype" { count(); return(CT_VATYPE); }
|
||||
"/*" { count(); BEGIN(COMMENT); }
|
||||
<COMMENT>{
|
||||
"/*" { count(); comment_level++; }
|
||||
"*"+"/" { count(); if (comment_level) { comment_level--; } else { BEGIN(INITIAL); } }
|
||||
"*"+ { count(); }
|
||||
[^/*\n]+ { count(); }
|
||||
[/] { count(); }
|
||||
\n { count(); }
|
||||
}
|
||||
\/\/.* { count(); }
|
||||
"any" { count(); return(ANY); }
|
||||
"anyfault" { count(); return(ANYFAULT); }
|
||||
"asm" { count(); return(ASM); }
|
||||
"assert" { count(); return(ASSERT); }
|
||||
"bitstruct" { count(); return(BITSTRUCT); }
|
||||
"bool" { count(); return(BOOL); }
|
||||
"break" { count(); return(BREAK); }
|
||||
"case" { count(); return(CASE); }
|
||||
"catch" { count(); return(CATCH); }
|
||||
"char" { count(); return(CHAR); }
|
||||
"const" { count(); return(CONST); }
|
||||
"continue" { count(); return(CONTINUE); }
|
||||
"default" { count(); return(DEFAULT); }
|
||||
"defer" { count(); return(DEFER); }
|
||||
"define" { count(); return(DEFINE); }
|
||||
"distinct" { count(); return(DISTINCT); }
|
||||
"do" { count(); return(DO); }
|
||||
"double" { count(); return(DOUBLE); }
|
||||
"else" { count(); return(ELSE); }
|
||||
"enum" { count(); return(ENUM); }
|
||||
"extern" { count(); return(EXTERN); }
|
||||
"false" { count(); return(FALSE); }
|
||||
"fault" { count(); return(FAULT); }
|
||||
"finalize" { count(); return(FINALIZE); }
|
||||
"float" { count(); return(FLOAT); }
|
||||
"bfloat16" { count(); return(BFLOAT16); }
|
||||
"float16" { count(); return(FLOAT16); }
|
||||
"float128" { count(); return(FLOAT128); }
|
||||
"fn" { count(); return(FN); }
|
||||
"for" { count(); return(FOR); }
|
||||
"foreach" { count(); return(FOREACH); }
|
||||
"foreach_r" { count(); return(FOREACH_R); }
|
||||
"fn" { count(); return(FUNC); }
|
||||
"ichar" { count(); return(ICHAR); }
|
||||
"if" { count(); return(IF); }
|
||||
"import" { count(); return(IMPORT); }
|
||||
"initialize" { count(); return(INITIALIZE); }
|
||||
"inline" { count(); return(INLINE); }
|
||||
"int" { count(); return(INT); }
|
||||
"uint" { count(); return(UINT); }
|
||||
"int128" { count(); return(INT128); }
|
||||
"iptr" { count(); return(IPTR); }
|
||||
"isz" { count(); return(ISZ); }
|
||||
"long" { count(); return(LONG); }
|
||||
"macro" { count(); return(MACRO); }
|
||||
"module" { count(); return(MODULE); }
|
||||
"nextcase" { count(); return(NEXTCASE); }
|
||||
"ulong" { count(); return(ULONG); }
|
||||
"null" { count(); return(NUL); }
|
||||
"return" { count(); return(RETURN); }
|
||||
"short" { count(); return(SHORT); }
|
||||
"ushort" { count(); return(USHORT); }
|
||||
"fault" { count(); return(ERROR); }
|
||||
"module" { count(); return(MODULE); }
|
||||
"struct" { count(); return(STRUCT); }
|
||||
"static" { count(); return(STATIC); }
|
||||
"switch" { count(); return(SWITCH); }
|
||||
"tlocal" { count(); return(TLOCAL); }
|
||||
"true" { count(); return(TRUE); }
|
||||
"try" { count(); return(TRY); }
|
||||
"typedef" { count(); return(TYPEDEF); }
|
||||
"typeid" { count(); return(TYPEID); }
|
||||
"uint" { count(); return(UINT); }
|
||||
"uint128" { count(); return(UINT128); }
|
||||
"ulong" { count(); return(ULONG); }
|
||||
"union" { count(); return(UNION); }
|
||||
"uptr" { count(); return(UPTR); }
|
||||
"ushort" { count(); return(USHORT); }
|
||||
"usz" { count(); return(USZ); }
|
||||
"var" { count(); return(VAR); }
|
||||
"void" { count(); return(VOID); }
|
||||
"while" { count(); return(WHILE); }
|
||||
"null" { count(); return(NUL); }
|
||||
"$for" { count(); return(CTFOR); }
|
||||
"$foreach" { count(); return(CTFOREACH); }
|
||||
"$case" { count(); return(CTCASE); }
|
||||
"$switch" { count(); return(CTSWITCH); }
|
||||
"$default" { count(); return(CTDEFAULT); }
|
||||
"$if" { count(); return(CTIF); }
|
||||
"$else" { count(); return(CTELSE); }
|
||||
"$endif" { count(); return(CTENDIF); }
|
||||
"$endswitch" { count(); return(CTENDIF); }
|
||||
"$endforeach" { count(); return(CTENDFOREACH); }
|
||||
"$endfor" { count(); return(CTENDFOR); }
|
||||
|
||||
@[_]*[A-Z]{UA}* { count(); return(AT_CONST_IDENT); }
|
||||
#[_]*[A-Z]{UA}* { count(); return(HASH_CONST_IDENT); }
|
||||
$[_]*[A-Z]{UA}* { count(); return(CT_CONST_IDENT); }
|
||||
[_]*[A-Z]{UA}* { count(); return(CONST_IDENT); }
|
||||
@[_]*[A-Z]{UA}*[a-z]{AN}* { count(); return(AT_TYPE_IDENT); }
|
||||
#[_]*[A-Z]{UA}*[a-z]{AN}* { count(); return(HASH_TYPE_IDENT); }
|
||||
$[_]*[A-Z]{UA}*[a-z]{AN}* { count(); return(CT_TYPE_IDENT); }
|
||||
[_]*[A-Z]{UA}*[a-z]{AN}* { count(); return(TYPE_IDENT); }
|
||||
@[_]*[a-z]{AN}* { count(); return(AT_IDENT); }
|
||||
#[_]*[a-z]{AN}* { count(); return(HASH_IDENT); }
|
||||
$[_]*[a-z]{AN}* { count(); return(CT_IDENT); }
|
||||
[_]*[a-z]{AN}* { count(); return(IDENT); }
|
||||
0[xX]{H}+ { count(); return(CONSTANT); }
|
||||
0{D}+? { count(); return(CONSTANT); }
|
||||
{D}+ { count(); return(CONSTANT); }
|
||||
L?'(\\.|[^\\'])+' { count(); return(CONSTANT); }
|
||||
@{CONST} { count(); return(AT_CONST_IDENT); }
|
||||
#{CONST} { count(); return(HASH_CONST_IDENT); }
|
||||
${CONST} { count(); return(CT_CONST_IDENT); }
|
||||
{CONST} { count(); return(CONST_IDENT); }
|
||||
@{TYPE} { count(); return(AT_TYPE_IDENT); }
|
||||
#{TYPE} { count(); return(HASH_TYPE_IDENT); }
|
||||
${TYPE} { count(); return(CT_TYPE_IDENT); }
|
||||
{TYPE} { count(); return(TYPE_IDENT); }
|
||||
@{IDENTIFIER} { count(); return(AT_IDENT); }
|
||||
#{IDENTIFIER} { count(); return(HASH_IDENT); }
|
||||
${IDENTIFIER} { count(); return(CT_IDENT); }
|
||||
{IDENTIFIER} { count(); return(IDENT); }
|
||||
0[xX]{HINT}{INTTYPE}? { count(); return(INTEGER); }
|
||||
0[oO]{OINT}{INTTYPE}? { count(); return(INTEGER); }
|
||||
0[bB]{BINT}{INTTYPE}? { count(); return(INTEGER); }
|
||||
{INT}{INTTYPE}? { count(); return(INTEGER); }
|
||||
x\'{HEX}+\' { count(); return(BYTES); }
|
||||
x\"{HEX}+\" { count(); return(BYTES); }
|
||||
x\`{HEX}+\` { count(); return(BYTES); }
|
||||
b64\'{B64}+\' { count(); return(BYTES); }
|
||||
b64\"{B64}+\" { count(); return(BYTES); }
|
||||
b64\`{B64}+\` { count(); return(BYTES); }
|
||||
|
||||
{D}+{E} { count(); return(CONSTANT); }
|
||||
{D}*"."{D}+({E})? { count(); return(CONSTANT); }
|
||||
{D}+"."{D}*({E})? { count(); return(CONSTANT); }
|
||||
{INT}{E}{REALTYPE}? { count(); return(REAL); }
|
||||
0[xX]{HINT}{P}{REALTYPE}? { count(); return(REAL); }
|
||||
{INT}"."{INT}{E}?{REALTYPE}? { count(); return(REAL); }
|
||||
0[xX]{HINT}"."{HINT}{P}{REALTYPE}? { count(); return(REAL); }
|
||||
|
||||
L?\"(\\.|[^\\"])*\" { count(); return(STRING_LITERAL); }
|
||||
\"(\\.|[^\\"])*\" { count(); return(STRING_LITERAL); }
|
||||
\'(\\.|[^\\'])*\' { count(); return(CHAR_LITERAL); }
|
||||
|
||||
"`" { count(); BEGIN(RAW_STRING); }
|
||||
<RAW_STRING>{
|
||||
"``" { count(); }
|
||||
"`" { count(); BEGIN(INITIAL); return(STRING_LITERAL); }
|
||||
"*"+ { count(); }
|
||||
<<EOF>> { BEGIN(INITIAL); return(RAW_STRING); }
|
||||
}
|
||||
|
||||
"..." { count(); return(ELLIPSIS); }
|
||||
".." { count(); return(DOTDOT); }
|
||||
">>=" { count(); return(RIGHT_ASSIGN); }
|
||||
"<<=" { count(); return(LEFT_ASSIGN); }
|
||||
">>=" { count(); return(SHR_ASSIGN); }
|
||||
"<<=" { count(); return(SHL_ASSIGN); }
|
||||
"+=" { count(); return(ADD_ASSIGN); }
|
||||
"-=" { count(); return(SUB_ASSIGN); }
|
||||
"*=" { count(); return(MUL_ASSIGN); }
|
||||
@@ -101,8 +199,8 @@ L?\"(\\.|[^\\"])*\" { count(); return(STRING_LITERAL); }
|
||||
"&=" { count(); return(AND_ASSIGN); }
|
||||
"^=" { count(); return(XOR_ASSIGN); }
|
||||
"|=" { count(); return(OR_ASSIGN); }
|
||||
">>" { count(); return(RIGHT_OP); }
|
||||
"<<" { count(); return(LEFT_OP); }
|
||||
">>" { count(); return(SHR_OP); }
|
||||
"<<" { count(); return(SHL_OP); }
|
||||
"++" { count(); return(INC_OP); }
|
||||
"--" { count(); return(DEC_OP); }
|
||||
"&&" { count(); return(AND_OP); }
|
||||
@@ -111,8 +209,13 @@ L?\"(\\.|[^\\"])*\" { count(); return(STRING_LITERAL); }
|
||||
">=" { count(); return(GE_OP); }
|
||||
"==" { count(); return(EQ_OP); }
|
||||
"!=" { count(); return(NE_OP); }
|
||||
"??" { count(); return(OPTELSE); }
|
||||
"::" { count(); return(SCOPE); }
|
||||
"?:" { count(); return(ELVIS); }
|
||||
"=>" { count(); return(IMPLIES); }
|
||||
"[<" { count(); return(LVEC); }
|
||||
">]" { count(); return(RVEC); }
|
||||
"$$" { count(); return(BUILTIN); }
|
||||
";" { count(); return(';'); }
|
||||
("{") { count(); return('{'); }
|
||||
("}") { count(); return('}'); }
|
||||
@@ -120,7 +223,6 @@ L?\"(\\.|[^\\"])*\" { count(); return(STRING_LITERAL); }
|
||||
":" { count(); return(':'); }
|
||||
"=" { count(); return('='); }
|
||||
"(" { count(); return('('); }
|
||||
"@" { count(); return(AT); }
|
||||
")" { count(); return(')'); }
|
||||
("[") { count(); return('['); }
|
||||
("]") { count(); return(']'); }
|
||||
@@ -138,8 +240,8 @@ L?\"(\\.|[^\\"])*\" { count(); return(STRING_LITERAL); }
|
||||
"^" { count(); return('^'); }
|
||||
"|" { count(); return('|'); }
|
||||
"?" { count(); return('?'); }
|
||||
"{|" { count(); return(FN_BLOCK_BEGIN); }
|
||||
"|}" { count(); return(FN_BLOCK_END); }
|
||||
"{|" { count(); return(LBRAPIPE); }
|
||||
"|}" { count(); return(RBRAPIPE); }
|
||||
[ \t\v\n\f] { count(); }
|
||||
. { /* ignore bad characters */ }
|
||||
|
||||
@@ -150,26 +252,6 @@ int yywrap(void)
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
void comment(void)
|
||||
{
|
||||
char c, c1;
|
||||
|
||||
loop:
|
||||
while ((c = input()) != '*' && c != 0)
|
||||
putchar(c);
|
||||
|
||||
if ((c1 = input()) != '/' && c != 0)
|
||||
{
|
||||
unput(c1);
|
||||
goto loop;
|
||||
}
|
||||
|
||||
if (c != 0)
|
||||
putchar(c1);
|
||||
}
|
||||
|
||||
|
||||
int column = 0;
|
||||
|
||||
void count(void)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,19 +1,20 @@
|
||||
module bigint;
|
||||
import libc;
|
||||
|
||||
macro max(a, b)
|
||||
macro @max(a, b)
|
||||
{
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
// Horribly bad implementation of BigInt with add/sub.
|
||||
public struct BigInt
|
||||
struct BigInt
|
||||
{
|
||||
byte* number;
|
||||
char* number;
|
||||
uint length;
|
||||
char sign;
|
||||
ichar sign;
|
||||
}
|
||||
|
||||
public fn void BigInt.init(BigInt* bigInt)
|
||||
fn void BigInt.init(BigInt* bigInt)
|
||||
{
|
||||
bigInt.number = malloc(1);
|
||||
bigInt.number[0] = 0;
|
||||
@@ -21,9 +22,9 @@ public fn void BigInt.init(BigInt* bigInt)
|
||||
bigInt.sign = 1;
|
||||
}
|
||||
|
||||
public fn void BigInt.initFromString(BigInt* bigInt, char* str)
|
||||
fn void BigInt.initFromString(BigInt* bigInt, char* str)
|
||||
{
|
||||
uint size = strlen(str);
|
||||
uint size = (uint)libc::strlen(str);
|
||||
bigInt.sign = 1;
|
||||
switch (str[0])
|
||||
{
|
||||
@@ -46,7 +47,7 @@ public fn void BigInt.initFromString(BigInt* bigInt, char* str)
|
||||
bigInt.length = size;
|
||||
}
|
||||
|
||||
public fn void BigInt.copyTo(BigInt* source, BigInt* target)
|
||||
fn void BigInt.copyTo(BigInt* source, BigInt* target)
|
||||
{
|
||||
target.number = realloc(target.number, source.length);
|
||||
target.sign = source.sign;
|
||||
@@ -54,7 +55,7 @@ public fn void BigInt.copyTo(BigInt* source, BigInt* target)
|
||||
for (uint i = 0; i < target.length; i++) target.number[i] = source.number[i];
|
||||
}
|
||||
|
||||
public fn void BigInt.destroy(BigInt* bigInt)
|
||||
fn void BigInt.destroy(BigInt* bigInt)
|
||||
{
|
||||
free(bigInt.number);
|
||||
}
|
||||
@@ -62,8 +63,8 @@ public fn void BigInt.destroy(BigInt* bigInt)
|
||||
fn void BigInt.addIgnoreSign(BigInt* a, BigInt* b, BigInt* result)
|
||||
{
|
||||
uint length = @max(a.length, b.length) + 1;
|
||||
byte* res = malloc(length);
|
||||
char carry = 0;
|
||||
char* res = malloc(length);
|
||||
ichar carry = 0;
|
||||
BigInt* x;
|
||||
BigInt* y;
|
||||
if (a.length > b.length)
|
||||
@@ -98,7 +99,7 @@ fn void BigInt.addIgnoreSign(BigInt* a, BigInt* b, BigInt* result)
|
||||
result.length = length;
|
||||
}
|
||||
|
||||
public fn void BigInt.getMaxVal(BigInt* bigInt, uint* pos, int* val)
|
||||
fn void BigInt.getMaxVal(BigInt* bigInt, uint* pos, int* val)
|
||||
{
|
||||
for (uint i = bigInt.length; i > 0; i++)
|
||||
{
|
||||
@@ -113,14 +114,14 @@ public fn void BigInt.getMaxVal(BigInt* bigInt, uint* pos, int* val)
|
||||
*val = 0;
|
||||
}
|
||||
|
||||
public fn char BigInt.compare(BigInt* a, BigInt* b)
|
||||
fn ichar BigInt.compare(BigInt* a, BigInt* b)
|
||||
{
|
||||
if (a.sign != b.sign) return a.sign;
|
||||
byte aMax;
|
||||
int aMax;
|
||||
uint aMaxPos;
|
||||
a.getMaxVal(&aMaxPos, &aMax);
|
||||
if (aMaxPos >= b.length) return a.sign;
|
||||
byte bMax;
|
||||
int bMax;
|
||||
uint bMaxPos;
|
||||
b.getMaxVal(&bMaxPos, &bMax);
|
||||
if (aMaxPos > bMaxPos) return a.sign;
|
||||
@@ -130,13 +131,13 @@ public fn char BigInt.compare(BigInt* a, BigInt* b)
|
||||
return 0;
|
||||
}
|
||||
|
||||
public fn char BigInt.compareNoSign(BigInt* a, BigInt* b)
|
||||
fn ichar BigInt.compareNoSign(BigInt* a, BigInt* b)
|
||||
{
|
||||
byte aMax;
|
||||
int aMax;
|
||||
uint aMaxPos;
|
||||
a.getMaxVal(&aMaxPos, &aMax);
|
||||
if (aMaxPos >= b.length) return 1;
|
||||
byte bMax;
|
||||
int bMax;
|
||||
uint bMaxPos;
|
||||
b.getMaxVal(&bMaxPos, &bMax);
|
||||
if (aMaxPos > bMaxPos) return 1;
|
||||
@@ -149,7 +150,7 @@ public fn char BigInt.compareNoSign(BigInt* a, BigInt* b)
|
||||
fn void BigInt.subIgnoreSign(BigInt* a, BigInt* b, BigInt* result)
|
||||
{
|
||||
uint length = @max(a.length, b.length);
|
||||
byte* res = malloc(length);
|
||||
char* res = malloc(length);
|
||||
|
||||
BigInt* x;
|
||||
BigInt* y;
|
||||
@@ -166,11 +167,11 @@ fn void BigInt.subIgnoreSign(BigInt* a, BigInt* b, BigInt* result)
|
||||
y = b;
|
||||
}
|
||||
|
||||
byte borrow = 0;
|
||||
char borrow = 0;
|
||||
for (uint i = 0; i < length; i++)
|
||||
{
|
||||
byte aValue = i >= x.length ? 0 : x.number[i];
|
||||
byte bValue = borrow + (i >= y.length ? 0 : y.number[i]);
|
||||
char aValue = i >= x.length ? 0 : x.number[i];
|
||||
char bValue = borrow + (i >= y.length ? 0 : y.number[i]);
|
||||
if (bValue > aValue)
|
||||
{
|
||||
borrow = 1;
|
||||
@@ -187,7 +188,7 @@ fn void BigInt.subIgnoreSign(BigInt* a, BigInt* b, BigInt* result)
|
||||
result.length = length;
|
||||
}
|
||||
|
||||
public fn void BigInt.add(BigInt* a, BigInt* b, BigInt* result)
|
||||
fn void BigInt.add(BigInt* a, BigInt* b, BigInt* result)
|
||||
{
|
||||
if (a.sign == b.sign)
|
||||
{
|
||||
@@ -205,12 +206,12 @@ public fn void BigInt.add(BigInt* a, BigInt* b, BigInt* result)
|
||||
}
|
||||
}
|
||||
|
||||
public fn char* BigInt.toCharArray(BigInt* bigInt)
|
||||
fn char* BigInt.toCharArray(BigInt* bigInt)
|
||||
{
|
||||
uint charLen = bigInt.length + 1 + (bigInt.sign < 0 ? 1 : 0);
|
||||
byte* out = malloc(charLen);
|
||||
out[charLen - 1] = '\0';
|
||||
byte* start = out;
|
||||
uint icharLen = bigInt.length + 1 + (bigInt.sign < 0 ? 1 : 0);
|
||||
char* out = malloc(icharLen);
|
||||
out[icharLen - 1] = '\0';
|
||||
char* start = out;
|
||||
if (bigInt.sign < 0)
|
||||
{
|
||||
out[0] = '-';
|
||||
@@ -219,7 +220,7 @@ public fn char* BigInt.toCharArray(BigInt* bigInt)
|
||||
bool nonZeroFound = false;
|
||||
for (uint i = bigInt.length; i > 0; i--)
|
||||
{
|
||||
byte digit = bigInt.number[i - 1];
|
||||
char digit = bigInt.number[i - 1];
|
||||
if (i > 1 && !nonZeroFound && digit == 0) continue;
|
||||
nonZeroFound = true;
|
||||
*(start++) = digit + '0';
|
||||
@@ -227,24 +228,24 @@ public fn char* BigInt.toCharArray(BigInt* bigInt)
|
||||
return out;
|
||||
}
|
||||
|
||||
public fn void BigInt.print(BigInt* bigInt)
|
||||
fn void BigInt.print(BigInt* bigInt)
|
||||
{
|
||||
char* chars = bigInt.toCharArray();
|
||||
puts(chars);
|
||||
libc::puts(chars);
|
||||
free(chars);
|
||||
}
|
||||
|
||||
/*
|
||||
public fn void BigInt.fprint(BigInt* bigInt, FILE* file)
|
||||
fn void BigInt.fprint(BigInt* bigInt, FILE* file)
|
||||
{
|
||||
char* chars = bigInt.toCharArray();
|
||||
fputs(chars, file);
|
||||
ichar* ichars = bigInt.toicharArray();
|
||||
fputs(ichars, file);
|
||||
putc('\n', file);
|
||||
free(chars);
|
||||
free(ichars);
|
||||
}*/
|
||||
|
||||
|
||||
public fn int main(int size, char** args)
|
||||
fn int main(int size, char** args)
|
||||
{
|
||||
BigInt minus2;
|
||||
BigInt minus1;
|
||||
@@ -257,7 +258,7 @@ public fn int main(int size, char** args)
|
||||
for (int i = 1; i <= 500; i++)
|
||||
{
|
||||
minus1.add(&minus2, ¤t);
|
||||
printf("%d : ", i);
|
||||
libc::printf("%d : ", i);
|
||||
current.print();
|
||||
minus1.copyTo(&minus2);
|
||||
current.copyTo(&minus1);
|
||||
|
||||
@@ -12,9 +12,9 @@ fn void readpgm(char* name, Pixmap* p)
|
||||
pnm_readpaminit(fp, &inpam);
|
||||
p.x = inpam.width;
|
||||
p.y = inpam.height;
|
||||
if (!(p.p = malloc(@safe(p.x + p.y)))
|
||||
if (!(p.p = malloc(@safe(p.x + p.y))))
|
||||
{
|
||||
@F1("Error at malloc");
|
||||
@f1("Error at malloc");
|
||||
}
|
||||
for (int i = 0; i < inpam.height; i++)
|
||||
{
|
||||
@@ -24,7 +24,7 @@ fn void readpgm(char* name, Pixmap* p)
|
||||
{
|
||||
p.p[@nowrap(i * inpam.width + j)] = sample;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,8 +32,8 @@ extern fn void printf(char *s);
|
||||
|
||||
fn void test_signedunsigned()
|
||||
{
|
||||
char a = 0 - 1;
|
||||
byte b = (byte)(a);
|
||||
ichar a = 0 - 1;
|
||||
char b = (char)(a);
|
||||
|
||||
printf("Signed-unsigned -1 0xFF \n");
|
||||
if (a > b) printf("a > b\n");
|
||||
|
||||
@@ -1,272 +0,0 @@
|
||||
module bigint;
|
||||
|
||||
macro @max(a, b)
|
||||
{
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
// Horribly bad implementation of BigInt with add/sub.
|
||||
public struct BigInt
|
||||
{
|
||||
byte& number;
|
||||
uint length;
|
||||
char sign;
|
||||
}
|
||||
|
||||
public fn void BigInt.init(BigInt& bigInt)
|
||||
{
|
||||
bigInt.number = malloc(1);
|
||||
bigInt.number[0] = 0;
|
||||
bigInt.length = 1;
|
||||
bigInt.sign = 1;
|
||||
}
|
||||
|
||||
public fn void BigInt.initFromString(BigInt& bigInt, char& str)
|
||||
{
|
||||
uint size = strlen(str);
|
||||
bigInt.sign = 1;
|
||||
switch (str[0])
|
||||
{
|
||||
case '-':
|
||||
bigInt.sign = -1;
|
||||
size--;
|
||||
str++;
|
||||
case '+':
|
||||
size--;
|
||||
str++;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
char* res = malloc(size);
|
||||
for (uint i = 0; i < size; i++)
|
||||
{
|
||||
res[i] = str[size - i - 1] - '0';
|
||||
}
|
||||
bigInt.number = res;
|
||||
bigInt.length = size;
|
||||
}
|
||||
|
||||
public fn BigInt& BigInt.newFromString(char& str)
|
||||
{
|
||||
BigInt& bigInt = malloc(sizeof(BigInt));
|
||||
bigInt.initFromString(str);
|
||||
return bigInt;
|
||||
}
|
||||
|
||||
public fn void BigInt.copyTo(BigInt& source, BigInt& target)
|
||||
{
|
||||
target.number = realloc(target.number, source.length);
|
||||
target.sign = source.sign;
|
||||
target.length = source.length;
|
||||
for (uint i = 0; i < target.length; i++) target.number[i] = source.number[i];
|
||||
}
|
||||
|
||||
public fn void BigInt.destroy(BigInt& bigInt)
|
||||
{
|
||||
free(bigInt.number);
|
||||
}
|
||||
|
||||
fn void BigInt.addIgnoreSign(BigInt& a, BigInt& b, BigInt& result)
|
||||
{
|
||||
uint length = @max(a.length, b.length) + 1;
|
||||
byte* res = malloc(length);
|
||||
char carry = 0;
|
||||
BigInt* x;
|
||||
BigInt* y;
|
||||
if (a.length > b.length)
|
||||
{
|
||||
x = a;
|
||||
y = b;
|
||||
}
|
||||
else
|
||||
{
|
||||
x = b;
|
||||
y = a;
|
||||
}
|
||||
for (uint i = 0; i < length; i++)
|
||||
{
|
||||
if (i >= y.length)
|
||||
{
|
||||
res[i] = carry + (i >= x.length ? 0 : x.number[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
res[i] = x.number[i] + y.number[i] + carry;
|
||||
}
|
||||
carry = 0;
|
||||
if (res[i] > 9)
|
||||
{
|
||||
carry = 1;
|
||||
res[i] -= 10;
|
||||
}
|
||||
}
|
||||
result.destroy();
|
||||
result.number = res;
|
||||
result.length = length;
|
||||
}
|
||||
|
||||
public fn void BigInt.getMaxVal(BigInt &bigInt, uint& pos, int& val)
|
||||
{
|
||||
for (uint i = bigInt.length; i > 0; i++)
|
||||
{
|
||||
if (bigInt.number[i] != 0)
|
||||
{
|
||||
*pos = i;
|
||||
*val = bigInt.number[i];
|
||||
return;
|
||||
}
|
||||
}
|
||||
*pos = 0;
|
||||
*val = 0;
|
||||
}
|
||||
|
||||
public fn char BigInt.compare(BigInt& a, BigInt& b)
|
||||
{
|
||||
if (a.sign != b.sign) return a.sign;
|
||||
byte aMax;
|
||||
uint aMaxPos;
|
||||
a.getMaxVal(&aMaxPos, &aMax);
|
||||
if (aMaxPos >= b.length) return a.sign;
|
||||
byte bMax;
|
||||
uint bMaxPos;
|
||||
b.getMaxVal(&bMaxPos, &bMax);
|
||||
if (aMaxPos > bMaxPos) return a.sign;
|
||||
if (aMaxPos < bMaxPos) return -a.sign;
|
||||
if (aMax > bMax) return a.sign;
|
||||
if (aMax < bMax) return -a.sign;
|
||||
return 0;
|
||||
}
|
||||
|
||||
public fn char BigInt.compareNoSign(BigInt& a, BigInt& b)
|
||||
{
|
||||
byte aMax;
|
||||
uint aMaxPos;
|
||||
a.getMaxVal(&aMaxPos, &aMax);
|
||||
if (aMaxPos >= b.length) return 1;
|
||||
byte bMax;
|
||||
uint bMaxPos;
|
||||
b.getMaxVal(&bMaxPos, &bMax);
|
||||
if (aMaxPos > bMaxPos) return 1;
|
||||
if (aMaxPos < bMaxPos) return -1;
|
||||
if (aMax > bMax) return 1;
|
||||
if (aMax < bMax) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
fn void BigInt.subIgnoreSign(BigInt& a, BigInt& b, BigInt& result)
|
||||
{
|
||||
uint length = @max(a.length, b.length);
|
||||
byte& res = malloc(length);
|
||||
|
||||
BigInt& x;
|
||||
BigInt& y;
|
||||
if (a.compareNoSign(b) < 0)
|
||||
{
|
||||
result.sign = -1;
|
||||
x = b;
|
||||
y = a;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.sign = 1;
|
||||
x = a;
|
||||
y = b;
|
||||
}
|
||||
|
||||
byte borrow = 0;
|
||||
for (uint i = 0; i < length; i++)
|
||||
{
|
||||
byte aValue = i >= x.length ? 0 : x.number[i];
|
||||
byte bValue = borrow + (i >= y.length ? 0 : y.number[i]);
|
||||
if (bValue > aValue)
|
||||
{
|
||||
borrow = 1;
|
||||
aValue += 10;
|
||||
}
|
||||
else
|
||||
{
|
||||
borrow = 0;
|
||||
}
|
||||
res[i] = aValue - bValue;
|
||||
}
|
||||
result.destroy();
|
||||
result.number = res;
|
||||
result.length = length;
|
||||
}
|
||||
|
||||
public fn void BigInt.add(BigInt& a, BigInt& b, BigInt& result)
|
||||
{
|
||||
if (a.sign == b.sign)
|
||||
{
|
||||
a.addIgnoreSign(b, result);
|
||||
result.sign = a.sign;
|
||||
return;
|
||||
}
|
||||
if (a.sign < 0)
|
||||
{
|
||||
b.subIgnoreSign(a, result);
|
||||
}
|
||||
else
|
||||
{
|
||||
a.subIgnoreSign(a, result);
|
||||
}
|
||||
}
|
||||
|
||||
public fn char* BigInt.toCharArray(BigInt* bigInt)
|
||||
{
|
||||
uint charLen = bigInt.length + 1 + (bigInt.sign < 0 ? 1 : 0);
|
||||
byte* out = malloc(charLen);
|
||||
out[charLen - 1] = '\0';
|
||||
byte* start = out;
|
||||
if (bigInt.sign < 0)
|
||||
{
|
||||
out[0] = '-';
|
||||
start++;
|
||||
}
|
||||
bool nonZeroFound = false;
|
||||
for (uint i = bigInt.length; i > 0; i--)
|
||||
{
|
||||
byte digit = bigInt.number[i - 1];
|
||||
if (i > 1 && !nonZeroFound && digit == 0) continue;
|
||||
nonZeroFound = true;
|
||||
*(start++) = digit + '0';
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
public fn void BigInt.print(BigInt& bigInt)
|
||||
{
|
||||
char* chars = bigInt.toCharArray();
|
||||
puts(chars);
|
||||
free(chars);
|
||||
}
|
||||
|
||||
/*
|
||||
public fn void BigInt.fprint(BigInt* bigInt, FILE* file)
|
||||
{
|
||||
char* chars = bigInt.toCharArray();
|
||||
fputs(chars, file);
|
||||
putc('\n', file);
|
||||
free(chars);
|
||||
}*/
|
||||
|
||||
|
||||
public fn int main(int size, char*& args)
|
||||
{
|
||||
BigInt minus2;
|
||||
BigInt minus1;
|
||||
BigInt current;
|
||||
minus2.initFromString("0");
|
||||
minus1.initFromString("1");
|
||||
current.initFromString("0");
|
||||
|
||||
for (int i = 1; i <= 500; i++)
|
||||
{
|
||||
minus1.add(&minus2, ¤t);
|
||||
printf("%d : ", i);
|
||||
current.print();
|
||||
minus1.copyTo(&minus2);
|
||||
current.copyTo(&minus1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -1,190 +0,0 @@
|
||||
module foo;
|
||||
|
||||
int oefk = 1;
|
||||
int bob = 'HELO';
|
||||
|
||||
typedef Foo* as Bob;
|
||||
|
||||
fn void testdefer(int x)
|
||||
{
|
||||
defer printf("A");
|
||||
defer
|
||||
{
|
||||
int x = 2;
|
||||
printf("B");
|
||||
x = x + 1;
|
||||
}
|
||||
//defer catch printf("B")
|
||||
//defer catch (error e) printf("%s", @name(e));
|
||||
//if (x = 1) return throw Error.FOO;
|
||||
printf("!");
|
||||
}
|
||||
|
||||
struct Foo
|
||||
{
|
||||
int i;
|
||||
Bar *x;
|
||||
}
|
||||
|
||||
struct Bar
|
||||
{
|
||||
Foo foo;
|
||||
Foo* fooPtr;
|
||||
Bob helo;
|
||||
struct bar
|
||||
{
|
||||
int a;
|
||||
}
|
||||
}
|
||||
|
||||
union Xyz
|
||||
{
|
||||
int x;
|
||||
int y;
|
||||
}
|
||||
|
||||
|
||||
$if (1)
|
||||
{
|
||||
struct Ogg
|
||||
{
|
||||
Zed *bob;
|
||||
}
|
||||
struct DOgg
|
||||
{
|
||||
Zed bob;
|
||||
}
|
||||
struct Zed
|
||||
{
|
||||
Foo b;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
struct Simple
|
||||
{
|
||||
int i;
|
||||
double j;
|
||||
}
|
||||
fn int boo()
|
||||
{
|
||||
Zed zfe;
|
||||
Simple s = { 1 };
|
||||
s = { 2 };
|
||||
//Simple x = { j = 1 };
|
||||
//Zed zfed = { 1 };
|
||||
int eok = 0;
|
||||
{
|
||||
eok = 2;
|
||||
}
|
||||
eok = eok * 2 + 1 * 2;
|
||||
eok = eok / 2 + 32 / 2;
|
||||
eok = eok % 2 - 32 % 45;
|
||||
|
||||
int de = eok;
|
||||
de++;
|
||||
de = de << 1;
|
||||
de = de >> 1;
|
||||
de = de ^ (byte)(1);
|
||||
de = de & 1;
|
||||
de = de | 1;
|
||||
if (de > 100 || de < 10 || de <= 1000 || de >= 1921 || de == 100 || de != 2)
|
||||
{
|
||||
return boo();
|
||||
}
|
||||
switch (eok)
|
||||
{
|
||||
case 1
|
||||
eok++;
|
||||
next;
|
||||
case 3:
|
||||
eok++;
|
||||
next;
|
||||
eok--;
|
||||
case 2:
|
||||
eok--;
|
||||
default:
|
||||
eok--;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (eok > 0)
|
||||
{
|
||||
eok++;
|
||||
}
|
||||
else
|
||||
{
|
||||
eok--;
|
||||
}
|
||||
int xfeef = -eok;
|
||||
/*
|
||||
bool xoek = !eok;
|
||||
int oekfefo = ~eok;
|
||||
int *booe = &eok;
|
||||
int **de = &booe;
|
||||
int &xfok = &eok;
|
||||
int &&bo = &xfok;
|
||||
int e12 = **bo;
|
||||
int fe = *(&eok);
|
||||
int x2 = *xfok;*/
|
||||
int x1 = 2;
|
||||
FOO:
|
||||
x1 = x1 + 1;
|
||||
goto FOO;
|
||||
bool z = 123 > 3.0;
|
||||
{
|
||||
int x = 0;
|
||||
}
|
||||
{
|
||||
int x = 1;
|
||||
}
|
||||
int j = 10;
|
||||
do
|
||||
{
|
||||
j = j + 10;
|
||||
} while (j > 2);
|
||||
return 1;
|
||||
}
|
||||
|
||||
fn int helo(int i)
|
||||
{
|
||||
return i + 1;
|
||||
}
|
||||
fn void while_test()
|
||||
{
|
||||
|
||||
int a = 10;
|
||||
|
||||
while (int b = 37; a < 0)
|
||||
{
|
||||
a++;
|
||||
int xy = 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn void test()
|
||||
{
|
||||
int a = 10;
|
||||
while (1)
|
||||
{
|
||||
int xy = 1;
|
||||
}
|
||||
int eokfe = boo();
|
||||
int i = -1;
|
||||
bool dwf = !2.0;
|
||||
ushort b = ~(byte)(0);
|
||||
int j as k;
|
||||
int l as m = 0;
|
||||
int o = 0, p = 3;
|
||||
short f = (char)((int)(-2));
|
||||
//int doek = ~2;
|
||||
return;
|
||||
}
|
||||
|
||||
fn void main()
|
||||
{
|
||||
helo(2);
|
||||
printf("Helo\n");
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user