Done with the ascii module.

This commit is contained in:
Kapendev 2024-08-04 19:55:08 +03:00
parent b37b6c3eb0
commit 03b915dca0
6 changed files with 228 additions and 199 deletions

View file

@ -4,7 +4,7 @@
Cleaning things in core. Cleaning things in core.
* [ ] ascii * [x] ascii
* [x] colors * [x] colors
* [x] containers * [x] containers
* [x] errors * [x] errors
@ -16,9 +16,10 @@ Cleaning things in core.
* [ ] types * [ ] types
* [ ] unions * [ ] unions
Was working on ascii. Done with ascii.
Was rewriting the tests. Now I am thinking of making the io module better.
Was going to write the toValue tests and then I am done! For example, readin does not tell you right now if something went wrong.
It just returns an empty list.
## IDEAS ## IDEAS

View file

@ -462,208 +462,121 @@ IStr toStr(T)(T value, ToStrOptions options = ToStrOptions()) {
} else static if (__traits(hasMember, T, "toStr")) { } else static if (__traits(hasMember, T, "toStr")) {
return value.toStr(); return value.toStr();
} else { } else {
static assert(0, "The `toStr` function does not handle the `" ~ T.stringof ~ "` type. Implement a `toStr` function for that type."); static assert(0, "Function `toStr` does not handle the `" ~ T.stringof ~ "` type. Implement a `toStr` function for that type.");
} }
} }
BasicResult!bool toBool(IStr str) { BasicResult!bool toBool(IStr str) {
auto result = BasicResult!bool(); if (str == "false") {
if (str == "true") { return BasicResult!bool(false);
result.value = true; } else if (str == "true") {
} else if (str == "false") { return BasicResult!bool(true);
result.value = false;
} else { } else {
result.error = BasicError.invalid; return BasicResult!bool(BasicError.invalid);
}
return result;
}
ulong toBoolWithNone(IStr str) {
auto conv = toBool(str);
if (conv.error) {
return false;
} else {
return conv.value;
} }
} }
BasicResult!ulong toUnsigned(IStr str) { BasicResult!ulong toUnsigned(IStr str) {
auto result = BasicResult!ulong(); if (str.length == 0 || str.length >= 18) {
if (str.length == 0) { return BasicResult!ulong(BasicError.invalid);
result.error = BasicError.invalid;
} else if (str.length >= 18) {
result.error = BasicError.overflow;
} else { } else {
if (str.length == 1 && str[0] == '+') {
return BasicResult!ulong(BasicError.invalid);
}
ulong value = 0;
ulong level = 1; ulong level = 1;
foreach_reverse (i, c; str) { foreach_reverse (i, c; str[(str[0] == '+' ? 1 : 0) .. $]) {
if (!isDigit(c)) { if (isDigit(c)) {
result.error = BasicError.invalid; value += (c - '0') * level;
break;
}
auto digit = c - '0';
result.value += digit * level;
level *= 10; level *= 10;
} else {
return BasicResult!ulong(BasicError.invalid);
} }
} }
return result; return BasicResult!ulong(value);
}
} }
BasicResult!ulong toUnsigned(char c) { BasicResult!ulong toUnsigned(char c) {
auto result = BasicResult!ulong();
if (isDigit(c)) { if (isDigit(c)) {
result.value = c - '0'; return BasicResult!ulong(c - '0');
} else { } else {
result.error = BasicError.invalid; return BasicResult!ulong(BasicError.invalid);
}
return result;
}
ulong toUnsignedWithNone(IStr str) {
auto conv = toUnsigned(str);
if (conv.error) {
return 0;
} else {
return conv.value;
}
}
ulong toUnsignedWithNone(char c) {
auto conv = toUnsigned(c);
if (conv.error) {
return 0;
} else {
return conv.value;
} }
} }
BasicResult!long toSigned(IStr str) { BasicResult!long toSigned(IStr str) {
auto result = BasicResult!long(); if (str.length == 0 || str.length >= 18) {
if (str.length == 0) { return BasicResult!long(BasicError.invalid);
result.error = BasicError.invalid;
} else if (str.length >= 18) {
result.error = BasicError.overflow;
} else { } else {
if (str[0] == '-') { auto temp = toUnsigned(str[(str[0] == '-' ? 1 : 0) .. $]);
auto conv = toUnsigned(str[1 .. $]); if (temp.isNone) {
if (conv.error) { return BasicResult!long(temp.error);
result.error = conv.error;
} else {
result.value = -conv.value;
} }
} else { return BasicResult!long(str[0] == '-' ? -temp.value : temp.value);
auto conv = toUnsigned(str);
if (conv.error) {
result.error = conv.error;
} else {
result.value = conv.value;
} }
} }
}
return result;
}
BasicResult!long toSigned(char c) { BasicResult!long toSigned(char c) {
auto result = BasicResult!long(); if (isDigit(c)) {
auto conv = toUnsigned(c); return BasicResult!long(c - '0');
if (conv.error) {
result.error = conv.error;
} else { } else {
result.value = cast(long) conv.value; return BasicResult!long(BasicError.invalid);
}
return result;
}
long toSignedWithNone(IStr str) {
auto conv = toSigned(str);
if (conv.error) {
return 0;
} else {
return conv.value;
}
}
long toSignedWithNone(char c) {
auto conv = toSigned(c);
if (conv.error) {
return 0;
} else {
return conv.value;
} }
} }
BasicResult!double toDouble(IStr str) { BasicResult!double toDouble(IStr str) {
auto result = BasicResult!double(); auto dotIndex = findStart(str, '.');
result.value = 0.0; if (dotIndex == -1) {
auto hasDot = false; auto temp = toSigned(str);
foreach (i, c; str) { return temp.isNone ? BasicResult!double(temp.error) : BasicResult!double(temp.value);
if (c == '.') {
hasDot = true;
auto lhs = toSigned(str[0 .. i]);
if (lhs.error) {
result.error = lhs.error;
} else { } else {
auto rhs = toSigned(str[i + 1 .. $]); auto left = toSigned(str[0 .. dotIndex]);
if (rhs.error) { auto right = toSigned(str[dotIndex + 1 .. $]);
result.error = rhs.error; if (left.isNone || right.isNone) {
return BasicResult!double(BasicError.invalid);
} else if (str[dotIndex + 1] == '-' || str[dotIndex + 1] == '+') {
return BasicResult!double(BasicError.invalid);
} else { } else {
auto rhsLevel = 10; auto sign = str[0] == '-' ? -1 : 1;
foreach (_; 1 .. str[i + 1 .. $].length) { auto level = 10;
rhsLevel *= 10; foreach (i; 1 .. str[dotIndex + 1 .. $].length) {
level *= 10;
} }
result.value = lhs.value + ((lhs.value < 0 ? -1 : 1) * rhs.value / cast(double) rhsLevel); return BasicResult!double(left.value + sign * (right.value / (cast(double) level)));
} }
} }
break;
}
}
if (!hasDot) {
auto conv = toSigned(str);
result.value = conv.value;
result.error = conv.error;
}
return result;
} }
double toDoubleWithNone(IStr str) { BasicResult!double toDouble(char c) {
auto conv = toDouble(str); if (isDigit(c)) {
if (conv.error) { return BasicResult!double(c - '0');
return 0.0;
} else { } else {
return conv.value; return BasicResult!double(BasicError.invalid);
} }
} }
BasicResult!T toEnum(T)(IStr str) { BasicResult!T toEnum(T)(IStr str) {
auto result = BasicResult!T();
switch (str) { switch (str) {
static foreach (member; __traits(allMembers, T)) { static foreach (member; __traits(allMembers, T)) {
mixin("case " ~ member.stringof ~ ": result.value = T." ~ member ~ "; goto switchExit;"); mixin("case " ~ member.stringof ~ ": return BasicResult!T(T." ~ member ~ ");");
} }
default: result.error = BasicError.invalid; default: return BasicResult!T(BasicError.invalid);
}
switchExit:
return result;
}
T toEnumWithNone(T)(IStr str) {
auto conv = toEnum!T(str);
if (conv.error) {
return T.init;
} else {
return conv.value;
} }
} }
@trusted @trusted
ICStr toCStr(IStr str) { BasicResult!ICStr toCStr(IStr str) {
static char[1024] buffer = void; static char[1024] buffer = void;
auto result = buffer[]; if (buffer.length < str.length) {
foreach (i, c; str) { return BasicResult!ICStr(BasicError.invalid);
result[i] = c; } else {
auto value = buffer[];
value.copyChars(str);
value[str.length] = '\0';
return BasicResult!ICStr(value.ptr);
} }
result[str.length] = '\0';
return result.ptr;
} }
// TODO: Check if the args count is the same with the `{}` count. // TODO: Check if the args count is the same with the `{}` count.
@ -738,6 +651,14 @@ void formatl(A...)(ref LStr text, IStr formatStr, A args) {
// Function test. // Function test.
@trusted @trusted
unittest { unittest {
enum TestEnum {
one,
two,
}
char[128] buffer = void;
Str str;
assert(isDigit("0123456789?") == false); assert(isDigit("0123456789?") == false);
assert(isDigit("0123456789") == true); assert(isDigit("0123456789") == true);
assert(isUpper("hello") == false); assert(isUpper("hello") == false);
@ -748,9 +669,6 @@ unittest {
assert(isCStr("hello") == false); assert(isCStr("hello") == false);
assert(isCStr("hello\0") == true); assert(isCStr("hello\0") == true);
char[128] buffer = void;
Str str;
str = buffer[]; str = buffer[];
str.copy("Hello"); str.copy("Hello");
assert(str == "Hello"); assert(str == "Hello");
@ -834,45 +752,94 @@ unittest {
assert(cStrToStr("Hello\0") == "Hello"); assert(cStrToStr("Hello\0") == "Hello");
enum TestEnum {
one,
two,
}
assert(enumToStr(TestEnum.one) == "one"); assert(enumToStr(TestEnum.one) == "one");
assert(enumToStr(TestEnum.two) == "two"); assert(enumToStr(TestEnum.two) == "two");
assert(toBool("F").isSome == false);
assert(toBool("F").unwrapOr() == false);
assert(toBool("T").isSome == false);
assert(toBool("T").unwrapOr() == false);
assert(toBool("false").isSome == true);
assert(toBool("false").unwrapOr() == false);
assert(toBool("true").isSome == true);
assert(toBool("true").unwrapOr() == true);
assert(toUnsigned("1_069").isSome == false);
assert(toUnsigned("1_069").unwrapOr() == 0);
assert(toUnsigned("+1069").isSome == true);
assert(toUnsigned("+1069").unwrapOr() == 1069);
assert(toUnsigned("1069").isSome == true);
assert(toUnsigned("1069").unwrapOr() == 1069);
assert(toUnsigned('+').isSome == false);
assert(toUnsigned('+').unwrapOr() == 0);
assert(toUnsigned('0').isSome == true);
assert(toUnsigned('0').unwrapOr() == 0);
assert(toUnsigned('9').isSome == true);
assert(toUnsigned('9').unwrapOr() == 9);
// TODO: ToValue tests write need!!! assert(toSigned("1_069").isSome == false);
auto text1 = "1.0"; assert(toSigned("1_069").unwrapOr() == 0);
auto conv1 = toDouble(text1); assert(toSigned("-1069").isSome == true);
assert(conv1.value == 1.0); assert(toSigned("-1069").unwrapOr() == -1069);
assert(conv1.error == BasicError.none); assert(toSigned("+1069").isSome == true);
assert(toSigned("+1069").unwrapOr() == 1069);
assert(toSigned("1069").isSome == true);
assert(toSigned("1069").unwrapOr() == 1069);
assert(toSigned('+').isSome == false);
assert(toSigned('+').unwrapOr() == 0);
assert(toSigned('0').isSome == true);
assert(toSigned('0').unwrapOr() == 0);
assert(toSigned('9').isSome == true);
assert(toSigned('9').unwrapOr() == 9);
auto text2 = "1"; assert(toDouble("1_069").isSome == false);
auto conv2 = toDouble(text2); assert(toDouble("1_069").unwrapOr() == 0);
assert(conv2.value == 1.0); assert(toDouble(".1069").isSome == false);
assert(conv2.error == BasicError.none); assert(toDouble(".1069").unwrapOr() == 0);
assert(toDouble("1069.").isSome == false);
assert(toDouble("1069.").unwrapOr() == 0);
assert(toDouble(".").isSome == false);
assert(toDouble(".").unwrapOr() == 0);
assert(toDouble("-1069.-69").isSome == false);
assert(toDouble("-1069.-69").unwrapOr() == 0);
assert(toDouble("-1069.+69").isSome == false);
assert(toDouble("-1069.+69").unwrapOr() == 0);
assert(toDouble("-1069").isSome == true);
assert(toDouble("-1069").unwrapOr() == -1069);
assert(toDouble("+1069").isSome == true);
assert(toDouble("+1069").unwrapOr() == 1069);
assert(toDouble("1069").isSome == true);
assert(toDouble("1069").unwrapOr() == 1069);
assert(toDouble("1069.0").isSome == true);
assert(toDouble("1069.0").unwrapOr() == 1069);
assert(toDouble("-1069.0095").isSome == true);
assert(toDouble("-1069.0095").unwrapOr() == -1069.0095);
assert(toDouble("+1069.0095").isSome == true);
assert(toDouble("+1069.0095").unwrapOr() == 1069.0095);
assert(toDouble("1069.0095").isSome == true);
assert(toDouble("1069.0095").unwrapOr() == 1069.0095);
assert(toDouble("-0.0095").isSome == true);
assert(toDouble("-0.0095").unwrapOr() == -0.0095);
assert(toDouble("+0.0095").isSome == true);
assert(toDouble("+0.0095").unwrapOr() == 0.0095);
assert(toDouble("0.0095").isSome == true);
assert(toDouble("0.0095").unwrapOr() == 0.0095);
assert(toDouble('+').isSome == false);
assert(toDouble('+').unwrapOr() == 0);
assert(toDouble('0').isSome == true);
assert(toDouble('0').unwrapOr() == 0);
assert(toDouble('9').isSome == true);
assert(toDouble('9').unwrapOr() == 9);
auto text3 = "1?"; assert(toEnum!TestEnum("?").isSome == false);
auto conv3 = toDouble(text3); assert(toEnum!TestEnum("?").unwrapOr() == TestEnum.one);
assert(conv3.value == 0.0); assert(toEnum!TestEnum("one").isSome == true);
assert(conv3.error == BasicError.invalid); assert(toEnum!TestEnum("one").unwrapOr() == TestEnum.one);
assert(toEnum!TestEnum("two").isSome == true);
assert(toEnum!TestEnum("two").unwrapOr() == TestEnum.two);
assert(format("") == ""); assert(toCStr("Hello").unwrapOr().length == "Hello".length);
assert(format("{}") == "{}"); assert(toCStr("Hello").unwrapOr().cStrToStr() == "Hello");
assert(format("{}", "1") == "1");
assert(format("{} {}", "1", "2") == "1 2");
assert(format("{} {} {}", "1", "2", "3") == "1 2 3");
assert(format("{} {} {}", 1, -2, 3.69) == "1 -2 3.69");
assert(format("{}", 420, 320, 220, 120, 20) == "420");
assert(format("", 1, -2, 3.69) == "");
assert(format("({})", format("({}, {})", false, true)) == "((false, true))");
// TODO: Uncoment when the N.00 bug is fixed. // TODO: Write tests for `format` when it is done.
// assert(format("{}", 0.00) == "0.00");
// assert(format("{}", 0.50) == "0.50");
// assert(format("{}", 1.00) == "1.00");
// assert(format("{}", 1.50) == "1.50");
} }

View file

@ -5,6 +5,8 @@
module popka.core.errors; module popka.core.errors;
import popka.core.traits;
@safe @nogc nothrow: @safe @nogc nothrow:
enum BasicError : ubyte { enum BasicError : ubyte {
@ -18,6 +20,61 @@ enum BasicError : ubyte {
} }
struct BasicResult(T) { struct BasicResult(T) {
static if (isNumberType!T) {
T value = 0;
} else {
T value; T value;
BasicError error; }
BasicError error = BasicError.some;
@safe @nogc nothrow:
this(T value) {
this.value = value;
error = BasicError.none;
}
this(BasicError error) {
this.error = error;
}
T unwrap() {
if (error) {
assert(0, "");
}
return value;
}
T unwrapOr(T dflt) {
if (error) {
return dflt;
} else {
return value;
}
}
T unwrapOr() {
return value;
}
bool isNone() {
return error != 0;
}
bool isSome() {
return error == 0;
}
}
// BasicResult test.
unittest {
assert(BasicResult!int().isNone == true);
assert(BasicResult!int().isSome == false);
assert(BasicResult!int().unwrapOr() == 0);
assert(BasicResult!int(0).isNone == false);
assert(BasicResult!int(0).isSome == true);
assert(BasicResult!int(0).unwrapOr() == 0);
assert(BasicResult!int(69).isNone == false);
assert(BasicResult!int(69).isSome == true);
assert(BasicResult!int(69).unwrapOr() == 69);
} }

View file

@ -39,7 +39,7 @@ void println(A...)(A args) {
@trusted @trusted
// TODO: Check the error values and let the user know what went wrong. // TODO: Check the error values and let the user know what went wrong.
void readText(IStr path, ref List!char text) { void readText(IStr path, ref List!char text) {
auto f = .fopen(toCStr(path), "rb"); auto f = .fopen(toCStr(path).unwrapOr(), "rb");
if (f == null) { if (f == null) {
text.clear(); text.clear();
return; return;
@ -76,7 +76,7 @@ List!char readText(IStr path) {
@trusted @trusted
// TODO: Check the error values and let the user know what went wrong. // TODO: Check the error values and let the user know what went wrong.
void writeText(IStr path, IStr text) { void writeText(IStr path, IStr text) {
auto f = .fopen(toCStr(path), "w"); auto f = .fopen(toCStr(path).unwrapOr(), "w");
if (f == null) { if (f == null) {
return; return;
} }

View file

@ -61,6 +61,10 @@ bool isDoubleType(T)() {
is(T == immutable(double)); is(T == immutable(double));
} }
bool isNumberType(T)() {
return isIntegerType!T || isDoubleType!T;
}
bool isCharType(T)() { bool isCharType(T)() {
return is(T == char) || return is(T == char) ||
is(T == const(char)) || is(T == const(char)) ||

View file

@ -212,7 +212,7 @@ struct Texture {
void load(const(char)[] path) { void load(const(char)[] path) {
free(); free();
if (path.length != 0) { if (path.length != 0) {
data = ray.LoadTexture(path.toAssetsPath.toCStr); data = ray.LoadTexture(path.toAssetsPath.toCStr().unwrapOr());
} }
if (isEmpty) printfln("Error: The file `{}` does not exist.", path); if (isEmpty) printfln("Error: The file `{}` does not exist.", path);
} }
@ -294,7 +294,7 @@ struct Font {
void load(const(char)[] path, uint size, const(dchar)[] runes = []) { void load(const(char)[] path, uint size, const(dchar)[] runes = []) {
free(); free();
if (path.length != 0) { if (path.length != 0) {
data = ray.LoadFontEx(path.toAssetsPath.toCStr, size, cast(int*) runes.ptr, cast(int) runes.length); data = ray.LoadFontEx(path.toAssetsPath.toCStr().unwrapOr(), size, cast(int*) runes.ptr, cast(int) runes.length);
} }
if (isEmpty) printfln("Error: The file `{}` does not exist.", path); if (isEmpty) printfln("Error: The file `{}` does not exist.", path);
} }
@ -343,7 +343,7 @@ struct Sound {
void load(const(char)[] path) { void load(const(char)[] path) {
free(); free();
if (path.length != 0) { if (path.length != 0) {
data = ray.LoadSound(path.toAssetsPath.toCStr); data = ray.LoadSound(path.toAssetsPath.toCStr().unwrapOr());
} }
if (isEmpty) printfln("Error: The file `{}` does not exist.", path); if (isEmpty) printfln("Error: The file `{}` does not exist.", path);
} }
@ -396,7 +396,7 @@ struct Music {
void load(const(char)[] path) { void load(const(char)[] path) {
free(); free();
if (path.length != 0) { if (path.length != 0) {
data = ray.LoadMusicStream(path.toAssetsPath.toCStr); data = ray.LoadMusicStream(path.toAssetsPath.toCStr().unwrapOr());
} }
if (isEmpty) printfln("Error: The file `{}` does not exist.", path); if (isEmpty) printfln("Error: The file `{}` does not exist.", path);
} }
@ -801,7 +801,7 @@ void openWindow(Vec2 size, const(char)[] title = "Popka", Color color = defaultB
} }
ray.SetConfigFlags(ray.FLAG_VSYNC_HINT | ray.FLAG_WINDOW_RESIZABLE); ray.SetConfigFlags(ray.FLAG_VSYNC_HINT | ray.FLAG_WINDOW_RESIZABLE);
ray.SetTraceLogLevel(ray.LOG_ERROR); ray.SetTraceLogLevel(ray.LOG_ERROR);
ray.InitWindow(cast(int) size.x, cast(int) size.y, toCStr(title)); ray.InitWindow(cast(int) size.x, cast(int) size.y, title.toCStr().unwrapOr());
ray.InitAudioDevice(); ray.InitAudioDevice();
ray.SetWindowMinSize(cast(int) (size.x * 0.25f), cast(int) (size.y * 0.25f)); ray.SetWindowMinSize(cast(int) (size.x * 0.25f), cast(int) (size.y * 0.25f));
ray.SetExitKey(ray.KEY_NULL); ray.SetExitKey(ray.KEY_NULL);