From f435375fd0fbdb08ad91a0fce0485e08c86623b2 Mon Sep 17 00:00:00 2001
From: Mark Meredith <mark.meredith@netl.doe.gov>
Date: Fri, 8 May 2020 10:56:07 -0400
Subject: [PATCH] Catch parse errors better

---
 src/csg/impl/levelset_3d.cpp  |  6 ++++--
 src/inputs/geometry.cpp       | 19 +++++++++----------
 src/inputs/mesh.cpp           |  2 +-
 src/inputs/parser.cpp         | 14 ++++++++++++--
 src/inputs/solver.cpp         | 26 ++++++++------------------
 src/inputs/solver.hpp         |  6 ++----
 src/inputs/solver_impl.hpp    |  7 +++++++
 src/inputs/tests/solver.t.cpp | 10 +---------
 src/inputs/time.cpp           |  2 +-
 subprojects/PEGTL             |  2 +-
 10 files changed, 46 insertions(+), 48 deletions(-)
 create mode 100644 src/inputs/solver_impl.hpp

diff --git a/src/csg/impl/levelset_3d.cpp b/src/csg/impl/levelset_3d.cpp
index 8f6821d..e3498d3 100644
--- a/src/csg/impl/levelset_3d.cpp
+++ b/src/csg/impl/levelset_3d.cpp
@@ -131,8 +131,10 @@ double signed_distance_3d(const Cylinder &cyl, double xx, double yy,
 double signed_distance_3d(const LinearExtrude &lin_ext, double xx, double yy,
                           double zz) {
   // TODO: support height, center and twist
-  double sign_z = (0 <= zz && zz <= lin_ext.height) ? 1.0 : 1.0; // unused zz warning workaround
-  return sign_z*signed_distance_2d(lin_ext.group, xx, yy);
+  double sign_z = (0 <= zz && zz <= lin_ext.height)
+                      ? 1.0
+                      : 1.0; // unused zz warning workaround
+  return sign_z * signed_distance_2d(lin_ext.group, xx, yy);
 }
 
 double signed_distance_3d(const RotateExtrude &rot_ext, double xx, double yy,
diff --git a/src/inputs/geometry.cpp b/src/inputs/geometry.cpp
index 7f48246..25ab51f 100644
--- a/src/inputs/geometry.cpp
+++ b/src/inputs/geometry.cpp
@@ -1,20 +1,20 @@
 #include <iostream>
 
-#include "solver.hpp"
+#include "solver_impl.hpp"
 
 namespace solver {
 
 std::optional<solver::GeometrySettings> make_geometry(solver::InputInfo ii) {
   solver::GeometrySettings geo;
 
-  if (!ii.count(CSG_FILENAME)) {
-    require(CSG_FILENAME);
-    return std::nullopt;
-  }
-  auto csg_filename = std::get<solver::StringArray>(ii[CSG_FILENAME]);
-  if (csg_filename.size() != 1) {
-    std::cout << CSG_FILENAME << " should only have 1 element" << std::endl;
-    return std::nullopt;
+  if (ii.count(CSG_FILENAME)) {
+    auto csg_filename = std::get<solver::StringArray>(ii[CSG_FILENAME]);
+    if (csg_filename.size() != 1) {
+      std::cout << CSG_FILENAME << " should only have 1 element" << std::endl;
+      return std::nullopt;
+    }
+  } else {
+    geo.csg_filename = "";
   }
 
   if (!ii.count(PROB_LO)) {
@@ -45,7 +45,6 @@ std::optional<solver::GeometrySettings> make_geometry(solver::InputInfo ii) {
     return std::nullopt;
   }
 
-  geo.csg_filename = csg_filename[0];
   std::get<0>(geo.axes).high = highs[0];
   std::get<0>(geo.axes).low = lows[0];
   std::get<0>(geo.axes).periodic = is_periodic[0];
diff --git a/src/inputs/mesh.cpp b/src/inputs/mesh.cpp
index 76fdb76..26eaecd 100644
--- a/src/inputs/mesh.cpp
+++ b/src/inputs/mesh.cpp
@@ -1,6 +1,6 @@
 #include <iostream>
 
-#include "solver.hpp"
+#include "solver_impl.hpp"
 
 namespace solver {
 
diff --git a/src/inputs/parser.cpp b/src/inputs/parser.cpp
index 2a4c78b..68a65c2 100644
--- a/src/inputs/parser.cpp
+++ b/src/inputs/parser.cpp
@@ -1,5 +1,6 @@
 // standard includes
 #include <assert.h>
+#include <iostream>
 #include <memory>
 
 // subproject includes
@@ -136,9 +137,18 @@ template <> struct action<keyval_line> {
 std::optional<parser_state> do_parse(std::string str) {
   parser_state st;
   memory_input in(str, "std::cin");
-  if (!parse<grammar, action>(in, st)) {
-    return std::nullopt;
+
+  try {
+    if (!parse<grammar, action>(in, st)) {
+      return std::nullopt;
+    }
+  } catch (const parse_error &e) {
+    const auto p = e.positions.front();
+    std::cerr << e.what() << std::endl
+              << in.line_at(p) << std::endl
+              << std::string(p.byte_in_line, ' ') << '^' << std::endl;
   }
+
   return st;
 }
 
diff --git a/src/inputs/solver.cpp b/src/inputs/solver.cpp
index 02a506d..fc1a46f 100644
--- a/src/inputs/solver.cpp
+++ b/src/inputs/solver.cpp
@@ -1,8 +1,7 @@
 #include <iostream>
-#include <optional>
 #include <sstream>
 
-#include "solver.hpp"
+#include "solver_impl.hpp"
 
 namespace solver {
 
@@ -10,35 +9,26 @@ void require(std::string key) {
   std::cout << "missing required key:  " << key << std::endl;
 }
 
-std::optional<solver::SolverSettings> do_make_solver(solver::InputInfo ii) {
+std::pair<solver::SolverSettings, std::vector<std::string>>
+make_solver(solver::InputInfo ii) {
+  std::vector<std::string> messages;
   solver::SolverSettings ss;
   auto new_geo = make_geometry(ii);
   if (!new_geo.has_value()) {
-    std::cout << "Problem making geometry" << std::endl;
-    return std::nullopt;
+    messages.push_back("Problem making geometry");
   }
   auto new_mesh = make_mesh(ii);
   if (!new_mesh.has_value()) {
-    std::cout << "Problem making mesh" << std::endl;
-    return std::nullopt;
+    messages.push_back("Problem making mesh");
   }
   auto new_time = make_time(ii);
   if (!new_time.has_value()) {
-    std::cout << "Problem making time" << std::endl;
-    return std::nullopt;
+    messages.push_back("Problem making time");
   }
   ss.geometry = new_geo.value();
   ss.time = new_time.value();
   ss.mesh = new_mesh.value();
-  return ss;
-}
-
-std::optional<solver::SolverSettings> make_solver(solver::InputInfo ii) {
-  auto maybe_state = do_make_solver(ii);
-  if (!maybe_state.has_value()) {
-    return std::nullopt;
-  }
-  return maybe_state.value();
+  return std::make_pair(ss, messages);
 }
 
 std::string serialize(solver::SolverSettings settings) {
diff --git a/src/inputs/solver.hpp b/src/inputs/solver.hpp
index 8136360..0d9aa33 100644
--- a/src/inputs/solver.hpp
+++ b/src/inputs/solver.hpp
@@ -57,10 +57,8 @@ struct SolverSettings {
   TimeSettings time;
 };
 
-std::optional<SolverSettings> make_solver(solver::InputInfo);
-std::optional<GeometrySettings> make_geometry(solver::InputInfo);
-std::optional<MeshSettings> make_mesh(solver::InputInfo);
-std::optional<TimeSettings> make_time(solver::InputInfo);
+std::pair<solver::SolverSettings, std::vector<std::string>>
+    make_solver(solver::InputInfo);
 std::string serialize(solver::SolverSettings);
 
 void require(std::string);
diff --git a/src/inputs/solver_impl.hpp b/src/inputs/solver_impl.hpp
new file mode 100644
index 0000000..5cd67e7
--- /dev/null
+++ b/src/inputs/solver_impl.hpp
@@ -0,0 +1,7 @@
+#include <solver.hpp>
+
+namespace solver {
+std::optional<GeometrySettings> make_geometry(solver::InputInfo);
+std::optional<MeshSettings> make_mesh(solver::InputInfo);
+std::optional<TimeSettings> make_time(solver::InputInfo);
+} // namespace solver
diff --git a/src/inputs/tests/solver.t.cpp b/src/inputs/tests/solver.t.cpp
index f160a05..e0ed6f7 100644
--- a/src/inputs/tests/solver.t.cpp
+++ b/src/inputs/tests/solver.t.cpp
@@ -5,12 +5,6 @@
 #include <inputs.hpp>
 #include <solver.hpp>
 
-TEST_CASE("empty (invalid) map", "[]") {
-  solver::InputInfo ii;
-  auto maybe_sv = solver::make_solver(ii);
-  CHECK_FALSE(maybe_sv.has_value());
-}
-
 TEST_CASE("from_origin", "[]") {
   solver::InputInfo ii;
 
@@ -42,9 +36,7 @@ TEST_CASE("from_origin", "[]") {
   ii[CFL] = solver::NumberArray({0.77});
   ii[TCOLL_RATIO] = solver::NumberArray({49.2});
 
-  auto maybe_sv = solver::make_solver(ii);
-  REQUIRE(maybe_sv.has_value());
-  auto sv = maybe_sv.value();
+  auto [sv, messages] = solver::make_solver(ii);
 
   SECTION(" Geometry fields ") {
     auto [xx, yy, zz] = sv.geometry.axes;
diff --git a/src/inputs/time.cpp b/src/inputs/time.cpp
index 483a001..bd6601e 100644
--- a/src/inputs/time.cpp
+++ b/src/inputs/time.cpp
@@ -1,6 +1,6 @@
 #include <iostream>
 
-#include "solver.hpp"
+#include "solver_impl.hpp"
 
 namespace solver {
 
diff --git a/subprojects/PEGTL b/subprojects/PEGTL
index ede3ce1..47e878a 160000
--- a/subprojects/PEGTL
+++ b/subprojects/PEGTL
@@ -1 +1 @@
-Subproject commit ede3ce17aef7da27648b195dc77814cb1d3c313d
+Subproject commit 47e878ad4fd72c91253c9d47b6f17e001ca2dfcf
-- 
GitLab