mirror of
https://github.com/Kapendev/parin.git
synced 2025-04-26 21:19:56 +03:00
Changed names and made generic types and errors.
This commit is contained in:
parent
0dc6afa4ea
commit
aa5a780f2b
15 changed files with 931 additions and 752 deletions
22
TODO.md
22
TODO.md
|
@ -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
|
||||
|
|
|
@ -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("{}") == "{}");
|
|
@ -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),
|
||||
);
|
||||
}
|
92
source/popka/core/colors.d
Normal file
92
source/popka/core/colors.d
Normal 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)");
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
597
source/popka/core/containers.d
Normal file
597
source/popka/core/containers.d
Normal 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);
|
||||
}
|
23
source/popka/core/errors.d
Normal file
23
source/popka/core/errors.d
Normal 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;
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
module popka.core.math;
|
||||
|
||||
import popka.core.stdc;
|
||||
import popka.core.strutils;
|
||||
import popka.core.ascii;
|
||||
|
||||
@safe @nogc nothrow:
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
28
source/popka/core/types.d
Normal 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)*;
|
|
@ -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();
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue