dyng
DynamicGraphLayout
parse.h
Go to the documentation of this file.
1 /*
2  Copyright 2020 František Bráblík
3 
4  Licensed under the Apache License, Version 2.0 (the "License");
5  you may not use this file except in compliance with the License.
6  You may obtain a copy of the License at
7 
8  http://www.apache.org/licenses/LICENSE-2.0
9 
10  Unless required by applicable law or agreed to in writing, software
11  distributed under the License is distributed on an "AS IS" BASIS,
12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  See the License for the specific language governing permissions and
14  limitations under the License.
15 */
31 #pragma once
32 
33 #include "dynamic_graph.h"
34 
35 #include <ostream>
36 #include <istream>
37 #include <string> // string literals
38 #include <cctype> // std::isspace
39 #include <stdexcept>
40 
41 namespace dyng {
42 
43 namespace detail {
44 
45 inline std::string reduce_whitespace(const std::string& str) {
46  std::string result;
47  bool ignore = false;
48  for (auto& ch : str) {
49  bool white = std::isspace(ch);
50  if (!ignore || !white) {
51  result += white ? ' ' : ch;
52  }
53  ignore = white;
54  }
55  if (!result.empty() && result.front() == ' ') {
56  result.erase(result.begin());
57  }
58  if (!result.empty() && result.back() == ' ') {
59  result.pop_back();
60  }
61  return result;
62 }
63 
64 inline std::vector<std::string> split(const std::string& str, char dlm = ' ') {
65  std::vector<std::string> result;
66  std::string chunk;
67  for (unsigned i = 0; i < str.length(); ++i) {
68  if (str[i] != dlm) {
69  chunk += str[i];
70  }
71  if (str[i] == dlm || i == str.length() - 1) {
72  result.push_back(chunk);
73  chunk.clear();
74  }
75  }
76  return result;
77 }
78 
79 inline bool skip_until(std::istream& in, char ch) {
80  do {
81  if (!in.good()) {
82  return false;
83  }
84  } while (in.get() != ch);
85  return true;
86 }
87 
88 inline std::string read_until(std::istream& in, char ch) {
89  using namespace std::string_literals;
90  std::string result;
91  char last;
92  do {
93  if (!in.good()) {
94  throw std::runtime_error("stream ended, expected '"s + ch + "'"s);
95  }
96  last = in.get();
97  if (last != ch) {
98  result += last;
99  }
100  } while (last != ch);
101  return result;
102 }
103 
104 inline void validate(char ch) {
105  using namespace std::string_literals;
106  if (ch == std::char_traits<char>::eof()) {
107  throw std::runtime_error("unexpected end of input");
108  }
109  if (!std::isspace(ch)) {
110  throw std::runtime_error("unexpected character '"s + ch + "'"s);
111  }
112 }
113 
114 } // namespace detail
115 
116 
117 inline std::ostream& operator<<(std::ostream& out, const node& n) {
118  out << "n " << n.id()
119  << " " << n.pos().x
120  << " " << n.pos().y
121  << ";";
122  return out;
123 }
124 
125 inline std::ostream& operator<<(std::ostream& out, const edge& e) {
126  out << "e " << e.id()
127  << " " << e.one_id()
128  << " " << e.two_id()
129  << ";";
130  return out;
131 }
132 
133 template<typename N, typename E>
134 inline std::ostream& operator<<(std::ostream& out, const graph<N, E>& g) {
135  out << "[\n";
136  for (const node& n : g.nodes()) {
137  out << n << "\n";
138  }
139  for (const edge& e : g.edges()) {
140  out << e << "\n";
141  }
142  out << "]\n";
143  return out;
144 }
145 
146 inline std::ostream& operator<<(std::ostream& out, const dynamic_graph& dgraph) {
147  out << "{\n";
148  for (const auto& s : dgraph.states()) {
149  out << s;
150  }
151  out << "}\n";
152  return out;
153 }
154 
155 inline std::istream& operator>>(std::istream& in, node& n) {
156  if (!detail::skip_until(in, 'n')) {
157  return in;
158  }
159  std::string str = detail::read_until(in, ';');
160  str = detail::reduce_whitespace(str);
161  auto bits = detail::split(str);
162  if (bits.size() != 3) {
163  throw std::runtime_error("invalid number of node parameters");
164  }
165  try {
166  n = node(std::stoi(bits[0]));
167  n.pos().x = std::stof(bits[1]);
168  n.pos().y = std::stof(bits[2]);
169  } catch (const std::exception&) {
170  throw std::runtime_error("invalid node parameters");
171  }
172  return in;
173 }
174 
175 inline std::istream& operator>>(std::istream& in, edge& e) {
176  if (!detail::skip_until(in, 'e')) {
177  return in;
178  }
179  std::string str = detail::read_until(in, ';');
180  str = detail::reduce_whitespace(str);
181  auto bits = detail::split(str);
182  if (bits.size() != 3) {
183  throw std::runtime_error("invalid number of edge parameters");
184  }
185  try {
186  e = edge(std::stoi(bits.at(0)), std::stoi(bits.at(1)), std::stoi(bits.at(2)));
187  } catch (const std::exception&) {
188  throw std::runtime_error("invalid edge parameters");
189  }
190  return in;
191 }
192 
193 template<typename N, typename E>
194 inline std::istream& operator>>(std::istream& in, graph<N, E>& g) {
195  if (!detail::skip_until(in, '[')) {
196  return in;
197  }
198  while (in.good()) {
199  switch (in.peek()) {
200  case ']':
201  in.get();
202  return in;
203  case 'n': {
204  node n(1337); // any value, will be overwritten
205  in >> n;
206  g.push_node(n);
207  } break;
208  case 'e': {
209  edge e(1337, 666, 420); // any values, will be overwritten
210  in >> e;
211  g.push_edge(e);
212  } break;
213  default:
214  detail::validate(in.get());
215  break;
216  }
217  }
218  return in;
219 }
220 
221 inline std::istream& operator>>(std::istream& in, dynamic_graph& dgraph) {
222  if (!detail::skip_until(in, '{')) {
223  return in;
224  }
225  std::vector<graph_state> states;
226  while (in.good()) {
227  switch (in.peek()) {
228  case '}':
229  dgraph.build(std::move(states));
230  in.get();
231  return in;
232  case '[': {
233  graph_state state;
234  in >> state;
235  states.push_back(std::move(state));
236  } break;
237  default:
238  detail::validate(in.get());
239  break;
240  }
241  }
242  throw std::out_of_range("stream ended, expected '}'");
243  return in;
244 }
245 
246 } // namespace dyng