mirror of
https://github.com/davidgiven/fluxengine.git
synced 2025-10-31 11:17:01 -07:00
Added a routine for parsing location lists using Lexy.
This commit is contained in:
@@ -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
142
lib/data/locations.cc
Normal 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
12
lib/data/locations.h
Normal 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);
|
||||
Reference in New Issue
Block a user