Spicy
parser.h
1 // Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details.
2 
3 #pragma once
4 
5 #include <string>
6 #include <tuple>
7 #include <type_traits>
8 #include <utility>
9 
10 #include <hilti/rt/exception.h>
11 #include <hilti/rt/fiber.h>
12 #include <hilti/rt/result.h>
13 #include <hilti/rt/type-info.h>
14 #include <hilti/rt/types/bytes.h>
15 #include <hilti/rt/types/null.h>
16 #include <hilti/rt/types/port.h>
17 #include <hilti/rt/types/reference.h>
18 #include <hilti/rt/types/struct.h>
19 
20 #include <spicy/rt/filter.h>
21 #include <spicy/rt/global-state.h>
22 #include <spicy/rt/mime.h>
23 #include <spicy/rt/parser-fwd.h>
24 #include <spicy/rt/sink.h>
25 #include <spicy/rt/typedefs.h>
26 
27 namespace spicy::rt {
28 
30 enum class Direction { Originator, Responder, Both, Undef };
31 
32 } // namespace spicy::rt
33 
34 namespace hilti::rt::detail::adl {
35 
36 inline std::string to_string(const ::spicy::rt::Direction& x, adl::tag /*unused*/) {
37  switch ( x ) {
38  case spicy::rt::Direction::Originator: return "originator";
39  case spicy::rt::Direction::Responder: return "responder";
40  case spicy::rt::Direction::Both: return "both";
41  case spicy::rt::Direction::Undef: return "undefined";
42  }
43 
45 };
46 
47 } // namespace hilti::rt::detail::adl
48 
49 namespace spicy::rt {
50 
51 inline std::ostream& operator<<(std::ostream& out, const Direction& d) { return out << hilti::rt::to_string(d); }
52 
54 struct ParserPort {
55  hilti::rt::Port port;
56  Direction direction;
57 
58  // Constructor used by code generator.
59  ParserPort(std::tuple<hilti::rt::Port, Direction> args) : port(std::get<0>(args)), direction(std::get<1>(args)) {}
60 };
61 
62 inline std::ostream& operator<<(std::ostream& out, const ParserPort& p) { return out << hilti::rt::to_string(p); }
63 
64 } // namespace spicy::rt
65 
66 namespace hilti::rt::detail::adl {
67 
68 inline std::string to_string(const spicy::rt::ParserPort& x, adl::tag /*unused*/) {
69  // TODO: Not sure why we need to explicit to_string() here.
70  if ( x.direction == spicy::rt::Direction::Both )
71  return x.port;
72  else
73  return fmt("%s (%s direction)", x.port, x.direction);
74 }
75 
76 } // namespace hilti::rt::detail::adl
77 
78 namespace spicy::rt {
79 
80 namespace detail {
81 
82 // Helper traits to detect whether a parser implements sink hooks.
83 
84 template<typename P>
85 struct has_on_gap {
86  template<typename U>
87  // If `->` gets wrapped to the next line cpplint misdetects this as a C-style cast.
88  // clang-format off
89  static auto test(int) -> decltype(
90  std::declval<U>().__on_0x25_gap(std::declval<uint64_t>(), std::declval<uint64_t>()), std::true_type());
91  // clang-format on
92  template<typename U>
93  static std::false_type test(...);
94  static constexpr bool value = std::is_same_v<decltype(test<P>(0)), std::true_type>;
95 };
96 
97 template<typename P>
99  template<typename U>
100  static auto test(int) -> decltype(std::declval<U>().__on_0x25_skipped(std::declval<uint64_t>()), std::true_type());
101  template<typename U>
102  static std::false_type test(...);
103  static constexpr bool value = std::is_same_v<decltype(test<P>(0)), std::true_type>;
104 };
105 
106 template<typename P>
108  template<typename U>
109  static auto test(int) -> decltype(std::declval<U>().__on_0x25_overlap(std::declval<uint64_t>(),
110  std::declval<const hilti::rt::Bytes&>(),
111  std::declval<const hilti::rt::Bytes&>()),
112  std::true_type());
113  template<typename U>
114  static std::false_type test(...);
115  static constexpr bool value = std::is_same_v<decltype(test<P>(0)), std::true_type>;
116 };
117 
118 template<typename P>
120  template<typename U>
121  static auto test(int) -> decltype(std::declval<U>().__on_0x25_undelivered(std::declval<uint64_t>(),
122  std::declval<const hilti::rt::Bytes&>()),
123  std::true_type());
124  template<typename U>
125  static std::false_type test(...);
126  static constexpr bool value = std::is_same_v<decltype(test<P>(0)), std::true_type>;
127 };
128 
129 } // namespace detail
130 
137 struct Parser {
138  Parser(std::string name, Parse1Function parse1, hilti::rt::any parse2, Parse3Function parse3,
139  ContextNewFunction context_new, const hilti::rt::TypeInfo* type, std::string description,
141  : name(std::move(name)),
142  parse1(parse1),
143  parse2(std::move(parse2)),
144  parse3(parse3),
145  context_new(context_new),
146  type(type),
147  description(std::move(description)),
148  mime_types(std::move(mime_types)),
149  ports(std::move(ports)) {}
150 
151  Parser(std::string name, Parse1Function parse1, hilti::rt::any parse2, Parse3Function parse3,
152  hilti::rt::Null /* null */, const hilti::rt::TypeInfo* type, std::string description,
154  : name(std::move(name)),
155  parse1(parse1),
156  parse2(std::move(parse2)),
157  parse3(parse3),
158  type(type),
159  description(std::move(description)),
160  mime_types(std::move(mime_types)),
161  ports(std::move(ports)) {}
162 
163  Parser(std::string name, hilti::rt::Null /* null */, hilti::rt::any parse2, hilti::rt::Null /* null */,
164  hilti::rt::Null /* null */, const hilti::rt::TypeInfo* type, std::string description,
166  : Parser(std::move(name), nullptr, parse2, nullptr, nullptr, type, std::move(description),
167  std::move(mime_types), std::move(ports)) {}
168 
169  Parser(const Parser&) = default;
170 
171  Parser() = default;
172  ~Parser() = default;
173  Parser(Parser&&) noexcept = default;
174  Parser& operator=(const Parser&) = default;
175  Parser& operator=(Parser&&) noexcept = default;
176 
181  std::optional<UnitContext> createContext() const {
182  if ( context_new )
183  return (*context_new)();
184  else
185  return {};
186  }
187 
189  std::string name;
190 
196  std::string linker_scope;
197 
203  Parse1Function parse1{};
204 
209  hilti::rt::any parse2;
210 
217  Parse3Function parse3{};
218 
223  ContextNewFunction context_new = nullptr;
224 
225  const hilti::rt::TypeInfo* type;
226 
230  std::string description;
231 
236 
241 
246  std::optional<detail::ParseSinkFunction> __parse_sink;
247 
249  std::optional<std::function<void(hilti::rt::StrongReferenceGeneric, uint64_t, uint64_t)>> __hook_gap;
250 
252  std::optional<std::function<void(hilti::rt::StrongReferenceGeneric, uint64_t, const hilti::rt::Bytes&,
253  const hilti::rt::Bytes&)>>
255 
257  std::optional<std::function<void(hilti::rt::StrongReferenceGeneric, uint64_t)>> __hook_skipped;
258 
260  std::optional<std::function<void(hilti::rt::StrongReferenceGeneric, uint64_t, const hilti::rt::Bytes&)>>
262 };
263 
265 inline const auto& parsers() { return detail::globalState()->parsers; }
266 
270 class ParseError : public hilti::rt::UserException {
271 public:
272  ParseError(const std::string& msg, const std::string& location = "")
273  : UserException(hilti::rt::fmt("parse error: %s", msg), location) {}
274 
275  ParseError(const hilti::rt::result::Error& e) : UserException(hilti::rt::fmt("parse error: %s", e.description())) {}
276 
277  virtual ~ParseError(); /* required to create vtable, see hilti::rt::Exception */
278 };
279 
285 class Backtrack : public ParseError {
286 public:
287  Backtrack() : ParseError("backtracking outside of &try scope") {}
288  virtual ~Backtrack();
289 };
290 
291 namespace detail {
292 
306 template<typename UnitRef>
307 inline void registerParser(::spicy::rt::Parser& p, // NOLINT(google-runtime-references)
308  std::string linker_scope, UnitRef /* not used, just for template instantiation */) {
309  // Note: This may may be called before spicy::rt::init(), and during
310  // hilti::rt::init(). Cannot rely on any library functionality being
311  // initialized yet.
312 
313  p.linker_scope = std::move(linker_scope);
314  globalState()->parsers.emplace_back(&p);
315 
316  using unit_type = typename UnitRef::element_type;
317 
319  ! std::is_base_of<hilti::rt::trait::hasParameters, unit_type>::value )
320  p.__parse_sink = sink::detail::parseFunction<unit_type>();
321 
322  if constexpr ( detail::has_on_gap<unit_type>::value )
323  p.__hook_gap = sink::detail::hookFunction<unit_type, &unit_type::__on_0x25_gap, uint64_t, uint64_t>();
324 
326  p.__hook_skipped = sink::detail::hookFunction<unit_type, &unit_type::__on_0x25_skipped, uint64_t>();
327 
329  p.__hook_overlap = sink::detail::hookFunction<unit_type, &unit_type::__on_0x25_overlap, uint64_t,
330  const hilti::rt::Bytes&, const hilti::rt::Bytes&>();
331 
333  p.__hook_undelivered = sink::detail::hookFunction<unit_type, &unit_type::__on_0x25_undelivered, uint64_t,
334  const hilti::rt::Bytes&>();
335 }
336 
341 void printParserState(const std::string& unit_id, const hilti::rt::ValueReference<hilti::rt::Stream>& data,
342  const hilti::rt::stream::View& cur, int64_t lahead,
343  const hilti::rt::stream::SafeConstIterator& lahead_end, const std::string& literal_mode,
344  bool trim);
345 
357 extern bool waitForInputOrEod(hilti::rt::ValueReference<hilti::rt::Stream>& data, // NOLINT(google-runtime-references)
358  const hilti::rt::stream::View& cur, uint64_t min,
360 
369 extern void waitForEod(hilti::rt::ValueReference<hilti::rt::Stream>& data, // NOLINT(google-runtime-references)
370  const hilti::rt::stream::View& cur,
372 
387 extern void waitForInput(hilti::rt::ValueReference<hilti::rt::Stream>& data, // NOLINT(google-runtime-references)
388  const hilti::rt::stream::View& cur, uint64_t min, const std::string& error_msg,
389  const std::string& location,
391 
402 extern bool waitForInputOrEod(hilti::rt::ValueReference<hilti::rt::Stream>& data, // NOLINT(google-runtime-references)
403  const hilti::rt::stream::View& cur,
405 
419 extern void waitForInput(hilti::rt::ValueReference<hilti::rt::Stream>& data, // NOLINT(google-runtime-references)
420  const hilti::rt::stream::View& cur, const std::string& error_msg, const std::string& location,
422 
432 
436 inline void backtrack() { throw Backtrack(); }
437 
449 std::optional<hilti::rt::stream::SafeConstIterator> unitFind(
451  const std::optional<hilti::rt::stream::SafeConstIterator>& i, const hilti::rt::Bytes& needle,
452  hilti::rt::stream::Direction d);
453 } // namespace detail
454 } // namespace spicy::rt
std::string to_string(T &&x)
Definition: extension-points.h:26
Definition: result.h:18
Definition: reference.h:633
Definition: parser.h:107
std::optional< std::function< void(hilti::rt::StrongReferenceGeneric, uint64_t, uint64_t)> > __hook_gap
Definition: parser.h:249
Definition: null.h:19
Definition: port.h:22
Definition: parser.h:285
hilti::rt::Vector< ParserPort > ports
Definition: parser.h:240
std::string linker_scope
Definition: parser.h:196
std::optional< std::function< void(hilti::rt::StrongReferenceGeneric, uint64_t, const hilti::rt::Bytes &, const hilti::rt::Bytes &)> > __hook_overlap
Definition: parser.h:254
Definition: parser.h:270
Definition: bytes.h:153
void cannot_be_reached() __attribute__((noreturn))
Definition: util.cc:52
Definition: stream.h:978
std::optional< detail::ParseSinkFunction > __parse_sink
Definition: parser.h:246
Definition: reference.h:321
Definition: parser.h:54
std::optional< UnitContext > createContext() const
Definition: parser.h:181
Definition: vector.h:249
Definition: deferred-expression.h:41
std::optional< std::function< void(hilti::rt::StrongReferenceGeneric, uint64_t, const hilti::rt::Bytes &)> > __hook_undelivered
Definition: parser.h:261
Definition: parser.h:85
Definition: parser.h:137
Definition: location.h:86
Definition: parser.h:98
Definition: stream.h:375
Definition: type-info.h:1146
hilti::rt::any parse2
Definition: parser.h:209
std::optional< std::function< void(hilti::rt::StrongReferenceGeneric, uint64_t)> > __hook_skipped
Definition: parser.h:257
hilti::rt::Vector< MIMEType > mime_types
Definition: parser.h:235
std::string description
Definition: parser.h:230
std::string fmt(const char *fmt, const Args &... args)
Definition: fmt.h:13
std::string name
Definition: parser.h:189