fix pretty much everything, read description

this is a big (or dare I say, huge) commit that pretty much fixes every
problem I've had so far with the basic parsing

I still need to do a custom operator>> for some types but at this point
the parser goes through the entire file and prints out everything which
is a large milestone I'd say

changes in this commit:
- add SECTION_LIST for a section containing a single list of values
  examples: TimingPoints, HitObjects
- improve operator>> for enums making it accept int values and fail
  properly when the value is incorrect (there's a warning I need to get
  rid of but it's ok for now)
- skip over blank lines: adding this single line (src/osuparser.cpp:52)
  made the parser go from shitting itself to actually going through the
  whole file and printing everything (with some errors but still)
- new string util: isBlank() used for the above
- new __VA_ARGS__ util: ARG_COUNT() and rename foreach.hpp to
  va_args_util.hpp as that name no longer fits
This commit is contained in:
jacekpoz 2024-04-26 19:05:35 +02:00
parent 5e0eb7efe3
commit b978fb2288
Signed by: poz
SSH key fingerprint: SHA256:JyLeVWE4bF3tDnFeUpUaJsPsNlJyBldDGV/dIKSLyN8
5 changed files with 71 additions and 17 deletions

View file

@ -10,7 +10,7 @@
#include <util_stream_ops.hpp>
#include <string_util.hpp>
#include <foreach.hpp>
#include <va_args_util.hpp>
#define CONFIG(name, ...) \
struct name {\
@ -22,6 +22,8 @@
__VA_ARGS__\
} name;
#define SECTION_LIST(name, type) std::vector<type> name;
#define VAR_UINT(section, name) uint name;
#define VAR_UINT_P(section, name, prefix) VAR_UINT(section, name)
#define VAR_UINT_D(section, name, default) uint name = default;
@ -62,7 +64,7 @@
#define VAR_ENUM_D(section, name, enum_type, default) enum_type name = enum_type::default;
#define ENUM_TO_STRING_CASE(name, e) case name::e: os << #e; break;
#define ENUM_FROM_STRING(name, e) if (input == #e) en = name::e;
#define ENUM_FROM_STRING(name, e) if (input == #e) { en = name::e; hasBeenSet = true; }
#define ENUM_STREAM_OPS(name, ...) \
inline std::ostream &operator<<(std::ostream &os, const name &en) {\
@ -76,11 +78,29 @@
std::string input;\
is >> input;\
if (isInt(input)) {\
en = static_cast<name>(std::stoul(input));\
return is;\
try {\
int inputNum = std::stoul(input);\
if (inputNum >= ARG_COUNT(__VA_ARGS__)) {\
/* TODO this and the comment below give me -Wmaybe-uninitialized
* which I would say is the expected outcome in this case
* but what do I know; find a way to silence the warning
* while still retaining this behaviour */\
is.setstate(std::ios_base::failbit);\
} else {\
en = static_cast<name>(inputNum);\
}\
} catch (std::invalid_argument const&) {\
/* the comment below */\
is.setstate(std::ios_base::failbit);\
}\
} else {\
toUpper(input);\
bool hasBeenSet = false;\
FOR_EACH(ENUM_FROM_STRING, name, __VA_ARGS__)\
if (!hasBeenSet) {\
is.setstate(std::ios_base::failbit);\
}\
}\
toUpper(input);\
FOR_EACH(ENUM_FROM_STRING, name, __VA_ARGS__)\
return is;\
}
@ -123,6 +143,7 @@
#undef CONFIG
#undef SECTION
#undef SECTION_LIST
#undef VAR_UINT
#undef VAR_UINT_P

View file

@ -3,6 +3,7 @@
#define CONFIG(name, ...)
#define SECTION(name, ...)
#define SECTION_LIST(name, type)
#define VAR_UINT(section, name)
#define VAR_UINT_P(section, name, prefix)
@ -162,18 +163,13 @@ CONFIG(Difficulty,
VAR_FLOAT(.difficulty, sliderMultiplier)
VAR_FLOAT(.difficulty, sliderTickRate)
)
SECTION(timingPoints,
VAR_LIST(.timingPoints, timingPoints, TimingPoint)
)
SECTION_LIST(timingPoints, TimingPoint)
SECTION(colours,
VAR_LIST_NUMBERED(.colours, combo, sf::Color)
VAR_COLOUR(.colours, sliderTrackOverride)
VAR_COLOUR(.colours, sliderBorder)
)
SECTION(hitObjects,
VAR_LIST(.hitObjects, hitObjects, HitObject)
)
SECTION_LIST(hitObjects, HitObject)
)
STRUCT_OUTPUT_STREAM_OP(Difficulty,
formatVersion,
@ -214,16 +210,17 @@ STRUCT_OUTPUT_STREAM_OP(Difficulty,
difficulty.approachRate,
difficulty.sliderMultiplier,
difficulty.sliderTickRate,
timingPoints.timingPoints,
timingPoints,
colours.combo,
colours.sliderTrackOverride,
colours.sliderBorder,
hitObjects.hitObjects
hitObjects
)
#ifdef CONFIG_DEFINED
#undef CONFIG
#undef SECTION
#undef SECTION_LIST
#undef VAR_UINT
#undef VAR_UINT_P

View file

@ -18,3 +18,7 @@ inline void toUpper(std::string &str) {
inline bool isInt(const std::string &str) {
return str.find_first_not_of("1234567890") == std::string::npos;
}
inline bool isBlank(const std::string &str) {
return str.find_first_not_of(" \n\t\v\r\f") == std::string::npos;
}

View file

@ -53,5 +53,20 @@
#define FE_49(WHAT, extra_arg, X, ...) WHAT(extra_arg, X)FE_48(WHAT, extra_arg, __VA_ARGS__)
#define FE_50(WHAT, extra_arg, X, ...) WHAT(extra_arg, X)FE_49(WHAT, extra_arg, __VA_ARGS__)
#define GET_MACRO(_0,_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_33,_34,_35,_36,_37,_38,_39,_40,_41,_42,_43,_44,_45,_46,_47,_48,_49,_50,NAME,...) NAME
#define FOR_EACH(action,extra_arg,...) GET_MACRO(_0,__VA_ARGS__,FE_50,FE_49,FE_48,FE_47,FE_46,FE_45,FE_44,FE_43,FE_42,FE_41,FE_40,FE_39,FE_38,FE_37,FE_36,FE_35,FE_34,FE_33,FE_32,FE_31,FE_30,FE_29,FE_28,FE_27,FE_26,FE_25,FE_24,FE_23,FE_22,FE_21,FE_20,FE_19,FE_18,FE_17,FE_16,FE_15,FE_14,FE_13,FE_12,FE_11,FE_10,FE_9,FE_8,FE_7,FE_6,FE_5,FE_4,FE_3,FE_2,FE_1,FE_0)(action,extra_arg,__VA_ARGS__)
#define NTH_ARG(_0,_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_33,_34,_35,_36,_37,_38,_39,_40,_41,_42,_43,_44,_45,_46,_47,_48,_49,_50,NAME,...) NAME
#define FOR_EACH(action,extra_arg,...) NTH_ARG(_0,__VA_ARGS__,FE_50,FE_49,FE_48,FE_47,FE_46,FE_45,FE_44,FE_43,FE_42,FE_41,FE_40,FE_39,FE_38,FE_37,FE_36,FE_35,FE_34,FE_33,FE_32,FE_31,FE_30,FE_29,FE_28,FE_27,FE_26,FE_25,FE_24,FE_23,FE_22,FE_21,FE_20,FE_19,FE_18,FE_17,FE_16,FE_15,FE_14,FE_13,FE_12,FE_11,FE_10,FE_9,FE_8,FE_7,FE_6,FE_5,FE_4,FE_3,FE_2,FE_1,FE_0)(action,extra_arg,__VA_ARGS__)
// https://stackoverflow.com/a/2124385
#define ARG_COUNT(...) \
ARG_COUNT_(__VA_ARGS__,FILLER())
#define ARG_COUNT_(...) \
NTH_ARG(__VA_ARGS__)
// one more than in NTH_ARG to get the count
// instead of the index of the last element
#define FILLER() \
51,50, \
49,48,47,46,45,44,43,42,41,40, \
39,38,37,36,35,34,33,32,31,30, \
29,28,27,26,25,24,23,22,21,20, \
19,18,17,16,15,14,13,12,11,10, \
9,8,7,6,5,4,3,2,1,0

View file

@ -49,6 +49,7 @@ inline std::string checkAndRemove(const std::string &str, const std::string &pre
std::string _prefix;\
\
while (std::getline(configFile, line)) {\
if (isBlank(line)) continue;\
/* remove \n*/\
line.pop_back();\
\
@ -69,6 +70,22 @@ inline std::string checkAndRemove(const std::string &str, const std::string &pre
__VA_ARGS__\
}
#define SECTION_LIST(name, type) \
if (currentSection == #name) {\
std::vector<type> value;\
std::stringstream ss(line);\
ssize_t line_len = line.size();\
type val;\
while (ss >> val) {\
std::getline(configFile, line);\
ss = std::stringstream(line);\
line_len = line.size();\
value.push_back(val);\
}\
configFile.seekg(configFile.tellg() - line_len);\
ret.name = value;\
}
#define CHECK_VAR(section, name, ...) \
if (line.starts_with(_prefix)) {\
__VA_ARGS__\