More refactorings in the stdlib. More Path functions. Updated Win32 format for types. Fix bug with codegen of defer if ... More string functions.

This commit is contained in:
Christoffer Lerno
2023-03-09 18:24:07 +01:00
committed by Christoffer Lerno
parent 39dd3e40a6
commit d2a16961cf
21 changed files with 1366 additions and 952 deletions

View File

@@ -133,6 +133,19 @@ macro bool os_is_win32()
return OS_TYPE == OsType.WIN32;
}
macro bool os_is_darwin()
{
$switch (OS_TYPE):
$case IOS:
$case MACOSX:
$case TVOS:
$case WATCHOS:
return true;
$default:
return false;
$endswitch;
}
macro bool os_is_posix()
{
$switch (OS_TYPE):

View File

@@ -1,232 +1,3 @@
module std::core::string;
typedef ZString = distinct inline char*;
typedef Char32 = uint;
typedef Char16 = ushort;
const uint SURROGATE_OFFSET @private = 0x10000;
const uint SURROGATE_GENERIC_MASK @private = 0xF800;
const uint SURROGATE_MASK @private = 0xFC00;
const uint SURROGATE_CODEPOINT_MASK @private = 0x03FF;
const uint SURROGATE_BITS @private = 10;
const uint SURROGATE_LOW_VALUE @private = 0xDC00;
const uint SURROGATE_HIGH_VALUE @private = 0xD800;
fault NumberConversion
{
EMPTY_STRING,
NEGATIVE_VALUE,
MALFORMED_INTEGER,
INTEGER_OVERFLOW,
MALFORMED_FLOAT,
FLOAT_OUT_OF_RANGE,
}
macro String printf(String fmt, ..., Allocator* using = mem::heap())
{
if (using == mem::temp())
{
DString str;
str.tinit();
str.printf(fmt, $vasplat());
return str.str();
}
@pool()
{
DString str;
str.tinit();
str.printf(fmt, $vasplat());
return str.copy_str(using);
};
}
macro String tprintf(String fmt, ...)
{
DString str;
str.tinit();
str.printf(fmt, $vasplat());
return str.str();
}
macro bool char_in_set(char c, String set)
{
foreach (ch : set) if (ch == c) return true;
return false;
}
/**
* @param [in] string
* @param [in] to_trim
**/
fn String String.trim(String string, String to_trim = "\t\n\r ")
{
usz start = 0;
usz len = string.len;
while (start < len && char_in_set(string[start], to_trim)) start++;
if (start == len) return string[:0];
usz end = len - 1;
while (end > start && char_in_set(string[end], to_trim)) end--;
return string[start..end];
}
/**
* @param [in] string
* @param [in] needle
**/
fn bool String.starts_with(String string, String needle)
{
if (needle.len > string.len) return false;
if (!needle.len) return true;
return string[:needle.len] == needle;
}
/**
* Split a string into parts, e.g "a|b|c" split with "|" yields { "a", "b", "c" }
*
* @param [in] s
* @param [in] needle
* @param [&inout] using "The allocator, defaults to the heap allocator"
* @param max "Max number of elements, 0 means no limit, defaults to 0"
* @require needle.len > 0 "The needle must be at least 1 character long"
* @ensure return.len > 0
**/
fn String[] String.split(String s, String needle, usz max = 0, Allocator* using = mem::heap())
{
usz capacity = 16;
usz i = 0;
String* holder = malloc(String, capacity, .using = using);
bool no_more = false;
while (!no_more)
{
usz! index = i == max - 1 ? SearchResult.MISSING! : s.index_of(needle);
String res @noinit;
if (try index)
{
res = s[:index];
s = s[index + needle.len..];
}
else
{
res = s;
no_more = true;
}
if (i == capacity)
{
capacity *= 2;
holder = realloc(holder, String.sizeof * capacity, .using = using);
}
holder[i++] = res;
}
return holder[:i];
}
/**
* This function is identical to String.split, but implicitly uses the
* temporary allocator.
*
* @param [in] s
* @param [in] needle
* @param max "Max number of elements, 0 means no limit, defaults to 0"
**/
fn String[] tsplit(String s, String needle, usz max = 0)
{
return s.split(needle, max, mem::temp()) @inline;
}
/**
* Find the index of the first incidence of a string.
*
* @param [in] s
* @param [in] needle
* @pure
* @ensure return < s.len
* @require needle.len > 0 "The needle must be len 1 or more"
**/
fn usz! String.index_of(String s, String needle)
{
usz match = 0;
usz needed = needle.len;
usz index_start = 0;
char search = needle[0];
foreach (usz i, char c : s)
{
if (c == search)
{
if (!match) index_start = i;
match++;
if (match == needed) return index_start;
search = needle[match];
continue;
}
if (match)
{
match = 0;
search = needle[0];
}
}
return SearchResult.MISSING!;
}
/**
* Find the index of the last incidence of a string.
*
* @param [in] s
* @param [in] needle
* @pure
* @ensure return < s.len
* @require needle.len > 0 "The needle must be len 1 or more"
**/
fn usz! String.rindex_of(String s, String needle)
{
usz match = 0;
usz needed = needle.len;
usz index_start = 0;
char search = needle[^1];
foreach_r (usz i, char c : s)
{
if (c == search)
{
if (!match) index_start = i;
match++;
if (match == needed) return index_start - needle.len + 1;
search = needle[^(match + 1)];
continue;
}
if (match)
{
match = 0;
search = needle[^1];
}
}
return SearchResult.MISSING!;
}
fn ZString String.zstr_copy(String s, Allocator* using = mem::heap())
{
usz len = s.len;
char* str = malloc(len + 1, .using = using);
mem::copy(str, s.ptr, len);
str[len] = 0;
return (ZString)str;
}
fn ZString String.zstr_tcopy(String s) => s.zstr_copy(mem::temp()) @inline;
fn String String.copy(String s, Allocator* using = mem::heap())
{
usz len = s.len;
char* str = malloc(len + 1, .using = using);
mem::copy(str, s.ptr, len);
str[len] = 0;
return (String)str[:len];
}
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;
module std::core::str;
fn VarString join(String[] s, String joiner)

View File

@@ -1,326 +1,299 @@
module std::core::string;
import libc;
typedef VarString = distinct void*;
typedef DynStr = VarString;
typedef DynString = VarString;
typedef VString = VarString;
typedef Text = VarString;
typedef ZString = distinct inline char*;
typedef Char32 = uint;
typedef Char16 = ushort;
const usz MIN_CAPACITY = 16;
const uint SURROGATE_OFFSET @private = 0x10000;
const uint SURROGATE_GENERIC_MASK @private = 0xF800;
const uint SURROGATE_MASK @private = 0xFC00;
const uint SURROGATE_CODEPOINT_MASK @private = 0x03FF;
const uint SURROGATE_BITS @private = 10;
const uint SURROGATE_LOW_VALUE @private = 0xDC00;
const uint SURROGATE_HIGH_VALUE @private = 0xD800;
fn VarString new_with_capacity(usz capacity, Allocator* allocator = mem::heap())
fault NumberConversion
{
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;
EMPTY_STRING,
NEGATIVE_VALUE,
MALFORMED_INTEGER,
INTEGER_OVERFLOW,
MALFORMED_FLOAT,
FLOAT_OUT_OF_RANGE,
}
fn VarString new(String c)
macro String printf(String fmt, ..., Allocator* using = mem::heap())
{
usz len = c.len;
VarString str = new_with_capacity(len);
StringData* data = str.data();
if (len)
if (using == mem::temp())
{
data.len = len;
mem::copy(&data.chars, c.ptr, len);
DString str;
str.tinit();
str.printf(fmt, $vasplat());
return str.str();
}
return (VarString)data;
}
fn ZString VarString.zstr(VarString str)
{
StringData* data = str.data();
if (!data) return (ZString)"";
if (data.capacity == data.len)
@pool()
{
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];
DString str;
str.tinit();
str.printf(fmt, $vasplat());
return str.copy_str(using);
};
}
fn usz VarString.capacity(VarString this)
macro String tprintf(String fmt, ...)
{
if (!this) return 0;
return this.data().capacity;
DString str;
str.tinit();
str.printf(fmt, $vasplat());
return str.str();
}
fn usz VarString.len(VarString this)
macro bool char_in_set(char c, String set)
{
if (!this) return 0;
return this.data().len;
foreach (ch : set) if (ch == c) return true;
return false;
}
/**
* @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()
* @param [in] string
* @param [in] to_trim
**/
fn void VarString.set(VarString str, usz index, char c)
fn String String.trim(String string, String to_trim = "\t\n\r ")
{
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;
}
usz start = 0;
usz len = string.len;
while (start < len && char_in_set(string[start], to_trim)) start++;
if (start == len) return string[:0];
usz end = len - 1;
while (end > start && char_in_set(string[end], to_trim)) end--;
return string[start..end];
}
/**
* @require c <= 0x10ffff
*/
fn void VarString.append_char32(VarString* str, Char32 c)
* @param [in] string
* @param [in] needle
**/
fn bool String.starts_with(String string, String needle)
{
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));
if (needle.len > string.len) return false;
if (!needle.len) return true;
return string[:needle.len] == needle;
}
fn VarString VarString.tcopy(VarString* str) => str.copy(mem::temp());
fn VarString VarString.copy(VarString* str, Allocator* allocator = null)
/**
* @param [in] string
* @param [in] needle
**/
fn bool String.ends_with(String string, String needle)
{
if (!str)
if (needle.len > string.len) return false;
if (!needle.len) return true;
return string[^needle.len..] == needle;
}
/**
* Strip the front of the string if the prefix exists.
*
* @param [in] string
* @param [in] needle
**/
fn String String.strip(String string, String needle)
{
if (!needle.len || !string.starts_with(needle)) return string;
return string[needle.len..];
}
/**
* Strip the end of the string if the suffix exists.
*
* @param [in] string
* @param [in] needle
**/
fn String String.strip_end(String string, String needle)
{
if (!needle.len || !string.ends_with(needle)) return string;
// Note that this is the safe way if we want to support zero length.
return string[:(string.len - needle.len)];
}
/**
* Split a string into parts, e.g "a|b|c" split with "|" yields { "a", "b", "c" }
*
* @param [in] s
* @param [in] needle
* @param [&inout] using "The allocator, defaults to the heap allocator"
* @param max "Max number of elements, 0 means no limit, defaults to 0"
* @require needle.len > 0 "The needle must be at least 1 character long"
* @ensure return.len > 0
**/
fn String[] String.split(String s, String needle, usz max = 0, Allocator* using = mem::heap())
{
usz capacity = 16;
usz i = 0;
String* holder = malloc(String, capacity, .using = using);
bool no_more = false;
while (!no_more)
{
if (allocator) return new_with_capacity(0, allocator);
return (VarString)null;
usz! index = i == max - 1 ? SearchResult.MISSING! : s.index_of(needle);
String res @noinit;
if (try index)
{
res = s[:index];
s = s[index + needle.len..];
}
else
{
res = s;
no_more = true;
}
if (i == capacity)
{
capacity *= 2;
holder = realloc(holder, String.sizeof * capacity, .using = using);
}
holder[i++] = res;
}
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;
return holder[:i];
}
fn ZString VarString.copy_zstr(VarString* str, Allocator* allocator = mem::heap())
/**
* This function is identical to String.split, but implicitly uses the
* temporary allocator.
*
* @param [in] s
* @param [in] needle
* @param max "Max number of elements, 0 means no limit, defaults to 0"
**/
fn String[] tsplit(String s, String needle, usz max = 0)
{
usz str_len = str.len();
if (!str_len)
return s.split(needle, max, mem::temp()) @inline;
}
/**
* Find the index of the first incidence of a string.
*
* @param [in] s
* @param [in] needle
* @pure
* @ensure return < s.len
* @require needle.len > 0 "The needle must be len 1 or more"
**/
fn usz! String.index_of(String s, String needle)
{
usz match = 0;
usz needed = needle.len;
usz index_start = 0;
char search = needle[0];
foreach (usz i, char c : s)
{
return (ZString)calloc(1, .using = allocator);
if (c == search)
{
if (!match) index_start = i;
match++;
if (match == needed) return index_start;
search = needle[match];
continue;
}
if (match)
{
match = 0;
search = needle[0];
}
}
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;
return SearchResult.MISSING!;
}
fn String VarString.copy_str(VarString* str, Allocator* allocator = mem::heap())
/**
* Find the index of the last incidence of a string.
*
* @param [in] s
* @param [in] needle
* @pure
* @ensure return < s.len
* @require needle.len > 0 "The needle must be len 1 or more"
**/
fn usz! String.rindex_of(String s, String needle)
{
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++)
usz match = 0;
usz needed = needle.len;
usz index_start = 0;
char search = needle[^1];
foreach_r (usz i, char c : s)
{
if (str1.chars[i] != str2.chars[i]) return false;
if (c == search)
{
if (!match) index_start = i;
match++;
if (match == needed) return index_start - needle.len + 1;
search = needle[^(match + 1)];
continue;
}
if (match)
{
match = 0;
search = needle[^1];
}
}
return true;
return SearchResult.MISSING!;
}
fn void VarString.destroy(VarString* str)
fn ZString String.zstr_copy(String s, Allocator* using = mem::heap())
{
if (!*str) return;
StringData* data = str.data();
if (!data) return;
free(data, .using = data.allocator);
*str = (VarString)null;
usz len = s.len;
char* str = malloc(len + 1, .using = using);
mem::copy(str, s.ptr, len);
str[len] = 0;
return (ZString)str;
}
fn bool VarString.less(VarString str, VarString other_string)
fn String String.concat(String s1, String s2, Allocator* using = mem::heap())
{
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;
usz full_len = s1.len + s2.len;
char* str = malloc(full_len + 1, .using = using);
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 void VarString.append_chars(VarString* this, String str)
fn String String.tconcat(String s1, String s2) => s1.concat(s2, mem::temp());
fn ZString String.zstr_tcopy(String s) => s.zstr_copy(mem::temp()) @inline;
fn String String.copy(String s, Allocator* using = mem::heap())
{
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;
usz len = s.len;
char* str = malloc(len + 1, .using = using);
mem::copy(str, s.ptr, len);
str[len] = 0;
return (String)str[:len];
}
fn Char32[] VarString.copy_utf32(VarString* this, Allocator* allocator = mem::heap())
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;
fn Char16[]! String.to_utf16(String s, Allocator* using = mem::heap())
{
return str::utf8to32(this.str(), allocator) @inline!!;
usz len16 = conv::utf16len_for_utf8(s);
Char16* data = malloc_checked(Char16, len16 + 1, .using = using)?;
conv::utf8to16_unsafe(s, data)?;
data[len16] = 0;
return data[:len16];
}
fn void VarString.append_string(VarString* this, VarString str)
fn String! from_utf16(Char16[] utf16, Allocator* using = mem::heap())
{
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;
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];
}
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:
$if (@convertible(value, Char32)):
str.append_char32(value);
$elif (@convertible(value, String)):
str.append_chars(value);
$else:
$assert(false, "Unsupported type for append use printf instead.");
$endif;
$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;
}
fn Char16[]! String.to_temp_utf16(String s) => s.to_utf16(mem::temp());

