Added a routine for parsing location lists using Lexy.

This commit is contained in:
David Given
2025-08-15 23:39:21 +02:00
parent dfa56c6b08
commit 99c0e95a2f
5 changed files with 172 additions and 4 deletions

View File

@@ -4,20 +4,22 @@ cxxlibrary(
name="data",
srcs=[
"./fluxmap.cc",
"./sector.cc",
"./layout.cc",
"./image.cc",
"./fluxmapreader.cc",
"./fluxpattern.cc",
"./image.cc",
"./layout.cc",
"./locations.cc",
"./sector.cc",
],
hdrs={
"lib/data/flux.h": "./flux.h",
"lib/data/fluxmap.h": "./fluxmap.h",
"lib/data/sector.h": "./sector.h",
"lib/data/layout.h": "./layout.h",
"lib/data/locations.h": "./locations.h",
"lib/data/image.h": "./image.h",
"lib/data/fluxmapreader.h": "./fluxmapreader.h",
"lib/data/fluxpattern.h": "./fluxpattern.h",
},
deps=["lib/core", "lib/config", "+protocol"],
deps=["lib/core", "lib/config", "+protocol", "dep/lexy"],
)

142
lib/data/locations.cc Normal file
View File

@@ -0,0 +1,142 @@
#include "lib/core/globals.h"
#include "lib/data/locations.h"
#include "lib/core/logger.h"
#include <lexy/dsl.hpp>
#include <lexy/input/string_input.hpp>
#include <lexy/callback.hpp>
#include <lexy/callback/container.hpp>
#include <lexy/callback/fold.hpp>
#include <lexy/action/parse.hpp>
#include <lexy_ext/report_error.hpp>
#include "fmt/ranges.h"
namespace
{
struct Member
{
int start, end, step;
};
}
namespace grammar
{
namespace dsl = lexy::dsl;
struct member
{
static constexpr auto rule = []()
{
auto integer = dsl::integer<int>(dsl::digits<>);
auto step = dsl::lit_c<'x'> >> integer;
auto end = dsl::lit_c<'-'> >> integer;
return integer + dsl::opt(end) + dsl::opt(step);
}();
static constexpr auto value = lexy::callback<Member>(
[](int start, std::optional<int> end, std::optional<int> step)
{
return Member{start, end.value_or(start), step.value_or(1)};
});
};
struct members
{
static constexpr auto rule =
dsl::list(dsl::p<member>, dsl::sep(dsl::lit_c<','>));
static constexpr auto value = lexy::fold_inplace<std::vector<int>>({},
[](std::vector<int>& result, const Member& item)
{
if (item.start < 0)
error("range start {} must be at least 0", item.start);
if (item.end < item.start)
error("range end {} must be at least the start", item.end);
if (item.step < 1)
error("range step {} must be at least one", item.step);
for (int i = item.start; i <= item.end; i += item.step)
result.push_back(i);
});
};
struct ch
{
static constexpr auto rule = dsl::lit_c<'c'> + dsl::p<members> +
dsl::lit_c<'h'> + dsl::p<members>;
static constexpr auto value = lexy::callback<std::vector<Location>>(
[](const std::vector<int>& cs, const std::vector<int>& hs)
{
std::vector<Location> result;
for (int c : cs)
for (int h : hs)
result.push_back(Location{c, h});
return result;
});
};
struct chs
{
static constexpr auto rule =
dsl::list(dsl::p<ch>, dsl::sep(dsl::ascii::space));
static constexpr auto value = lexy::fold_inplace<std::vector<Location>>(
{},
[](std::vector<Location>& result, const std::vector<Location>& item)
{
result.insert(result.end(), item.begin(), item.end());
});
};
};
struct _error_collector
{
struct _sink
{
typedef std::vector<std::string> return_type;
return_type _results;
template <typename Input, typename Reader, typename Tag>
void operator()(const lexy::error_context<Input>& context,
const lexy::error<Reader, Tag>& error)
{
auto context_location =
lexy::get_input_location(context.input(), context.position());
auto location = lexy::get_input_location(
context.input(), error.position(), context_location.anchor());
std::string s;
if constexpr (std::is_same_v<Tag, lexy::expected_literal> ||
std::is_same_v<Tag, lexy::expected_keyword>)
s = fmt::format("expected '{}'", error.string());
else if constexpr (std::is_same_v<Tag, lexy::expected_char_class>)
s = fmt::format("expected {}", error.name());
else
s = error.message();
_results.push_back(fmt::format("{} at '{}'", s, error.position()));
}
return_type finish() &&
{
return _results;
}
};
constexpr auto sink() const
{
return _sink{};
}
};
constexpr auto error_collector = _error_collector();
std::vector<Location> parseLocationsString(const std::string& s)
{
auto input = lexy::string_input(s);
auto result = lexy::parse<grammar::chs>(input, error_collector);
if (result.is_error())
{
error(fmt::format(
"range parse error: {}", fmt::join(result.errors(), "; ")));
}
return result.value();
}

12
lib/data/locations.h Normal file
View File

@@ -0,0 +1,12 @@
#pragma once
class Location
{
public:
bool operator==(const Location&) const = default;
int track;
int head;
};
extern std::vector<Location> parseLocationsString(const std::string& s);