......@@ -14,7 +14,7 @@ test:all:
- cmake --build build --target unit_tests
- ./build/src/tests/unit_tests
- cmake --build build --target mfix-parser
- find -name inputs
- find -name inputs |xargs -l ./build/mfix-parser
# - find -name inputs
# - find -name inputs |xargs -l ./build/mfix-parser
tags:
- mfix-exa
......@@ -3,7 +3,9 @@
################################################################################
add_library(parser
inputs_file.cpp
parser.cpp
solver.cpp
)
target_include_directories(parser PRIVATE
......
......
// Copyright (c) 2014-2019 Dr. Colin Hirsch and Daniel Frey
// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
#ifndef TAO_PEGTL_SRC_EXAMPLES_PEGTL_DOUBLE_HPP
#define TAO_PEGTL_SRC_EXAMPLES_PEGTL_DOUBLE_HPP
#include <tao/pegtl.hpp>
namespace double_ {
// A grammar for doubles suitable for std::stod without locale support.
// See also: http://en.cppreference.com/w/cpp/string/basic_string/stof
using namespace TAO_PEGTL_NAMESPACE;
// clang-format off
struct plus_minus : opt< one< '+', '-' > > {};
struct dot : one< '.' > {};
struct inf : seq< istring< 'i', 'n', 'f' >,
opt< istring< 'i', 'n', 'i', 't', 'y' > > > {};
struct nan : seq< istring< 'n', 'a', 'n' >,
opt< one< '(' >,
plus< alnum >,
one< ')' > > > {};
template< typename D >
struct number : if_then_else< dot,
plus< D >,
seq< plus< D >, opt< dot, star< D > > > > {};
struct e : one< 'e', 'E' > {};
struct p : one< 'p', 'P' > {};
struct exponent : seq< plus_minus, plus< digit > > {};
struct decimal : seq< number< digit >, opt< e, exponent > > {};
struct hexadecimal : seq< one< '0' >, one< 'x', 'X' >, number< xdigit >, opt< p, exponent > > {};
struct grammar : seq< plus_minus, sor< hexadecimal, decimal, inf, nan > > {};
// clang-format on
} // namespace double_
#endif
......@@ -10,12 +10,42 @@
namespace parser {
using InputArray = std::vector<double>;
using InputValue = std::variant<std::string, InputArray>;
using InputInfo = std::map<std::string, InputValue>;
using NumberArray = std::vector<double>;
using StringArray = std::vector<std::string>;
using InputInfo = std::map<std::string, std::variant<NumberArray, StringArray>>;
std::optional<InputInfo> parse_inputs(std::string);
} // namespace parser
namespace solver_settings {
struct GeometryAxis {
double low;
double high;
bool periodic;
};
struct GeometrySettings {
std::tuple<GeometryAxis, GeometryAxis, GeometryAxis> axes;
};
struct SolverSettings {
GeometrySettings geometry;
};
std::optional<SolverSettings> make_solver(parser::InputInfo);
} // namespace solver_settings
namespace inputs_file {
class InputsFile {
public:
InputsFile(std::string inputs_path) : m_path(inputs_path) {}
solver_settings::SolverSettings load();
void save(solver_settings::SolverSettings);
protected:
std::string m_path;
};
} // namespace inputs_file
#endif
#include <cassert>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include "inputs.hpp"
template <class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template <class... Ts>
overloaded(Ts...)->overloaded<Ts...>; // not needed as of C++20
namespace inputs_file {
solver_settings::SolverSettings InputsFile::load() {
std::ifstream infile(m_path);
std::stringstream buffer;
buffer << infile.rdbuf();
infile.close();
auto maybe_inputinfo = parser::parse_inputs(buffer.str());
assert(maybe_inputinfo.has_value());
auto inputinfo = maybe_inputinfo.value();
// for (auto it = inputinfo.begin(); it != inputinfo.end(); ++it) {
// std::visit(
// overloaded{
// [](std::string ss) { std::cout << ss << std::endl; },
// [](parser::NumberArray aa) { std::cout << aa.size() << std::endl;
// },
// [](parser::StringArray aa) { std::cout << aa.size() << std::endl;
// },
// },
// it->second);
// }
auto maybe_solversettings = solver_settings::make_solver(inputinfo);
assert(maybe_solversettings.has_value());
return maybe_solversettings.value();
solver_settings::SolverSettings ss;
return ss;
};
void InputsFile::save(solver_settings::SolverSettings settings) {
std::ofstream out(m_path);
auto [xx, yy, zz] = settings.geometry.axes;
out << std::endl;
out << " geometry.is_periodic = " << xx.periodic << " " << yy.periodic << " "
<< zz.periodic << std::endl;
;
out << " geometry.prob_lo = " << xx.low << " " << yy.low << " " << zz.low
<< std::endl;
;
out << " geometry.prob_hi = " << xx.high << " " << yy.high << " "
<< zz.high;
;
out.close();
};
} // namespace inputs_file
......@@ -6,17 +6,17 @@
#include <tao/pegtl.hpp>
// includes
#include "double.hpp"
#include "inputs.hpp"
using namespace TAO_PEGTL_NAMESPACE;
using namespace tao::pegtl;
namespace {
struct parser_state {
std::string current_key;
std::vector<double> current_vec;
parser::InputValue current_value;
std::variant<std::vector<double>, std::vector<std::string>> current_value;
std::vector<double> current_num_vec;
std::vector<std::string> current_str_vec;
parser::InputInfo info;
};
......@@ -32,31 +32,39 @@ struct escaped_c
struct escaped : sor<escaped_x, escaped_u, escaped_U, escaped_c> {};
struct key : plus<sor<plus<alnum>, one<'_'>, one<'-'>, one<'.'>>> {};
struct character
: if_must_else<one<'\\'>, escaped, utf8::range<0x20, 0x10FFFF>> {};
struct string_literal : if_must<one<'"'>, until<one<'"'>, character>> {};
struct implicit_string : if_must<alnum, until<eolf, character>> {};
struct whitespace_delimited_array : list<double_::grammar, plus<one<' '>>> {};
struct quoted_string : if_must<one<'"'>, until<one<'"'>, character>> {};
struct unquoted_string : key {};
struct string_literal : sor<quoted_string, unquoted_string> {};
struct L_FUN : pad<string<'('>, space> {};
struct R_FUN : pad<string<')'>, space> {};
struct L_ARR : pad<string<'['>, space> {};
struct R_ARR : pad<string<']'>, space> {};
struct L_BLK : pad<string<'{'>, space> {};
struct R_BLK : pad<string<'}'>, space> {};
struct S_CLN : pad<string<';'>, space> {};
struct plus_minus : opt<one<'+', '-'>> {};
struct dot : one<'.'> {};
struct name : identifier {};
struct decimal
: if_then_else<dot, plus<digit>, seq<plus<digit>, opt<dot, star<digit>>>> {
};
struct e : one<'e', 'E'> {};
struct p : one<'p', 'P'> {};
struct exponent : seq<plus_minus, plus<digit>> {};
struct double_ : seq<plus_minus, decimal, opt<e, exponent>> {};
struct value
: sor<whitespace_delimited_array, string_literal, implicit_string> {};
struct num_array : list<double_, plus<one<' '>>> {};
struct str_array : list<string_literal, plus<one<' '>>> {};
struct value : sor<num_array, str_array> {};
struct comment : if_must<one<'#'>, until<eolf>> {};
struct key : list<identifier, one<'.'>> {};
struct keyval : seq<pad<key, space>, string<'='>, pad<value, space>> {};
struct grammar : pad<sor<keyval, comment>, space> {};
struct value_item : pad<value, blank> {};
struct value_list : list_must<value_item, one<','>> {};
struct keyval_line
: if_must<pad<key, space>, string<'='>, value_list, sor<comment, eolf>> {};
struct comment_line : seq<one<'#'>, until<eolf>> {};
struct blank_line : seq<star<one<' '>>, until<eolf>> {};
struct line : sor<comment_line, keyval_line, blank_line> {};
struct grammar : until<eof, line> {};
template <typename Rule> struct action {};
......@@ -68,45 +76,56 @@ template <> struct action<key> {
}
};
template <> struct action<string_literal> {
template <> struct action<quoted_string> {
template <typename Input>
static void apply(const Input &in, parser_state &st) {
std::stringstream ss(in.string());
std::string v;
ss >> v;
st.current_value = v.substr(1, v.length() - 2); // strip quotes
auto str_val = v.substr(1, v.length() - 2); // strip quotes
st.current_str_vec.push_back(str_val);
}
};
template <> struct action<implicit_string> {
template <> struct action<unquoted_string> {
template <typename Input>
static void apply(const Input &in, parser_state &st) {
std::stringstream ss(in.string());
std::string v;
ss >> v;
st.current_value = v;
st.current_str_vec.push_back(v);
}
};
template <> struct action<double_::grammar> {
template <> struct action<double_> {
template <typename Input>
static void apply(const Input &in, parser_state &st) {
std::stringstream ss(in.string());
double v;
ss >> v;
st.current_vec.push_back(v);
st.current_num_vec.push_back(v);
}
};
template <> struct action<num_array> {
template <typename Input>
static void apply(const Input &in, parser_state &st) {
std::stringstream ss(in.string());
st.current_value = st.current_num_vec;
st.current_num_vec.clear();
}
};
template <> struct action<whitespace_delimited_array> {
template <> struct action<str_array> {
template <typename Input>
static void apply(const Input &in, parser_state &st) {
std::stringstream ss(in.string());
st.current_value = st.current_vec;
st.current_vec.clear();
st.current_value = st.current_str_vec;
st.current_str_vec.clear();
}
};
template <> struct action<keyval> {
template <> struct action<keyval_line> {
template <typename Input>
static void apply(const Input &in, parser_state &st) {
st.info[st.current_key] = st.current_value;
......
......
src/solver.cpp 0 → 100644
#include <iostream>
#include <optional>
#include "inputs.hpp"
namespace {
void require(parser::InputInfo ii, std::string key) {
if (!ii.count(key)) {
std::cout << "missing required key: " << key << std::endl;
}
}
std::optional<solver_settings::SolverSettings>
do_make_solver(parser::InputInfo ii) {
solver_settings::SolverSettings ss;
require(ii, "geometry.prob_lo");
require(ii, "geometry.prob_hi");
require(ii, "geometry.is_periodic");
auto lows = std::get<parser::NumberArray>(ii["geometry.prob_lo"]);
auto highs = std::get<parser::NumberArray>(ii["geometry.prob_hi"]);
auto is_periodic = std::get<parser::NumberArray>(ii["geometry.is_periodic"]);
std::get<0>(ss.geometry.axes).high = highs[0];
std::get<0>(ss.geometry.axes).low = lows[0];
std::get<0>(ss.geometry.axes).periodic = is_periodic[0];
std::get<1>(ss.geometry.axes).high = highs[1];
std::get<1>(ss.geometry.axes).low = lows[1];
std::get<1>(ss.geometry.axes).periodic = is_periodic[1];
std::get<2>(ss.geometry.axes).high = highs[2];
std::get<2>(ss.geometry.axes).low = lows[2];
std::get<2>(ss.geometry.axes).periodic = is_periodic[2];
return ss;
}
} // namespace
namespace solver_settings {
std::optional<solver_settings::SolverSettings>
make_solver(parser::InputInfo ii) {
auto maybe_state = do_make_solver(ii);
if (!maybe_state.has_value()) {
return std::nullopt;
}
return maybe_state.value();
}
} // namespace solver_settings
......@@ -3,9 +3,13 @@
################################################################################
add_executable(unit_tests EXCLUDE_FROM_ALL
../inputs_file.cpp
../parser.cpp
parser/main.cpp
parser/inputs.t.cpp
../solver.cpp
inputs.t.cpp
parser.t.cpp
main.cpp
solver.t.cpp
)
target_include_directories(unit_tests PRIVATE
......
......
// tests
#include "catch2/catch.hpp"
#include <fstream>
#include <inputs.hpp>
#include <iostream>
#include <sstream>
TEST_CASE("load_save_file", "[]") {
std::string test_path("abc");
std::ofstream outfile(test_path);
std::string data(R"(
geometry.is_periodic = 0 0 0 # Is periodic in each direction?
geometry.prob_lo = 0. 0. 0. # lo corner of physical domain
geometry.prob_hi = 0.004 0.001 0.001 # hi corner of physical domain
)");
outfile << data;
outfile.close();
inputs_file::InputsFile test_file(test_path);
auto solver_setup = test_file.load();
std::get<1>(solver_setup.geometry.axes).periodic = 1;
test_file.save(solver_setup);
std::ifstream infile(test_path);
std::stringstream buffer;
buffer << infile.rdbuf();
infile.close();
CHECK(buffer.str() == R"(
geometry.is_periodic = 0 1 0
geometry.prob_lo = 0 0 0
geometry.prob_hi = 0.004 0.001 0.001)");
}
File moved
......@@ -9,14 +9,14 @@ TEST_CASE("comment", "[]") {
CHECK(maybe_st.has_value());
}
TEST_CASE("double_int", "[]") {
TEST_CASE("integer", "[]") {
auto maybe_st = parser::parse_inputs(R"(
mfix.max_step = 100
)");
REQUIRE(maybe_st.has_value());
auto info = maybe_st.value();
auto value = info["mfix.max_step"];
auto aa = std::get<parser::InputArray>(value);
auto aa = std::get<parser::NumberArray>(value);
CHECK(aa.size() == 1);
CHECK(aa[0] == 100);
}
......@@ -28,7 +28,7 @@ mfix.stop_time = 0.20
REQUIRE(maybe_st.has_value());
auto info = maybe_st.value();
auto value = info["mfix.stop_time"];
auto aa = std::get<parser::InputArray>(value);
auto aa = std::get<parser::NumberArray>(value);
CHECK(aa.size() == 1);
CHECK(aa[0] == 0.2);
}
......@@ -40,8 +40,9 @@ TEST_CASE("string", "[]") {
REQUIRE(maybe_st.has_value());
auto info = maybe_st.value();
auto value = info["mfix.particle_init_type"];
auto ss = std::get<std::string>(value);
CHECK(ss == "Auto");
auto ss = std::get<parser::StringArray>(value);
CHECK(ss.size() == 1);
CHECK(ss[0] == "Auto");
}
TEST_CASE("implicit string", "[]") {
......@@ -51,11 +52,12 @@ fluid.solve = fluid
REQUIRE(maybe_st.has_value());
auto info = maybe_st.value();
auto value = info["fluid.solve"];
auto ss = std::get<std::string>(value);
CHECK(ss == "fluid");
auto ss = std::get<parser::StringArray>(value);
CHECK(ss.size() == 1);
CHECK(ss[0] == "fluid");
}
TEST_CASE("array", "[]") {
TEST_CASE("double array", "[]") {
auto maybe_st = parser::parse_inputs(R"(
mfix.gravity = -9.81 0.0 0.0
)");
......@@ -63,12 +65,61 @@ TEST_CASE("array", "[]") {
REQUIRE(maybe_st.has_value());
auto info = maybe_st.value();
auto value = info["mfix.gravity"];
auto aa = std::get<parser::InputArray>(value);
auto aa = std::get<parser::NumberArray>(value);
CHECK(aa.size() == 3);
CHECK(aa[0] == -9.81);
CHECK(aa[1] == 0.0);
CHECK(aa[2] == 0.0);
}
TEST_CASE("string array", "[]") {
auto maybe_st = parser::parse_inputs(R"(
mfix.regions = abc ijk xyz
)");
REQUIRE(maybe_st.has_value());
auto info = maybe_st.value();
auto value = info["mfix.regions"];
auto aa = std::get<parser::StringArray>(value);
CHECK(aa.size() == 3);
CHECK(aa[0] == "abc");
CHECK(aa[1] == "ijk");
CHECK(aa[2] == "xyz");
}
TEST_CASE("multiple arrays", "[]") {
auto maybe_st = parser::parse_inputs(R"(
geometry.prob_lo = -4 -1 -2
geometry.prob_hi = 0.004 0.001 0.001
geometry.is_periodic = 1 0 1
)");
REQUIRE(maybe_st.has_value());
auto info = maybe_st.value();
CHECK(info.count("geometry.prob_hi"));
CHECK(info.count("geometry.prob_lo"));
CHECK(info.count("geometry.is_periodic"));
auto highs = info["geometry.prob_lo"];
auto aa = std::get<parser::NumberArray>(highs);
CHECK(aa.size() == 3);
CHECK(aa[0] == -4);
CHECK(aa[1] == -1);
CHECK(aa[2] == -2);
auto lows = info["geometry.prob_hi"];
auto bb = std::get<parser::NumberArray>(lows);
CHECK(bb.size() == 3);
CHECK(bb[0] == 0.004);
CHECK(bb[1] == 0.001);
CHECK(bb[2] == 0.001);
auto pers = info["geometry.is_periodic"];
auto cc = std::get<parser::NumberArray>(pers);
CHECK(cc.size() == 3);
CHECK(cc[0] == 1);
CHECK(cc[1] == 0);
CHECK(cc[2] == 1);
}
TEST_CASE("whole_file", "[]") {
auto maybe_st = parser::parse_inputs(R"(
......
......
// tests
#include "catch2/catch.hpp"
#include <inputs.hpp>
TEST_CASE("from_origin", "[]") {
parser::InputInfo ii;
ii["geometry.prob_lo"] = parser::NumberArray({0, 0, 0});
ii["geometry.prob_hi"] = parser::NumberArray({0.004, 0.001, 0.001});
ii["geometry.is_periodic"] = parser::NumberArray({0, 0, 0});
auto maybe_sv = solver_settings::make_solver(ii);
REQUIRE(maybe_sv.has_value());
auto sv = maybe_sv.value();
auto [xx, yy, zz] = sv.geometry.axes;
CHECK(xx.high == 0.004);
CHECK(xx.low == 0);
CHECK(yy.high == 0.001);
CHECK(yy.low == 0);
CHECK(zz.high == 0.001);
CHECK(zz.low == 0);
CHECK_FALSE(xx.periodic);
CHECK_FALSE(yy.periodic);
CHECK_FALSE(zz.periodic);
}
TEST_CASE("negative_positive", "[]") {
parser::InputInfo ii;
ii["geometry.prob_lo"] = std::vector<double>({-4, -5, -6});
ii["geometry.prob_hi"] = std::vector<double>({11.1, 22.2, 33.3});
ii["geometry.is_periodic"] = parser::NumberArray({1, 0, 1});
auto maybe_sv = solver_settings::make_solver(ii);
REQUIRE(maybe_sv.has_value());
auto sv = maybe_sv.value();
auto [xx, yy, zz] = sv.geometry.axes;
CHECK(xx.high == 11.1);
CHECK(xx.low == -4);
CHECK(xx.periodic);
CHECK(yy.high == 22.2);
CHECK(yy.low == -5);
CHECK(zz.high == 33.3);
CHECK(zz.low == -6);
CHECK(zz.periodic);
CHECK_FALSE(yy.periodic);
}
Subproject commit 054e44694f6c8cd24259b70f48cc09d84a9af395
Subproject commit ede3ce17aef7da27648b195dc77814cb1d3c313d