326
lib/std/core/varstring.c3 Normal file
View File

@@ -0,0 +1,326 @@
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:
$if (@convertible(value, Char32)):
str.append_char32(value);
$elif (@convertible(value, String)):
str.append_chars(value);
$else:
$assert(false, "Unsupported type for append use printf instead.");
$endif;
$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;
}

View File

@@ -1,182 +1,3 @@
module std::io::dir;
import std::io::os;
// In progress.
typedef Path = distinct String;
const PREFERRED_SEPARATOR = env::os_is_win32() ? '\\' : '/';
fault PathResult
{
INVALID_PATH
}
fn String! getcwd(Allocator* using = mem::heap())
{
return os::getcwd(using);
}
fn String! tgetcwd()
{
return getcwd(mem::temp()) @inline;
}
macro bool is_separator(char c)
{
$if (env::os_is_win32()):
return c == '/' || c == '\\';
$else:
return c == '/';
$endif;
}
const bool[256] RESERVED_PATH_CHAR_POSIX = {
[0] = true,
['/'] = true,
};
const bool[256] RESERVED_PATH_CHAR_WIN32 = {
[0..31] = true,
['>'] = true,
['<'] = true,
[':'] = true,
['\"'] = true,
['/'] = true,
['\\'] = true,
['|'] = true,
['?'] = true,
['*'] = true,
};
macro bool is_reserved_path_char(char c)
{
$if (env::os_is_win32()):
return RESERVED_PATH_CHAR_WIN32[c];
$else:
return RESERVED_PATH_CHAR_POSIX[c];
$endif;
}
fn usz! root_name_len(String path) @private
{
usz len = path.len;
if (!len) return 0;
$if (env::os_is_win32()):
switch (path[0])
{
case '\\':
if (len < 2 || path[1] != '\\') break;
if (len == 2 || is_separator(path[2])) return PathResult.INVALID_PATH!;
for (usz i = 2; i < len; i++)
{
char c = path[i];
if (is_separator(c)) return i;
if (is_reserved_path_char(c)) return PathResult.INVALID_PATH!;
}
return len;
case 'A'..'Z':
case 'a'..'z':
if (len < 2 || path[1] != ':') break;
if (len < 3 || !is_separator(path[2])) return PathResult.INVALID_PATH!;
return 2;
}
$endif;
return 0;
}
fn void! normalize_path(String* path_ref) @private
{
String path = *path_ref;
if (!path.len) return;
usz path_start = root_name_len(path)?;
usz len = path_start;
bool previous_was_separator = false;
usz path_len = path.len;
for (usz i = path_start; i < path_len; i++)
{
char c = path[i];
// Fold foo///bar into foo/bar
if (is_separator(c))
{
if (previous_was_separator)
{
continue;
}
path.ptr[len++] = PREFERRED_SEPARATOR;
previous_was_separator = true;
continue;
}
// If we get . we have different things that might happen:
if (c == '.' && i < path_len - 1)
{
// Is this ./ or /./ ?
if ((previous_was_separator || i == path_start) && is_separator(path[i + 1]))
{
// Then we skip this
i += 2;
continue;
}
// Is this /../ in that case we must walk back and erase(!)
if (i < path_len - 2 && previous_was_separator && path[i + 1] == '.' && is_separator(path[i + 2]))
{
assert(len > path_start);
len--;
while (len > path_start && !is_separator(path[len - 1]))
{
len--;
}
i += 2;
continue;
}
}
if (i != len) path[len] = c;
previous_was_separator = false;
len++;
}
path.ptr[len] = 0;
*path_ref = path[:len];
}
fn Path new_path(String path, Allocator* using = using)
{
String copy = path.copy(using);
normalize_path(&copy)!!;
return (Path)copy;
}
fn Path Path.root_name(Path path)
{
String path_str = (String)path;
usz len = root_name_len(path_str)!!;
if (!len) return (Path)"";
return (Path)path_str[0:len];
}
fn Path Path.root_directory(Path path)
{
String path_str = (String)path;
usz len = path_str.len;
if (!len) return (Path)"";
$if (env::os_is_win32()):
usz root_len = root_name_len(path_str)!!;
if (root_len == len || !is_separator(path_str[root_len])) return (Path)"";
return (Path)path_str[root_len..root_len];
$else:
if (!is_separator(path_str[0])) return (Path)"";
for (usz i = 1; i < len; i++)
{
if (is_separator(path_str[i]))
{
return (Path)path_str[:i];
}
}
return path;
$endif;
}
fn void Path.destroy(Path path)
{
free(path.ptr);
}

