My Project
tstrings.h
1 #ifndef TSTRINGS_H_INCLUDED_
2 #define TSTRINGS_H_INCLUDED_
3 
4 #define KBLIB_DEF_MACROS 1
5 #include "kblib/kblib.h"
6 
7 #include "cppcoro/generator.hpp"
8 #include <map>
9 #include <memory>
10 #include <numeric>
11 #include <optional>
12 #include <string>
13 #include <string_view>
14 #include <type_traits>
15 #include <variant>
16 #include <vector>
17 
18 #include "containers.h"
19 #include "error.h"
20 #include "floating_array.h"
21 #include "random.h"
22 
23 // TODO(killerbee): Switch to lazy evaluation
24 
25 // move to grammar file header
26 // translate old-style doubled-character escapes into backslash escapes
27 inline std::string compat_filter(std::string_view in) {
28  std::string ret;
29  bool esc = false;
30  for (auto [c, i] : kblib::enumerate(in)) {
31  if (esc) {
32  if (c == in[i - 1]) {
33  ret.push_back('\\');
34  ret.push_back(c);
35  } else {
36  ret.push_back(in[i - 1]);
37  ret.push_back(c);
38  }
39  }
40  if (kblib::contains("<>{}", c)) {
41  esc = true;
42  } else {
43  ret.push_back(c);
44  }
45  }
46  return ret;
47 }
48 // remove backslash-escape sequences
49 [[nodiscard]] inline std::string unescape(std::string_view in) {
50  bool b_esc = false;
51  bool d_esc = false;
52  std::string ret;
53  char prev{};
54  for (auto& c : in) {
55  if (d_esc) {
56  if (c == prev) {
57  ret.push_back(c);
58  } else {
59  ret.push_back(prev);
60  ret.push_back(c);
61  }
62  d_esc = false;
63  } else if (b_esc) {
64  ret.push_back(c);
65  b_esc = false;
66  } else {
67  if (c == '\\') {
68  b_esc = true;
69  } else if (c == '<' or c == '>') {
70  d_esc = true;
71  } else {
72  ret.push_back(c);
73  }
74  }
75  prev = c;
76  }
77  return ret;
78 }
79 
80 std::pair<int, bool> argsDeclared(std::string_view in);
81 
82 struct node;
83 
84 using arg_t = std::variant<std::string, std::vector<std::string>, const node*,
85  wordgen_error /*, std::unique_ptr<node>*/>;
86 using argslist = std::vector<arg_t>;
87 
88 [[nodiscard]] std::string stringize(const arg_t& arg);
89 [[nodiscard]] std::string stringize(arg_t&& arg);
90 
91 class transformer {
92  public:
93  [[nodiscard]] virtual arg_t operator()(argslist) const = 0;
94  virtual ~transformer() noexcept = default;
95 };
96 
97 class Echo final : public transformer {
98  public:
99  [[nodiscard]] arg_t operator()(argslist v) const override;
100 };
101 
102 class bytecode_machine;
103 
104 class Evaluator final : public transformer {
105  public:
106  Evaluator(bytecode_machine* owner) : owner(owner) {}
107  [[nodiscard]] arg_t operator()(argslist v) const override;
108 
109  private:
110  bytecode_machine* owner;
111 };
112 
113 namespace helpers {
114 template <typename T>
115 using _t_h =
116  std::is_convertible<decltype(std::declval<T>()(std::declval<argslist>())),
117  arg_t>;
118 
119 template <typename T, typename = std::void_t<>>
121  // can't find is_invocable
122  // std::is_invocable<arg_t, T, argslist>
123  public std::false_type {};
124 
125 template <typename T>
126 struct is_transformer<T, std::void_t<_t_h<T>>> : public _t_h<T> {};
127 
128 template <typename T>
129 constexpr bool is_transformer_v = is_transformer<T>::value;
130 }; // namespace helpers
131 
132 template <typename Callable,
133  typename std::enable_if_t<helpers::is_transformer_v<Callable>, int> =
134  0>
135 class dynamic_transformer final : public transformer {
136  public:
137  dynamic_transformer(Callable _c) : c(std::move(_c)) {}
138  [[nodiscard]] arg_t operator()(argslist v) const override {
139  return c(std::move(v));
140  }
141 
142  private:
143  Callable c;
144 };
145 
146 template <typename Callable>
147 [[nodiscard]] std::unique_ptr<dynamic_transformer<std::decay_t<Callable>>>
148 make_transformer(Callable&& c) {
149  return std::make_unique<dynamic_transformer<std::decay_t<Callable>>>(
150  std::forward<Callable>(c));
151 }
152 
153 enum class op : unsigned char {
154  null = 0, // monostate
155  text, // string
156  push_func, // transformer*
157  call_func, // monostate
158  argument, // int
159  special_argument, // string
160  ellipsis, // string
161 };
162 
163 using op_data =
164  std::variant<std::monostate, int, std::string, const transformer*>;
165 
166 struct bytecode_op {
167  op type;
168  op_data data;
169 };
170 using bytecodes = boost::container::small_vector<bytecode_op, 16>;
171 
172 struct token;
173 class datafile;
174 
175 struct counters {
176  int depthLimit{};
177  int expansionsLimit{};
178  int* expansions{nullptr};
179  int depth{0};
180 };
181 inline counters incr(counters& c) {
182  ++(*c.expansions);
183  return {c.depthLimit, c.expansionsLimit, c.expansions, c.depth + 1};
184 }
185 
186 using ArgsType =
188 
189 ArgsType copy(const ArgsType& special, const std::vector<std::string>& vals);
190 
191 [[nodiscard]] tmap<std::string, std::unique_ptr<transformer>>
192 default_transformers(bytecode_machine*, datafile*, RandomGenerator&);
193 [[nodiscard]] std::vector<std::pair<std::string, int>> default_names();
194 
195 // One per data file
197  private:
198  tmap<std::string, std::unique_ptr<transformer>> transformers;
199  std::vector<std::pair<std::string, int>> arg_names;
200 
201  public:
202  bytecode_machine(datafile* owner, RandomGenerator& rng)
203  : transformers(default_transformers(this, owner, rng)),
204  arg_names(default_names()) {
205  transformers["."] = std::make_unique<Echo>();
206  }
207  // t must map to a contiguous sequence of negative numbers ending at -1
208  bytecode_machine(decltype(transformers) t, decltype(arg_names) a)
209  : transformers(std::move(t)), arg_names(std::move(a)) {
210  transformers["."] = std::make_unique<Echo>();
211  }
212 
213  [[nodiscard]] bytecodes compile(const std::string&) const;
214  [[nodiscard]] cppcoro::generator<std::string>
215  to_bytes_test(std::string_view) const;
216  [[nodiscard]] std::string eval(const bytecodes&,
217  const std::vector<std::string>&,
218  const counters&, int argc) const;
219 
220  std::map<std::string, arg_t> variables;
221 
222  // Given the name of a special argument, return its index in an ArgsType
223 
224  /* Special argument documentation:
225  *
226  * #p: escaped | (pipe) character
227  * #lt: escaped < (less than) character
228  * #gt: escaped > (greater than) character
229  * #b: escaped \ (backslash) character
230  *
231  * #d: current expansion depth
232  * #D: maximum expansion depth
233  *
234  * #e: current expansion count
235  * #E: maximum expansion count
236  *
237  * #c: number of normal arguments passed to this node
238  * #C: number of positional arguments declared by this node
239  *
240  * #a: a list containing the full series of declared arguments
241  * ...: a list containing all extra (variadic) arguments
242  * #A: the concatenation of #a and ...
243  *
244  */
245  [[nodiscard]] int special_argument(std::string_view) const;
246  [[nodiscard]] int min_argument() const;
247 
248  // Returns the transformer with the given name
249  [[nodiscard]] const transformer& lookup(const std::string& name) const
250  noexcept(false) {
251  return *transformers.at(name);
252  }
253 
254  // Returns a string which is a valid name for t
255  // O(transformers.size()), so call it sparingly
256 
257  // Note that the reverse mapping is not unique, and the returned string is
258  // unspecified in the ambiguous case. That is: given: std::string fun;
259  // reverse_lookup(lookup(fun)) == fun
260  // is unspecified
261  // While:
262  // given: const transformer *t;
263  // &lookup(reverse_lookup(*t)) == t
264  // is guaranteed
265  [[nodiscard]] const std::string& reverse_lookup(const transformer& t) const
266  noexcept(false);
267 
268  // register a new transformer by name
269  // returns false if a transformer with that name already exists
270  bool set(std::string key, std::unique_ptr<transformer> val) {
271  return transformers.emplace(std::move(key), std::move(val)).second;
272  }
273  // re-assign a name to a new transformer (key must already be associated with
274  // a transformer)
275  void reset(const std::string& key, std::unique_ptr<transformer> val) {
276  using namespace std::literals;
277  // . is an invariant function
278  assert(key != "."sv);
279  auto it = transformers.find(key);
280  assert(it !=
281  transformers.end()); // reset does not register new transformers
282  it->second = std::move(val);
283  }
284  // clears a key
285  void unset(const std::string& key) {
286  using namespace std::literals;
287  assert(key != "."sv);
288  auto it = transformers.find(key);
289  assert(it != transformers.end()); // key must be registered already
290  transformers.erase(it);
291  }
292 
293  private:
294  friend bytecode_op token_to_bytecode(token in, const bytecode_machine& b);
295 };
296 
297 [[nodiscard]] std::string to_string(const bytecode_op& op,
298  const bytecode_machine& b);
299 [[nodiscard]] std::string to_string(const op_data& op,
300  const bytecode_machine& b);
301 [[nodiscard]] std::string serialize(const bytecode_op& op,
302  const bytecode_machine& b);
303 
304 #endif // TSTRINGS_H_INCLUDED_
Definition: interpolate.h:167
Definition: tstrings.h:97
Definition: tstrings.h:135
Definition: error.h:158
Definition: tstrings.h:104
Definition: interpolate.h:198
Definition: tstrings.cpp:16
Definition: tstrings.h:91
Definition: floating_array.h:21
Definition: tstrings.h:120
Definition: tstrings.h:166
Definition: tstrings.h:113
Definition: tstrings.h:175
Definition: tstrings.h:196