From 335db1c0e42498bd5d699bfb5865b9008e79b817 Mon Sep 17 00:00:00 2001 From: Jonathan Date: Fri, 5 Aug 2022 23:17:15 -0400 Subject: [PATCH] move map_source_location to parser as parser_source_location since the locations actually come from there rather than from the map; now the parser keeps track of the location, so it's a bit easier to follow --- bsputil/bsputil.cc | 1 + bsputil/decompile.cpp | 5 +- common/bspfile.cc | 4 ++ common/entdata.cc | 18 +----- common/parser.cc | 18 +++--- common/settings.cc | 6 -- include/common/bspfile.hh | 3 + include/common/bspfile_generic.hh | 3 + include/common/entdata.h | 21 +++++-- include/common/parser.hh | 101 +++++++++++++++++++++++++++--- include/common/settings.hh | 7 +-- include/qbsp/map.hh | 66 ++----------------- light/entities.cc | 16 ++--- light/light.cc | 5 +- qbsp/brush.cc | 4 +- qbsp/map.cc | 49 +++++++-------- qbsp/qbsp.cc | 8 +-- tests/test.cc | 36 +++++------ tests/test_light.cc | 12 ++-- tests/test_qbsp.cc | 15 ++--- vis/vis.cc | 2 +- 21 files changed, 215 insertions(+), 185 deletions(-) diff --git a/bsputil/bsputil.cc b/bsputil/bsputil.cc index 5d47f053..71166ff2 100644 --- a/bsputil/bsputil.cc +++ b/bsputil/bsputil.cc @@ -463,6 +463,7 @@ settings::common_settings bsputil_options; int main(int argc, char **argv) { bspdata_t bspdata; + // FIXME: doesn't this get overwritten by ConvertBSPFormat below? mbsp_t &bsp = bspdata.bsp.emplace(); fmt::print("---- bsputil / ericw-tools {} ----\n", ERICWTOOLS_VERSION); diff --git a/bsputil/decompile.cpp b/bsputil/decompile.cpp index bb2d7d31..e1d3e693 100644 --- a/bsputil/decompile.cpp +++ b/bsputil/decompile.cpp @@ -1000,7 +1000,7 @@ static void DecompileEntity( continue; } else if (modelNum > 0 && keyValue.first == "origin") { auto &value = keyValue.second; - parser_t parser(value); + parser_t parser(value, { }); qvec3d vec; parser.parse_token(); vec[0] = stof(parser.token); @@ -1182,7 +1182,8 @@ static void DecompileEntity( void DecompileBSP(const mbsp_t *bsp, const decomp_options &options, std::ofstream &file) { - auto entdicts = EntData_Parse(bsp->dentdata); + parser_t parser{bsp->dentdata, { bsp->file.string() }}; + auto entdicts = EntData_Parse(parser); for (size_t i = 0; i < entdicts.size(); ++i) { // entity 0 is implicitly worldspawn (model 0) diff --git a/common/bspfile.cc b/common/bspfile.cc index 64cb1216..11feba6e 100644 --- a/common/bspfile.cc +++ b/common/bspfile.cc @@ -1953,6 +1953,8 @@ bool ConvertBSPFormat(bspdata_t *bspdata, const bspversion_t *to_version) // Conversions to bspver_generic mbsp_t mbsp{}; + mbsp.file = bspdata->file; + if (std::holds_alternative(bspdata->bsp)) { ConvertQ1BSPToGeneric(std::get(bspdata->bsp), mbsp); } else if (std::holds_alternative(bspdata->bsp)) { @@ -2167,6 +2169,8 @@ void LoadBSPFile(fs::path &filename, bspdata_t *bspdata) logging::funcprint("'{}'\n", filename); + bspdata->file = filename; + /* load the file header */ fs::data file_data = fs::load(filename); diff --git a/common/entdata.cc b/common/entdata.cc index 34cce427..3000ae34 100644 --- a/common/entdata.cc +++ b/common/entdata.cc @@ -182,10 +182,8 @@ void entdict_t::parse(parser_base_t &parser) } } -void EntData_ParseInto(const std::string &entdata, std::vector &vector) +void EntData_ParseInto(parser_t &parser, std::vector &vector) { - parser_t parser(entdata); - /* go through all the entities */ while (1) { /* parse the opening brace */ @@ -197,20 +195,6 @@ void EntData_ParseInto(const std::string &entdata, std::vector &vecto } } -/* - * ================== - * EntData_Parse - * ================== - */ -std::vector EntData_Parse(const std::string &entdata) -{ - std::vector result; - - EntData_ParseInto(entdata, result); - - return result; -} - /* * ================ * EntData_Write diff --git a/common/parser.cc b/common/parser.cc index 4687a30a..7277c0d9 100644 --- a/common/parser.cc +++ b/common/parser.cc @@ -43,15 +43,15 @@ skipspace: if (flags & PARSE_OPTIONAL) return false; if (flags & PARSE_SAMELINE) - FError("line {}: Line is incomplete", linenum); + FError("{}: Line is incomplete", location); return false; } if (*pos == '\n') { if (flags & PARSE_OPTIONAL) return false; if (flags & PARSE_SAMELINE) - FError("line {}: Line is incomplete", linenum); - linenum++; + FError("{}: Line is incomplete", location); + location.line_number.value()++; } pos++; } @@ -67,15 +67,15 @@ skipspace: if (flags & PARSE_OPTIONAL) return false; if (flags & PARSE_SAMELINE) - FError("line {}: Line is incomplete", linenum); + FError("{}: Line is incomplete", location); while (*pos++ != '\n') { if (!*pos) { if (flags & PARSE_SAMELINE) - FError("line {}: Line is incomplete", linenum); + FError("{}: Line is incomplete", location); return false; } } - linenum++; // count the \n the preceding while() loop just consumed + location.line_number.value()++; // count the \n the preceding while() loop just consumed goto skipspace; } if (flags & PARSE_COMMENT) @@ -88,7 +88,7 @@ skipspace: pos++; while (*pos != '"') { if (!*pos) - FError("line {}: EOF inside quoted token", linenum); + FError("{}: EOF inside quoted token", location); if (*pos == '\\') { // small note. the vanilla quake engine just parses the "foo" stuff then goes and looks for \n // explicitly within strings. this means ONLY \n works, and double-quotes cannot be used either in maps @@ -118,13 +118,13 @@ skipspace: break; case '\"': if (pos[2] == '\r' || pos[2] == '\n') { - logging::print("WARNING: line {}: escaped double-quote at end of string\n", linenum); + logging::print("WARNING: {}: escaped double-quote at end of string\n", location); } else { *token_p++ = *pos++; } break; default: - logging::print("WARNING: line {}: Unrecognised string escape - \\{}\n", linenum, pos[1]); + logging::print("WARNING: {}: Unrecognised string escape - \\{}\n", location, pos[1]); break; } } diff --git a/common/settings.cc b/common/settings.cc index 710a21b9..10428e68 100644 --- a/common/settings.cc +++ b/common/settings.cc @@ -34,12 +34,6 @@ setting_base::setting_base( } } -bool setting_base::parseString(const std::string &string, source source) -{ - parser_t p{string}; - return parse("", p, source); -} - setting_group performance_group{"Performance", 10}; setting_group logging_group{"Logging", 5}; setting_group game_group{"Game", 15}; diff --git a/include/common/bspfile.hh b/include/common/bspfile.hh index bfeb3ed3..686b7fa0 100644 --- a/include/common/bspfile.hh +++ b/include/common/bspfile.hh @@ -432,6 +432,9 @@ struct bspdata_t { const bspversion_t *version, *loadversion; + // the file path that this BSP was loaded from + fs::path file; + // Stay in monostate until a BSP type is requested. std::variant bsp; diff --git a/include/common/bspfile_generic.hh b/include/common/bspfile_generic.hh index e8aca7f1..0344f1da 100644 --- a/include/common/bspfile_generic.hh +++ b/include/common/bspfile_generic.hh @@ -436,6 +436,9 @@ struct mbsp_t // the BSP version that we came from, if any const bspversion_t *loadversion; + // the BSP we were converted from, if any + fs::path file; + std::vector dmodels; mvis_t dvis; std::vector dlightdata; diff --git a/include/common/entdata.h b/include/common/entdata.h index 9323d54d..363ba38e 100644 --- a/include/common/entdata.h +++ b/include/common/entdata.h @@ -26,12 +26,11 @@ #include #include #include "qvec.hh" +#include "parser.hh" using keyvalue_t = std::pair; using keyvalues_t = std::vector; -struct parser_base_t; - class entdict_t { keyvalues_t keyvalues; @@ -69,6 +68,20 @@ public: void parse(parser_base_t &parser); }; -void EntData_ParseInto(const std::string &entdata, std::vector &vector); -std::vector EntData_Parse(const std::string &entdata); +void EntData_ParseInto(parser_t &parser, std::vector &vector); + +/* + * ================== + * EntData_Parse + * ================== + */ +inline std::vector EntData_Parse(parser_t &parser) +{ + std::vector result; + + EntData_ParseInto(parser, result); + + return result; +} + std::string EntData_Write(const std::vector &ents); diff --git a/include/common/parser.hh b/include/common/parser.hh index 438cf6e2..6b1a2fab 100644 --- a/include/common/parser.hh +++ b/include/common/parser.hh @@ -36,6 +36,81 @@ enum : int32_t using parseflags = int32_t; +// kind of a parallel to std::source_location in C++20 +// but this represents a location in a parser. +struct parser_source_location +{ + // the source name of this location; may be a .map file path, + // or some other string that describes where this location came + // to be. note that because the locations only live for the lifetime + // of the object it is belonging to, whatever this string + // points to must out-live the object. + std::shared_ptr source_name = nullptr; + + // the line number that this location is associated to, if any. Synthetic + // locations may not necessarily have an associated line number. + std::optional line_number = std::nullopt; + + // reference to a location of the object that derived us. this is mainly + // for synthetic locations; ie a bspbrush_t's sides aren't themselves generated + // by a source or line, but they are derived from a mapbrush_t which does have + // a location. The object it points to must outlive this object. this is mainly + // for debugging. + std::optional> derivative = std::nullopt; + + parser_source_location() = default; + inline parser_source_location(const std::string &source) : source_name(std::make_unique(source)) { } + inline parser_source_location(const char *source) : source_name(std::make_unique(source)) { } + inline parser_source_location(const std::string &source, size_t line) : source_name(std::make_unique(source)), line_number(line) { } + inline parser_source_location(const char *source, size_t line) : source_name(std::make_unique(source)), line_number(line) { } + + // check if this source location is valid + explicit operator bool() const { return source_name != nullptr; } + + // return a modified source location with only the line changed + inline parser_source_location on_line(size_t new_line) const + { + parser_source_location loc(*this); + loc.line_number = new_line; + return loc; + } + + // return a new, constructed source location derived from this one + template + inline parser_source_location derive(Args&&... args) + { + parser_source_location loc(std::forward(args)...); + loc.derivative = *this; + return loc; + } + + // if we update to C++20 we could use this to track where location objects come from: + // std::source_location created_location; +}; + +// FMT support +template<> +struct fmt::formatter +{ + constexpr auto parse(format_parse_context &ctx) -> decltype(ctx.begin()) { return ctx.end(); } + + template + auto format(const parser_source_location &v, FormatContext &ctx) -> decltype(ctx.out()) + { + if (v.source_name) { + format_to(ctx.out(), "{}", *v.source_name.get()); + } else { + format_to(ctx.out(), "unknown/unset location"); + } + + if (v.line_number.has_value()) { + format_to(ctx.out(), "[line {}]", v.line_number.value()); + } + + return ctx.out(); + } +}; + template constexpr auto untie(const std::tuple &tuple) { @@ -47,8 +122,14 @@ using untied_t = decltype(untie(std::declval())); struct parser_base_t { - std::string token; + std::string token; // the last token parsed by parse_token bool was_quoted = false; // whether the current token was from a quoted string or not + parser_source_location location; // parse location, if any + + inline parser_base_t(parser_source_location base_location) : + location(base_location) + { + } virtual bool parse_token(parseflags flags = PARSE_NORMAL) = 0; @@ -66,30 +147,30 @@ struct parser_t : parser_base_t { const char *pos; const char *end; - uint32_t linenum = 1; // base constructor; accept raw start & length - inline parser_t(const void *start, size_t length) - : pos(reinterpret_cast(start)), end(reinterpret_cast(start) + length) + inline parser_t(const void *start, size_t length, parser_source_location base_location) + : parser_base_t(base_location.on_line(1)), + pos(reinterpret_cast(start)), end(reinterpret_cast(start) + length) { } // pull from string_view; note that the string view must live for the entire // duration of the parser's life time - inline parser_t(const std::string_view &view) : parser_t(&view.front(), view.size()) { } + inline parser_t(const std::string_view &view, parser_source_location base_location) : parser_t(&view.front(), view.size(), base_location) { } // pull from fs::data; note that the data must live for the entire // duration of the parser's life time, and must has_value() - inline parser_t(const fs::data &data) : parser_t(data.value().data(), data.value().size()) { } + inline parser_t(const fs::data &data, parser_source_location base_location) : parser_t(data.value().data(), data.value().size(), base_location) { } // pull from C string; made explicit because this is error-prone - explicit parser_t(const char *str) : parser_t(str, strlen(str)) { } + explicit parser_t(const char *str, parser_source_location base_location) : parser_t(str, strlen(str), base_location) { } bool parse_token(parseflags flags = PARSE_NORMAL) override; - using state_type = decltype(std::tie(pos, linenum)); + using state_type = decltype(std::tie(pos, location)); - constexpr state_type state() { return state_type(pos, linenum); } + constexpr state_type state() { return state_type(pos, location); } bool at_end() const override { return pos >= end; } @@ -112,7 +193,7 @@ struct token_parser_t : parser_base_t std::vector tokens; size_t cur = 0; - inline token_parser_t(int argc, const char **args) : tokens(args, args + argc) { } + inline token_parser_t(int argc, const char **args, parser_source_location base_location) : parser_base_t(base_location), tokens(args, args + argc) { } using state_type = decltype(std::tie(cur)); diff --git a/include/common/settings.hh b/include/common/settings.hh index 979b6a63..8f08c5c3 100644 --- a/include/common/settings.hh +++ b/include/common/settings.hh @@ -139,9 +139,6 @@ public: // copies value and source virtual bool copyFrom(const setting_base &other) = 0; - // convenience form of parse() that constructs a temporary parser_t - bool parseString(const std::string &string, source source); - // resets value to default, and source to source::DEFAULT virtual void reset() = 0; virtual bool parse(const std::string &settingName, parser_base_t &parser, source source) = 0; @@ -769,7 +766,7 @@ public: return; } - parser_t p{value}; + parser_t p{value, { }}; setting->parse(name, p, source); } @@ -846,7 +843,7 @@ public: // do the actual parsing virtual void initialize(int argc, const char **argv) { - token_parser_t p(argc, argv); + token_parser_t p(argc, argv, { "command line" }); parse(p); } // after parsing has concluded, handle the side effects diff --git a/include/qbsp/map.hh b/include/qbsp/map.hh index 15ca2d0f..719a8840 100644 --- a/include/qbsp/map.hh +++ b/include/qbsp/map.hh @@ -38,71 +38,13 @@ struct bspbrush_t; -// kind of a parallel to std::source_location in C++20 -// but this represents a location in a .map file where -// something happens. -struct map_source_location -{ - // the source name of this location; may be a .map file path, - // or some other string that describes where this location came - // to be. note that because the locations only live for the lifetime - // of the object it is belonging to, whatever this string - // points to must out-live the object. - std::shared_ptr source_name = nullptr; - - // the line number that this location is associated to, if any. Synthetic - // locations may not necessarily have an associated line number. - std::optional line_number = std::nullopt; - - // reference to a location of the object that derived us. this is mainly - // for synthetic locations; ie a bspbrush_t's sides aren't themselves generated - // by a source or line, but they are derived from a mapbrush_t which does have - // a location. The object it points to must outlive this object. this is mainly - // for debugging. - std::optional> derivative = std::nullopt; - - explicit operator bool() const { return source_name != nullptr; } - - // return a modified source location with only the line changeed - inline map_source_location on_line(size_t new_line) const - { - return { source_name, new_line, derivative }; - } - - // if we update to C++20 we could use this to track where location objects come from: - // std::source_location created_location; -}; - -// FMT support -template<> -struct fmt::formatter -{ - constexpr auto parse(format_parse_context &ctx) -> decltype(ctx.begin()) { return ctx.end(); } - - template - auto format(const map_source_location &v, FormatContext &ctx) -> decltype(ctx.out()) - { - if (v.source_name) { - format_to(ctx.out(), "{}", *v.source_name.get()); - } else { - format_to(ctx.out(), "unknown/unset location"); - } - - if (v.line_number.has_value()) { - format_to(ctx.out(), "[line {}]", v.line_number.value()); - } - - return ctx.out(); - } -}; - struct mapface_t { size_t planenum; std::array planepts{}; std::string texname{}; int texinfo = 0; - map_source_location line; + parser_source_location line; bool bevel = false; bool visible = false; winding_t winding; // winding used to calculate bevels @@ -137,7 +79,7 @@ public: brushformat_t format = brushformat_t::NORMAL; aabb3d bounds {}; std::optional outputnumber; /* only set for original brushes */ - map_source_location line; + parser_source_location line; }; struct lumpdata @@ -185,6 +127,8 @@ public: int32_t areaportalnum = 0; std::array portalareas = {}; + + parser_source_location location; }; struct maptexdata_t @@ -402,7 +346,7 @@ extern mapdata_t map; void CalculateWorldExtent(void); -bool ParseEntity(parser_t &parser, mapentity_t *entity, const map_source_location &map_source); +bool ParseEntity(parser_t &parser, mapentity_t *entity); void ProcessExternalMapEntity(mapentity_t *entity); void ProcessAreaPortal(mapentity_t *entity); diff --git a/light/entities.cc b/light/entities.cc index d72ec7a8..37ff1674 100644 --- a/light/entities.cc +++ b/light/entities.cc @@ -772,7 +772,10 @@ void LoadEntities(const settings::worldspawn_keys &cfg, const mbsp_t *bsp) { logging::funcheader(); - entdicts = EntData_Parse(bsp->dentdata); + { + parser_t parser{bsp->dentdata, { bsp->file.string() }}; + entdicts = EntData_Parse(parser); + } // Make warnings for (auto &entdict : entdicts) { @@ -1302,12 +1305,11 @@ static void GL_SubdivideSurface(const mface_t *face, const modelinfo_t *face_mod SubdividePolygon(face, face_modelinfo, bsp, face->numedges, verts, light_options.surflight_subdivide.value()); } -static bool ParseEntityLights(std::ifstream &f) +static bool ParseEntityLights(std::ifstream &f, const fs::path &fname) { std::string str{std::istreambuf_iterator(f), std::istreambuf_iterator()}; - parser_t p(str); - - EntData_ParseInto(str, radlights); + parser_t p(str, { fname.string() }); + EntData_ParseInto(p, radlights); return true; } @@ -1320,14 +1322,14 @@ bool ParseLightsFile(const fs::path &fname) // use entity-style format if (fname.extension() == ".ent") { - return ParseEntityLights(f); + return ParseEntityLights(f, fname); } while (!f.eof()) { std::string buf; std::getline(f, buf); - parser_t parser(buf); + parser_t parser(buf, { fname.string() }); if (!parser.parse_token()) continue; diff --git a/light/light.cc b/light/light.cc index b6c1db50..18e0373c 100644 --- a/light/light.cc +++ b/light/light.cc @@ -114,7 +114,7 @@ setting_group experimental_group{"Experimental options", 60}; void light_settings::initialize(int argc, const char **argv) { - token_parser_t p(argc - 1, argv + 1); + token_parser_t p(argc - 1, argv + 1, { "command line" }); auto remainder = parse(p); if (remainder.size() <= 0 || remainder.size() > 1) { @@ -1074,7 +1074,8 @@ static void LoadTextures(const mbsp_t *bsp) // gather textures used by _project_texture. // FIXME: I'm sure we can resolve this so we don't parse entdata twice. - auto entdicts = EntData_Parse(bsp->dentdata); + parser_t parser{bsp->dentdata, { bsp->file.string() }}; + auto entdicts = EntData_Parse(parser); for (auto &entdict : entdicts) { if (entdict.get("classname").find("light") == 0) { const auto &tex = entdict.get("_project_texture"); diff --git a/qbsp/brush.cc b/qbsp/brush.cc index 09a71f06..cf64345d 100644 --- a/qbsp/brush.cc +++ b/qbsp/brush.cc @@ -632,10 +632,10 @@ void bspbrush_t::update_bounds() for (size_t i = 0; i < 3; i++) { // todo: map_source_location in bspbrush_t if (this->bounds.mins()[0] <= -qbsp_options.worldextent.value() || this->bounds.maxs()[0] >= qbsp_options.worldextent.value()) { - logging::print("WARNING: {}: brush bounds out of range\n", mapbrush ? mapbrush->line : map_source_location()); + logging::print("WARNING: {}: brush bounds out of range\n", mapbrush ? mapbrush->line : parser_source_location()); } if (this->bounds.mins()[0] >= qbsp_options.worldextent.value() || this->bounds.maxs()[0] <= -qbsp_options.worldextent.value()) { - logging::print("WARNING: {}: no visible sides on brush\n", mapbrush ? mapbrush->line : map_source_location()); + logging::print("WARNING: {}: no visible sides on brush\n", mapbrush ? mapbrush->line : parser_source_location()); } } diff --git a/qbsp/map.cc b/qbsp/map.cc index 75fde4e9..8d5a3319 100644 --- a/qbsp/map.cc +++ b/qbsp/map.cc @@ -1158,7 +1158,7 @@ static void SetTexinfo_QuArK( */ determinant = a * d - b * c; if (fabs(determinant) < ZERO_EPSILON) { - logging::print("WARNING: line {}: Face with degenerate QuArK-style texture axes\n", parser.linenum); + logging::print("WARNING: {}: Face with degenerate QuArK-style texture axes\n", parser.location); for (i = 0; i < 3; i++) out->vecs.at(0, i) = out->vecs.at(1, i) = 0; } else { @@ -1317,7 +1317,7 @@ static void ParsePlaneDef(parser_t &parser, std::array &planepts) return; parse_error: - FError("line {}: Invalid brush plane format", parser.linenum); + FError("{}: Invalid brush plane format", parser.location); } static void ParseValve220TX(parser_t &parser, qmat &axis, qvec2d &shift, vec_t &rotate, qvec2d &scale) @@ -1347,7 +1347,7 @@ static void ParseValve220TX(parser_t &parser, qmat &axis, qvec2d &s return; parse_error: - FError("line {}: couldn't parse Valve220 texture info", parser.linenum); + FError("{}: couldn't parse Valve220 texture info", parser.location); } static void ParseBrushPrimTX(parser_t &parser, qmat &texMat) @@ -1378,7 +1378,7 @@ static void ParseBrushPrimTX(parser_t &parser, qmat &texMat) return; parse_error: - FError("line {}: couldn't parse Brush Primitives texture info", parser.linenum); + FError("{}: couldn't parse Brush Primitives texture info", parser.location); } static void ParseTextureDef(parser_t &parser, mapface_t &mapface, const mapbrush_t &brush, maptexinfo_t *tx, @@ -1603,7 +1603,7 @@ static std::optional ParseBrushFace(parser_t &parser, const mapbrush_ int i, j; mapface_t face; - face.line = brush.line.on_line(parser.linenum); + face.line = parser.location; ParsePlaneDef(parser, planepts); @@ -1612,7 +1612,7 @@ static std::optional ParseBrushFace(parser_t &parser, const mapbrush_ ParseTextureDef(parser, face, brush, &tx, face.planepts, face.get_plane()); if (!normal_ok) { - logging::print("WARNING: line {}: Brush plane with no normal\n", parser.linenum); + logging::print("WARNING: {}: Brush plane with no normal\n", parser.location); return std::nullopt; } @@ -1790,13 +1790,13 @@ inline void AddBrushBevels(mapentity_t &e, mapbrush_t &b) } } -static mapbrush_t ParseBrush(parser_t &parser, mapentity_t &entity, const map_source_location &entity_source) +static mapbrush_t ParseBrush(parser_t &parser, mapentity_t &entity) { mapbrush_t brush; // ericw -- brush primitives if (!parser.parse_token(PARSE_PEEK)) - FError("{}: unexpected EOF after { beginning brush", entity_source); + FError("{}: unexpected EOF after { beginning brush", parser.location); if (parser.token == "(") { brush.format = brushformat_t::NORMAL; @@ -1820,7 +1820,7 @@ static mapbrush_t ParseBrush(parser_t &parser, mapentity_t &entity, const map_so // set linenum after first parsed token if (!brush.line) { - brush.line = entity_source.on_line(parser.linenum); + brush.line = parser.location; } if (parser.token == "}") @@ -1836,13 +1836,13 @@ static mapbrush_t ParseBrush(parser_t &parser, mapentity_t &entity, const map_so bool discardFace = false; for (auto &check : brush.faces) { if (qv::epsilonEqual(check.get_plane(), face->get_plane())) { - logging::print("line {}: Brush with duplicate plane\n", parser.linenum); + logging::print("line {}: Brush with duplicate plane\n", parser.location); discardFace = true; continue; } if (qv::epsilonEqual(-check.get_plane(), face->get_plane())) { /* FIXME - this is actually an invalid brush */ - logging::print("line {}: Brush with duplicate plane\n", parser.linenum); + logging::print("line {}: Brush with duplicate plane\n", parser.location); continue; } } @@ -1867,15 +1867,15 @@ static mapbrush_t ParseBrush(parser_t &parser, mapentity_t &entity, const map_so return brush; } -bool ParseEntity(parser_t &parser, mapentity_t *entity, const map_source_location &map_source) +bool ParseEntity(parser_t &parser, mapentity_t *entity) { + entity->location = parser.location; + if (!parser.parse_token()) return false; - map_source_location entity_source = map_source.on_line(parser.linenum); - if (parser.token != "{") { - FError("{}: Invalid entity format, { not found", entity_source); + FError("{}: Invalid entity format, { not found", parser.location); } entity->mapbrushes.clear(); @@ -1889,7 +1889,7 @@ bool ParseEntity(parser_t &parser, mapentity_t *entity, const map_source_locatio // once we run into the first brush, set up textures state. EnsureTexturesLoaded(); - entity->mapbrushes.emplace_back(ParseBrush(parser, *entity, entity_source)); + entity->mapbrushes.emplace_back(ParseBrush(parser, *entity)); } else { ParseEpair(parser, entity); } @@ -2010,11 +2010,10 @@ static mapentity_t LoadExternalMap(const std::string &filename) FError("Couldn't load external map file \"{}\".\n", filename); } - parser_t parser(file->data(), file->size()); - map_source_location entity_source { std::make_shared(filename), parser.linenum }; + parser_t parser(file, { filename }); // parse the worldspawn - if (!ParseEntity(parser, &dest, entity_source)) { + if (!ParseEntity(parser, &dest)) { FError("'{}': Couldn't parse worldspawn entity\n", filename); } const std::string &classname = dest.epairs.get("classname"); @@ -2024,7 +2023,7 @@ static mapentity_t LoadExternalMap(const std::string &filename) // parse any subsequent entities, move any brushes to worldspawn mapentity_t dummy{}; - while (ParseEntity(parser, &dummy, entity_source = entity_source.on_line(parser.linenum))) { + while (ParseEntity(parser, &dummy)) { // move the brushes to the worldspawn dest.mapbrushes.insert(dest.mapbrushes.end(), std::make_move_iterator(dummy.mapbrushes.begin()), std::make_move_iterator(dummy.mapbrushes.end())); @@ -2355,13 +2354,12 @@ void LoadMapFile(void) return; } - parser_t parser(file->data(), file->size()); - map_source_location entity_source { std::make_shared(qbsp_options.map_path.string()) }; + parser_t parser(file, { qbsp_options.map_path.string() }); for (int i = 0;; i++) { mapentity_t &entity = map.entities.emplace_back(); - if (!ParseEntity(parser, &entity, entity_source.on_line(parser.linenum))) { + if (!ParseEntity(parser, &entity)) { break; } } @@ -2381,13 +2379,12 @@ void LoadMapFile(void) return; } - parser_t parser(file->data(), file->size()); - map_source_location entity_source { std::make_shared(qbsp_options.add.value()) }; + parser_t parser(file, { qbsp_options.add.value() }); for (int i = 0;; i++) { mapentity_t &entity = map.entities.emplace_back(); - if (!ParseEntity(parser, &entity, entity_source.on_line(parser.linenum))) { + if (!ParseEntity(parser, &entity)) { break; } diff --git a/qbsp/qbsp.cc b/qbsp/qbsp.cc index b04ea217..2115daec 100644 --- a/qbsp/qbsp.cc +++ b/qbsp/qbsp.cc @@ -83,12 +83,12 @@ void qbsp_settings::initialize(int argc, const char **argv) { if (auto file = fs::load("qbsp.ini")) { logging::print("Loading options from qbsp.ini\n"); - parser_t p(file->data(), file->size()); + parser_t p(file, { "qbsp.ini" }); parse(p); } try { - token_parser_t p(argc - 1, argv + 1); + token_parser_t p(argc - 1, argv + 1, { "command line" }); auto remainder = parse(p); if (remainder.size() <= 0 || remainder.size() > 2) { @@ -113,7 +113,7 @@ void qbsp_settings::load_texture_def(const std::string &pathname) } fs::data data = fs::load(pathname); - parser_t parser(data); + parser_t parser(data, { pathname }); while (true) { if (!parser.parse_token() || parser.at_end()) { @@ -158,7 +158,7 @@ void qbsp_settings::load_entity_def(const std::string &pathname) } fs::data data = fs::load(pathname); - parser_t parser(data); + parser_t parser(data, { pathname }); while (true) { if (!parser.parse_token() || parser.at_end()) { diff --git a/tests/test.cc b/tests/test.cc index b7e2c7dc..c0eec9b6 100644 --- a/tests/test.cc +++ b/tests/test.cc @@ -10,7 +10,7 @@ TEST_CASE("booleanFlagImplicit", "[settings]") settings::setting_container settings; settings::setting_bool boolSetting(&settings, "locked", false); const char *arguments[] = {"qbsp.exe", "-locked"}; - token_parser_t p{std::size(arguments) - 1, arguments + 1}; + token_parser_t p{std::size(arguments) - 1, arguments + 1, { }}; settings.parse(p); REQUIRE(boolSetting.value() == true); } @@ -20,7 +20,7 @@ TEST_CASE("booleanFlagExplicit", "[settings]") settings::setting_container settings; settings::setting_bool boolSetting(&settings, "locked", false); const char *arguments[] = {"qbsp.exe", "-locked", "1"}; - token_parser_t p{std::size(arguments) - 1, arguments + 1}; + token_parser_t p{std::size(arguments) - 1, arguments + 1, { }}; settings.parse(p); REQUIRE(boolSetting.value() == true); } @@ -30,7 +30,7 @@ TEST_CASE("booleanFlagStray", "[settings]") settings::setting_container settings; settings::setting_bool boolSetting(&settings, "locked", false); const char *arguments[] = {"qbsp.exe", "-locked", "stray"}; - token_parser_t p{std::size(arguments) - 1, arguments + 1}; + token_parser_t p{std::size(arguments) - 1, arguments + 1, { }}; settings.parse(p); REQUIRE(boolSetting.value() == true); } @@ -41,7 +41,7 @@ TEST_CASE("scalarSimple", "[settings]") settings::setting_container settings; settings::setting_scalar scalarSetting(&settings, "scale", 1.0); const char *arguments[] = {"qbsp.exe", "-scale", "1.25"}; - token_parser_t p{std::size(arguments) - 1, arguments + 1}; + token_parser_t p{std::size(arguments) - 1, arguments + 1, { }}; settings.parse(p); REQUIRE(scalarSetting.value() == 1.25); } @@ -51,7 +51,7 @@ TEST_CASE("scalarNegative", "[settings]") settings::setting_container settings; settings::setting_scalar scalarSetting(&settings, "scale", 1.0); const char *arguments[] = {"qbsp.exe", "-scale", "-0.25"}; - token_parser_t p{std::size(arguments) - 1, arguments + 1}; + token_parser_t p{std::size(arguments) - 1, arguments + 1, { }}; settings.parse(p); REQUIRE(scalarSetting.value() == -0.25); } @@ -61,7 +61,7 @@ TEST_CASE("scalarInfinity", "[settings]") settings::setting_container settings; settings::setting_scalar scalarSetting(&settings, "scale", 1.0, 0.0, std::numeric_limits::infinity()); const char *arguments[] = {"qbsp.exe", "-scale", "INFINITY"}; - token_parser_t p{std::size(arguments) - 1, arguments + 1}; + token_parser_t p{std::size(arguments) - 1, arguments + 1, { }}; settings.parse(p); REQUIRE(scalarSetting.value() == std::numeric_limits::infinity()); } @@ -71,7 +71,7 @@ TEST_CASE("scalarNAN", "[settings]") settings::setting_container settings; settings::setting_scalar scalarSetting(&settings, "scale", 1.0); const char *arguments[] = {"qbsp.exe", "-scale", "NAN"}; - token_parser_t p{std::size(arguments) - 1, arguments + 1}; + token_parser_t p{std::size(arguments) - 1, arguments + 1, { }}; settings.parse(p); REQUIRE(std::isnan(scalarSetting.value())); } @@ -81,7 +81,7 @@ TEST_CASE("scalarScientific", "[settings]") settings::setting_container settings; settings::setting_scalar scalarSetting(&settings, "scale", 1.0); const char *arguments[] = {"qbsp.exe", "-scale", "1.54334E-34"}; - token_parser_t p{std::size(arguments) - 1, arguments + 1}; + token_parser_t p{std::size(arguments) - 1, arguments + 1, { }}; settings.parse(p); REQUIRE(scalarSetting.value() == 1.54334E-34); } @@ -91,7 +91,7 @@ TEST_CASE("scalarEOF", "[settings]") settings::setting_container settings; settings::setting_scalar scalarSetting(&settings, "scale", 1.0); const char *arguments[] = {"qbsp.exe", "-scale"}; - token_parser_t p{std::size(arguments) - 1, arguments + 1}; + token_parser_t p{std::size(arguments) - 1, arguments + 1, { }}; REQUIRE_THROWS_AS(settings.parse(p), settings::parse_exception); } @@ -100,7 +100,7 @@ TEST_CASE("scalarStray", "[settings]") settings::setting_container settings; settings::setting_scalar scalarSetting(&settings, "scale", 1.0); const char *arguments[] = {"qbsp.exe", "-scale", "stray"}; - token_parser_t p{std::size(arguments) - 1, arguments + 1}; + token_parser_t p{std::size(arguments) - 1, arguments + 1, { }}; REQUIRE_THROWS_AS(settings.parse(p), settings::parse_exception); } @@ -110,7 +110,7 @@ TEST_CASE("vec3Simple", "[settings]") settings::setting_container settings; settings::setting_vec3 scalarSetting(&settings, "origin", 0, 0, 0); const char *arguments[] = {"qbsp.exe", "-origin", "1", "2", "3"}; - token_parser_t p{std::size(arguments) - 1, arguments + 1}; + token_parser_t p{std::size(arguments) - 1, arguments + 1, { }}; settings.parse(p); REQUIRE(scalarSetting.value() == (qvec3d{1, 2, 3})); } @@ -120,7 +120,7 @@ TEST_CASE("vec3Complex", "[settings]") settings::setting_container settings; settings::setting_vec3 scalarSetting(&settings, "origin", 0, 0, 0); const char *arguments[] = {"qbsp.exe", "-origin", "-12.5", "-INFINITY", "NAN"}; - token_parser_t p{std::size(arguments) - 1, arguments + 1}; + token_parser_t p{std::size(arguments) - 1, arguments + 1, { }}; settings.parse(p); REQUIRE(scalarSetting.value()[0] == -12.5); REQUIRE(scalarSetting.value()[1] == -std::numeric_limits::infinity()); @@ -132,7 +132,7 @@ TEST_CASE("vec3Incomplete", "[settings]") settings::setting_container settings; settings::setting_vec3 scalarSetting(&settings, "origin", 0, 0, 0); const char *arguments[] = {"qbsp.exe", "-origin", "1", "2"}; - token_parser_t p{std::size(arguments) - 1, arguments + 1}; + token_parser_t p{std::size(arguments) - 1, arguments + 1, { }}; REQUIRE_THROWS_AS(settings.parse(p), settings::parse_exception); } @@ -141,7 +141,7 @@ TEST_CASE("vec3Stray", "[settings]") settings::setting_container settings; settings::setting_vec3 scalarSetting(&settings, "origin", 0, 0, 0); const char *arguments[] = {"qbsp.exe", "-origin", "1", "2", "abc"}; - token_parser_t p{std::size(arguments) - 1, arguments + 1}; + token_parser_t p{std::size(arguments) - 1, arguments + 1, { }}; REQUIRE_THROWS_AS(settings.parse(p), settings::parse_exception); } @@ -151,7 +151,7 @@ TEST_CASE("stringSimple", "[settings]") settings::setting_container settings; settings::setting_string stringSetting(&settings, "name", ""); const char *arguments[] = {"qbsp.exe", "-name", "i am a string with spaces in it"}; - token_parser_t p{std::size(arguments) - 1, arguments + 1}; + token_parser_t p{std::size(arguments) - 1, arguments + 1, { }}; settings.parse(p); REQUIRE(stringSetting.value() == arguments[2]); } @@ -164,7 +164,7 @@ TEST_CASE("remainder", "[settings]") settings::setting_bool flagSetting(&settings, "flag", false); const char *arguments[] = { "qbsp.exe", "-name", "string", "-flag", "remainder one", "remainder two"}; - token_parser_t p{std::size(arguments) - 1, arguments + 1}; + token_parser_t p{std::size(arguments) - 1, arguments + 1, { }}; auto remainder = settings.parse(p); REQUIRE(remainder[0] == "remainder one"); REQUIRE(remainder[1] == "remainder two"); @@ -177,7 +177,7 @@ TEST_CASE("doubleHyphen", "[settings]") settings::setting_bool boolSetting(&settings, "locked", false); settings::setting_string stringSetting(&settings, "name", ""); const char *arguments[] = {"qbsp.exe", "--locked", "--name", "my name!"}; - token_parser_t p{std::size(arguments) - 1, arguments + 1}; + token_parser_t p{std::size(arguments) - 1, arguments + 1, { }}; settings.parse(p); REQUIRE(boolSetting.value() == true); REQUIRE(stringSetting.value() == "my name!"); @@ -233,7 +233,7 @@ TEST_CASE("copyMangle", "[settings]") settings::setting_container settings; settings::setting_mangle sunvec{&settings, {"sunlight_mangle"}, 0.0, 0.0, 0.0}; - parser_t p(std::string_view("0.0 -90.0 0.0")); + parser_t p(std::string_view("0.0 -90.0 0.0"), { }); CHECK(sunvec.parse("", p, settings::source::COMMANDLINE)); CHECK(Catch::Approx(0).margin(1e-6) == sunvec.value()[0]); CHECK(Catch::Approx(0).margin(1e-6) == sunvec.value()[1]); diff --git a/tests/test_light.cc b/tests/test_light.cc index 8866a2a1..8e85d89e 100644 --- a/tests/test_light.cc +++ b/tests/test_light.cc @@ -933,14 +933,16 @@ TEST_CASE("delayDefault", "[settings]") TEST_CASE("delayParseInt", "[settings]") { light_t light; - CHECK(light.formula.parseString("2", settings::source::MAP)); + parser_t p("2", { }); + CHECK(light.formula.parse(light.formula.primaryName(), p, settings::source::MAP)); CHECK(LF_INVERSE2 == light.formula.value()); } TEST_CASE("delayParseIntUnknown", "[settings]") { light_t light; - CHECK(light.formula.parseString("500", settings::source::MAP)); + parser_t p("500", { }); + CHECK(light.formula.parse(light.formula.primaryName(), p, settings::source::MAP)); // not sure if we should be strict and reject parsing this? CHECK(500 == light.formula.value()); } @@ -948,13 +950,15 @@ TEST_CASE("delayParseIntUnknown", "[settings]") TEST_CASE("delayParseFloat", "[settings]") { light_t light; - CHECK(light.formula.parseString("2.0", settings::source::MAP)); + parser_t p("2.0", { }); + CHECK(light.formula.parse(light.formula.primaryName(), p, settings::source::MAP)); CHECK(LF_INVERSE2 == light.formula.value()); } TEST_CASE("delayParseString", "[settings]") { light_t light; - CHECK(light.formula.parseString("inverse2", settings::source::MAP)); + parser_t p("inverse2", { }); + CHECK(light.formula.parse(light.formula.primaryName(), p, settings::source::MAP)); CHECK(LF_INVERSE2 == light.formula.value()); } diff --git a/tests/test_qbsp.cc b/tests/test_qbsp.cc index 6624e469..f6be58f0 100644 --- a/tests/test_qbsp.cc +++ b/tests/test_qbsp.cc @@ -39,17 +39,15 @@ static mapentity_t LoadMap(const char *map) ::map.entities.clear(); - parser_t parser(map); + parser_t parser(map, { Catch::getResultCapture().getCurrentTestName() }); // FIXME: ??? mapentity_t &entity = ::map.entities.emplace_back(); - map_source_location entity_source { std::make_shared(Catch::getResultCapture().getCurrentTestName()), 0 }; - mapentity_t worldspawn; // FIXME: adds the brush to the global map... - Q_assert(ParseEntity(parser, &worldspawn, entity_source)); + Q_assert(ParseEntity(parser, &worldspawn)); CalculateWorldExtent(); @@ -1048,7 +1046,8 @@ TEST_CASE("origin", "[testmaps_q1]") REQUIRE(qvec3f(0, 0, 0) == bsp.dmodels[1].origin); // check that the origin brush updated the entity lump - auto ents = EntData_Parse(bsp.dentdata); + parser_t parser(bsp.dentdata, { "qbsp_origin.bsp" }); + auto ents = EntData_Parse(parser); auto it = std::find_if(ents.begin(), ents.end(), [](const entdict_t &dict) -> bool { return dict.get("classname") == "rotate_object"; }); @@ -1392,7 +1391,8 @@ TEST_CASE("areaportal", "[testmaps_q2]") CHECK(0 == void_leaf->area); // a solid leaf gets the invalid area // check the func_areaportal entity had its "style" set - auto ents = EntData_Parse(bsp.dentdata); + parser_t parser(bsp.dentdata, { "qbsp_q2_areaportal.bsp" }); + auto ents = EntData_Parse(parser); auto it = std::find_if(ents.begin(), ents.end(), [](const entdict_t &dict) { return dict.get("classname") == "func_areaportal"; }); @@ -1779,7 +1779,8 @@ TEST_CASE("q1_merge_maps", "[testmaps_q1]") { REQUIRE(BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {-5,0,16}, {0, 0, 1})); // check that the worldspawn keys from the base map are used - auto ents = EntData_Parse(bsp.dentdata); + parser_t parser(bsp.dentdata, { "q1_merge_maps_base.bsp" }); + auto ents = EntData_Parse(parser); REQUIRE(ents.size() == 3); // worldspawn, info_player_start, func_wall REQUIRE(ents[0].get("classname") == "worldspawn"); diff --git a/vis/vis.cc b/vis/vis.cc index bace24d3..c9960727 100644 --- a/vis/vis.cc +++ b/vis/vis.cc @@ -63,7 +63,7 @@ setting_group advanced_group{"Advanced", 300}; void vis_settings::initialize(int argc, const char **argv) { - token_parser_t p(argc - 1, argv + 1); + token_parser_t p(argc - 1, argv + 1, { "command line" }); auto remainder = parse(p); if (remainder.size() <= 0 || remainder.size() > 1) {