View File

@@ -2,8 +2,6 @@ module std::io;
import libc;
fn void! File.open(File* file, String filename, String mode)
{
file.file = os::native_fopen(filename, mode)?;

View File

@@ -1,90 +1,12 @@
module std::io::file;
import libc;
struct FileInfo
fn bool is_file(String path)
{
ulong size;
return os::native_is_file(path);
}
fn bool exits(String path)
fn usz! get_size(String path)
{
File f;
if (catch(f.open(path, "r"))) return false;
(void)f.close();
return true;
}
$switch (env::OS_TYPE):
$case MACOSX:
$case IOS:
$case WATCHOS:
$case TVOS:
struct DarwinTimespec @private
{
long tv_sec;
long tv_nsec;
}
struct Darwin64Stat @private
{
int st_dev;
ushort st_mode;
ushort st_nlink;
ulong st_ino;
uint st_uid;
uint st_gid;
int st_rdev;
DarwinTimespec st_atimespec; // time of last access
DarwinTimespec st_mtimespec; // time of last data modification
DarwinTimespec st_ctimespec; // time of last status change
DarwinTimespec st_birthtimespec; // time of file creation(birth)
long st_size;
long st_blocks;
int st_blocksize;
uint st_flags;
uint st_gen;
int st_lspare;
long[2] st_qspare;
}
extern fn int _stat(ZString str, Darwin64Stat* stat) @extern("stat64");
fn void! FileInfo.read(FileInfo* info, Path path)
{
@pool()
{
Darwin64Stat stat;
int res = _stat(((String)path).zstr_tcopy(), &stat);
if (res != 0)
{
switch (libc::errno())
{
case errno::EBADF:
return IoError.FILE_NOT_VALID!;
case errno::EFAULT:
unreachable("Invalid stat");
case errno::EIO:
return IoError.GENERAL_ERROR!;
case errno::EACCES:
return IoError.NO_PERMISSION!;
case errno::ELOOP:
return IoError.NO_PERMISSION!;
case errno::ENAMETOOLONG:
return IoError.NAME_TOO_LONG!;
case errno::ENOENT:
return IoError.FILE_NOT_FOUND!;
case errno::ENOTDIR:
return IoError.FILE_NOT_DIR!;
case errno::EOVERFLOW:
return IoError.GENERAL_ERROR!;
default:
return IoError.UNKNOWN_ERROR!;
}
}
info.size = stat.st_size;
};
}
$default:
macro void! FileInfo.read(FileInfo* info, Path path)
{
$assert("Unsupported function");
}
$endswitch;
return os::native_file_size(path);
}

