shell.hpp
Go to the documentation of this file.
1 // Copyright (C) 2020-2024 Jonathan Müller and lexy contributors
2 // SPDX-License-Identifier: BSL-1.0
3 
4 #ifndef LEXY_EXT_SHELL_HPP_INCLUDED
5 #define LEXY_EXT_SHELL_HPP_INCLUDED
6 
7 #include <cstdio>
8 
11 #include <lexy/error.hpp>
12 #include <lexy/input/base.hpp>
13 #include <lexy/lexeme.hpp>
14 
15 namespace lexy_ext
16 {
17 #if 0
18 class Prompt
20 {
21  using encoding = ...;
22 
24  void primary_prompt();
25 
27  void continuation_prompt();
28 
30  void eof_prompt();
31 
33  bool is_open() const;
34 
35  struct read_line_callback
36  {
41  std::size_t operator()(char_type* buffer, std::size_t size);
42 
44  void done() &&;
45  };
46 
48  auto read_line()
49  {
50  return read_line_callback{...};
51  }
52 
53  struct write_message_callback
54  {
56  void operator()(const char_type* buffer, std::size_t size);
57 
59  void done() &&;
60  };
61 
64  auto write_message(Args&&... config_args)
65  {
66  return write_message_callback{...};
67  }
68 };
69 #endif
70 
72 template <typename Encoding = lexy::default_encoding>
74 {
75  using encoding = Encoding;
76  using char_type = typename Encoding::char_type;
77  static_assert(sizeof(char_type) == sizeof(char), "only support single-byte encodings");
78 
79  void primary_prompt() noexcept
80  {
81  std::fputs("> ", stdout);
82  }
83 
84  void continuation_prompt() noexcept
85  {
86  std::fputs(". ", stdout);
87  }
88 
89  void eof_prompt() noexcept
90  {
91  // We write an additional newline to prevent output on the same line.
92  std::fputs("\n", stdout);
93  }
94 
95  bool is_open() const noexcept
96  {
97  return std::feof(stdin) == 0 && std::ferror(stdin) == 0;
98  }
99 
101  {
102  std::size_t operator()(char_type* buffer, std::size_t size)
103  {
104  LEXY_PRECONDITION(size > 1);
105 
106  auto memory = reinterpret_cast<char*>(buffer);
107  if (auto str = std::fgets(memory, int(size), stdin))
108  return std::strlen(str);
109  else
110  return 0;
111  }
112 
113  void done() && {}
114  };
115  auto read_line()
116  {
117  return read_line_callback{};
118  }
119 
121  {
122  bool _last_was_newline = true; // If we printed nothing, it's a newline.
123 
124  void operator()(const char_type* buffer, std::size_t size)
125  {
126  std::fprintf(stdout, "%.*s", int(size), reinterpret_cast<const char*>(buffer));
127  if (size > 0)
128  _last_was_newline = buffer[size - 1] == '\n';
129  }
130 
131  void done() &&
132  {
133  if (!_last_was_newline)
134  std::putchar('\n');
135  }
136  };
138  {
139  return write_message_callback{};
140  }
141 };
142 } // namespace lexy_ext
143 
144 namespace lexy_ext
145 {
147 template <typename Prompt = default_prompt<>>
148 class shell
149 {
150 public:
151  using encoding = typename Prompt::encoding;
152  using char_type = typename encoding::char_type;
153  using prompt_type = Prompt;
154 
155  shell() = default;
156  explicit shell(Prompt prompt) : _prompt(LEXY_MOV(prompt)) {}
157 
159  bool is_open() const noexcept
160  {
161  return _prompt.is_open();
162  }
163 
165  class input
166  {
167  public:
168  using encoding = typename Prompt::encoding;
170 
171  struct marker
172  {
174 
175  constexpr iterator position() const noexcept
176  {
177  return _it;
178  }
179  };
180 
181  auto reader() const&
182  {
183  return *this;
184  }
185 
186  auto peek() const
187  {
188  if (is_eof())
189  return encoding::eof();
190  else
191  return encoding::to_int_type(_shell->_buffer.read_data()[_idx]);
192  }
193 
194  void bump() noexcept
195  {
196  ++_idx;
197  }
198 
199  auto position() const noexcept
200  {
201  return iterator(_shell->_buffer, _idx);
202  }
203 
204  marker current() const noexcept
205  {
206  return {position()};
207  }
208  void reset(marker m) noexcept
209  {
210  _idx = m._it.index();
211  }
212 
213  private:
214  explicit input(shell* s) : _shell(s), _idx(0)
215  {
216  _shell->_buffer.clear();
217  _shell->_prompt.primary_prompt();
218  if (!_shell->append_next_line())
219  _shell->_prompt.eof_prompt();
220  }
221 
222  bool is_eof() const
223  {
224  if (_idx != _shell->_buffer.read_size())
225  // We're still having characters in the read buffer.
226  return false;
227  else if (!_shell->_prompt.is_open())
228  // The prompt has been closed by the user.
229  return true;
230  else
231  {
232  // We've reached the end of the buffer, but the user might be willing to type
233  // another line.
234  _shell->_prompt.continuation_prompt();
235  auto did_append = _shell->append_next_line();
236  if (!did_append)
237  _shell->_prompt.eof_prompt();
238  return !did_append;
239  }
240  }
241 
243  std::size_t _idx;
244 
245  friend shell;
246  };
247 
252  {
253  return input(this);
254  }
255 
256  class writer
257  {
258  public:
259  writer(const writer&) = delete;
260  writer& operator=(const writer&) = delete;
261 
262  ~writer() noexcept
263  {
264  LEXY_MOV(_writer).done();
265  }
266 
268  {
269  struct iterator
270  {
271  typename Prompt::write_message_callback* _writer;
272 
273  iterator& operator*() noexcept
274  {
275  return *this;
276  }
277  iterator& operator++(int) noexcept
278  {
279  return *this;
280  }
281 
282  iterator& operator=(char c)
283  {
284  auto chr = static_cast<char_type>(c);
285  // clang-cl requires this->.
286  (*this->_writer)(&chr, 1);
287  return *this;
288  }
289  };
290 
291  return iterator{&_writer};
292  }
293 
294  writer& operator()(const char_type* str, std::size_t length)
295  {
296  _writer(str, length);
297  return *this;
298  }
300  {
301  auto length = std::size_t(0);
302  for (auto ptr = str; *ptr; ++ptr)
303  ++length;
304  _writer(str, length);
305  return *this;
306  }
308  {
309  _writer(&c, 1);
310  return *this;
311  }
312 
313  template <typename CharT,
314  typename = std::enable_if_t<encoding::template is_secondary_char_type<CharT>()>>
315  writer& operator()(const CharT* str, std::size_t length)
316  {
317  return operator()(reinterpret_cast<const char_type*>(str), length);
318  }
319  template <typename CharT,
320  typename = std::enable_if_t<encoding::template is_secondary_char_type<CharT>()>>
321  writer& operator()(const CharT* str)
322  {
323  return operator()(reinterpret_cast<const char_type*>(str));
324  }
325  template <typename CharT,
326  typename = std::enable_if_t<encoding::template is_secondary_char_type<CharT>()>>
327  writer& operator()(CharT c)
328  {
329  return operator()(char_type(c));
330  }
331 
333  {
334  // We know that the iterator is contiguous.
335  auto data = &*lexeme.begin();
336  _writer(data, lexeme.size());
337  return *this;
338  }
339 
340  private:
341  explicit writer(typename Prompt::write_message_callback&& writer)
343  {}
344 
345  LEXY_EMPTY_MEMBER typename Prompt::write_message_callback _writer;
346 
347  friend shell;
348  };
349 
352  template <typename... Args>
353  auto write_message(Args&&... args)
354  {
355  return writer{_prompt.write_message(LEXY_FWD(args)...)};
356  }
357 
358  Prompt& get_prompt() noexcept
359  {
360  return _prompt;
361  }
362  const Prompt& get_prompt() const noexcept
363  {
364  return _prompt;
365  }
366 
367 private:
368  // Returns whether or not we've read anything.
370  {
371  // Grwo buffer if necessary.
372  constexpr auto min_capacity = 128;
373  if (_buffer.write_size() < min_capacity)
374  _buffer.grow();
375 
376  for (auto reader = _prompt.read_line(); true;)
377  {
378  const auto buffer_size = _buffer.write_size();
379 
380  // Read into the entire write area of the buffer from the file,
381  // commiting what we've just read.
382  const auto read = reader(_buffer.write_data(), buffer_size);
383  _buffer.commit(read);
384 
385  // Check whether we've read the entire line.
386  if (_buffer.write_data()[-1] == '\n')
387  {
388  LEXY_MOV(reader).done();
389  return true;
390  }
391  else if (read < buffer_size)
392  {
393  LEXY_ASSERT(!_prompt.is_open(), "read error but prompt still open?!");
394  return false;
395  }
396 
397  // We've filled the entire buffer and need more space.
398  // This grow might be unnecessary if we're just so happen to reach the newline with the
399  // next character, but checking this requires reading more input.
400  _buffer.grow();
401  }
402 
403  return false;
404  }
405 
408 };
409 
410 //=== convenience typedefs ===//
411 template <typename Prompt = default_prompt<>>
413 
414 template <typename Tag, typename Prompt = default_prompt<>>
416 
417 template <typename Prompt = default_prompt<>>
419 } // namespace lexy_ext
420 
421 #endif // LEXY_EXT_SHELL_HPP_INCLUDED
422 
lexy_ext::shell::prompt_for_input
auto prompt_for_input()
Definition: shell.hpp:251
cx::size
constexpr auto size(const C &c) -> decltype(c.size())
Definition: wildcards.hpp:636
lexy_ext::shell::input::marker
Definition: shell.hpp:171
LEXY_MOV
#define LEXY_MOV(...)
Definition: config.hpp:29
lexy_ext::default_prompt::read_line_callback
Definition: shell.hpp:100
lexy_ext::shell::input::encoding
typename Prompt::encoding encoding
Definition: shell.hpp:168
lexy_ext::shell::writer::output_iterator
auto output_iterator()
Definition: shell.hpp:267
lexy_ext::shell::input::peek
auto peek() const
Definition: shell.hpp:186
lexy_ext::default_prompt::write_message_callback::operator()
void operator()(const char_type *buffer, std::size_t size)
Definition: shell.hpp:124
lexy_ext::shell::input
This is both Reader and Input.
Definition: shell.hpp:165
magic_enum::char_type
string_view::value_type char_type
Definition: magic_enum.hpp:145
lexy_ext::shell::input::marker::position
constexpr iterator position() const noexcept
Definition: shell.hpp:175
lexy_ext::shell
Reads input from an interactive shell.
Definition: shell.hpp:148
lexy::_detail::buffer_builder::read_data
const T * read_data() const noexcept
Definition: buffer_builder.hpp:51
lexy_ext::default_prompt
Prompt using stdin and stdout.
Definition: shell.hpp:73
lexy_ext::default_prompt::read_line_callback::operator()
std::size_t operator()(char_type *buffer, std::size_t size)
Definition: shell.hpp:102
lexy_ext
Definition: compiler_explorer.hpp:12
lexy_ext::shell::input::_shell
shell * _shell
Definition: shell.hpp:242
lexy_ext::shell::char_type
typename encoding::char_type char_type
Definition: shell.hpp:152
lexy_ext::default_prompt<>::char_type
typename lexy::default_encoding ::char_type char_type
Definition: shell.hpp:76
lexy_ext::default_prompt::write_message_callback::done
void done() &&
Definition: shell.hpp:131
LEXY_FWD
#define LEXY_FWD(...)
Definition: config.hpp:30
lexy_ext::shell::input::_idx
std::size_t _idx
Definition: shell.hpp:243
lexy_ext::shell::input::marker::_it
iterator _it
Definition: shell.hpp:173
lexy_ext::default_prompt::write_message_callback::_last_was_newline
bool _last_was_newline
Definition: shell.hpp:122
lexy::_detail::buffer_builder< char_type >
lexy_ext::shell::writer::~writer
~writer() noexcept
Definition: shell.hpp:262
lexy::_detail::buffer_builder::write_data
T * write_data() noexcept
Definition: buffer_builder.hpp:61
lexy::_detail::buffer_builder::stable_iterator
Definition: buffer_builder.hpp:108
lexy_ext::shell::input::input
input(shell *s)
Definition: shell.hpp:214
lexy_ext::shell::writer::_writer
LEXY_EMPTY_MEMBER Prompt::write_message_callback _writer
Definition: shell.hpp:345
lexy::error_context
Contains information about the context of an error, production is type-erased.
Definition: error.hpp:233
lexy_ext::shell::writer::operator()
writer & operator()(const char_type *str)
Definition: shell.hpp:299
LEXY_PRECONDITION
#define LEXY_PRECONDITION(Expr)
Definition: assert.hpp:36
lexy_ext::shell::input::reset
void reset(marker m) noexcept
Definition: shell.hpp:208
lexy_ext::default_prompt::primary_prompt
void primary_prompt() noexcept
Definition: shell.hpp:79
lexy_ext::shell::input::bump
void bump() noexcept
Definition: shell.hpp:194
lexy_ext::shell::write_message
auto write_message(Args &&... args)
Definition: shell.hpp:353
lexy_ext::shell::input::current
marker current() const noexcept
Definition: shell.hpp:204
lexy::error
Generic failure.
Definition: error.hpp:14
lexy_ext::default_prompt::eof_prompt
void eof_prompt() noexcept
Definition: shell.hpp:89
lexy_ext::shell::prompt_type
Prompt prompt_type
Definition: shell.hpp:153
buffer_builder.hpp
lexy_ext::shell::writer::writer
writer(const writer &)=delete
lexy_ext::shell::encoding
typename Prompt::encoding encoding
Definition: shell.hpp:151
lexy::_detail::buffer_builder::clear
void clear() noexcept
Definition: buffer_builder.hpp:71
lexy_ext::shell::shell
shell(Prompt prompt)
Definition: shell.hpp:156
lexy_ext::shell::writer::operator()
writer & operator()(lexy::lexeme_for< input > lexeme)
Definition: shell.hpp:332
lexy_ext::shell::writer::operator()
writer & operator()(CharT c)
Definition: shell.hpp:327
lexeme.hpp
lexy_ext::shell::shell
shell()=default
lexy::buffer
buffer(const CharT *, const CharT *) -> buffer< deduce_encoding< CharT >>
lexy::_detail::buffer_builder::grow
void grow()
Definition: buffer_builder.hpp:86
lexy_ext::shell::writer::operator()
writer & operator()(const CharT *str, std::size_t length)
Definition: shell.hpp:315
assert.hpp
lexy_ext::shell::is_open
bool is_open() const noexcept
Whether or not the shell is still open.
Definition: shell.hpp:159
lexy_ext::default_prompt::write_message
auto write_message()
Definition: shell.hpp:137
lexy_ext::shell::writer::shell
friend shell
Definition: shell.hpp:347
lexy::default_encoding
An encoding where the input is some 8bit encoding (ASCII, UTF-8, extended ASCII etc....
Definition: encoding.hpp:27
lexy::_detail::buffer_builder::read_size
std::size_t read_size() const noexcept
Definition: buffer_builder.hpp:55
lexy_ext::default_prompt::write_message_callback
Definition: shell.hpp:120
lexy_ext::shell::writer::writer
writer(typename Prompt::write_message_callback &&writer)
Definition: shell.hpp:341
lexy_ext::shell::input::position
auto position() const noexcept
Definition: shell.hpp:199
lexy_ext::shell::writer::operator()
writer & operator()(char_type c)
Definition: shell.hpp:307
lexy_ext::shell::_buffer
lexy::_detail::buffer_builder< char_type > _buffer
Definition: shell.hpp:406
lexy_ext::shell::_prompt
LEXY_EMPTY_MEMBER Prompt _prompt
Definition: shell.hpp:407
lexy_ext::shell::writer
Definition: shell.hpp:256
lexy_ext::default_prompt::read_line_callback::done
void done() &&
Definition: shell.hpp:113
lexy::lexeme
Definition: lexeme.hpp:16
lexy_ext::shell::input::reader
auto reader() const &
Definition: shell.hpp:181
lexy::_detail::buffer_builder::commit
void commit(std::size_t n) noexcept
Definition: buffer_builder.hpp:78
lexy_ext::default_prompt::is_open
bool is_open() const noexcept
Definition: shell.hpp:95
lexy::lexeme
lexeme(const Reader &, typename Reader::iterator) -> lexeme< typename Reader::canonical_reader >
lexy_ext::shell::get_prompt
const Prompt & get_prompt() const noexcept
Definition: shell.hpp:362
lexy_ext::shell::writer::operator()
writer & operator()(const char_type *str, std::size_t length)
Definition: shell.hpp:294
base.hpp
LEXY_EMPTY_MEMBER
#define LEXY_EMPTY_MEMBER
Definition: config.hpp:193
lexy_ext::shell::input::is_eof
bool is_eof() const
Definition: shell.hpp:222
lexy_ext::default_prompt::read_line
auto read_line()
Definition: shell.hpp:115
lexy_ext::shell::writer::operator()
writer & operator()(const CharT *str)
Definition: shell.hpp:321
lexy_ext::shell::append_next_line
bool append_next_line()
Definition: shell.hpp:369
lexy::_detail::buffer_builder::write_size
std::size_t write_size() const noexcept
Definition: buffer_builder.hpp:65
lexy_ext::shell::input::shell
friend shell
Definition: shell.hpp:245
lexy_ext::default_prompt::continuation_prompt
void continuation_prompt() noexcept
Definition: shell.hpp:84
lexy_ext::shell::input::iterator
typename lexy::_detail::buffer_builder< char_type >::stable_iterator iterator
Definition: shell.hpp:169
lexyd::eof
constexpr auto eof
Matches EOF.
Definition: eof.hpp:72
LEXY_ASSERT
#define LEXY_ASSERT(Expr, Msg)
Definition: assert.hpp:37
lexy_ext::shell::get_prompt
Prompt & get_prompt() noexcept
Definition: shell.hpp:358
lexy_ext::shell::writer::operator=
writer & operator=(const writer &)=delete
error.hpp


behaviortree_cpp_v4
Author(s): Davide Faconti
autogenerated on Fri Dec 13 2024 03:19:17