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

This commit is contained in:
Jonathan 2022-08-05 23:17:15 -04:00
parent c6fabb290d
commit 335db1c0e4
21 changed files with 215 additions and 185 deletions

View File

@ -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<mbsp_t>();
fmt::print("---- bsputil / ericw-tools {} ----\n", ERICWTOOLS_VERSION);

View File

@ -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)

View File

@ -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<bsp29_t>(bspdata->bsp)) {
ConvertQ1BSPToGeneric(std::get<bsp29_t>(bspdata->bsp), mbsp);
} else if (std::holds_alternative<q2bsp_t>(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);

View File

@ -182,10 +182,8 @@ void entdict_t::parse(parser_base_t &parser)
}
}
void EntData_ParseInto(const std::string &entdata, std::vector<entdict_t> &vector)
void EntData_ParseInto(parser_t &parser, std::vector<entdict_t> &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<entdict_t> &vecto
}
}
/*
* ==================
* EntData_Parse
* ==================
*/
std::vector<entdict_t> EntData_Parse(const std::string &entdata)
{
std::vector<entdict_t> result;
EntData_ParseInto(entdata, result);
return result;
}
/*
* ================
* EntData_Write

View File

@ -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;
}
}

View File

@ -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};

View File

@ -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<std::monostate, mbsp_t, bsp29_t, bsp2rmq_t, bsp2_t, q2bsp_t, q2bsp_qbism_t> bsp;

View File

@ -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<dmodelh2_t> dmodels;
mvis_t dvis;
std::vector<uint8_t> dlightdata;

View File

@ -26,12 +26,11 @@
#include <vector>
#include <string_view>
#include "qvec.hh"
#include "parser.hh"
using keyvalue_t = std::pair<std::string, std::string>;
using keyvalues_t = std::vector<keyvalue_t>;
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<entdict_t> &vector);
std::vector<entdict_t> EntData_Parse(const std::string &entdata);
void EntData_ParseInto(parser_t &parser, std::vector<entdict_t> &vector);
/*
* ==================
* EntData_Parse
* ==================
*/
inline std::vector<entdict_t> EntData_Parse(parser_t &parser)
{
std::vector<entdict_t> result;
EntData_ParseInto(parser, result);
return result;
}
std::string EntData_Write(const std::vector<entdict_t> &ents);

View File

@ -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<std::string> 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<size_t> 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<std::reference_wrapper<const parser_source_location>> derivative = std::nullopt;
parser_source_location() = default;
inline parser_source_location(const std::string &source) : source_name(std::make_unique<std::string>(source)) { }
inline parser_source_location(const char *source) : source_name(std::make_unique<std::string>(source)) { }
inline parser_source_location(const std::string &source, size_t line) : source_name(std::make_unique<std::string>(source)), line_number(line) { }
inline parser_source_location(const char *source, size_t line) : source_name(std::make_unique<std::string>(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<typename ... Args>
inline parser_source_location derive(Args&&... args)
{
parser_source_location loc(std::forward<Args>(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<parser_source_location>
{
constexpr auto parse(format_parse_context &ctx) -> decltype(ctx.begin()) { return ctx.end(); }
template<typename FormatContext>
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<typename... T>
constexpr auto untie(const std::tuple<T...> &tuple)
{
@ -47,8 +122,14 @@ using untied_t = decltype(untie(std::declval<T>()));
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<const char *>(start)), end(reinterpret_cast<const char *>(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<const char *>(start)), end(reinterpret_cast<const char *>(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<std::string_view> 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));

View File

@ -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

View File

@ -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<std::string> 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<size_t> 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<std::reference_wrapper<const map_source_location>> 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<map_source_location>
{
constexpr auto parse(format_parse_context &ctx) -> decltype(ctx.begin()) { return ctx.end(); }
template<typename FormatContext>
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<qvec3d, 3> 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<uint32_t> 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<int32_t, 2> 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);

View File

@ -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<char>(f), std::istreambuf_iterator<char>()};
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;

View File

@ -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");

View File

@ -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());
}
}

View File

@ -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<qvec3d, 3> &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<vec_t, 2, 3> &axis, qvec2d &shift, vec_t &rotate, qvec2d &scale)
@ -1347,7 +1347,7 @@ static void ParseValve220TX(parser_t &parser, qmat<vec_t, 2, 3> &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<vec_t, 2, 3> &texMat)
@ -1378,7 +1378,7 @@ static void ParseBrushPrimTX(parser_t &parser, qmat<vec_t, 2, 3> &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<mapface_t> 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<mapface_t> 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<std::string>(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<std::string>(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<std::string>(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;
}

View File

@ -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()) {

View File

@ -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<vec_t>::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<vec_t>::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<vec_t>::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]);

View File

@ -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());
}

View File

@ -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<std::string>(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");

View File

@ -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) {