shell.hpp
Go to the documentation of this file.
1 // Copyright (C) 2020-2023 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  auto reader() const&
172  {
173  return *this;
174  }
175 
176  auto peek() const
177  {
178  if (is_eof())
179  return encoding::eof();
180  else
181  return encoding::to_int_type(_shell->_buffer.read_data()[_idx]);
182  }
183 
184  void bump() noexcept
185  {
186  ++_idx;
187  }
188 
189  auto position() const noexcept
190  {
191  return iterator(_shell->_buffer, _idx);
192  }
193 
194  void set_position(iterator new_pos) noexcept
195  {
196  _idx = new_pos.index();
197  }
198 
199  private:
200  explicit input(shell* s) : _shell(s), _idx(0)
201  {
202  _shell->_buffer.clear();
203  _shell->_prompt.primary_prompt();
204  if (!_shell->append_next_line())
205  _shell->_prompt.eof_prompt();
206  }
207 
208  bool is_eof() const
209  {
210  if (_idx != _shell->_buffer.read_size())
211  // We're still having characters in the read buffer.
212  return false;
213  else if (!_shell->_prompt.is_open())
214  // The prompt has been closed by the user.
215  return true;
216  else
217  {
218  // We've reached the end of the buffer, but the user might be willing to type
219  // another line.
220  _shell->_prompt.continuation_prompt();
221  auto did_append = _shell->append_next_line();
222  if (!did_append)
223  _shell->_prompt.eof_prompt();
224  return !did_append;
225  }
226  }
227 
229  std::size_t _idx;
230 
231  friend shell;
232  };
233 
238  {
239  return input(this);
240  }
241 
242  class writer
243  {
244  public:
245  writer(const writer&) = delete;
246  writer& operator=(const writer&) = delete;
247 
248  ~writer() noexcept
249  {
250  LEXY_MOV(_writer).done();
251  }
252 
254  {
255  struct iterator
256  {
257  typename Prompt::write_message_callback* _writer;
258 
259  iterator& operator*() noexcept
260  {
261  return *this;
262  }
263  iterator& operator++(int) noexcept
264  {
265  return *this;
266  }
267 
268  iterator& operator=(char c)
269  {
270  auto chr = static_cast<char_type>(c);
271  // clang-cl requires this->.
272  (*this->_writer)(&chr, 1);
273  return *this;
274  }
275  };
276 
277  return iterator{&_writer};
278  }
279 
280  writer& operator()(const char_type* str, std::size_t length)
281  {
282  _writer(str, length);
283  return *this;
284  }
286  {
287  auto length = std::size_t(0);
288  for (auto ptr = str; *ptr; ++ptr)
289  ++length;
290  _writer(str, length);
291  return *this;
292  }
294  {
295  _writer(&c, 1);
296  return *this;
297  }
298 
299  template <typename CharT,
300  typename = std::enable_if_t<encoding::template is_secondary_char_type<CharT>()>>
301  writer& operator()(const CharT* str, std::size_t length)
302  {
303  return operator()(reinterpret_cast<const char_type*>(str), length);
304  }
305  template <typename CharT,
306  typename = std::enable_if_t<encoding::template is_secondary_char_type<CharT>()>>
307  writer& operator()(const CharT* str)
308  {
309  return operator()(reinterpret_cast<const char_type*>(str));
310  }
311  template <typename CharT,
312  typename = std::enable_if_t<encoding::template is_secondary_char_type<CharT>()>>
313  writer& operator()(CharT c)
314  {
315  return operator()(char_type(c));
316  }
317 
319  {
320  // We know that the iterator is contiguous.
321  auto data = &*lexeme.begin();
322  _writer(data, lexeme.size());
323  return *this;
324  }
325 
326  private:
327  explicit writer(typename Prompt::write_message_callback&& writer)
329  {}
330 
331  LEXY_EMPTY_MEMBER typename Prompt::write_message_callback _writer;
332 
333  friend shell;
334  };
335 
338  template <typename... Args>
339  auto write_message(Args&&... args)
340  {
341  return writer{_prompt.write_message(LEXY_FWD(args)...)};
342  }
343 
344  Prompt& get_prompt() noexcept
345  {
346  return _prompt;
347  }
348  const Prompt& get_prompt() const noexcept
349  {
350  return _prompt;
351  }
352 
353 private:
354  // Returns whether or not we've read anything.
356  {
357  // Grwo buffer if necessary.
358  constexpr auto min_capacity = 128;
359  if (_buffer.write_size() < min_capacity)
360  _buffer.grow();
361 
362  for (auto reader = _prompt.read_line(); true;)
363  {
364  const auto buffer_size = _buffer.write_size();
365 
366  // Read into the entire write area of the buffer from the file,
367  // commiting what we've just read.
368  const auto read = reader(_buffer.write_data(), buffer_size);
369  _buffer.commit(read);
370 
371  // Check whether we've read the entire line.
372  if (_buffer.write_data()[-1] == '\n')
373  {
374  LEXY_MOV(reader).done();
375  return true;
376  }
377  else if (read < buffer_size)
378  {
379  LEXY_ASSERT(!_prompt.is_open(), "read error but prompt still open?!");
380  return false;
381  }
382 
383  // We've filled the entire buffer and need more space.
384  // This grow might be unnecessary if we're just so happen to reach the newline with the
385  // next character, but checking this requires reading more input.
386  _buffer.grow();
387  }
388 
389  return false;
390  }
391 
394 };
395 
396 //=== convenience typedefs ===//
397 template <typename Prompt = default_prompt<>>
399 
400 template <typename Tag, typename Prompt = default_prompt<>>
402 
403 template <typename Prompt = default_prompt<>>
405 } // namespace lexy_ext
406 
407 #endif // LEXY_EXT_SHELL_HPP_INCLUDED
408 
lexy_ext::shell::prompt_for_input
auto prompt_for_input()
Definition: shell.hpp:237
cx::size
constexpr auto size(const C &c) -> decltype(c.size())
Definition: wildcards.hpp:636
LEXY_MOV
#define LEXY_MOV(...)
Definition: config.hpp:21
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:253
lexy_ext::shell::input::set_position
void set_position(iterator new_pos) noexcept
Definition: shell.hpp:194
lexy_ext::shell::input::peek
auto peek() const
Definition: shell.hpp:176
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
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:228
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:22
lexy_ext::shell::input::_idx
std::size_t _idx
Definition: shell.hpp:229
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:248
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:200
lexy_ext::shell::writer::_writer
LEXY_EMPTY_MEMBER Prompt::write_message_callback _writer
Definition: shell.hpp:331
lexy::error_context
Contains information about the context of an error, production is type-erased.
Definition: error.hpp:198
lexy_ext::shell::writer::operator()
writer & operator()(const char_type *str)
Definition: shell.hpp:285
LEXY_PRECONDITION
#define LEXY_PRECONDITION(Expr)
Definition: assert.hpp:36
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:184
lexy_ext::shell::write_message
auto write_message(Args &&... args)
Definition: shell.hpp:339
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:318
lexy_ext::shell::writer::operator()
writer & operator()(CharT c)
Definition: shell.hpp:313
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:301
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:333
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:327
lexy_ext::shell::input::position
auto position() const noexcept
Definition: shell.hpp:189
lexy_ext::shell::writer::operator()
writer & operator()(char_type c)
Definition: shell.hpp:293
lexy_ext::shell::_buffer
lexy::_detail::buffer_builder< char_type > _buffer
Definition: shell.hpp:392
lexy_ext::shell::_prompt
LEXY_EMPTY_MEMBER Prompt _prompt
Definition: shell.hpp:393
lexy_ext::shell::writer
Definition: shell.hpp:242
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:171
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:348
lexy_ext::shell::writer::operator()
writer & operator()(const char_type *str, std::size_t length)
Definition: shell.hpp:280
base.hpp
LEXY_EMPTY_MEMBER
#define LEXY_EMPTY_MEMBER
Definition: config.hpp:170
lexy_ext::shell::input::is_eof
bool is_eof() const
Definition: shell.hpp:208
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:307
lexy_ext::shell::append_next_line
bool append_next_line()
Definition: shell.hpp:355
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:231
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:344
lexy_ext::shell::writer::operator=
writer & operator=(const writer &)=delete
error.hpp


behaviortree_cpp_v4
Author(s): Davide Faconti
autogenerated on Fri Jun 28 2024 02:20:08