266
lib/std/io/io_path.c3 Normal file
View File

@@ -0,0 +1,266 @@
module std::io::path;
const PREFERRED_SEPARATOR = env::os_is_win32() ? '\\' : '/';
fault PathResult
{
INVALID_PATH
}
typedef Path = distinct String;
fn Path! getcwd(Allocator* using = mem::heap())
{
return (Path)os::getcwd(using);
}
fn Path! tgetcwd()
{
return getcwd(mem::temp()) @inline;
}
fn bool is_dir(String path)
{
return os::native_is_dir(path);
}
macro bool is_separator(char c)
{
$if (env::os_is_win32()):
return c == '/' || c == '\\';
$else:
return c == '/';
$endif;
}
fn Path! new(String path, Allocator* using = mem::heap())
{
Path copy = (Path)path.copy(using);
copy.normalize()?;
return (Path)copy;
}
/**
* Append the string to the current path.
*
* @param [in] path
* @param [in] filename
* @ensure return.len >= filename.len + path.len
**/
fn Path! Path.append(Path path, String filename, Allocator* using = mem::heap())
{
if (!path.len) return new(filename, using)?;
if (is_separator(path[^1]))
{
return (Path)path.as_str().concat(filename, using);
}
// Handle temp nested temp allocations.
TempAllocator* temp = mem::temp();
usz mark = temp.used;
defer if (using != temp) temp.reset(mark);
DString dstr = dstring::new_with_capacity(path.len + 1 + filename.len, .using = temp);
dstr.append(path.as_str());
dstr.append(PREFERRED_SEPARATOR);
dstr.append(filename);
Path p = using == temp ? (Path)dstr.str() : (Path)dstr.copy_str(using);
p.normalize()?;
return p;
}
fn Path! temp_directory(Allocator* using = mem::heap())
{
return os::native_temp_directory(using);
}
fn Path Path.root_name(Path path)
{
usz len = root_name_len(path.as_str())!!;
if (!len) return (Path)"";
return path[:len];
}
fn usz! root_name_len(String path) @local
{
usz len = path.len;
if (!len) return 0;
$if (env::os_is_win32()):
switch (path[0])
{
case '\\':
if (len < 2 || path[1] != '\\') break;
if (len == 2 || is_separator(path[2])) return PathResult.INVALID_PATH!;
for (usz i = 2; i < len; i++)
{
char c = path[i];
if (is_separator(c)) return i;
if (is_reserved_path_char(c)) return PathResult.INVALID_PATH!;
}
return len;
case 'A'..'Z':
case 'a'..'z':
if (len < 2 || path[1] != ':') break;
if (len < 3 || !is_separator(path[2])) return PathResult.INVALID_PATH!;
return 2;
}
$endif;
return 0;
}
fn Path! Path.parent(Path path)
{
foreach_r(i, c : path)
{
if (is_separator(c) && i != path.len - 1)
{
return (Path)path[:i];
}
}
return PathResult.INVALID_PATH!;
}
fn void! Path.normalize(Path* path)
{
String path_str = path.as_str();
if (!path_str.len) return;
usz path_start = root_name_len(path_str)?;
usz len = path_start;
bool previous_was_separator = false;
usz path_len = path.len;
for (usz i = path_start; i < path_len; i++)
{
char c = path_str[i];
// Fold foo///bar into foo/bar
if (is_separator(c))
{
if (previous_was_separator)
{
continue;
}
path_str.ptr[len++] = PREFERRED_SEPARATOR;
previous_was_separator = true;
continue;
}
// If we get . we have different things that might happen:
if (c == '.' && i < path_len - 1)
{
// Is this ./ or /./ ?
if ((previous_was_separator || i == path_start) && is_separator(path_str[i + 1]))
{
// Then we skip this
i += 2;
continue;
}
// Is this /../ in that case we must walk back and erase(!)
if (i < path_len - 2 && previous_was_separator && path_str[i + 1] == '.' && is_separator(path_str[i + 2]))
{
assert(len > path_start);
len--;
while (len > path_start && !is_separator(path_str[len - 1]))
{
len--;
}
i += 2;
continue;
}
}
if (i != len) path_str[len] = c;
previous_was_separator = false;
len++;
}
path_str.ptr[len] = 0;
*path = (Path)path_str[:len];
}
fn Path Path.root_directory(Path path)
{
String path_str = path.as_str();
usz len = path_str.len;
if (!len) return (Path)"";
$if (env::os_is_win32()):
usz root_len = root_name_len(path_str)!!;
if (root_len == len || !is_separator(path_str[root_len])) return (Path)"";
return (Path)path_str[root_len..root_len];
$else:
if (!is_separator(path_str[0])) return (Path)"";
for (usz i = 1; i < len; i++)
{
if (is_separator(path_str[i]))
{
return (Path)path_str[:i];
}
}
return path;
$endif;
}
fn usz! Path.file_size(Path path)
{
return os::native_file_size(path.as_str());
}
fn String Path.as_str(Path path)
{
return (String)path;
}
fn bool Path.file_or_dir_exists(Path path)
{
return os::native_file_or_dir_exists(path.as_str());
}
fn bool Path.is_dir(Path path)
{
return os::native_is_dir(path.as_str());
}
fn bool Path.has_suffix(Path path, String str)
{
return path.as_str().ends_with(str);
}
fn bool Path.is_file(Path path)
{
return os::native_is_file(path.as_str());
}
fn void Path.free(Path path)
{
free(path.ptr);
}
const bool[256] RESERVED_PATH_CHAR_POSIX = {
[0] = true,
['/'] = true,
};
const bool[256] RESERVED_PATH_CHAR_WIN32 = {
[0..31] = true,
['>'] = true,
['<'] = true,
[':'] = true,
['\"'] = true,
['/'] = true,
['\\'] = true,
['|'] = true,
['?'] = true,
['*'] = true,
};
macro bool is_reserved_path_char(char c)
{
$if (env::os_is_win32()):
return RESERVED_PATH_CHAR_WIN32[c];
$else:
return RESERVED_PATH_CHAR_POSIX[c];
$endif;
}

View File

