1 /*
2     PADRING -- a padring generator for ASICs.
3 
4     Copyright (c) 2019, Niels Moseley <niels@symbioticeda.com>
5 
6     Permission to use, copy, modify, and/or distribute this software for any
7     purpose with or without fee is hereby granted, provided that the above
8     copyright notice and this permission notice appear in all copies.
9 
10     THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11     WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12     MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13     ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14     WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15     ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16     OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 
18 */
19 
20 #ifndef linereader_h
21 #define linereader_h
22 
23 #include <iterator>
24 #include <iostream>
25 #include <list>
26 #include <string>
27 #include <string_view>
28 
29 /** Takes a line and produces a list of std::string_view objects,
30  *  one for each whitespace-separated chunk.
31  **/
32 class TextChunkifier
33 {
34 public:
TextChunkifier(const std::string & separators)35     TextChunkifier(const std::string &separators) : m_separators(separators) {}
36 
37     /** Submit a string and generate a list of std::string_view objects/chunks.
38      *  Use iterators to access the chunks **/
submitString(const std::string & line)39     void submitString(const std::string &line)
40     {
41         uint32_t state = 0;
42         int32_t  idx = 0;
43         int32_t  startIdx = -1; // -1 signals no start found
44 
45         m_chunks.clear();
46 
47         while(idx < line.size())
48         {
49             char c = line[idx];
50             switch(state)
51             {
52             case 0: // idle state
53                 if (m_separators.find(c) != std::string::npos)
54                 {
55                     startIdx = -1;  // no start found
56                     continue;
57                 }
58                 else
59                 {
60                     // non whitespace char ->
61                     // the start of a new chunk
62                     startIdx = idx;
63                     state = 1;
64                 }
65                 break;
66             case 1: // inside a chunk
67                 if (m_separators.find(c) != std::string::npos)
68                 {
69                     // chunk ended, so we emit it!
70                     std::string_view chunk{line.c_str() + startIdx, static_cast<uint32_t>(idx - startIdx)};
71                     m_chunks.push_back(chunk);
72                     startIdx = -1;  // no start found
73                     state = 0;
74                 }
75                 break;
76             default:
77                 state = 0;
78             }
79             idx++;
80         }
81 
82         // check if there was a chunk at the end of the line.
83         // if there is one, emit it!
84         if (startIdx != -1)
85         {
86             std::string_view chunk{line.c_str() + startIdx, static_cast<uint32_t>(idx - startIdx)};
87             m_chunks.push_back(chunk);
88         }
89     }
90 
91     /** get the first chunk as a std::string_view object */
getFirstChunk()92     std::string_view getFirstChunk() const
93     {
94         if (m_chunks.size() < 1)
95         {
96             return std::string_view();
97         }
98         else
99         {
100             return m_chunks.front();
101         }
102     }
103 
104     typedef std::list<std::string_view> itertype;
105 
begin()106     inline itertype::iterator begin() noexcept { return m_chunks.begin(); }
cbegin()107     inline itertype::const_iterator cbegin() const noexcept { return m_chunks.cbegin(); }
end()108     inline itertype::iterator end() noexcept { return m_chunks.end(); }
cend()109     inline itertype::const_iterator cend() const noexcept { return m_chunks.cend(); }
110 
111 protected:
112     std::string m_separators;
113     std::list<std::string_view > m_chunks;
114 };
115 
116 
117 /** a stream based line reader with accept function to allow
118     lexing/parsing. */
119 class LineReader
120 {
121 public:
LineReader(std::istream & is)122     LineReader(std::istream &is)
123         : m_is(is)
124     {
125         m_lineNum = 0;
126         nextLine();
127     }
128 
129     /** accept the current line and advance to
130         the next */
accept()131     void accept()
132     {
133         nextLine();
134     }
135 
136     /** true if the end of file/stream is encountered */
eof()137     bool eof() const
138     {
139         return m_eof;
140     }
141 
142     /** return the current line number */
getLineNumber()143     uint32_t getLineNumber() const
144     {
145         return m_lineNum;
146     }
147 
148     /** return the current line as string_view */
getLine()149     const std::string& getLine() const
150     {
151         return m_line;
152     }
153 
154 protected:
155 
156     /** read the next line and update the line number
157         and eof boolean.
158     */
nextLine()159     void nextLine()
160     {
161         if (!std::getline(m_is, m_line))
162         {
163             m_eof = true;
164         }
165         else
166         {
167             m_lineNum++;
168             m_eof = false;
169         }
170     }
171 
172     std::string     m_line;
173     std::istream    &m_is;
174 
175     bool            m_eof;
176     uint32_t        m_lineNum;
177 };
178 
179 
180 /** a stream based line reader with accept function to allow
181     lexing/parsing. It will split the string into chunks based
182     on a set of separators
183 */
184 class ChunkyLineReader
185 {
186 public:
187     ChunkyLineReader(std::istream &is, const std::string separators = " \t")
m_is(is)188         : m_is(is), m_chunkifier(separators)
189     {
190         m_lineNum = 0;
191         nextLine();
192     }
193 
194     /** accept the current line and advance to
195         the next */
accept()196     void accept()
197     {
198         nextLine();
199     }
200 
201     /** true if the end of file/stream is encountered */
eof()202     bool eof() const
203     {
204         return m_eof;
205     }
206 
207     /** return the current line number */
getLineNumber()208     uint32_t getLineNumber() const
209     {
210         return m_lineNum;
211     }
212 
213     /** get this first chunk in the list */
getFirstChunk()214     std::string_view getFirstChunk() const
215     {
216         return m_chunkifier.getFirstChunk();
217     }
218 
219     typedef std::list<std::string_view> itertype;
220 
221     /** iterator to access the string_view chunks */
begin()222     inline itertype::iterator begin() noexcept { return m_chunkifier.begin(); }
223 
224     /** iterator to access the string_view chunks */
cbegin()225     inline itertype::const_iterator cbegin() const noexcept { return m_chunkifier.cbegin(); }
226 
227     /** iterator to access the string_view chunks */
end()228     inline itertype::iterator end() noexcept { return m_chunkifier.end(); }
229 
230     /** iterator to access the string_view chunks */
cend()231     inline itertype::const_iterator cend() const noexcept { return m_chunkifier.cend(); }
232 
233 protected:
234 
235     /** read the next line and update the line number
236         and eof boolean.
237     */
nextLine()238     void nextLine()
239     {
240         if (!std::getline(m_is, m_line))
241         {
242             m_eof = true;
243         }
244         else
245         {
246             m_lineNum++;
247             m_eof = false;
248             m_chunkifier.submitString(m_line);
249         }
250     }
251 
252     TextChunkifier  m_chunkifier;
253     std::string     m_line;
254 
255     std::istream    &m_is;
256 
257     bool            m_eof;
258     uint32_t        m_lineNum;
259 };
260 
261 #endif