Changed names and made generic types and errors.

This commit is contained in:
Kapendev 2024-08-01 20:00:10 +03:00
parent 0dc6afa4ea
commit aa5a780f2b
15 changed files with 931 additions and 752 deletions

22
TODO.md
View file

@ -1,5 +1,27 @@
# TODO
## NOW
Cleaning things in core.
* [ ] ascii
* [x] colors
* [x] containers
* [x] errors
* [ ] io
* [ ] math
* [ ] package
* [ ] stdc
* [ ] traits
* [ ] types
* [ ] unions
Was working on ascii.
Was about to rewrite the tests.
Might fix the doubleToStr function too.
## IDEAS
* doubleToStr
* GUI
* HashMap

View file

@ -1,13 +1,14 @@
// Copyright 2024 Alexandros F. G. Kapretsos
// SPDX-License-Identifier: MIT
/// The strutils module contains procedures
/// designed to assist with string manipulation tasks.
/// The `ascii` module provides functions designed to assist with ascii strings.
module popka.core.strutils;
module popka.core.ascii;
import popka.core.containers;
import popka.core.errors;
import popka.core.traits;
import popka.core.container;
import popka.core.types;
@safe @nogc nothrow:
@ -18,37 +19,22 @@ enum alphaChars = upperChars ~ lowerChars;
enum spaceChars = " \t\v\r\n\f";
version (Windows) {
enum pathSeparator = '\\';
enum otherPathSeparator = '/';
enum pathSep = '\\';
enum otherPathSep = '/';
} else {
enum pathSeparator = '/';
enum otherPathSeparator = '\\';
}
enum ToValueResultError : ubyte {
none,
invalid,
overflow,
enum pathSep = '/';
enum otherPathSep = '\\';
}
struct ToStrOptions {
ubyte floatPrecision = 2;
}
struct ToValueResult(T) {
T value;
ToValueResultError error;
}
bool isStrz(const(char)[] str) {
return str.length != 0 && str[$ - 1] == '\0';
ubyte doublePrecision = 2;
}
bool isDigit(char c) {
return c >= '0' && c <= '9';
}
bool isDigit(const(char)[] str) {
bool isDigit(IStr str) {
foreach (c; str) {
if (!isDigit(c)) return false;
}
@ -59,7 +45,7 @@ bool isUpper(char c) {
return c >= 'A' && c <= 'Z';
}
bool isUpper(const(char)[] str) {
bool isUpper(IStr str) {
foreach (c; str) {
if (!isUpper(c)) return false;
}
@ -70,7 +56,7 @@ bool isLower(char c) {
return c >= 'a' && c <= 'z';
}
bool isLower(const(char)[] str) {
bool isLower(IStr str) {
foreach (c; str) {
if (!isLower(c)) return false;
}
@ -81,7 +67,7 @@ bool isAlpha(char c) {
return isLower(c) || isUpper(c);
}
bool isAlpha(const(char)[] str) {
bool isAlpha(IStr str) {
foreach (c; str) {
if (!isAlpha(c)) return false;
}
@ -95,18 +81,22 @@ bool isSpace(char c) {
return false;
}
bool isSpace(const(char)[] str) {
bool isSpace(IStr str) {
foreach (c; str) {
if (!isSpace(c)) return false;
}
return true;
}
bool isCStr(IStr str) {
return str.length != 0 && str[$ - 1] == '\0';
}
char toUpper(char c) {
return isLower(c) ? cast(char) (c - 32) : c;
}
void toUpper(char[] str) {
void toUpper(Str str) {
foreach (ref c; str) {
c = toUpper(c);
}
@ -116,30 +106,30 @@ char toLower(char c) {
return isUpper(c) ? cast(char) (c + 32) : c;
}
void toLower(char[] str) {
void toLower(Str str) {
foreach (ref c; str) {
c = toLower(c);
}
}
@trusted
size_t length(const(char)* strz) {
size_t result = 0;
while (strz[result] != '\0') {
Sz length(ICStr str) {
Sz result = 0;
while (str[result] != '\0') {
result += 1;
}
return result;
}
bool equals(const(char)[] str, const(char)[] other) {
bool equals(IStr str, IStr other) {
return str == other;
}
bool equals(const(char)[] str, char other) {
bool equals(IStr str, char other) {
return equals(str, charToStr(other));
}
bool equalsIgnoreCase(const(char)[] str, const(char)[] other) {
bool equalsNoCase(IStr str, IStr other) {
if (str.length != other.length) return false;
foreach (i; 0 .. str.length) {
if (toUpper(str[i]) != toUpper(other[i])) return false;
@ -147,29 +137,29 @@ bool equalsIgnoreCase(const(char)[] str, const(char)[] other) {
return true;
}
bool equalsIgnoreCase(const(char)[] str, char other) {
return equalsIgnoreCase(str, charToStr(other));
bool equalsNoCase(IStr str, char other) {
return equalsNoCase(str, charToStr(other));
}
bool startsWith(const(char)[] str, const(char)[] start) {
bool startsWith(IStr str, IStr start) {
if (str.length < start.length) return false;
return str[0 .. start.length] == start;
}
bool startsWith(const(char)[] str, char start) {
bool startsWith(IStr str, char start) {
return startsWith(str, charToStr(start));
}
bool endsWith(const(char)[] str, const(char)[] end) {
bool endsWith(IStr str, IStr end) {
if (str.length < end.length) return false;
return str[$ - end.length .. $] == end;
}
bool endsWith(const(char)[] str, char end) {
bool endsWith(IStr str, char end) {
return endsWith(str, charToStr(end));
}
int count(const(char)[] str, const(char)[] item) {
int count(IStr str, IStr item) {
int result = 0;
if (str.length < item.length || item.length == 0) return result;
foreach (i; 0 .. str.length - item.length) {
@ -181,11 +171,11 @@ int count(const(char)[] str, const(char)[] item) {
return result;
}
int count(const(char)[] str, char item) {
int count(IStr str, char item) {
return count(str, charToStr(item));
}
int findStart(const(char)[] str, const(char)[] item) {
int findStart(IStr str, IStr item) {
if (str.length < item.length || item.length == 0) return -1;
foreach (i; 0 .. str.length - item.length + 1) {
if (str[i .. i + item.length] == item) return cast(int) i;
@ -193,11 +183,11 @@ int findStart(const(char)[] str, const(char)[] item) {
return -1;
}
int findStart(const(char)[] str, char item) {
int findStart(IStr str, char item) {
return findStart(str, charToStr(item));
}
int findEnd(const(char)[] str, const(char)[] item) {
int findEnd(IStr str, IStr item) {
if (str.length < item.length || item.length == 0) return -1;
foreach_reverse (i; 0 .. str.length - item.length + 1) {
if (str[i .. i + item.length] == item) return cast(int) i;
@ -205,20 +195,12 @@ int findEnd(const(char)[] str, const(char)[] item) {
return -1;
}
int findEnd(const(char)[] str, char item) {
int findEnd(IStr str, char item) {
return findEnd(str, charToStr(item));
}
const(char)[] advance(const(char)[] str, size_t amount) {
if (str.length < amount) {
return str[$ .. $];
} else {
return str[amount .. $];
}
}
const(char)[] trimStart(const(char)[] str) {
const(char)[] result = str;
IStr trimStart(IStr str) {
IStr result = str;
while (result.length > 0) {
if (isSpace(result[0])) result = result[1 .. $];
else break;
@ -226,8 +208,8 @@ const(char)[] trimStart(const(char)[] str) {
return result;
}
const(char)[] trimEnd(const(char)[] str) {
const(char)[] result = str;
IStr trimEnd(IStr str) {
IStr result = str;
while (result.length > 0) {
if (isSpace(result[$ - 1])) result = result[0 .. $ - 1];
else break;
@ -235,11 +217,19 @@ const(char)[] trimEnd(const(char)[] str) {
return result;
}
const(char)[] trim(const(char)[] str) {
IStr trim(IStr str) {
return str.trimStart().trimEnd();
}
const(char)[] removePrefix(const(char)[] str, const(char)[] prefix) {
IStr advance(IStr str, Sz amount) {
if (str.length < amount) {
return str[$ .. $];
} else {
return str[amount .. $];
}
}
IStr removePrefix(IStr str, IStr prefix) {
if (str.startsWith(prefix)) {
return str[prefix.length .. $];
} else {
@ -247,7 +237,7 @@ const(char)[] removePrefix(const(char)[] str, const(char)[] prefix) {
}
}
const(char)[] removeSuffix(const(char)[] str, const(char)[] suffix) {
IStr removeSuffix(IStr str, IStr suffix) {
if (str.endsWith(suffix)) {
return str[0 .. $ - suffix.length];
} else {
@ -255,19 +245,19 @@ const(char)[] removeSuffix(const(char)[] str, const(char)[] suffix) {
}
}
void copyStrChars(char[] str, const(char)[] source, size_t startIndex = 0) {
void copyStrChars(Str str, IStr source, Sz startIndex = 0) {
foreach (i, c; source) {
str[startIndex + i] = c;
}
}
void copyStr(ref char[] str, const(char)[] source, size_t startIndex = 0) {
void copyStr(ref Str str, IStr source, Sz startIndex = 0) {
copyStrChars(str, source, startIndex);
str = str[0 .. startIndex + source.length];
}
const(char)[] pathDir(const(char)[] path) {
auto end = findEnd(path, pathSeparator);
IStr pathDir(IStr path) {
auto end = findEnd(path, pathSep);
if (end == -1) {
return ".";
} else {
@ -276,7 +266,7 @@ const(char)[] pathDir(const(char)[] path) {
}
// TODO: Make it more safe? Look at how Python does it.
const(char)[] pathConcat(const(char)[][] args...) {
IStr pathConcat(IStr[] args...) {
static char[1024][4] buffers = void;
static byte bufferIndex = 0;
@ -292,7 +282,7 @@ const(char)[] pathConcat(const(char)[][] args...) {
result.copyStrChars(arg, length);
length += arg.length;
if (i != args.length - 1) {
result.copyStrChars(charToStr(pathSeparator), length);
result.copyStrChars(charToStr(pathSep), length);
length += 1;
}
}
@ -300,7 +290,7 @@ const(char)[] pathConcat(const(char)[][] args...) {
return result;
}
const(char)[] skipValue(ref inout(char)[] str, const(char)[] separator) {
IStr skipValue(ref inout(char)[] str, IStr separator) {
if (str.length < separator.length || separator.length == 0) {
str = str[$ .. $];
return "";
@ -320,19 +310,19 @@ const(char)[] skipValue(ref inout(char)[] str, const(char)[] separator) {
return line;
}
const(char)[] skipValue(ref inout(char)[] str, char separator) {
IStr skipValue(ref inout(char)[] str, char separator) {
return skipValue(str, charToStr(separator));
}
const(char)[] skipLine(ref inout(char)[] str) {
IStr skipLine(ref inout(char)[] str) {
return skipValue(str, '\n');
}
const(char)[] boolToStr(bool value) {
IStr boolToStr(bool value) {
return value ? "true" : "false";
}
const(char)[] charToStr(char value) {
IStr charToStr(char value) {
static char[1] buffer = void;
auto result = buffer[];
@ -341,7 +331,7 @@ const(char)[] charToStr(char value) {
return result;
}
const(char)[] unsignedToStr(ulong value) {
IStr unsignedToStr(ulong value) {
static char[64] buffer = void;
auto result = buffer[];
@ -359,7 +349,7 @@ const(char)[] unsignedToStr(ulong value) {
return result;
}
const(char)[] signedToStr(long value) {
IStr signedToStr(long value) {
static char[64] buffer = void;
auto result = buffer[];
@ -374,7 +364,8 @@ const(char)[] signedToStr(long value) {
return result;
}
const(char)[] doubleToStr(double value, uint precision = 2) {
// TODO: Fix N.00 bug and make it more simple.
IStr doubleToStr(double value, uint precision = 2) {
static char[64] buffer = void;
if (value != value) {
@ -428,7 +419,12 @@ const(char)[] doubleToStr(double value, uint precision = 2) {
return result;
}
const(char)[] enumToStr(T)(T value) {
@trusted
IStr cStrToStr(ICStr value) {
return value[0 .. value.length];
}
IStr enumToStr(T)(T value) {
static char[64] buffer = void;
auto result = buffer[];
@ -447,12 +443,7 @@ const(char)[] enumToStr(T)(T value) {
return result;
}
@trusted
const(char)[] strzToStr(const(char)* value) {
return value[0 .. value.length];
}
const(char)[] toStr(T)(T value, ToStrOptions options = ToStrOptions()) {
IStr toStr(T)(T value, ToStrOptions options = ToStrOptions()) {
static if (isCharType!T) {
return charToStr(value);
} else static if (isBoolType!T) {
@ -462,11 +453,11 @@ const(char)[] toStr(T)(T value, ToStrOptions options = ToStrOptions()) {
} else static if (isSignedType!T) {
return signedToStr(value);
} else static if (isDoubleType!T) {
return doubleToStr(value, options.floatPrecision);
return doubleToStr(value, options.doublePrecision);
} else static if (isStrType!T) {
return value;
} else static if (isStrzType!T) {
return strzToStr(value);
} else static if (isCStrType!T) {
return cStrToStr(value);
} else static if (isEnumType!T) {
return enumToStr(value);
} else static if (__traits(hasMember, T, "toStr")) {
@ -476,19 +467,19 @@ const(char)[] toStr(T)(T value, ToStrOptions options = ToStrOptions()) {
}
}
ToValueResult!bool toBool(const(char)[] str) {
auto result = ToValueResult!bool();
BasicResult!bool toBool(IStr str) {
auto result = BasicResult!bool();
if (str == "true") {
result.value = true;
} else if (str == "false") {
result.value = false;
} else {
result.error = ToValueResultError.invalid;
result.error = BasicError.invalid;
}
return result;
}
ulong toBoolWithNone(const(char)[] str) {
ulong toBoolWithNone(IStr str) {
auto conv = toBool(str);
if (conv.error) {
return false;
@ -497,17 +488,17 @@ ulong toBoolWithNone(const(char)[] str) {
}
}
ToValueResult!ulong toUnsigned(const(char)[] str) {
auto result = ToValueResult!ulong();
BasicResult!ulong toUnsigned(IStr str) {
auto result = BasicResult!ulong();
if (str.length == 0) {
result.error = ToValueResultError.invalid;
result.error = BasicError.invalid;
} else if (str.length >= 18) {
result.error = ToValueResultError.overflow;
result.error = BasicError.overflow;
} else {
ulong level = 1;
foreach_reverse (i, c; str) {
if (!isDigit(c)) {
result.error = ToValueResultError.invalid;
result.error = BasicError.invalid;
break;
}
auto digit = c - '0';
@ -518,17 +509,17 @@ ToValueResult!ulong toUnsigned(const(char)[] str) {
return result;
}
ToValueResult!ulong toUnsigned(char c) {
auto result = ToValueResult!ulong();
BasicResult!ulong toUnsigned(char c) {
auto result = BasicResult!ulong();
if (isDigit(c)) {
result.value = c - '0';
} else {
result.error = ToValueResultError.invalid;
result.error = BasicError.invalid;
}
return result;
}
ulong toUnsignedWithNone(const(char)[] str) {
ulong toUnsignedWithNone(IStr str) {
auto conv = toUnsigned(str);
if (conv.error) {
return 0;
@ -546,12 +537,12 @@ ulong toUnsignedWithNone(char c) {
}
}
ToValueResult!long toSigned(const(char)[] str) {
auto result = ToValueResult!long();
BasicResult!long toSigned(IStr str) {
auto result = BasicResult!long();
if (str.length == 0) {
result.error = ToValueResultError.invalid;
result.error = BasicError.invalid;
} else if (str.length >= 18) {
result.error = ToValueResultError.overflow;
result.error = BasicError.overflow;
} else {
if (str[0] == '-') {
auto conv = toUnsigned(str[1 .. $]);
@ -572,8 +563,8 @@ ToValueResult!long toSigned(const(char)[] str) {
return result;
}
ToValueResult!long toSigned(char c) {
auto result = ToValueResult!long();
BasicResult!long toSigned(char c) {
auto result = BasicResult!long();
auto conv = toUnsigned(c);
if (conv.error) {
result.error = conv.error;
@ -583,7 +574,7 @@ ToValueResult!long toSigned(char c) {
return result;
}
long toSignedWithNone(const(char)[] str) {
long toSignedWithNone(IStr str) {
auto conv = toSigned(str);
if (conv.error) {
return 0;
@ -601,8 +592,8 @@ long toSignedWithNone(char c) {
}
}
ToValueResult!double toDouble(const(char)[] str) {
auto result = ToValueResult!double();
BasicResult!double toDouble(IStr str) {
auto result = BasicResult!double();
result.value = 0.0;
auto hasDot = false;
foreach (i, c; str) {
@ -634,7 +625,7 @@ ToValueResult!double toDouble(const(char)[] str) {
return result;
}
double toDoubleWithNone(const(char)[] str) {
double toDoubleWithNone(IStr str) {
auto conv = toDouble(str);
if (conv.error) {
return 0.0;
@ -643,19 +634,19 @@ double toDoubleWithNone(const(char)[] str) {
}
}
ToValueResult!T toEnum(T)(const(char)[] str) {
auto result = ToValueResult!T();
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;");
}
default: result.error = ToValueResultError.invalid;
default: result.error = BasicError.invalid;
}
switchExit:
return result;
}
T toEnumWithNone(T)(const(char)[] str) {
T toEnumWithNone(T)(IStr str) {
auto conv = toEnum!T(str);
if (conv.error) {
return T.init;
@ -665,7 +656,7 @@ T toEnumWithNone(T)(const(char)[] str) {
}
@trusted
const(char)* toStrz(const(char)[] str) {
ICStr toCStr(IStr str) {
static char[1024] buffer = void;
auto result = buffer[];
@ -677,7 +668,7 @@ const(char)* toStrz(const(char)[] str) {
}
// TODO: Check if the args count is the same with the `{}` count.
const(char)[] format(A...)(const(char)[] formatStr, A args) {
IStr format(A...)(IStr formatStr, A args) {
static char[1024][8] buffers = void;
static byte bufferIndex = 0;
@ -716,7 +707,7 @@ const(char)[] format(A...)(const(char)[] formatStr, A args) {
}
// TODO: Check if the args count is the same with the `{}` count.
void formatl(A...)(ref List!char text, const(char)[] formatStr, A args) {
void formatl(A...)(ref LStr text, IStr formatStr, A args) {
text.clear();
auto formatStrIndex = 0;
@ -745,6 +736,7 @@ void formatl(A...)(ref List!char text, const(char)[] formatStr, A args) {
}
}
// TODO: Rewrite the test.
unittest {
assert(isDigit("0123456789?") == false);
assert(isDigit("0123456789") == true);
@ -755,7 +747,7 @@ unittest {
assert(isSpace(" \t\r\n ") == true);
char[128] buffer = void;
char[] str = [];
Str str = [];
str = buffer[];
str.copyStr("Hello");
@ -767,20 +759,15 @@ unittest {
str.copyStr("Hello");
assert(str.equals("HELLO") == false);
assert(str.equalsIgnoreCase("HELLO") == true);
assert(str.equalsNoCase("HELLO") == true);
assert(str.startsWith("HELL") == false);
assert(str.startsWithIgnoreCase("HELL") == true);
assert(str.endsWith("LO") == false);
assert(str.endsWithIgnoreCase("LO") == true);
str = buffer[];
str.copyStr("Hello hello world.");
assert(str.count("HELLO") == 0);
assert(str.countIgnoreCase("HELLO") == 2);
assert(str.findStart("HELLO") == -1);
assert(str.findStartIgnoreCase("HELLO") == 0);
assert(str.findEnd("HELLO") == -1);
assert(str.findEndIgnoreCase("HELLO") == 6);
assert(str.advance(0) == str);
assert(str.advance(1) == str[1 .. $]);
assert(str.advance(str.length) == "");
@ -811,17 +798,17 @@ unittest {
auto text1 = "1.0";
auto conv1 = toDouble(text1);
assert(conv1.value == 1.0);
assert(conv1.error == ToValueResultError.none);
assert(conv1.error == BasicError.none);
auto text2 = "1";
auto conv2 = toDouble(text2);
assert(conv2.value == 1.0);
assert(conv2.error == ToValueResultError.none);
assert(conv2.error == BasicError.none);
auto text3 = "1?";
auto conv3 = toDouble(text3);
assert(conv3.value == 0.0);
assert(conv3.error == ToValueResultError.invalid);
assert(conv3.error == BasicError.invalid);
assert(format("") == "");
assert(format("{}") == "{}");

View file

@ -1,87 +0,0 @@
// Copyright 2024 Alexandros F. G. Kapretsos
// SPDX-License-Identifier: MIT
/// The color module specializes in handling color-related operations.
module popka.core.color;
import popka.core.strutils;
@safe @nogc nothrow:
enum black = Color();
enum gray = toRGB(0xdfdfdf);
enum gray1 = toRGB(0x202020);
enum gray2 = toRGB(0x606060);
enum gray3 = toRGB(0x9f9f9f);
enum gray4 = toRGB(0xdfdfdf);
enum white = Color(255);
enum red = Color(255, 0, 0);
enum green = Color(0, 255, 0);
enum blue = Color(0, 0, 255);
enum yellow = Color(255, 255, 0);
enum magenta = Color(255, 0, 255);
enum cyan = Color(0, 255, 255);
enum blank = Color(0, 0, 0, 0);
struct Color {
ubyte r;
ubyte g;
ubyte b;
ubyte a = 255;
enum zero = Color(0, 0, 0, 0);
enum one = Color(1, 1, 1, 1);
@safe @nogc nothrow:
pragma(inline, true)
this(ubyte r, ubyte g, ubyte b, ubyte a = 255) {
this.r = r;
this.g = g;
this.b = b;
this.a = a;
}
pragma(inline, true)
this(ubyte r) {
this(r, r, r, 255);
}
pragma(inline, true)
this(ubyte[4] rgba) {
this(rgba[0], rgba[1], rgba[2], rgba[3]);
}
pragma(inline, true)
this(ubyte[3] rgb) {
this(rgb[0], rgb[1], rgb[2], 255);
}
Color alpha(ubyte value) {
return Color(r, g, b, value);
}
const(char)[] toStr() {
return "{} {} {} {}".format(r, g, b, a);
}
}
Color toRGB(uint rgb) {
return Color(
(rgb & 0xFF0000) >> 16,
(rgb & 0xFF00) >> 8,
(rgb & 0xFF),
);
}
Color toRGBA(uint rgba) {
return Color(
(rgba & 0xFF000000) >> 24,
(rgba & 0xFF0000) >> 16,
(rgba & 0xFF00) >> 8,
(rgba & 0xFF),
);
}

View file

@ -0,0 +1,92 @@
// Copyright 2024 Alexandros F. G. Kapretsos
// SPDX-License-Identifier: MIT
/// The `colors` module provides color-related types and functions.
module popka.core.colors;
import popka.core.ascii;
import popka.core.types;
@safe @nogc nothrow:
enum blank = Color();
enum black = Color(0);
enum white = Color(255);
enum red = Color(255, 0, 0);
enum green = Color(0, 255, 0);
enum blue = Color(0, 0, 255);
enum yellow = Color(255, 255, 0);
enum magenta = Color(255, 0, 255);
enum cyan = Color(0, 255, 255);
enum gray1 = toRgb(0x202020);
enum gray2 = toRgb(0x606060);
enum gray3 = toRgb(0x9f9f9f);
enum gray4 = toRgb(0xdfdfdf);
alias gray = gray1;
struct Color {
ubyte r;
ubyte g;
ubyte b;
ubyte a;
enum zero = Color(0, 0, 0, 0);
enum one = Color(1, 1, 1, 1);
@safe @nogc nothrow:
pragma(inline, true)
this(ubyte r, ubyte g, ubyte b, ubyte a = 255) {
this.r = r;
this.g = g;
this.b = b;
this.a = a;
}
pragma(inline, true)
this(ubyte r) {
this(r, r, r, 255);
}
Color alpha(ubyte a) {
return Color(r, g, b, a);
}
IStr toStr() {
return "({} {} {} {})".format(r, g, b, a);
}
}
Color toRgb(uint rgb) {
return Color(
(rgb & 0xFF0000) >> 16,
(rgb & 0xFF00) >> 8,
(rgb & 0xFF),
);
}
Color toRgba(uint rgba) {
return Color(
(rgba & 0xFF000000) >> 24,
(rgba & 0xFF0000) >> 16,
(rgba & 0xFF00) >> 8,
(rgba & 0xFF),
);
}
unittest {
assert(toRgb(0xff0000) == red);
assert(toRgb(0x00ff00) == green);
assert(toRgb(0x0000ff) == blue);
assert(toRgba(0xff0000ff) == red);
assert(toRgba(0x00ff00ff) == green);
assert(toRgba(0x0000ffff) == blue);
assert(black.toStr() == "(0 0 0 255)");
assert(black.alpha(69).toStr() == "(0 0 0 69)");
}

View file

@ -1,425 +0,0 @@
// Copyright 2024 Alexandros F. G. Kapretsos
// SPDX-License-Identifier: MIT
/// The container module serves as a hub for various data structures.
module popka.core.container;
import popka.core.stdc;
@safe @nogc nothrow:
struct List(T) {
T[] items;
size_t capacity;
@safe @nogc nothrow:
this(size_t length) {
foreach (i; 0 .. length) {
append(T.init);
}
}
this(const(T)[] args) {
foreach (arg; args) {
append(arg);
}
}
this(List!T list) {
foreach (item; list.items) {
append(item);
}
}
this(FlagList!T list) {
foreach (item; list.items) {
append(item);
}
}
T[] opSlice(size_t dim)(size_t i, size_t j) {
return items[i .. j];
}
T[] opIndex() {
return items[];
}
// D calls this when the slice op is used and it works like this: opIndex(opSlice(i, j))
T[] opIndex(T[] slice) {
return slice;
}
// Returning a ref will let people take a pointer to that value.
ref T opIndex(size_t i) {
return items[i];
}
@trusted
void opIndexAssign(const(T) rhs, size_t i) {
items[i] = cast(T) rhs;
}
@trusted
void opIndexOpAssign(string op)(const(T) rhs, size_t i) {
mixin("items[i] " ~ op ~ "= cast(T) rhs;");
}
size_t opDollar(size_t dim)() {
return items.length;
}
@trusted
T* ptr() {
return items.ptr;
}
size_t length() {
return items.length;
}
@trusted
void append(const(T)[] args...) {
foreach (arg; args) {
size_t newLength = length + 1;
if (newLength > capacity) {
capacity = findListCapacity(newLength);
items = (cast(T*) realloc(items.ptr, capacity * T.sizeof))[0 .. newLength];
} else {
items = items.ptr[0 .. newLength];
}
items[$ - 1] = cast(T) arg;
}
}
void remove(size_t i) {
items[i] = items[$ - 1];
items = items[0 .. $ - 1];
}
T pop() {
if (length > 0) {
T temp = items[$ - 1];
remove(length - 1);
return temp;
} else {
return T.init;
}
}
void resize(size_t length) {
if (length <= this.length) {
items = items[0 .. length];
} else {
foreach (i; 0 .. length - this.length) {
this.append(T.init);
}
}
}
@trusted
void reserve(size_t capacity) {
auto targetCapacity = findListCapacity(capacity);
if (targetCapacity > this.capacity) {
this.capacity = targetCapacity;
items = (cast(T*) realloc(items.ptr, this.capacity * T.sizeof))[0 .. length];
}
}
@trusted
void fill(const(T) value) {
foreach (ref item; items) {
item = cast(T) value;
}
}
void clear() {
items = items[0 .. 0];
}
@trusted
void free() {
.free(items.ptr);
items = [];
capacity = 0;
}
}
struct FlagList(T) {
List!T data;
List!bool flags;
size_t hotIndex;
size_t openIndex;
size_t length;
@safe @nogc nothrow:
this(size_t length) {
foreach (i; 0 .. length) {
append(T.init);
}
}
this(const(T)[] args...) {
foreach (arg; args) {
append(arg);
}
}
this(List!T list) {
foreach (item; list.items) {
append(item);
}
}
this(FlagList!T list) {
data.resize(list.data.length);
flags.resize(list.flags.length);
foreach (i; 0 .. flags.length) {
data[i] = list.data[i];
flags[i] = list.flags[i];
}
hotIndex = list.hotIndex;
openIndex = list.openIndex;
length = list.length;
}
ref T opIndex(size_t i) {
if (!flags[i]) {
assert(0, "ID doesn't exist.");
}
return data[i];
}
@trusted
void opIndexAssign(const(T) rhs, size_t i) {
if (!flags[i]) {
assert(0, "ID doesn't exist.");
}
data[i] = cast(T) rhs;
}
@trusted
void opIndexOpAssign(string op)(const(T) rhs, size_t i) {
if (!flags[i]) {
assert(0, "ID doesn't exist.");
}
mixin("data[i] ", op, "= cast(T) rhs;");
}
bool hasID(size_t id) {
return id < flags.length && flags[id];
}
@trusted
void append(const(T)[] args...) {
foreach (arg; args) {
if (openIndex == flags.length) {
data.append(arg);
flags.append(true);
hotIndex = openIndex;
openIndex = flags.length;
length += 1;
} else {
auto isFull = true;
foreach (i; openIndex .. flags.length) {
if (!flags[i]) {
data[i] = arg;
flags[i] = true;
hotIndex = i;
openIndex = i;
isFull = false;
break;
}
}
if (isFull) {
data.append(arg);
flags.append(true);
hotIndex = openIndex;
openIndex = flags.length;
}
length += 1;
}
}
}
void remove(size_t i) {
if (!flags[i]) {
assert(0, "ID doesn't exist.");
}
flags[i] = false;
hotIndex = i;
if (i < openIndex) {
openIndex = i;
}
length -= 1;
}
void clear() {
data.clear();
flags.clear();
hotIndex = 0;
openIndex = 0;
length = 0;
}
void free() {
data.free();
flags.free();
hotIndex = 0;
openIndex = 0;
length = 0;
}
auto ids() {
struct Range {
bool[] flags;
size_t id;
bool empty() {
return id == flags.length;
}
size_t front() {
return id;
}
void popFront() {
id += 1;
while (id != flags.length && !flags[id]) {
id += 1;
}
}
}
size_t id = 0;
while (id < flags.length && !flags[id]) {
id += 1;
}
return Range(flags.items, id);
}
auto items() {
struct Range {
T[] data;
bool[] flags;
size_t id;
bool empty() {
return id == flags.length;
}
ref T front() {
return data[id];
}
void popFront() {
id += 1;
while (id != flags.length && !flags[id]) {
id += 1;
}
}
}
size_t id = 0;
while (id < flags.length && !flags[id]) {
id += 1;
}
return Range(data.items, flags.items, id);
}
}
size_t findListCapacity(size_t length) {
enum defaultCapacity = 64;
size_t result = defaultCapacity;
while (result < length) {
result *= 2;
}
return result;
}
struct Grid(T) {
List!T cells;
size_t rowCount;
size_t colCount;
@safe @nogc nothrow:
this(size_t rowCount, size_t colCount) {
resize(rowCount, colCount);
}
// TODO: Learn how to slice only rows, ...
T[] opSlice(size_t dim)(size_t i, size_t j) {
return items[i .. j];
}
T[] opIndex() {
return cells[];
}
T[] opIndex(T[] slice) {
return slice;
}
ref T opIndex(size_t i) {
return cells[i];
}
ref T opIndex(size_t row, size_t col) {
return cells[colCount * row + col];
}
void opIndexAssign(T rhs, size_t i) {
cells[i] = rhs;
}
void opIndexAssign(T rhs, size_t row, size_t col) {
cells[colCount * row + col] = rhs;
}
void opIndexOpAssign(string op)(T rhs, size_t i) {
mixin("cells[i] " ~ op ~ "= rhs;");
}
void opIndexOpAssign(string op)(T rhs, size_t row, size_t col) {
mixin("cells[colCount * row + col] " ~ op ~ "= rhs;");
}
size_t opDollar(size_t dim)() {
static if (dim == 0) {
return rowCount;
} else {
return colCount;
}
}
size_t length() {
return cells.length;
}
void resize(size_t rowCount, size_t colCount) {
this.cells.resize(rowCount * colCount);
this.rowCount = rowCount;
this.colCount = colCount;
}
void fill(T value) {
cells.fill(value);
}
void clear() {
cells.clear();
rowCount = 0;
colCount = 0;
}
void free() {
cells.free();
rowCount = 0;
colCount = 0;
}
}

View file

@ -0,0 +1,597 @@
// Copyright 2024 Alexandros F. G. Kapretsos
// SPDX-License-Identifier: MIT
/// The `containers` module provides various data structures.
module popka.core.containers;
import popka.core.ascii;
import popka.core.stdc;
import popka.core.types;
@safe @nogc nothrow:
enum defaultListCapacity = 64;
alias LStr = List!char;
alias LStr16 = List!wchar;
alias LStr32 = List!dchar;
struct List(T) {
T[] items;
Sz capacity;
@safe @nogc nothrow:
this(const(T)[] args...) {
foreach (arg; args) {
append(arg);
}
}
this(List!T list) {
foreach (item; list.items) {
append(item);
}
}
this(FlagList!T list) {
foreach (item; list.items) {
append(item);
}
}
T[] opSlice(Sz dimension)(Sz i, Sz j) {
return items[i .. j];
}
T[] opIndex() {
return items[];
}
// D calls this function when the slice operator is used. Does something but I do not remember what lol.
T[] opIndex(T[] slice) {
return slice;
}
// D will let you get the pointer of the array item if you return a ref value.
ref T opIndex(Sz i) {
return items[i];
}
@trusted
void opIndexAssign(const(T) rhs, Sz i) {
items[i] = cast(T) rhs;
}
@trusted
void opIndexOpAssign(IStr op)(const(T) rhs, Sz i) {
mixin("items[i] " ~ op ~ "= cast(T) rhs;");
}
bool opEquals(List!T rhs) {
return items == rhs.items;
}
@trusted
bool opEquals(const(T)[] rhs) {
return items == cast(T[]) rhs;
}
Sz opDollar(Sz dimension)() {
return items.length;
}
Sz length() {
return items.length;
}
@trusted
T* ptr() {
return items.ptr;
}
@trusted
void append(const(T)[] args...) {
foreach (arg; args) {
Sz newLength = length + 1;
if (newLength > capacity) {
capacity = findListCapacity(newLength);
items = (cast(T*) realloc(items.ptr, capacity * T.sizeof))[0 .. newLength];
} else {
items = items.ptr[0 .. newLength];
}
items[$ - 1] = cast(T) arg;
}
}
void remove(Sz i) {
items[i] = items[$ - 1];
items = items[0 .. $ - 1];
}
T pop() {
if (length > 0) {
T temp = items[$ - 1];
remove(length - 1);
return temp;
} else {
return T.init;
}
}
void resize(Sz length) {
if (length <= this.length) {
items = items[0 .. length];
} else {
foreach (i; 0 .. length - this.length) {
this.append(T.init);
}
}
}
@trusted
void reserve(Sz capacity) {
auto targetCapacity = findListCapacity(capacity);
if (targetCapacity > this.capacity) {
this.capacity = targetCapacity;
items = (cast(T*) realloc(items.ptr, this.capacity * T.sizeof))[0 .. length];
}
}
@trusted
void fill(const(T) value) {
foreach (ref item; items) {
item = cast(T) value;
}
}
void clear() {
items = items[0 .. 0];
}
@trusted
void free() {
.free(items.ptr);
items = [];
capacity = 0;
}
}
struct FlagList(T) {
List!T data;
List!bool flags;
Sz hotIndex;
Sz openIndex;
Sz length;
@safe @nogc nothrow:
this(const(T)[] args...) {
foreach (arg; args) {
append(arg);
}
}
this(List!T list) {
foreach (item; list.items) {
append(item);
}
}
this(FlagList!T list) {
data.resize(list.data.length);
flags.resize(list.flags.length);
foreach (i; 0 .. flags.length) {
data[i] = list.data[i];
flags[i] = list.flags[i];
}
hotIndex = list.hotIndex;
openIndex = list.openIndex;
length = list.length;
}
ref T opIndex(Sz i) {
if (!has(i)) {
assert(0, "ID `[{}]` does not exist.".format(i));
}
return data[i];
}
@trusted
void opIndexAssign(const(T) rhs, Sz i) {
if (!has(i)) {
assert(0, "ID `[{}]` does not exist.".format(i));
}
data[i] = cast(T) rhs;
}
@trusted
void opIndexOpAssign(IStr op)(const(T) rhs, Sz i) {
if (!has(i)) {
assert(0, "ID `[{}]` does not exist.".format(i));
}
mixin("data[i] ", op, "= cast(T) rhs;");
}
Sz capacity() {
return data.capacity;
}
@trusted
T* ptr() {
return data.ptr;
}
bool has(Sz id) {
return id < flags.length && flags[id];
}
@trusted
void append(const(T)[] args...) {
foreach (arg; args) {
if (openIndex == flags.length) {
data.append(arg);
flags.append(true);
hotIndex = openIndex;
openIndex = flags.length;
length += 1;
} else {
auto isFull = true;
foreach (i; openIndex .. flags.length) {
if (!flags[i]) {
data[i] = arg;
flags[i] = true;
hotIndex = i;
openIndex = i;
isFull = false;
break;
}
}
if (isFull) {
data.append(arg);
flags.append(true);
hotIndex = flags.length - 1;
openIndex = flags.length;
}
length += 1;
}
}
}
void remove(Sz i) {
if (!has(i)) {
assert(0, "ID `[{}]` does not exist.".format(i));
}
flags[i] = false;
hotIndex = i;
if (i < openIndex) {
openIndex = i;
}
length -= 1;
}
@trusted
void fill(const(T) value) {
foreach (ref item; items) {
item = cast(T) value;
}
}
void clear() {
data.clear();
flags.clear();
hotIndex = 0;
openIndex = 0;
length = 0;
}
void free() {
data.free();
flags.free();
hotIndex = 0;
openIndex = 0;
length = 0;
}
auto ids() {
struct Range {
bool[] flags;
Sz id;
bool empty() {
return id == flags.length;
}
Sz front() {
return id;
}
void popFront() {
id += 1;
while (id != flags.length && !flags[id]) {
id += 1;
}
}
}
Sz id = 0;
while (id < flags.length && !flags[id]) {
id += 1;
}
return Range(flags.items, id);
}
auto items() {
struct Range {
T[] data;
bool[] flags;
Sz id;
bool empty() {
return id == flags.length;
}
ref T front() {
return data[id];
}
void popFront() {
id += 1;
while (id != flags.length && !flags[id]) {
id += 1;
}
}
}
Sz id = 0;
while (id < flags.length && !flags[id]) {
id += 1;
}
return Range(data.items, flags.items, id);
}
}
struct Grid(T) {
List!T tiles;
Sz rowCount;
Sz colCount;
@safe @nogc nothrow:
this(Sz rowCount, Sz colCount) {
resize(rowCount, colCount);
}
T[] opIndex() {
return tiles[];
}
ref T opIndex(Sz row, Sz col) {
if (!has(row, col)) {
assert(0, "Tile `[{}, {}]` does not exist.".format(row, col));
}
return tiles[colCount * row + col];
}
void opIndexAssign(T rhs, Sz row, Sz col) {
if (!has(row, col)) {
assert(0, "Tile `[{}, {}]` does not exist.".format(row, col));
}
tiles[colCount * row + col] = rhs;
}
void opIndexOpAssign(IStr op)(T rhs, Sz row, Sz col) {
if (!has(row, col)) {
assert(0, "Tile `[{}, {}]` does not exist.".format(row, col));
}
mixin("tiles[colCount * row + col] " ~ op ~ "= rhs;");
}
Sz opDollar(Sz dimension)() {
static if (dimension == 0) {
return rowCount;
} else static if (dimension == 1) {
return colCount;
} else {
assert(0, "WTF!");
}
}
Sz length() {
return tiles.length;
}
Sz capacity() {
return tiles.capacity;
}
@trusted
T* ptr() {
return tiles.ptr;
}
bool has(Sz row, Sz col) {
return row < rowCount && col < colCount;
}
void resize(Sz rowCount, Sz colCount) {
this.tiles.resize(rowCount * colCount);
this.rowCount = rowCount;
this.colCount = colCount;
}
void fill(T value) {
tiles.fill(value);
}
void clear() {
tiles.clear();
rowCount = 0;
colCount = 0;
}
void free() {
tiles.free();
rowCount = 0;
colCount = 0;
}
}
Sz findListCapacity(Sz length) {
Sz result = defaultListCapacity;
while (result < length) {
result *= 2;
}
return result;
}
// Function test.
unittest {
assert(findListCapacity(0) == defaultListCapacity);
assert(findListCapacity(defaultListCapacity) == defaultListCapacity);
assert(findListCapacity(defaultListCapacity + 1) == defaultListCapacity * 2);
}
// List test.
unittest {
LStr text;
text = LStr();
assert(text.length == 0);
assert(text.capacity == 0);
assert(text.ptr == null);
text = LStr("abc");
assert(text.length == 3);
assert(text.capacity == defaultListCapacity);
assert(text.ptr != null);
text.free();
assert(text.length == 0);
assert(text.capacity == 0);
assert(text.ptr == null);
text = LStr("Hello world!");
assert(text.length == "Hello world!".length);
assert(text.capacity == defaultListCapacity);
assert(text.ptr != null);
assert(text[] == text.items);
assert(text[0] == text.items[0]);
assert(text[0 .. $] == text.items[0 .. $]);
assert(text[0] == 'H');
text[0] = 'h';
assert(text[0] == 'h');
text.append("!!");
assert(text == "hello world!!!");
assert(text.pop() == '!');
assert(text.pop() == '!');
assert(text == "hello world!");
text.resize(0);
assert(text == "");
assert(text.length == 0);
assert(text.capacity == defaultListCapacity);
assert(text.pop() == char.init);
text.resize(1);
assert(text[0] == char.init);
assert(text.length == 1);
assert(text.capacity == defaultListCapacity);
text.clear();
text.reserve(5);
assert(text.length == 0);
assert(text.capacity == defaultListCapacity);
text.reserve(defaultListCapacity + 1);
assert(text.length == 0);
assert(text.capacity == defaultListCapacity * 2);
text.free();
}
// FlagList test.
unittest {
FlagList!int numbers;
numbers = FlagList!int();
assert(numbers.length == 0);
assert(numbers.capacity == 0);
assert(numbers.ptr == null);
assert(numbers.hotIndex == 0);
assert(numbers.openIndex == 0);
numbers = FlagList!int(1, 2, 3);
assert(numbers.length == 3);
assert(numbers.capacity == defaultListCapacity);
assert(numbers.ptr != null);
assert(numbers.hotIndex == 2);
assert(numbers.openIndex == 3);
assert(numbers[0] == 1);
assert(numbers[1] == 2);
assert(numbers[2] == 3);
assert(numbers.has(0) == true);
assert(numbers.has(1) == true);
assert(numbers.has(2) == true);
assert(numbers.has(3) == false);
numbers.remove(1);
assert(numbers.has(0) == true);
assert(numbers.has(1) == false);
assert(numbers.has(2) == true);
assert(numbers.has(3) == false);
assert(numbers.hotIndex == 1);
assert(numbers.openIndex == 1);
numbers.append(1);
assert(numbers.has(0) == true);
assert(numbers.has(1) == true);
assert(numbers.has(2) == true);
assert(numbers.has(3) == false);
assert(numbers.hotIndex == 1);
assert(numbers.openIndex == 1);
numbers.append(4);
assert(numbers.has(0) == true);
assert(numbers.has(1) == true);
assert(numbers.has(2) == true);
assert(numbers.has(3) == true);
assert(numbers.hotIndex == 3);
assert(numbers.openIndex == 4);
numbers.clear();
numbers.append(1);
assert(numbers.has(0) == true);
assert(numbers.has(1) == false);
assert(numbers.has(2) == false);
assert(numbers.has(3) == false);
assert(numbers.hotIndex == 0);
assert(numbers.openIndex == 1);
numbers.free();
assert(numbers.length == 0);
assert(numbers.capacity == 0);
assert(numbers.ptr == null);
assert(numbers.hotIndex == 0);
assert(numbers.openIndex == 0);
}
// Grid test
unittest {
Grid!int numbers;
numbers = Grid!int();
assert(numbers.length == 0);
assert(numbers.capacity == 0);
assert(numbers.ptr == null);
assert(numbers.rowCount == 0);
assert(numbers.colCount == 0);
numbers = Grid!int(8, 8);
assert(numbers.length == 8 * 8);
assert(numbers.capacity == defaultListCapacity);
assert(numbers.ptr != null);
assert(numbers.rowCount == 8);
assert(numbers.colCount == 8);
assert(numbers[0, 0] == 0);
assert(numbers[7, 7] == 0);
assert(numbers.has(7, 8) == false);
assert(numbers.has(8, 7) == false);
assert(numbers.has(8, 8) == false);
numbers.free();
assert(numbers.length == 0);
assert(numbers.capacity == 0);
assert(numbers.ptr == null);
assert(numbers.rowCount == 0);
assert(numbers.colCount == 0);
}

View file

@ -0,0 +1,23 @@
// Copyright 2024 Alexandros F. G. Kapretsos
// SPDX-License-Identifier: MIT
/// The `errors` module provides a set of error codes and data structures for error handling.
module popka.core.errors;
@safe @nogc nothrow:
enum BasicError : ubyte {
none,
some,
invalid,
overflow,
notFound,
cantRead,
cantWrite,
}
struct BasicResult(T) {
T value;
BasicError error;
}

View file

@ -1,26 +1,26 @@
// Copyright 2024 Alexandros F. G. Kapretsos
// SPDX-License-Identifier: MIT
/// The io module facilitates input and output operations,
/// offering functionalities such as file reading and writing.
/// The `io` module provides input and output functions such as file reading.
module popka.core.io;
import popka.core.container;
import popka.core.containers;
import popka.core.stdc;
import popka.core.strutils;
import popka.core.ascii;
import popka.core.traits;
import popka.core.types;
@safe @nogc nothrow:
@trusted
void printf(A...)(const(char)[] str, A args) {
.fputs(format("{}\0", format(str, args)).ptr, .stdout);
void printf(A...)(IStr text, A args) {
.fputs(format("{}\0", format(text, args)).ptr, .stdout);
}
@trusted
void printfln(A...)(const(char)[] str, A args) {
.fputs(format("{}\n\0", format(str, args)).ptr, .stdout);
void printfln(A...)(IStr text, A args) {
.fputs(format("{}\n\0", format(text, args)).ptr, .stdout);
}
void print(A...)(A args) {
@ -37,8 +37,9 @@ void println(A...)(A args) {
}
@trusted
void readText(const(char)[] path, ref List!char text) {
auto f = .fopen(toStrz(path), "rb");
// 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");
if (f == null) {
text.clear();
return;
@ -66,83 +67,19 @@ void readText(const(char)[] path, ref List!char text) {
.fclose(f);
}
List!char readText(const(char)[] path) {
List!char readText(IStr path) {
List!char result;
readText(path, result);
return result;
}
@trusted
void writeText(const(char)[] path, const(char)[] text) {
auto f = .fopen(toStrz(path), "w");
// 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");
if (f == null) {
return;
}
// NOTE: Maybe check error value.
.fwrite(text.ptr, char.sizeof, text.length, f);
.fclose(f);
}
// TODO: See what works.
// NOTE: Testing stuff to see how to make it easy to use.
// Does not do any error checking for now and works only with booleans, integers and floats.
void readConfig(A...)(const(char)[] path, ref A args) {
auto file = readText(path);
auto group = cast(const(char)[]) "";
auto lineNumber = 0;
auto view = file.items;
while (view.length != 0) {
auto line = skipLine(view).trim();
lineNumber += 1;
if (line.length == 0) {
continue;
}
if (line[0] == '[' && line[$ - 1] == ']') {
group = line[1 .. $ - 1];
continue;
} else if (line[0] == '#' || line[0] == ';') {
continue;
}
static foreach (arg; args) {
if (group == typeof(arg).stringof) {
auto separatorIndex = line.findStart('=');
auto key = line[0 .. separatorIndex].trimEnd();
auto value = line[separatorIndex + 1 .. $].trimStart();
static foreach (member; arg.tupleof) {
if (key == member.stringof) {
auto target = typeof(member).init;
static if (isIntegerType!(typeof(member))) {
auto conv = toSigned(value);
if (conv.error) {
println("Line ", lineNumber, ": Can not parse value.");
} else {
target = cast(typeof(member)) conv.value;
}
mixin("arg.", member.stringof, "= target;");
} else static if (isDoubleType!(typeof(member))) {
auto conv = toDouble(value);
if (conv.error) {
println("Line ", lineNumber, ": Can not parse value.");
} else {
target = cast(typeof(member)) conv.value;
}
mixin("arg.", member.stringof, "= target;");
} else static if (isBoolType!(typeof(member))) {
auto conv = toBool(value);
if (conv.error) {
println("Line ", lineNumber, ": Can not parse value.");
} else {
target = cast(typeof(member)) conv.value;
}
mixin("arg.", member.stringof, "= target;");
}
}
}
goto loopExit;
}
}
loopExit:
}
file.free();
}

View file

@ -7,7 +7,7 @@
module popka.core.math;
import popka.core.stdc;
import popka.core.strutils;
import popka.core.ascii;
@safe @nogc nothrow:

View file

@ -3,10 +3,12 @@
module popka.core;
public import popka.core.color;
public import popka.core.container;
public import popka.core.ascii;
public import popka.core.colors;
public import popka.core.containers;
public import popka.core.errors;
public import popka.core.io;
public import popka.core.math;
public import popka.core.strutils;
public import popka.core.sumtype;
public import popka.core.traits;
public import popka.core.types;
public import popka.core.unions;

View file

@ -8,6 +8,8 @@ module popka.core.traits;
@safe @nogc nothrow:
import popka.core.types;
alias AliasArgs(A...) = A;
bool isBoolType(T)() {
@ -73,14 +75,14 @@ bool isPrimaryType(T)() {
isCharType!T;
}
bool isPtrType(T)() {
return is(T : const(void)*);
}
bool isArrayType(T)() {
return is(T : const(A)[N], A, N);
}
bool isPtrType(T)() {
return is(T : const(void)*);
}
bool isSliceType(T)() {
return is(T : const(A)[], A);
}
@ -94,11 +96,11 @@ bool isStructType(T)() {
}
bool isStrType(T)() {
return is(T : const(char)[]);
return is(T : IStr);
}
bool isStrzType(T)() {
return is(T : const(char)*);
bool isCStrType(T)() {
return is(T : ICStr);
}
int findInAliasArgs(T, A...)() {
@ -115,7 +117,7 @@ bool isInAliasArgs(T, A...)() {
return findInAliasArgs!(T, A) != -1;
}
const(char)[] toCleanNumber(alias i)() {
IStr toCleanNumber(alias i)() {
enum str = i.stringof;
static if (str.length >= 3 && (((str[$ - 1] == 'L' || str[$ - 1] == 'l') && (str[$ - 2] == 'U' || str[$ - 2] == 'u')) || ((str[$ - 1] == 'U' || str[$ - 1] == 'u') && (str[$ - 2] == 'L' || str[$ - 2] == 'l')))) {
return str[0 .. $ - 2];

28
source/popka/core/types.d Normal file
View file

@ -0,0 +1,28 @@
// Copyright 2024 Alexandros F. G. Kapretsos
// SPDX-License-Identifier: MIT
/// The `types` module provides a collection of type definitions.
module popka.core.types;
// Popka utilizes two groups of attributes:
// 1. @safe @nogc nothrow
// 2. @trusted @nogc nothrow
@safe @nogc nothrow:
alias Sz = size_t;
alias Str = char[];
alias Str16 = wchar[];
alias Str32 = dchar[];
alias IStr = const(char)[];
alias IStr16 = const(wchar)[];
alias IStr32 = const(dchar)[];
alias CStr = char*;
alias CStr16 = wchar*;
alias CStr32 = dchar*;
alias ICStr = const(char)*;
alias ICStr16 = const(wchar)*;
alias ICStr32 = const(dchar)*;

View file

@ -1,9 +1,9 @@
// Copyright 2024 Alexandros F. G. Kapretsos
// SPDX-License-Identifier: MIT
/// The sumtype module defines a data structure that can hold one of several possible types.
/// The `unions` module defines a data structure that can hold one of several possible types.
module popka.core.sumtype;
module popka.core.unions;
import popka.core.traits;
@ -163,6 +163,7 @@ bool isSumType(T)() {
return is(T : SumType!A, A...);
}
// TODO: WTF?
int checkCommonBase(T)() {
static assert(isSumType!T, "Type `" ~ T.stringof ~ "` is not a sum type.");
@ -201,10 +202,10 @@ unittest {
struct MyType {
mixin addBase!int;
}
alias Entity1 = SumType!(int, MyType);
assert(hasCommonBase!Entity1 == true);
alias Entity2 = SumType!(MyType, int);
assert(hasCommonBase!Entity2 == false);
// alias Entity1 = SumType!(int, MyType);
// assert(hasCommonBase!Entity1 == true);
// alias Entity2 = SumType!(MyType, int);
// assert(hasCommonBase!Entity2 == false);
Maybe!int result;
result = Maybe!int();

View file

@ -6,9 +6,9 @@
module popka.game.dialogue;
import popka.core.container;
import popka.core.containers;
import popka.core.io;
import popka.core.strutils;
import popka.core.ascii;
import popka.game.engine;
@safe @nogc nothrow:

View file

@ -6,18 +6,18 @@
module popka.game.engine;
import ray = popka.vendor.ray;
import popka.core.color;
import popka.core.container;
import popka.core.colors;
import popka.core.containers;
import popka.core.io;
import popka.core.math;
import popka.core.strutils;
import popka.core.ascii;
@trusted @nogc nothrow:
EngineState engineState;
enum defaultFPS = 60;
enum defaultBackgroundColor = toRGB(0x2A363A);
enum defaultBackgroundColor = toRgb(0x2A363A);
enum defaultTempLoadTextCapacity = 8192;
enum toggleFullscreenWaitTime = 0.135f;
@ -212,7 +212,7 @@ struct Texture {
void load(const(char)[] path) {
free();
if (path.length != 0) {
data = ray.LoadTexture(path.toAssetsPath.toStrz);
data = ray.LoadTexture(path.toAssetsPath.toCStr);
}
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.toStrz, size, cast(int*) runes.ptr, cast(int) runes.length);
data = ray.LoadFontEx(path.toAssetsPath.toCStr, 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.toStrz);
data = ray.LoadSound(path.toAssetsPath.toCStr);
}
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.toStrz);
data = ray.LoadMusicStream(path.toAssetsPath.toCStr);
}
if (isEmpty) printfln("Error: The file `{}` does not exist.", path);
}
@ -623,11 +623,11 @@ const(char)[] toAssetsPath(const(char)[] path) {
auto result = buffer[];
result.copyStrChars(assetsPath);
result[assetsPath.length] = pathSeparator;
result[assetsPath.length] = pathSep;
foreach (i, c; path) {
auto ii = i + assetsPath.length + 1;
if (c == otherPathSeparator) {
result[ii] = pathSeparator;
if (c == otherPathSep) {
result[ii] = pathSep;
} else {
result[ii] = c;
}
@ -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, toStrz(title));
ray.InitWindow(cast(int) size.x, cast(int) size.y, toCStr(title));
ray.InitAudioDevice();
ray.SetWindowMinSize(cast(int) (size.x * 0.25f), cast(int) (size.y * 0.25f));
ray.SetExitKey(ray.KEY_NULL);