@@ -185,4 +185,8 @@ extern fn void* _wfopen(Char16*, Char16*) @local;
extern fn void* _wfreopen(Char16*, Char16*, CFile) @local;
extern fn int _fseeki64(CFile, long, int) @local;
extern fn long _ftelli64(CFile) @local;
$endif;
$endif;
$if (env::os_is_posix()):
extern fn CInt access(ZString path, CInt mode);
$endif;

View File

@@ -0,0 +1,101 @@
module std::io::file::os;
import libc;
$if (env::os_is_darwin()):
struct DarwinTimespec @private
{
long tv_sec;
long tv_nsec;
}
struct Darwin64Stat @private
{
int st_dev;
ushort st_mode;
ushort st_nlink;
ulong st_ino;
uint st_uid;
uint st_gid;
int st_rdev;
DarwinTimespec st_atimespec; // time of last access
DarwinTimespec st_mtimespec; // time of last data modification
DarwinTimespec st_ctimespec; // time of last status change
DarwinTimespec st_birthtimespec; // time of file creation(birth)
long st_size;
long st_blocks;
int st_blocksize;
uint st_flags;
uint st_gen;
int st_lspare;
long[2] st_qspare;
}
extern fn int _stat(ZString str, Darwin64Stat* stat) @extern("stat64");
const S_IFMT = 0o170000; // type of file mask
const S_IFIFO = 0o010000; // named pipe (fifo)
const S_IFCHR = 0o020000; // character special
const S_IFDIR = 0o040000; // directory
const S_IFBLK = 0o060000; // block special
const S_IFREG = 0o100000; // regular
const S_IFLNK = 0o120000; // symbolic link
const S_IFSOCK = 0o140000; // socket
fn usz! native_file_size(String path)
{
Darwin64Stat stat;
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));
}
fn bool native_is_file(String path)
{
Darwin64Stat stat;
return try(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);
}
fn void! read_stat(Darwin64Stat* stat, String path) @local
{
@pool()
{
int res = _stat(path.zstr_tcopy(), stat);
if (res != 0)
{
switch (libc::errno())
{
case errno::EBADF:
return IoError.FILE_NOT_VALID!;
case errno::EFAULT:
unreachable("Invalid stat");
case errno::EIO:
return IoError.GENERAL_ERROR!;
case errno::EACCES:
return IoError.NO_PERMISSION!;
case errno::ELOOP:
return IoError.NO_PERMISSION!;
case errno::ENAMETOOLONG:
return IoError.NAME_TOO_LONG!;
case errno::ENOENT:
return IoError.FILE_NOT_FOUND!;
case errno::ENOTDIR:
return IoError.FILE_NOT_DIR!;
case errno::EOVERFLOW:
return IoError.GENERAL_ERROR!;
default:
return IoError.UNKNOWN_ERROR!;
}
}
};
}
$endif;

View File

@@ -0,0 +1,70 @@
module std::io::file::os;
// native_temp_directory, for non Win32
$if (!env::os_is_win32()):
fn Path! native_temp_directory(Allocator* using = mem::heap())
{
foreach (String env : { "TMPDIR", "TMP", "TEMP", "TEMPDIR" })
{
String tmpdir = env::get_var(env) ?? "";
if (tmpdir) return path::new(tmpdir, using);
}
return path::new("/tmp", using);
}
$endif;
$if (!env::os_is_darwin() && !env::os_is_win32()):
fn usz! native_file_size(String path)
{
File f;
f.open(path, "r")?;
defer (void)f.close();
return f.seek(0, Seek.END)?;
}
$if (env::os_is_posix() && env::COMPILER_LIBC_AVAILABLE):
fn bool native_file_or_dir_exists(String path)
{
@pool()
{
return os::access(path.zstr_tcopy(), 0 /* F_OK */) != -1;
};
}
fn bool native_is_file(String path)
{
File f;
if (catch(f.open(path, "r"))) return false;
(void)f.close();
return true;
}
fn bool native_is_dir(String path)
{
return native_file_or_dir_exists(path) && !native_is_file(path);
}
$else:
fn bool native_file_or_dir_exists(String path)
{
unreachable("Tried to call file_or_dir_exists without support.");
}
fn bool native_is_dir(String path)
{
unreachable("Tried to call is_dir without support.");
}
fn bool native_is_file(String path)
{
unreachable("Tried to call is_file without support.");
}
$endif;
$endif;

View File

@@ -0,0 +1,71 @@
module std::io::file::os;
import std::os::win32::files;
$if (env::os_is_win32()):
fn usz! native_file_size(String path)
{
@pool()
{
Char16[] path16 = path.to_temp_utf16()?;
Win32_FILE_ATTRIBUTE_DATA data;
files::win32_GetFileAttributesExW(path16, Win32_GET_FILEEX_INFO_LEVELS.STANDARD, &data);
Win32_LARGE_INTEGER size;
size.lowPart = data.nFileSizeLow;
size.highPart = data.nFileSizeHigh;
return (usz)size.quadPart;
};
}
fn bool native_file_or_dir_exists(String path)
{
@pool()
{
return files::win32_PathFileExistsW(path.to_temp_utf16()) ?? false;
};
}
fn bool native_is_file(String path)
{
File f;
if (catch(f.open(path, "r"))) return false;
(void)f.close();
return true;
}
fn bool native_is_dir(String path)
{
return native_file_or_dir_exists(path) && !native_is_file(path);
}
fn Path! native_temp_directory(Allocator* using = mem::heap())
{
Char16[256] default_buffer;
Char16* buff = &default_buffer;
// Free if we allocated on the heap.
defer if (buff != &default_buffer) free(buff);
Win32_DWORD len = files::win32_GetTempPathW(default_buffer.len, buff);
if (!len) return IoError.GENERAL_ERROR!;
// Too long, allocate
if (len > default_buffer.len)
{
// Allocate on the heap
buff = tmalloc(Char16, len);
if (!files::win32_GetTempPathW(len, buff)) return IoError.GENERAL_ERROR!;
}
return (Path)string::from_utf16(buff[:len], using);
}
/*
}else if(method == file_size_methods::get_attributes){
WIN32_FILE_ATTRIBUTE_DATA file_attr_data;
if(GetFileAttributesEx(path, GetFileExInfoStandard, &file_attr_data)){
file_size.LowPart = file_attr_data.nFileSizeLow;
file_size.HighPart = file_attr_data.nFileSizeHigh;
}
}
*/
$endif;

View File

