Beatmup
listing.cpp
Go to the documentation of this file.
1 /*
2  Beatmup image and signal processing library
3  Copyright (C) 2020, lnstadrum
4 
5  This program is free software: you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation, either version 3 of the License, or
8  (at your option) any later version.
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18 
19 #include "listing.h"
20 #include "input_stream.h"
21 #include "../exception.h"
22 #include <cstring>
23 
24 
25 using namespace Beatmup;
26 
27 
29 public:
30  typedef std::map<std::string, std::vector<Listing::Block>> Content;
31 private:
33  std::vector<Listing::Block>* currentChapter;
34  std::string chapterIndent;
36 
37  void extractKeyValuePair(std::string line) {
38  const auto delim = line.find(":");
39  if (delim != std::string::npos) {
40  size_t i = delim + 1;
41  while (i < line.size() && line[i] == ' ')
42  ++i;
43  currentChapter->back().mapping.emplace(line.substr(0, delim), line.substr(i));
44  return;
45  }
46  throw RuntimeError("Line " + std::to_string(lineCounter) + ": cannot extract key-value pair from " + line);
47  }
48 
49  void processLine(std::string line) {
50  const std::string originalLine = line;
51  lineCounter++;
52 
53  if (line.empty())
54  return;
55 
56  // CR+LF
57  if (line.back() == '\r')
58  line.pop_back();
59 
60  // process comments
61  auto numberSign = line.find('#');
62  if (numberSign != std::string::npos) {
63  bool quoted = false;
64  for (size_t i = 0; i < numberSign - 1; ++i)
65  if (line[i] == '"')
66  quoted = !quoted;
67  // the number sign '#' is not quoted
68  if (!quoted)
69  line = line.substr(0, numberSign);
70  }
71 
72  // right trim
73  auto l = line.find_last_not_of(' ');
74  if (l == std::string::npos)
75  return;
76  if (l != line.length() - 1)
77  line = line.substr(0, l + 1);
78 
79  // empty
80  if (line.empty())
81  return;
82 
83  // new chapter
84  if (line.front() != ' ' && line.front() != '-' && line.back() == ':') {
85  line.pop_back();
86  auto it = chapters.emplace(std::piecewise_construct, std::forward_as_tuple(line), std::forward_as_tuple());
87  currentChapter = &it.first->second;
88  return;
89  }
90 
91  // new entry
92  if (currentChapter) {
93  // no chapter indent yet defined, expecting the one
94  if (chapterIndent.empty()) {
95  auto i = line.find_first_not_of(' ');
96  if (i != std::string::npos && i + 2 < line.length() && line[i] == '-' && line[i + 1] == ' ') {
97  chapterIndent = line.substr(0, i + 2);
98  currentChapter->push_back(lineCounter);
99  extractKeyValuePair(line.substr(i + 2));
100  return;
101  }
102  }
103 
104  // if the line starts with the chapter indent, new entry
105  else if (line.substr(0, chapterIndent.length()) == chapterIndent) {
106  currentChapter->push_back(lineCounter);
107  extractKeyValuePair(line.substr(chapterIndent.length()));
108  return;
109  }
110 
111  // if the line starts with a whitespace ident as long as the chapter indent, new key-value pair
112  else if (line.find_first_not_of(' ') == chapterIndent.length()) {
113  extractKeyValuePair(line.substr(chapterIndent.length()));
114  return;
115  }
116  }
117 
118  throw RuntimeError("Line " + std::to_string(lineCounter) + ": unexpected indent\n" + originalLine);
119  }
120 
121 
122 public:
124  static const int SIZE = 1024;
125  char buffer[SIZE];
126  std::string line;
127  while (!stream.eof()) {
128  std::memset(buffer, 0, SIZE);
129  stream(buffer, SIZE);
130  int i = 0;
131  while (i < SIZE && buffer[i] != 0) {
132  if (buffer[i] == '\n') {
133  processLine(line);
134  line = "";
135  }
136  else
137  line += buffer[i];
138  ++i;
139  }
140  }
141  processLine(line);
142  }
143 
144 
145  Parser(std::istream& stream, Content& chapters) : chapters(chapters), currentChapter(nullptr), lineCounter(0) {
146  for (std::string line; std::getline(stream, line); )
147  processLine(line);
148  }
149 };
150 
151 
152 void Listing::Block::printOut(std::ostream& str) {
153  bool first = true;
154  for (auto& it : mapping) {
155  str << (first ? " - " : " ") << it.first << ": " << it.second << std::endl;
156  first = false;
157  }
158 }
159 
160 
161 std::string Listing::Block::operator[](const std::string& key) const {
162  auto it = mapping.find(key);
163  if (it == mapping.cend())
164  if (lineNumber > 0) {
165  throw InvalidArgument("Key not found in a block at line " + std::to_string(lineNumber) + ": " + key);
166  }
167  else {
168  throw InvalidArgument("Key not found: " + key);
169  }
170  return it->second;
171 }
172 
173 
175  Parser parser(stream, chapters);
176 }
177 
178 
179 Listing::Listing(std::istream& stream) {
180  Parser parser(stream, chapters);
181 }
182 
183 
184 void Listing::printOut(std::ostream& str) {
185  for (auto& it : chapters) {
186  str << it.first << ":" << std::endl;
187  for (auto& i : it.second)
188  i.printOut(str);
189  }
190 }
191 
192 const std::vector<Listing::Block>& Listing::operator[](const std::string& key) const {
193  auto it = chapters.find(key);
194  if (it == chapters.cend())
195  throw new InvalidArgument("Key not found: " + key);
196  return it->second;
197 }
198 
199 
200 void Listing::emplace(const std::string& key, Block&& block) {
201  if (!has(key))
202  chapters.emplace(std::piecewise_construct, std::forward_as_tuple(key), std::forward_as_tuple());
203  chapters[key].emplace_back(block);
204 }
Minimal input stream interface.
Definition: input_stream.h:27
virtual bool eof() const =0
Returns true, if the end of the stream is reached (i.e., all the data is read or the stream is empty)...
Set of key-value pairs.
Definition: listing.h:46
std::map< std::string, std::string > mapping
Definition: listing.h:50
void printOut(std::ostream &stream)
Prints out the block to an output stream.
Definition: listing.cpp:152
std::string operator[](const std::string &key) const
Retrieves a value by key.
Definition: listing.cpp:161
const std::vector< Block > & operator[](const std::string &key) const
Retrieves a chapter in the listing by its name.
Definition: listing.cpp:192
bool has(const std::string &key) const
Returns true if a specific chapter is present in the listing.
Definition: listing.h:132
void printOut(std::ostream &stream)
Prints out the listing to an output stream.
Definition: listing.cpp:184
std::map< std::string, std::vector< Block > > chapters
Definition: listing.h:114
void emplace(const std::string &key, Block &&block)
Adds a block to a chapter.
Definition: listing.cpp:200
std::vector< Listing::Block > * currentChapter
Definition: listing.cpp:33
void processLine(std::string line)
Definition: listing.cpp:49
Parser(InputStream &stream, Content &chapters)
Definition: listing.cpp:123
void extractKeyValuePair(std::string line)
Definition: listing.cpp:37
std::string chapterIndent
Definition: listing.cpp:34
std::map< std::string, std::vector< Listing::Block > > Content
Definition: listing.cpp:30
Parser(std::istream &stream, Content &chapters)
Definition: listing.cpp:145
std::string to_string(Beatmup::NNets::ActivationFunction function)
Beatmup::AffineMapping & mapping
JNIEnv jobject jstring str