From 03b915dca0e7ca9f4da56829bc22dff814dd37b3 Mon Sep 17 00:00:00 2001 From: Kapendev Date: Sun, 4 Aug 2024 19:55:08 +0300 Subject: [PATCH] Done with the ascii module. --- TODO.md | 9 +- source/popka/core/ascii.d | 339 +++++++++++++++++-------------------- source/popka/core/errors.d | 61 ++++++- source/popka/core/io.d | 4 +- source/popka/core/traits.d | 4 + source/popka/game/engine.d | 10 +- 6 files changed, 228 insertions(+), 199 deletions(-) diff --git a/TODO.md b/TODO.md index bbe75f9..72b4022 100644 --- a/TODO.md +++ b/TODO.md @@ -4,7 +4,7 @@ Cleaning things in core. -* [ ] ascii +* [x] ascii * [x] colors * [x] containers * [x] errors @@ -16,9 +16,10 @@ Cleaning things in core. * [ ] types * [ ] unions -Was working on ascii. -Was rewriting the tests. -Was going to write the toValue tests and then I am done! +Done with ascii. +Now I am thinking of making the io module better. +For example, readin does not tell you right now if something went wrong. +It just returns an empty list. ## IDEAS diff --git a/source/popka/core/ascii.d b/source/popka/core/ascii.d index ede0e41..5500298 100644 --- a/source/popka/core/ascii.d +++ b/source/popka/core/ascii.d @@ -462,208 +462,121 @@ IStr toStr(T)(T value, ToStrOptions options = ToStrOptions()) { } else static if (__traits(hasMember, T, "toStr")) { return value.toStr(); } 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) { - auto result = BasicResult!bool(); - if (str == "true") { - result.value = true; - } else if (str == "false") { - result.value = false; + if (str == "false") { + return BasicResult!bool(false); + } else if (str == "true") { + return BasicResult!bool(true); } else { - result.error = BasicError.invalid; - } - return result; -} - -ulong toBoolWithNone(IStr str) { - auto conv = toBool(str); - if (conv.error) { - return false; - } else { - return conv.value; + return BasicResult!bool(BasicError.invalid); } } BasicResult!ulong toUnsigned(IStr str) { - auto result = BasicResult!ulong(); - if (str.length == 0) { - result.error = BasicError.invalid; - } else if (str.length >= 18) { - result.error = BasicError.overflow; + if (str.length == 0 || str.length >= 18) { + return BasicResult!ulong(BasicError.invalid); } else { - ulong level = 1; - foreach_reverse (i, c; str) { - if (!isDigit(c)) { - result.error = BasicError.invalid; - break; - } - auto digit = c - '0'; - result.value += digit * level; - level *= 10; + if (str.length == 1 && str[0] == '+') { + return BasicResult!ulong(BasicError.invalid); } + ulong value = 0; + ulong level = 1; + foreach_reverse (i, c; str[(str[0] == '+' ? 1 : 0) .. $]) { + if (isDigit(c)) { + value += (c - '0') * level; + level *= 10; + } else { + return BasicResult!ulong(BasicError.invalid); + } + } + return BasicResult!ulong(value); } - return result; } BasicResult!ulong toUnsigned(char c) { - auto result = BasicResult!ulong(); if (isDigit(c)) { - result.value = c - '0'; + return BasicResult!ulong(c - '0'); } else { - result.error = 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; + return BasicResult!ulong(BasicError.invalid); } } BasicResult!long toSigned(IStr str) { - auto result = BasicResult!long(); - if (str.length == 0) { - result.error = BasicError.invalid; - } else if (str.length >= 18) { - result.error = BasicError.overflow; + if (str.length == 0 || str.length >= 18) { + return BasicResult!long(BasicError.invalid); } else { - if (str[0] == '-') { - auto conv = toUnsigned(str[1 .. $]); - if (conv.error) { - result.error = conv.error; - } else { - result.value = -conv.value; - } - } else { - auto conv = toUnsigned(str); - if (conv.error) { - result.error = conv.error; - } else { - result.value = conv.value; - } + auto temp = toUnsigned(str[(str[0] == '-' ? 1 : 0) .. $]); + if (temp.isNone) { + return BasicResult!long(temp.error); } + return BasicResult!long(str[0] == '-' ? -temp.value : temp.value); } - return result; } BasicResult!long toSigned(char c) { - auto result = BasicResult!long(); - auto conv = toUnsigned(c); - if (conv.error) { - result.error = conv.error; + if (isDigit(c)) { + return BasicResult!long(c - '0'); } else { - result.value = cast(long) conv.value; - } - 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; + return BasicResult!long(BasicError.invalid); } } BasicResult!double toDouble(IStr str) { - auto result = BasicResult!double(); - result.value = 0.0; - auto hasDot = false; - foreach (i, c; str) { - if (c == '.') { - hasDot = true; - auto lhs = toSigned(str[0 .. i]); - if (lhs.error) { - result.error = lhs.error; - } else { - auto rhs = toSigned(str[i + 1 .. $]); - if (rhs.error) { - result.error = rhs.error; - } else { - auto rhsLevel = 10; - foreach (_; 1 .. str[i + 1 .. $].length) { - rhsLevel *= 10; - } - result.value = lhs.value + ((lhs.value < 0 ? -1 : 1) * rhs.value / cast(double) rhsLevel); - } + auto dotIndex = findStart(str, '.'); + if (dotIndex == -1) { + auto temp = toSigned(str); + return temp.isNone ? BasicResult!double(temp.error) : BasicResult!double(temp.value); + } else { + auto left = toSigned(str[0 .. dotIndex]); + auto right = toSigned(str[dotIndex + 1 .. $]); + if (left.isNone || right.isNone) { + return BasicResult!double(BasicError.invalid); + } else if (str[dotIndex + 1] == '-' || str[dotIndex + 1] == '+') { + return BasicResult!double(BasicError.invalid); + } else { + auto sign = str[0] == '-' ? -1 : 1; + auto level = 10; + foreach (i; 1 .. str[dotIndex + 1 .. $].length) { + level *= 10; } - break; + return BasicResult!double(left.value + sign * (right.value / (cast(double) level))); } } - if (!hasDot) { - auto conv = toSigned(str); - result.value = conv.value; - result.error = conv.error; - } - return result; } -double toDoubleWithNone(IStr str) { - auto conv = toDouble(str); - if (conv.error) { - return 0.0; +BasicResult!double toDouble(char c) { + if (isDigit(c)) { + return BasicResult!double(c - '0'); } else { - return conv.value; + return BasicResult!double(BasicError.invalid); } } BasicResult!T toEnum(T)(IStr str) { - auto result = BasicResult!T(); switch (str) { 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; - } - switchExit: - return result; -} - -T toEnumWithNone(T)(IStr str) { - auto conv = toEnum!T(str); - if (conv.error) { - return T.init; - } else { - return conv.value; + default: return BasicResult!T(BasicError.invalid); } } @trusted -ICStr toCStr(IStr str) { +BasicResult!ICStr toCStr(IStr str) { static char[1024] buffer = void; - auto result = buffer[]; - foreach (i, c; str) { - result[i] = c; + if (buffer.length < str.length) { + return BasicResult!ICStr(BasicError.invalid); + } 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. @@ -738,6 +651,14 @@ void formatl(A...)(ref LStr text, IStr formatStr, A args) { // Function test. @trusted unittest { + enum TestEnum { + one, + two, + } + + char[128] buffer = void; + Str str; + assert(isDigit("0123456789?") == false); assert(isDigit("0123456789") == true); assert(isUpper("hello") == false); @@ -748,9 +669,6 @@ unittest { assert(isCStr("hello") == false); assert(isCStr("hello\0") == true); - char[128] buffer = void; - Str str; - str = buffer[]; str.copy("Hello"); assert(str == "Hello"); @@ -833,46 +751,95 @@ unittest { assert(doubleToStr(69.0, 3) == "69.000"); assert(cStrToStr("Hello\0") == "Hello"); - - enum TestEnum { - one, - two, - } assert(enumToStr(TestEnum.one) == "one"); 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!!! - auto text1 = "1.0"; - auto conv1 = toDouble(text1); - assert(conv1.value == 1.0); - assert(conv1.error == BasicError.none); + assert(toSigned("1_069").isSome == false); + assert(toSigned("1_069").unwrapOr() == 0); + assert(toSigned("-1069").isSome == true); + assert(toSigned("-1069").unwrapOr() == -1069); + 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"; - auto conv2 = toDouble(text2); - assert(conv2.value == 1.0); - assert(conv2.error == BasicError.none); + assert(toDouble("1_069").isSome == false); + assert(toDouble("1_069").unwrapOr() == 0); + assert(toDouble(".1069").isSome == false); + 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); + + assert(toEnum!TestEnum("?").isSome == false); + assert(toEnum!TestEnum("?").unwrapOr() == TestEnum.one); + assert(toEnum!TestEnum("one").isSome == true); + assert(toEnum!TestEnum("one").unwrapOr() == TestEnum.one); + assert(toEnum!TestEnum("two").isSome == true); + assert(toEnum!TestEnum("two").unwrapOr() == TestEnum.two); - auto text3 = "1?"; - auto conv3 = toDouble(text3); - assert(conv3.value == 0.0); - assert(conv3.error == BasicError.invalid); + assert(toCStr("Hello").unwrapOr().length == "Hello".length); + assert(toCStr("Hello").unwrapOr().cStrToStr() == "Hello"); - assert(format("") == ""); - assert(format("{}") == "{}"); - 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. - // assert(format("{}", 0.00) == "0.00"); - // assert(format("{}", 0.50) == "0.50"); - // assert(format("{}", 1.00) == "1.00"); - // assert(format("{}", 1.50) == "1.50"); + // TODO: Write tests for `format` when it is done. } diff --git a/source/popka/core/errors.d b/source/popka/core/errors.d index c15c04b..d417764 100644 --- a/source/popka/core/errors.d +++ b/source/popka/core/errors.d @@ -5,6 +5,8 @@ module popka.core.errors; +import popka.core.traits; + @safe @nogc nothrow: enum BasicError : ubyte { @@ -18,6 +20,61 @@ enum BasicError : ubyte { } struct BasicResult(T) { - T value; - BasicError error; + static if (isNumberType!T) { + T value = 0; + } else { + T value; + } + 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); } diff --git a/source/popka/core/io.d b/source/popka/core/io.d index f5d4dcd..77823c9 100644 --- a/source/popka/core/io.d +++ b/source/popka/core/io.d @@ -39,7 +39,7 @@ void println(A...)(A args) { @trusted // TODO: Check the error values and let the user know what went wrong. void readText(IStr path, ref List!char text) { - auto f = .fopen(toCStr(path), "rb"); + auto f = .fopen(toCStr(path).unwrapOr(), "rb"); if (f == null) { text.clear(); return; @@ -76,7 +76,7 @@ List!char readText(IStr path) { @trusted // TODO: Check the error values and let the user know what went wrong. void writeText(IStr path, IStr text) { - auto f = .fopen(toCStr(path), "w"); + auto f = .fopen(toCStr(path).unwrapOr(), "w"); if (f == null) { return; } diff --git a/source/popka/core/traits.d b/source/popka/core/traits.d index ca25d63..a3d5b2d 100644 --- a/source/popka/core/traits.d +++ b/source/popka/core/traits.d @@ -61,6 +61,10 @@ bool isDoubleType(T)() { is(T == immutable(double)); } +bool isNumberType(T)() { + return isIntegerType!T || isDoubleType!T; +} + bool isCharType(T)() { return is(T == char) || is(T == const(char)) || diff --git a/source/popka/game/engine.d b/source/popka/game/engine.d index 67dae7c..779b537 100644 --- a/source/popka/game/engine.d +++ b/source/popka/game/engine.d @@ -212,7 +212,7 @@ struct Texture { void load(const(char)[] path) { free(); 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); } @@ -294,7 +294,7 @@ struct Font { void load(const(char)[] path, uint size, const(dchar)[] runes = []) { free(); 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); } @@ -343,7 +343,7 @@ struct Sound { void load(const(char)[] path) { free(); 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); } @@ -396,7 +396,7 @@ struct Music { void load(const(char)[] path) { free(); 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); } @@ -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.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.SetWindowMinSize(cast(int) (size.x * 0.25f), cast(int) (size.y * 0.25f)); ray.SetExitKey(ray.KEY_NULL);