@@ -1,6 +1,33 @@
module std::os::win32::files;
$if (env::OS_TYPE == OsType.WIN32):
$if (env::os_is_win32()):
enum Win32_GET_FILEEX_INFO_LEVELS
{
STANDARD,
MAX,
}
struct Win32_FILETIME
{
Win32_DWORD dwLowDateTime;
Win32_DWORD dwHighDateTime;
}
struct Win32_FILE_ATTRIBUTE_DATA
{
Win32_DWORD dwFileAttributes;
Win32_FILETIME ftCreationTime;
Win32_FILETIME ftLastAccessTime;
Win32_FILETIME ftLastWriteTime;
Win32_DWORD nFileSizeHigh;
Win32_DWORD nFileSizeLow;
}
extern fn bool win32_GetFileAttributesExW(Win32_LPCWSTR, Win32_GET_FILEEX_INFO_LEVELS, Win32_LPVOID) @extern("GetFileAttributesExW");
extern fn bool win32_PathFileExistsW(Win32_LPCWSTR) @extern("PathFileExistsW");
extern fn Win32_DWORD win32_GetTempPathW(Win32_DWORD nBufferLength, Win32_LPWSTR lpBuffer) @extern("GetTempPathW");
/*
extern ulong _win32_GetCurrentDirectoryW(ulong, Char16* buffer) @extern("GetCurrentDirectoryW");
extern bool _win32_CreateSymbolicLinkW(Char16* symlink_file, Char16* target_file, ulong flags) @extern("CreateSymbolicLinkW");

View File

@@ -1,176 +1,186 @@
module std::os::win32;
typedef Bool = int;
typedef Boolean = Byte;
typedef Byte = char;
typedef CChar = cinterop::CChar;
typedef Char = cinterop::CChar;
typedef Colorref = DWord;
typedef DWord = uint;
typedef DWordlong = ulong;
typedef DWordPtr = ULongPtr;
typedef DWord32 = uint;
typedef DWord64 = ulong;
typedef Float = float;
typedef HAccel = Handle;
typedef HalfPtr = int;
typedef Handle = PVoid;
typedef HBitmap = Handle;
typedef HBrush = Handle;
typedef HColorspace = Handle;
typedef HConv = Handle;
typedef HConvlist = Handle;
typedef HCursor = HIcon;
typedef HDc = Handle;
typedef HDDEdata = Handle;
typedef HDesk = Handle;
typedef HDrop = Handle;
typedef HDwp = Handle;
typedef HFile = int;
typedef HFont = Handle;
typedef HGdiobj = Handle;
typedef HGlobal = Handle;
typedef HHook = Handle;
typedef HIcon = Handle;
typedef HInstance = Handle;
typedef HKey = Handle;
typedef HKl = Handle;
typedef HLocal = Handle;
typedef HMenu = Handle;
typedef HMetafile = Handle;
typedef HModule = Handle;
typedef HMonitor = Handle;
typedef HPalette = Handle;
typedef HPen = Handle;
typedef HResult = Long;
typedef HRgn = Handle;
typedef HRsrc = Handle;
typedef HSz = Handle;
typedef HWinsta = Handle;
typedef HWnd = Handle;
typedef Int = int;
typedef IntPtr = iptr;
typedef Int8 = ichar;
typedef Int16 = short;
typedef Int32 = int;
typedef Int64 = long;
typedef Langid = Word;
typedef Lcid = DWord;
typedef Lctype = DWord;
typedef Lgrpid = DWord;
typedef Long = int;
typedef Longlong = long;
typedef LongPtr = iptr;
typedef Long32 = int;
typedef Long64 = long;
typedef LParam = LongPtr;
typedef LPBool = Bool*;
typedef LPByte = Byte*;
typedef LPColorref = DWord*;
typedef LPCStr = CChar*;
typedef LPCTstr = LPCWstr;
typedef LPCVoid = void*;
typedef LPCWstr = WChar*;
typedef LPDWord = DWord*;
typedef LPHandle = Handle*;
typedef LPInt = int*;
typedef LPLong = int*;
typedef LPStr = CChar*;
typedef LPTstr = LPWstr;
typedef LPVoid = void*;
typedef LPWord = Word*;
typedef LPWstr = WChar*;
typedef LResult = LongPtr;
typedef PBool = Bool*;
typedef PBoolean = Boolean*;
typedef PByte = Byte*;
typedef PChar = Char*;
typedef PCstr = Char*;
typedef PCTstr = LPCWstr;
typedef PCUnicodeString = UnicodeString*;
typedef PCWstr = Char16*;
typedef PDWord = DWord*;
typedef PDWordlong = DWordlong*;
typedef PDWordPtr = DWordPtr*;
typedef PDWord32 = DWord32*;
typedef PDWord64 = DWord64*;
typedef PFloat = Float*;
typedef PHalfPtr = HalfPtr*;
typedef PHandle = Handle*;
typedef PHKey = HKey*;
typedef PInt = int*;
typedef PIntPtr = IntPtr*;
typedef PInt8 = Int8*;
typedef PInt16 = Int16*;
typedef PInt32 = Int32*;
typedef PInt64 = Int64*;
typedef PLcid = PDWord;
typedef PLong = Long*;
typedef PLonglong = Longlong*;
typedef PLongPtr = LongPtr*;
typedef PLong32 = Long32*;
typedef PLong64 = Long64*;
typedef Pointer32 = uint;
typedef Pointer64 = uptr;
typedef PointerSigned = iptr;
typedef PointerUnsigned = uptr;
typedef PShort = Short*;
typedef PSizeT = usz*;
typedef PSSizeT = isz*;
typedef PStr = Char*;
typedef PTByte = TByte*;
typedef PTChar = TChar*;
typedef PTStr = LPWstr;
typedef PUChar = UChar*;
typedef PUHalfPtr = UHalfPtr*;
typedef PUInt = UInt*;
typedef PUIntPtr = UIntPtr*;
typedef PUInt8 = UInt8*;
typedef PUInt16 = UInt16*;
typedef PUInt32 = UInt32*;
typedef PUInt64 = UInt64*;
typedef PULong = ULong*;
typedef PULonglong = ULonglong*;
typedef PULongPtr = ULongPtr*;
typedef PULong32 = ULong32*;
typedef PULong64 = ULong64*;
typedef PUnicodeString = UnicodeString*;
typedef PUShort = UShort*;
typedef PVoid = void*;
typedef PWChar = WChar*;
typedef PWord = Word*;
typedef PWStr = WChar*;
typedef QWord = ulong;
typedef ScHandle = Handle;
typedef ScLong = LPVoid;
typedef ServiceStatusHandle = Handle;
typedef Short = short;
typedef SizeT = usz;
typedef SSizeT = isz;
typedef TByte = WChar;
typedef TChar = WChar;
typedef UChar = char;
typedef UHalfPtr = uint;
typedef UInt = uint;
typedef UIntPtr = uptr;
typedef UInt8 = char;
typedef UInt16 = ushort;
typedef UInt32 = uint;
typedef UInt64 = ulong;
typedef ULong = uint;
typedef ULonglong = ulong;
typedef ULongPtr = ulong;
typedef ULong32 = uint;
typedef ULong64 = ulong;
typedef UShort = ushort;
typedef Usn = Longlong;
typedef WChar = Char16;
typedef Word = ushort;
typedef WParam = UIntPtr;
typedef Win32_BOOL = int;
typedef Win32_BOOLEAN = Win32_BYTE;
typedef Win32_BYTE = char;
typedef Win32_CCHAR = cinterop::CChar;
typedef Win32_CHAR = cinterop::CChar;
typedef Win32_COLORREF = Win32_DWORD;
typedef Win32_DWORD = uint;
typedef Win32_DWORDLONG = ulong;
typedef Win32_DWORD_PTR = Win32_ULONG_PTR;
typedef Win32_DWORD32 = uint;
typedef Win32_DWORD64 = ulong;
typedef Win32_FLOAT = float;
typedef Win32_HACCEL = Win32_HANDLE;
typedef Win32_HALF_PTR = int;
typedef Win32_HANDLE = Win32_PVOID;
typedef Win32_HBITMAP = Win32_HANDLE;
typedef Win32_HBRUSH = Win32_HANDLE;
typedef Win32_HCOLORSPACE = Win32_HANDLE;
typedef Win32_HCONV = Win32_HANDLE;
typedef Win32_HCONVLIST = Win32_HANDLE;
typedef Win32_HCURSOR = Win32_HICON;
typedef Win32_HDC = Win32_HANDLE;
typedef Win32_HDDEDATA = Win32_HANDLE;
typedef Win32_HDESK = Win32_HANDLE;
typedef Win32_HDROP = Win32_HANDLE;
typedef Win32_HDWP = Win32_HANDLE;
typedef Win32_HFILE = int;
typedef Win32_HFONT = Win32_HANDLE;
typedef Win32_HGDIOBJ = Win32_HANDLE;
typedef Win32_HGLOBAL = Win32_HANDLE;
typedef Win32_HHOOK = Win32_HANDLE;
typedef Win32_HICON = Win32_HANDLE;
typedef Win32_HINSTANCE = Win32_HANDLE;
typedef Win32_HKEY = Win32_HANDLE;
typedef Win32_HKL = Win32_HANDLE;
typedef Win32_HLOCAL = Win32_HANDLE;
typedef Win32_HMENU = Win32_HANDLE;
typedef Win32_HMETAFILE = Win32_HANDLE;
typedef Win32_HMODULE = Win32_HANDLE;
typedef Win32_HMONITOR = Win32_HANDLE;
typedef Win32_HPALETTE = Win32_HANDLE;
typedef Win32_HPEN = Win32_HANDLE;
typedef Win32_HRESULT = Win32_LONG;
typedef Win32_HRGN = Win32_HANDLE;
typedef Win32_HRSRC = Win32_HANDLE;
typedef Win32_HSZ = Win32_HANDLE;
typedef Win32_HWINSTA = Win32_HANDLE;
typedef Win32_HWND = Win32_HANDLE;
typedef Win32_INT = int;
typedef Win32_INT_PTR = iptr;
typedef Win32_INT8 = ichar;
typedef Win32_INT16 = short;
typedef Win32_INT32 = int;
typedef Win32_INT64 = long;
typedef Win32_LANGID = Win32_WORD;
typedef Win32_LCID = Win32_DWORD;
typedef Win32_LCTYPE = Win32_DWORD;
typedef Win32_LGRPID = Win32_DWORD;
typedef Win32_LONG = int;
typedef Win32_LONGLONG = long;
typedef Win32_LONG_PTR = iptr;
typedef Win32_LONG32 = int;
typedef Win32_LONG64 = long;
typedef Win32_LPARAM = Win32_LONG_PTR;
typedef Win32_LPBOOL = Win32_BOOL*;
typedef Win32_LPBYTE = Win32_BYTE*;
typedef Win32_LPCOLORREF = Win32_DWORD*;
typedef Win32_LPCSTR = Win32_CCHAR*;
typedef Win32_LPCTSTR = Win32_LPCWSTR;
typedef Win32_LPCVOID = void*;
typedef Win32_LPCWSTR = Win32_WCHAR*;
typedef Win32_LPDWORD = Win32_DWORD*;
typedef Win32_LPHANDLE = Win32_HANDLE*;
typedef Win32_LPINT = int*;
typedef Win32_LPLONG = int*;
typedef Win32_LPSTR = Win32_CCHAR*;
typedef Win32_LPTSTR = Win32_LPWSTR;
typedef Win32_LPVOID = void*;
typedef Win32_LPWORD = Win32_WORD*;
typedef Win32_LPWSTR = Win32_WCHAR*;
typedef Win32_LRESULT = Win32_LONG_PTR;
typedef Win32_PBOOL = Win32_BOOL*;
typedef Win32_PBOOLEAN = Win32_BOOLEAN*;
typedef Win32_PBYTE = Win32_BYTE*;
typedef Win32_PCHAR = Win32_CHAR*;
typedef Win32_PCSTR = Win32_CHAR*;
typedef Win32_PCTSTR = Win32_LPCWSTR;
typedef Win32_PCUNICODE_STRING = Win32_UNICODE_STRING*;
typedef Win32_PCWSTR = Char16*;
typedef Win32_PDWORD = Win32_DWORD*;
typedef Win32_PDWORDLONG = Win32_DWORDLONG*;
typedef Win32_PDWORDPTR = Win32_DWORD_PTR*;
typedef Win32_PDWORD32 = Win32_DWORD32*;
typedef Win32_PDWORD64 = Win32_DWORD64*;
typedef Win32_PFLOAT = Win32_FLOAT*;
typedef Win32_PHALFPTR = Win32_HALF_PTR*;
typedef Win32_PHANDLE = Win32_HANDLE*;
typedef Win32_PHKEY = Win32_HKEY*;
typedef Win32_PINT = int*;
typedef Win32_PINTPTR = Win32_INT_PTR*;
typedef Win32_PINT8 = Win32_INT8*;
typedef Win32_PINT16 = Win32_INT16*;
typedef Win32_PINT32 = Win32_INT32*;
typedef Win32_PINT64 = Win32_INT64*;
typedef Win32_PLCID = Win32_PDWORD;
typedef Win32_PLONG = Win32_LONG*;
typedef Win32_PLONGLONG = Win32_LONGLONG*;
typedef Win32_PLONG_PTR = Win32_LONG_PTR*;
typedef Win32_PLONG32 = Win32_LONG32*;
typedef Win32_PLONG64 = Win32_LONG64*;
typedef Win32_POINTER_32 = uint;
typedef Win32_POINTER_64 = uptr;
typedef Win32_POINTER_SIGNED = iptr;
typedef Win32_POINTER_UNSIGNED = uptr;
typedef Win32_PSHORT = Win32_SHORT*;
typedef Win32_PSIZE_T = usz*;
typedef Win32_PSSIZE_T = isz*;
typedef Win32_PSTR = Win32_CHAR*;
typedef Win32_PTBYTE = Win32_TBYTE*;
typedef Win32_PTCHAR = Win32_TCHAR*;
typedef Win32_PTSTR = Win32_LPWSTR;
typedef Win32_PUCHAR = Win32_UCHAR*;
typedef Win32_PUHALFPTR = Win32_UHALF_PTR*;
typedef Win32_PUINT = Win32_UINT*;
typedef Win32_PUINTPTR = Win32_UINT_PTR*;
typedef Win32_PUINT8 = Win32_UINT8*;
typedef Win32_PUINT16 = Win32_UINT16*;
typedef Win32_PUINT32 = Win32_UINT32*;
typedef Win32_PUINT64 = Win32_UINT64*;
typedef Win32_PULONG = Win32_ULONG*;
typedef Win32_PULONGLONG = Win32_ULONGLONG*;
typedef Win32_PULONG_PTR = Win32_ULONG_PTR*;
typedef Win32_PULONG32 = Win32_ULONG32*;
typedef Win32_PULONG64 = Win32_ULONG64*;
typedef Win32_PUNICODE_STRING = Win32_UNICODE_STRING*;
typedef Win32_PUSHORT = Win32_USHORT*;
typedef Win32_PVOID = void*;
typedef Win32_PWCHAR = Win32_WCHAR*;
typedef Win32_PWORD = Win32_WORD*;
typedef Win32_PWSTR = Win32_WCHAR*;
typedef Win32_QWORD = ulong;
typedef Win32_SC_HANDLE = Win32_HANDLE;
typedef Win32_SC_LOCK = Win32_LPVOID;
typedef Win32_SERVICE_STATUS_HANDLE = Win32_HANDLE;
typedef Win32_SHORT = short;
typedef Win32_SIZE_T = usz;
typedef Win32_SSIZE_T = isz;
typedef Win32_TBYTE = Win32_WCHAR;
typedef Win32_TCHAR = Win32_WCHAR;
typedef Win32_UCHAR = char;
typedef Win32_UHALF_PTR = uint;
typedef Win32_UINT = uint;
typedef Win32_UINT_PTR = uptr;
typedef Win32_UINT8 = char;
typedef Win32_UINT16 = ushort;
typedef Win32_UINT32 = uint;
typedef Win32_UINT64 = ulong;
typedef Win32_ULONG = uint;
typedef Win32_ULONGLONG = ulong;
typedef Win32_ULONG_PTR = ulong;
typedef Win32_ULONG32 = uint;
typedef Win32_ULONG64 = ulong;
typedef Win32_USHORT = ushort;
typedef Win32_USN = Win32_LONGLONG;
typedef Win32_WCHAR = Char16;
typedef Win32_WORD = ushort;
typedef Win32_WPARAM = Win32_UINT_PTR;
struct UnicodeString
struct Win32_UNICODE_STRING
{
UShort length;
UShort maximum_length;
PWStr buffer;
Win32_USHORT length;
Win32_USHORT maximum_length;
Win32_PWSTR buffer;
}
union Win32_LARGE_INTEGER
{
struct
{
Win32_DWORD lowPart;
Win32_LONG highPart;
}
ulong quadPart;
}

View File

@@ -661,16 +661,8 @@ RETRY:
case AST_BLOCK_EXIT_STMT:
case AST_RETURN_STMT:
MACRO_COPY_EXPR(ast->return_stmt.expr);
if (ast->return_stmt.cleanup == ast->return_stmt.cleanup_fail)
{
MACRO_COPY_ASTID(ast->return_stmt.cleanup);
ast->return_stmt.cleanup_fail = ast->return_stmt.cleanup;
}
else
{
MACRO_COPY_ASTID(ast->return_stmt.cleanup);
MACRO_COPY_ASTID(ast->return_stmt.cleanup_fail);
}
MACRO_COPY_ASTID(ast->return_stmt.cleanup);
MACRO_COPY_ASTID(ast->return_stmt.cleanup_fail);
break;
case AST_SWITCH_STMT:
case AST_IF_CATCH_SWITCH_STMT:

View File

@@ -147,6 +147,7 @@ static void linker_setup_windows(const char ***args_ref, LinkerType linker_type)
add_arg("ntdll.lib");
add_arg("user32.lib");
add_arg("shell32.lib");
add_arg("Shlwapi.lib");
add_arg("legacy_stdio_definitions.lib");
if (active_target.win.crt_linking == WIN_CRT_STATIC)

View File

@@ -1426,7 +1426,6 @@ void llvm_emit_panic_on_true(GenContext *c, LLVMValueRef value, const char *pani
void llvm_emit_stmt(GenContext *c, Ast *ast)
{
if (ast->ast_kind != AST_COMPOUND_STMT) EMIT_LOC(c, ast);
assert(!c->catch_block && "Did not expect a catch block here.");
switch (ast->ast_kind)
{
case AST_POISONED:

View File

@@ -331,11 +331,15 @@ static inline bool sema_defer_by_result(AstId defer_top, AstId defer_bottom)
static inline void sema_inline_return_defers(SemaContext *context, Ast *stmt, AstId defer_top, AstId defer_bottom)
{
stmt->return_stmt.cleanup_fail = stmt->return_stmt.cleanup = context_get_defers(context, defer_top, defer_bottom, true);
stmt->return_stmt.cleanup = context_get_defers(context, defer_top, defer_bottom, true);
if (stmt->return_stmt.expr && IS_OPTIONAL(stmt->return_stmt.expr) && sema_defer_by_result(context->active_scope.defer_last, context->block_return_defer))
{
stmt->return_stmt.cleanup_fail = context_get_defers(context, context->active_scope.defer_last, context->block_return_defer, false);
}
else
{
stmt->return_stmt.cleanup_fail = stmt->return_stmt.cleanup ? astid(copy_ast_defer(astptr(stmt->return_stmt.cleanup))) : 0;
}
}
/**
@@ -487,12 +491,6 @@ static inline bool sema_analyse_return_stmt(SemaContext *context, Ast *statement
if (!first) goto SKIP_ENSURE;
if (statement->return_stmt.cleanup)
{
// If we have the same ast on cleanup / cleanup-fail we need to separate them.
if (type_is_optional(expected_rtype) &&
statement->return_stmt.cleanup == statement->return_stmt.cleanup_fail)
{
statement->return_stmt.cleanup_fail = astid(copy_ast_defer(astptr(statement->return_stmt.cleanup)));
}
Ast *last = ast_last(astptr(statement->return_stmt.cleanup));
last->next = first;
}

View File

@@ -1 +1 @@
#define COMPILER_VERSION "0.4.103"
#define COMPILER_VERSION "0.4.104"

View File

@@ -12,6 +12,43 @@ fn void test_starts_with()
assert(!s.starts_with("o"));
}
fn void test_strip()
{
String s = "ofke";
assert(s.strip("of") == "ke");
assert(s.strip("ofke") == "");
assert(s.strip("ofkes") == "ofke");
assert(s.strip("ofkf") == "ofke");
assert(s.strip("") == "ofke");
s = "";
assert(s.strip("") == "");
assert(s.strip("o") == "");
}
fn void test_strip_end()
{
String s = "ofke";
assert(s.strip_end("ke") == "of");
assert(s.strip_end("ofke") == "");
assert(s.strip_end("ofkes") == "ofke");
assert(s.strip_end("ofkf") == "ofke");
assert(s.strip_end("") == "ofke");
s = "";
assert(s.strip_end("") == "");
assert(s.strip_end("o") == "");
}
fn void test_ends_with()
{
String s = "ofke";
assert(s.ends_with("ke"));
assert(s.ends_with("ofke"));
assert(!s.ends_with("ofkes"));
assert(!s.ends_with("ofkf"));
s = "";
assert(s.ends_with(""));
assert(!s.ends_with("e"));
}
fn void test_trim()
{
String s = " \t\nabc ";

View File

@@ -0,0 +1,14 @@
module std::io::path @test;
fn void! test_parent()
{
Path p = path::new("")?;
assert(catch(p.parent()));
p = path::new("/")?;
assert(catch(p.parent()));
p = path::new("/a/b/c")?;
assert(path::new("/a/b")? == p.parent()?);
p = path::new("/a/b/c/")?;
assert(path::new("/a/b")? == p.parent()?);
}