mirror of
https://github.com/matrix-construct/construct
synced 2025-02-18 01:30:12 +01:00
ircd::spirit: Optimize functor, qi::rule toward eliminating indirect branching.
This commit is contained in:
parent
7a423c94be
commit
29fda76769
7 changed files with 569 additions and 140 deletions
|
@ -16,37 +16,56 @@
|
|||
// some false asserts around boolean character tests in spirit.
|
||||
#define BOOST_DISABLE_ASSERTS
|
||||
|
||||
// This prevents spirit grammar rules from generating a very large and/or deep
|
||||
// call-graph where rules compose together using wrapped indirect calls through
|
||||
// boost::function -- this is higly inefficient as the grammar's logic ends up
|
||||
// being a fraction of the generated code and the rest is invocation related
|
||||
// overhead. By force-flattening here we can allow each entry-point into
|
||||
// spirit to compose rules at once and eliminate the wrapping complex.
|
||||
#if RB_CXX_EPOCH <= 12 // NOTE: disabled for crashing clang-13.
|
||||
#pragma clang attribute push ([[gnu::always_inline]], apply_to = function)
|
||||
#pragma clang attribute push ([[gnu::flatten]], apply_to = function)
|
||||
// These trip include guards on some headers which pollute the unit such as
|
||||
// those which include <iostream>, etc. These headers contain static objects
|
||||
// and initializations which prevent optimization of our static grammars.
|
||||
#if defined(__clang__)
|
||||
#define BOOST_PROTO_DEBUG_HPP_EAN_12_31_2006
|
||||
#define BOOST_SPIRIT_DEBUG_HANDLER_DECEMBER_05_2008_0734PM
|
||||
#define BOOST_SPIRIT_SIMPLE_TRACE_DECEMBER_06_2008_1102AM
|
||||
#define BOOST_SPIRIT_KARMA_DEBUG_HANDLER_APR_21_2010_0148PM
|
||||
#define BOOST_SPIRIT_KARMA_SIMPLE_TRACE_APR_21_2010_0155PM
|
||||
#define BOOST_PHOENIX_DEBUG_HPP
|
||||
#endif
|
||||
|
||||
// These must be included prior to the internal visibility/linkage sections.
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/function.hpp>
|
||||
#include "function.h"
|
||||
#include <boost/type_index.hpp>
|
||||
#include <boost/function/function_fwd.hpp>
|
||||
|
||||
#pragma GCC visibility push (internal)
|
||||
// Upstream includes for spirit; these would otherwise generate layers of
|
||||
// template noise in the symbol table but they can be strongly hidden.
|
||||
#pragma GCC visibility push(internal)
|
||||
#pragma clang attribute push(__attribute__((internal_linkage)), apply_to=any(function,record,variable))
|
||||
#include <boost/none.hpp>
|
||||
#include "function.h"
|
||||
#include <boost/fusion/sequence.hpp>
|
||||
#include <boost/fusion/iterator.hpp>
|
||||
#include <boost/fusion/adapted.hpp>
|
||||
#include <boost/fusion/functional.hpp>
|
||||
#include <boost/fusion/algorithm.hpp>
|
||||
#include <boost/fusion/include/std_pair.hpp>
|
||||
#include <boost/spirit/include/qi.hpp>
|
||||
#include <boost/spirit/include/karma.hpp>
|
||||
#include <boost/proto/traits.hpp>
|
||||
#include <boost/proto/extends.hpp>
|
||||
#include <boost/proto/operators.hpp>
|
||||
#include <boost/proto/tags.hpp>
|
||||
#include <boost/proto/proto_fwd.hpp>
|
||||
#include <boost/proto/make_expr.hpp>
|
||||
#include <boost/proto/transform.hpp>
|
||||
#include <boost/proto/deep_copy.hpp>
|
||||
#include <boost/spirit/include/phoenix.hpp>
|
||||
#include <boost/spirit/repository/include/qi_seek.hpp>
|
||||
#include <boost/spirit/repository/include/qi_subrule.hpp>
|
||||
#include "char.h"
|
||||
#include "expr.h"
|
||||
#pragma clang attribute pop
|
||||
#pragma GCC visibility pop
|
||||
|
||||
#if RB_CXX_EPOCH <= 12
|
||||
#pragma clang attribute pop
|
||||
#pragma clang attribute pop
|
||||
#endif
|
||||
// Spirit includes
|
||||
#pragma GCC visibility push(protected)
|
||||
#include <boost/spirit/include/support.hpp>
|
||||
#include <boost/spirit/include/qi.hpp>
|
||||
#include "qi_rule.h"
|
||||
#include "qi_char.h"
|
||||
#include <boost/spirit/include/karma.hpp>
|
||||
#include "karma_rule.h"
|
||||
#include <boost/spirit/repository/include/qi_seek.hpp>
|
||||
#include <boost/spirit/repository/include/qi_subrule.hpp>
|
||||
#pragma GCC visibility pop
|
||||
|
|
79
include/ircd/spirit/expr.h
Normal file
79
include/ircd/spirit/expr.h
Normal file
|
@ -0,0 +1,79 @@
|
|||
// The Construct
|
||||
//
|
||||
// Copyright (C) The Construct Developers, Authors & Contributors
|
||||
// Copyright (C) 2016-2022 Jason Volk <jason@zemos.net>
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice is present in all copies. The
|
||||
// full license for this software is available in the LICENSE file.
|
||||
|
||||
#pragma once
|
||||
#define HAVE_IRCD_SPIRIT_EXPR_H
|
||||
|
||||
namespace ircd::spirit
|
||||
{
|
||||
template<class T>
|
||||
struct [[gnu::nodebug]] expr;
|
||||
}
|
||||
|
||||
/// Expression storage wrapper. This could also be called `named_expression`.
|
||||
/// This allows spirit expressions to be instantiated in global variables
|
||||
/// along with a simple name string (and potentially other metadata). Why not
|
||||
/// just use a rule, you say? The rule templates perform type erasure on the
|
||||
/// original expression by constructing function objects which are prototyped
|
||||
/// to the rule such that the expression is lost behind a call. Compilers are
|
||||
/// able to optimize this like it was never even there, but only when all rules
|
||||
/// are constructed in a local stack frame. **Clang is unable to compose rules
|
||||
/// defined at global scope without indirect calls**.
|
||||
///
|
||||
/// Declarations of this object are blessed with C++17 template deduction. At
|
||||
/// global scope, compose expressions first before constructing a single rule
|
||||
/// right before parsing. !!! Do not allow global scope rules to refer to each
|
||||
/// other, even at function scope. !!! Do not allow exprs to refer to rules,
|
||||
/// ever. !!! Only allow global scope rules to refer to exprs, and exprs to
|
||||
/// other exprs.
|
||||
///
|
||||
/// Rules constructed at function scope usually generate fully inline parsers.
|
||||
/// Rules constructed at global scope usually generate parsers behind a direct
|
||||
/// call. Everything else generates a soup of handler functions behind a web
|
||||
/// of indirect calls.
|
||||
///
|
||||
template<class T>
|
||||
struct [[clang::internal_linkage]]
|
||||
ircd::spirit::expr
|
||||
:boost::proto::result_of::deep_copy<T>::type
|
||||
{
|
||||
const char *const &name;
|
||||
|
||||
constexpr expr(T &&, const char *const &name);
|
||||
constexpr expr(const T &, const char *const &name);
|
||||
};
|
||||
|
||||
template<class T>
|
||||
constexpr
|
||||
ircd::spirit::expr<T>::expr(const T &e,
|
||||
const char *const &name)
|
||||
:boost::proto::result_of::deep_copy<T>::type
|
||||
{
|
||||
boost::proto::deep_copy(e)
|
||||
}
|
||||
,name
|
||||
{
|
||||
name
|
||||
}
|
||||
{}
|
||||
|
||||
template<class T>
|
||||
constexpr
|
||||
ircd::spirit::expr<T>::expr(T &&e,
|
||||
const char *const &name)
|
||||
:boost::proto::result_of::deep_copy<T>::type
|
||||
{
|
||||
boost::proto::deep_copy(std::move(e))
|
||||
}
|
||||
,name
|
||||
{
|
||||
name
|
||||
}
|
||||
{}
|
|
@ -9,154 +9,152 @@
|
|||
// full license for this software is available in the LICENSE file.
|
||||
|
||||
#pragma once
|
||||
#define HAVE_IRCD_SPIRIT_BOOST_FUNCTION_H
|
||||
#define HAVE_IRCD_SPIRIT_FUNCTION_H
|
||||
|
||||
#if !defined(__clang__)
|
||||
#pragma GCC visibility push (internal)
|
||||
namespace boost::spirit
|
||||
{
|
||||
// Spirit uses `boost::function` by default, but since it's in boost:: it
|
||||
// simply declares as `function`. We can do a lot better by dropping in
|
||||
// `std::function` instead.
|
||||
using std::function;
|
||||
}
|
||||
#pragma GCC visibility pop
|
||||
#endif
|
||||
// This all prevents spirit grammar rules from generating a very large and/or
|
||||
// deep call-graph where rules compose together with indirect calls and thunks
|
||||
// through boost::function -- this is higly inefficient as the grammar's logic
|
||||
// ends up being a fraction of the generated code and the rest is invocation
|
||||
// related overhead since lots of optimizations become inhibited.
|
||||
|
||||
#if defined(__clang__)
|
||||
#pragma GCC visibility push (internal)
|
||||
namespace boost::spirit
|
||||
// Spirit uses `boost::function` by default, but since it's in boost:: it
|
||||
// declares as `function`. We can immediately do a lot better by simply
|
||||
// dropping in `std::function` as said `function` instead. However, this does
|
||||
// not go far enough for most cases so we maximize optimization with a bespoke
|
||||
// function object.
|
||||
|
||||
// Our optimized function object is built in ircd::spirit so as to not
|
||||
// interpose itself by default; see below for controlling conditions.
|
||||
namespace ircd::spirit
|
||||
{
|
||||
template<class prototype>
|
||||
struct function;
|
||||
class function;
|
||||
|
||||
template<class R,
|
||||
class... args>
|
||||
struct function<R (args...)>;
|
||||
class function<R (args...)>;
|
||||
}
|
||||
|
||||
// Fuse-box controlling spirit::qi parsers.
|
||||
namespace boost::spirit::qi
|
||||
{
|
||||
#if defined(__clang__)
|
||||
using ircd::spirit::function;
|
||||
#else
|
||||
using std::function;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Fusebox controlling spirit::karma generators.
|
||||
namespace boost::spirit::karma
|
||||
{
|
||||
#if defined(__clang__)
|
||||
using std::function;
|
||||
#else
|
||||
using std::function;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Internal optimized function object.
|
||||
template<class R,
|
||||
class... args>
|
||||
class [[clang::internal_linkage]]
|
||||
boost::spirit::function<R (args...)>
|
||||
ircd::spirit::function<R (args...)>
|
||||
{
|
||||
size_t s
|
||||
{
|
||||
0UL
|
||||
};
|
||||
|
||||
R (*f)(const void *const &, args...)
|
||||
R (*f)(void *, args...)
|
||||
{
|
||||
nullptr
|
||||
};
|
||||
|
||||
std::unique_ptr<char, decltype(&std::free)> o
|
||||
void *t
|
||||
{
|
||||
nullptr, std::free
|
||||
nullptr
|
||||
};
|
||||
|
||||
template<class T>
|
||||
static R handler(void *, args...);
|
||||
|
||||
public:
|
||||
explicit operator bool() const noexcept
|
||||
{
|
||||
assert(f);
|
||||
assert(o);
|
||||
return true;
|
||||
}
|
||||
explicit operator bool() const noexcept;
|
||||
|
||||
decltype(auto) operator()(args&&...) const;
|
||||
decltype(auto) operator()(args&&...);
|
||||
R operator()(args...) const;
|
||||
|
||||
template<class binder>
|
||||
[[clang::internal_linkage]]
|
||||
function &operator=(binder) noexcept;
|
||||
template<class T>
|
||||
function &operator=(T *const t) & noexcept;
|
||||
|
||||
template<class T>
|
||||
function(T *const t) noexcept;
|
||||
|
||||
function(const function &, void *const &) noexcept;
|
||||
function() = default;
|
||||
function(const function &);
|
||||
function(function &&) noexcept = default;
|
||||
function(function &&) = default;
|
||||
function(const function &) = delete;
|
||||
function &operator=(function &&) = default;
|
||||
function &operator=(const function &) = delete;
|
||||
~function() noexcept = default;
|
||||
};
|
||||
|
||||
template<class R,
|
||||
class... args>
|
||||
boost::spirit::function<R (args...)>::function(const function &o)
|
||||
:s{o.s}
|
||||
,f{o.f}
|
||||
,o
|
||||
{
|
||||
this->s?
|
||||
ircd::aligned_alloc(64, this->s):
|
||||
std::unique_ptr<char, decltype(&std::free)>
|
||||
{
|
||||
nullptr, std::free
|
||||
}
|
||||
}
|
||||
{
|
||||
memcpy(this->o.get(), o.o.get(), this->s);
|
||||
}
|
||||
inline
|
||||
ircd::spirit::function<R (args...)>::function(const function &f,
|
||||
void *const &t)
|
||||
noexcept
|
||||
:f{f.f}
|
||||
,t{t}
|
||||
{}
|
||||
|
||||
template<class R,
|
||||
class... args>
|
||||
template<class binder>
|
||||
[[clang::reinitializes]]
|
||||
boost::spirit::function<R (args...)> &
|
||||
boost::spirit::function<R (args...)>::operator=(binder o)
|
||||
template<class T>
|
||||
inline
|
||||
ircd::spirit::function<R (args...)>::function(T *const t)
|
||||
noexcept
|
||||
:f{this->handler<T>}
|
||||
,t{t}
|
||||
{}
|
||||
|
||||
template<class R,
|
||||
class... args>
|
||||
template<class T>
|
||||
inline ircd::spirit::function<R (args...)> &
|
||||
ircd::spirit::function<R (args...)>::operator=(T *const t)
|
||||
& noexcept
|
||||
{
|
||||
constexpr auto alignment
|
||||
{
|
||||
std::max(std::alignment_of<binder>::value, 64UL)
|
||||
};
|
||||
|
||||
this->s = sizeof(binder);
|
||||
this->o = ircd::aligned_alloc(alignment, this->s);
|
||||
this->f = [](const void *const &o, args&&... a)
|
||||
{
|
||||
const auto &object
|
||||
{
|
||||
*reinterpret_cast<const binder *>(o)
|
||||
};
|
||||
|
||||
return object(std::forward<args>(a)...);
|
||||
};
|
||||
|
||||
new (this->o.get()) binder
|
||||
{
|
||||
std::move(o)
|
||||
};
|
||||
|
||||
this->f = this->handler<T>;
|
||||
this->t = t;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class R,
|
||||
class... args>
|
||||
[[gnu::always_inline, gnu::artificial]]
|
||||
inline decltype(auto)
|
||||
boost::spirit::function<R (args...)>::operator()(args&&... a)
|
||||
template<class T>
|
||||
[[gnu::section("text.ircd.spirit")]]
|
||||
inline R
|
||||
ircd::spirit::function<R (args...)>::handler(void *const t,
|
||||
args... a)
|
||||
{
|
||||
assert(bool(*this));
|
||||
const auto o
|
||||
{
|
||||
std::addressof(*this->o)
|
||||
};
|
||||
|
||||
return this->f(o, std::forward<args>(a)...);
|
||||
assert(t);
|
||||
return static_cast<T *>(t)->operator()(a...);
|
||||
}
|
||||
|
||||
template<class R,
|
||||
class... args>
|
||||
[[gnu::always_inline, gnu::artificial]]
|
||||
inline decltype(auto)
|
||||
boost::spirit::function<R (args...)>::operator()(args&&... a)
|
||||
[[gnu::always_inline]]
|
||||
inline R
|
||||
ircd::spirit::function<R (args...)>::operator()(args... a)
|
||||
const
|
||||
{
|
||||
assert(bool(*this));
|
||||
const auto o
|
||||
{
|
||||
std::addressof(*this->o)
|
||||
};
|
||||
|
||||
return this->f(o, std::forward<args>(a)...);
|
||||
assert(this->f);
|
||||
return this->f(this->t, a...);
|
||||
}
|
||||
|
||||
#pragma GCC visibility pop
|
||||
#endif
|
||||
template<class R,
|
||||
class... args>
|
||||
inline ircd::spirit::function<R (args...)>::operator
|
||||
bool()
|
||||
const noexcept
|
||||
{
|
||||
assert(!f || (f && t));
|
||||
return f;
|
||||
}
|
||||
|
|
|
@ -26,19 +26,19 @@ __attribute__((visibility("internal")))
|
|||
BOOST_SPIRIT_TERMINAL(custom1);
|
||||
BOOST_SPIRIT_TERMINAL(custom2);
|
||||
|
||||
template<class gen,
|
||||
template<class rule,
|
||||
class... attr>
|
||||
bool parse(const char *&start, const char *const &stop, gen&&, attr&&...);
|
||||
bool parse(const char *&start, const char *const &stop, rule&&, attr&&...);
|
||||
|
||||
template<class parent_error,
|
||||
size_t error_show_max = 128,
|
||||
class gen,
|
||||
class rule,
|
||||
class... attr>
|
||||
bool parse(const char *&start, const char *const &stop, gen&&, attr&&...);
|
||||
bool parse(const char *&start, const char *const &stop, rule&&, attr&&...);
|
||||
|
||||
template<class gen,
|
||||
template<class rule,
|
||||
class... attr>
|
||||
bool parse(std::nothrow_t, const char *&start, const char *const &stop, gen&&, attr&&...) noexcept;
|
||||
bool parse(std::nothrow_t, const char *&start, const char *const &stop, rule&&, attr&&...) noexcept;
|
||||
}}
|
||||
|
||||
namespace boost {
|
||||
|
@ -157,18 +157,18 @@ struct ircd::spirit::substring_view
|
|||
/// Failures must not throw: If the grammar contains any epsilon expressions or
|
||||
/// callbacks which throw it is UB. This overload exists to force suppression
|
||||
/// of EH from the base of a complex/opaque rule tree.
|
||||
template<class gen,
|
||||
template<class rule,
|
||||
class... attr>
|
||||
[[using gnu: always_inline, gnu_inline, artificial]]
|
||||
extern inline bool
|
||||
ircd::spirit::parse(std::nothrow_t,
|
||||
const char *&start,
|
||||
const char *const &stop,
|
||||
gen&& g,
|
||||
rule&& r,
|
||||
attr&&... a)
|
||||
noexcept try
|
||||
{
|
||||
return ircd::spirit::parse(start, stop, std::forward<gen>(g), std::forward<attr>(a)...);
|
||||
return ircd::spirit::parse(start, stop, std::forward<rule>(r), std::forward<attr>(a)...);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
|
@ -181,17 +181,17 @@ catch(...)
|
|||
/// translated into our expectation_failure describing the failure.
|
||||
template<class parent_error,
|
||||
size_t error_show_max,
|
||||
class gen,
|
||||
class rule,
|
||||
class... attr>
|
||||
[[using gnu: always_inline, gnu_inline, artificial]]
|
||||
extern inline bool
|
||||
ircd::spirit::parse(const char *&start,
|
||||
const char *const &stop,
|
||||
gen&& g,
|
||||
rule&& r,
|
||||
attr&&... a)
|
||||
try
|
||||
{
|
||||
return ircd::spirit::parse(start, stop, std::forward<gen>(g), std::forward<attr>(a)...);
|
||||
return ircd::spirit::parse(start, stop, std::forward<rule>(r), std::forward<attr>(a)...);
|
||||
}
|
||||
catch(const qi::expectation_failure<const char *> &e)
|
||||
{
|
||||
|
@ -203,14 +203,14 @@ catch(const qi::expectation_failure<const char *> &e)
|
|||
|
||||
/// Low-level qi::parse entry point. Throws boost's qi::expectation_failure;
|
||||
/// Try not to allow this exception to escape the calling unit.
|
||||
template<class gen,
|
||||
template<class rule,
|
||||
class... attr>
|
||||
[[using gnu: always_inline, gnu_inline, artificial]]
|
||||
extern inline bool
|
||||
ircd::spirit::parse(const char *&start,
|
||||
const char *const &stop,
|
||||
gen&& g,
|
||||
rule&& r,
|
||||
attr&&... a)
|
||||
{
|
||||
return qi::parse(start, stop, std::forward<gen>(g), std::forward<attr>(a)...);
|
||||
return qi::parse(start, stop, std::forward<rule>(r), std::forward<attr>(a)...);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// The Construct
|
||||
//
|
||||
// Copyright (C) The Construct Developers, Authors & Contributors
|
||||
// Copyright (C) 2016-2020 Jason Volk <jason@zemos.net>
|
||||
// Copyright (C) 2016-2022 Jason Volk <jason@zemos.net>
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
|
@ -9,7 +9,7 @@
|
|||
// full license for this software is available in the LICENSE file.
|
||||
|
||||
#pragma once
|
||||
#define HAVE_IRCD_SPIRIT_CHAR_H
|
||||
#define HAVE_IRCD_SPIRIT_QI_CHAR_H
|
||||
|
||||
#if defined(__clang__)
|
||||
template<class Derived>
|
332
include/ircd/spirit/qi_rule.h
Normal file
332
include/ircd/spirit/qi_rule.h
Normal file
|
@ -0,0 +1,332 @@
|
|||
// The Construct
|
||||
//
|
||||
// Copyright (C) The Construct Developers, Authors & Contributors
|
||||
// Copyright (C) 2016-2022 Jason Volk <jason@zemos.net>
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice is present in all copies. The
|
||||
// full license for this software is available in the LICENSE file.
|
||||
|
||||
#pragma once
|
||||
#define HAVE_IRCD_SPIRIT_QI_RULE_H
|
||||
|
||||
/// Custom reimplementation/interposition of the qi::rule template by
|
||||
/// specializing the `const char *` iterator, which is common to our parsers.
|
||||
///
|
||||
/// The goal here is to support our custom lightweight function object (see
|
||||
/// function.h). This implementation is optimized for our grammar's patterns,
|
||||
/// which generally involve constant/immutable exprs and static global rules.
|
||||
///
|
||||
/// Dynamic memory has been removed which would otherwise impede optimization
|
||||
/// at global scope:
|
||||
///
|
||||
/// - For the rule's name, which was formerly std::string. The name is now a
|
||||
/// `const char *` which should point to a literal.
|
||||
///
|
||||
/// - To support the custom function object, a fixed array is contained
|
||||
/// directly within this class. The parser_binder is placement new'ed there.
|
||||
///
|
||||
template<class T1,
|
||||
class T2,
|
||||
class T3,
|
||||
class T4>
|
||||
struct boost::spirit::qi::rule<const char *, T1, T2, T3, T4>
|
||||
:proto::extends
|
||||
<
|
||||
typename proto::terminal<reference<const rule<const char *, T1, T2, T3, T4>>>::type
|
||||
,rule<const char *, T1, T2, T3, T4>
|
||||
>
|
||||
,parser
|
||||
<
|
||||
rule<const char *, T1, T2, T3, T4>
|
||||
>
|
||||
{
|
||||
using Iterator = const char *;
|
||||
using iterator_type = Iterator;
|
||||
using this_type = rule<iterator_type, T1, T2, T3, T4>;
|
||||
using reference_ = reference<const this_type>;
|
||||
using terminal = typename proto::terminal<reference_>::type;
|
||||
using terminal_type = typename proto::terminal<this_type>::type;
|
||||
using parser_type = parser<this_type>;
|
||||
using base_type = proto::extends<terminal, this_type>;
|
||||
using template_params = mpl::vector<T1, T2, T3, T4>;
|
||||
using locals_type = typename spirit::detail::extract_locals<template_params>::type;
|
||||
using skipper_type = typename spirit::detail::extract_component<qi::domain, template_params>::type;
|
||||
using encoding_type = typename spirit::detail::extract_encoding<template_params>::type;
|
||||
using sig_type = typename spirit::detail::extract_sig<template_params, encoding_type, qi::domain>::type;
|
||||
using attr_type = typename spirit::detail::attr_from_sig<sig_type>::type;
|
||||
using attr_reference_type = attr_type &;
|
||||
using parameter_types = typename spirit::detail::params_from_sig<sig_type>::type;
|
||||
using parameter_types_size = typename fusion::result_of::size<parameter_types>::type;
|
||||
using context_type = context<fusion::cons<attr_reference_type, parameter_types>, locals_type>;
|
||||
using function_prototype = bool (Iterator &, const Iterator &, context_type &, const skipper_type &);
|
||||
using function_type = function<function_prototype>;
|
||||
using encoding_modifier_type = typename mpl::if_
|
||||
<
|
||||
is_same<encoding_type, unused_type>,
|
||||
unused_type,
|
||||
tag::char_code<tag::encoding, encoding_type>
|
||||
>::type;
|
||||
|
||||
template<class Context,
|
||||
class Iterator>
|
||||
struct attribute
|
||||
{
|
||||
using type = attr_type;
|
||||
};
|
||||
|
||||
static constexpr const size_t ¶ms_size
|
||||
{
|
||||
parameter_types_size::value
|
||||
};
|
||||
|
||||
private:
|
||||
BOOST_STATIC_ASSERT_MSG
|
||||
(
|
||||
!is_reference<attr_type>::value,
|
||||
"Reference qualifier on Qi rule attribute is meaningless"
|
||||
);
|
||||
|
||||
template<class Auto,
|
||||
class Expr>
|
||||
static decltype(auto) define(Expr&& expr, mpl::false_)
|
||||
{
|
||||
BOOST_SPIRIT_ASSERT_MATCH(qi::domain, Expr);
|
||||
}
|
||||
|
||||
template<class Auto,
|
||||
class Expr>
|
||||
[[warn_unused_result]]
|
||||
static decltype(auto) define(Expr&& expr, mpl::true_)
|
||||
{
|
||||
return detail::bind_parser<const Auto>
|
||||
(
|
||||
compile<qi::domain>(std::forward<Expr>(expr), encoding_modifier_type())
|
||||
);
|
||||
}
|
||||
|
||||
template<class Expr>
|
||||
static decltype(auto) create(Expr&& expr)
|
||||
{
|
||||
return define<mpl::false_>
|
||||
(
|
||||
std::forward<Expr>(expr), traits::matches<qi::domain, Expr>()
|
||||
);
|
||||
}
|
||||
|
||||
/// Slightly exceeds the worst-case for currently in use compiled
|
||||
/// expressions. It is possible to build an expression which exceeds
|
||||
/// this value, and in that case, feel free to increase this size.
|
||||
static constexpr const size_t &buf_sz
|
||||
{
|
||||
2048 - 32 // offsetof buf
|
||||
};
|
||||
|
||||
function_type func;
|
||||
const char *_name;
|
||||
size_t _size;
|
||||
[[clang::loader_uninitialized]] uint8_t buf alignas(32) [buf_sz];
|
||||
|
||||
public:
|
||||
using parameterized_subject_type = const rule;
|
||||
const rule &get_parameterized_subject() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
reference_ alias() const noexcept
|
||||
{
|
||||
return reference_(*this);
|
||||
}
|
||||
|
||||
std::string_view name() const
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
template<class Context>
|
||||
info what(Context&) const noexcept
|
||||
{
|
||||
return info(std::string(name()));
|
||||
}
|
||||
|
||||
template<class C,
|
||||
class S,
|
||||
class A>
|
||||
bool parse(Iterator &, const Iterator &, C &, const S &, A &) const;
|
||||
|
||||
template<class C,
|
||||
class S,
|
||||
class A,
|
||||
class P>
|
||||
bool parse(Iterator &, const Iterator &, C &, const S &, A &, const P &) const;
|
||||
|
||||
// bring in the operator() overloads
|
||||
#include <boost/spirit/home/qi/nonterminal/detail/fcall.hpp>
|
||||
|
||||
/// Expression compiler; primary construction.
|
||||
template<class Expr>
|
||||
rule(const Expr &expr, const char *const name = "<unnamed>") noexcept;
|
||||
|
||||
/// Renaming constructor. This supports a special case when this rule is
|
||||
/// constructed from a single other rule, but a name is still provided as
|
||||
/// the second argument; prevents forced indirection and bad codegen.
|
||||
template<class Expr>
|
||||
explicit rule(const rule &o, const char *const name) noexcept;
|
||||
|
||||
/// Copy construction is supported through the rename-ctor, just using the
|
||||
/// name from the rhs.
|
||||
rule(const rule &o)
|
||||
:rule(o, o._name)
|
||||
{}
|
||||
|
||||
rule() = delete;
|
||||
rule(rule &&o) = delete;
|
||||
rule& operator=(rule &&) = delete;
|
||||
rule& operator=(const rule &) = delete;
|
||||
~rule() noexcept = default;
|
||||
};
|
||||
|
||||
template<class T1,
|
||||
class T2,
|
||||
class T3,
|
||||
class T4>
|
||||
template<class Expr>
|
||||
inline
|
||||
boost::spirit::qi::rule<const char *, T1, T2, T3, T4>::rule(const Expr &expr,
|
||||
const char *const name)
|
||||
noexcept
|
||||
:base_type(terminal::make(reference_(*this)))
|
||||
,func(new (buf) decltype(create(expr)) (create(expr)))
|
||||
,_name(name)
|
||||
,_size(sizeof(decltype(create(expr))))
|
||||
{
|
||||
/// If this assertion has tripped first ensure you are not copying
|
||||
/// an instance of this rule, otherwise see the comment on buf_sz.
|
||||
static_assert
|
||||
(
|
||||
sizeof(decltype(create(expr))) <= buf_sz,
|
||||
"Instance buffer is insufficient for the binder object."
|
||||
);
|
||||
}
|
||||
|
||||
template<class T1,
|
||||
class T2,
|
||||
class T3,
|
||||
class T4>
|
||||
template<class Expr>
|
||||
inline
|
||||
boost::spirit::qi::rule<const char *, T1, T2, T3, T4>::rule(const rule &o,
|
||||
const char *const name)
|
||||
noexcept
|
||||
:base_type(terminal::make(reference_(*this)))
|
||||
,func(o.func, buf)
|
||||
,_name(name)
|
||||
,_size(o._size)
|
||||
{
|
||||
assert(_size <= buf_sz);
|
||||
memcpy(buf, o.buf, _size);
|
||||
}
|
||||
|
||||
template<class T1,
|
||||
class T2,
|
||||
class T3,
|
||||
class T4>
|
||||
template<class Context,
|
||||
class Skipper,
|
||||
class Attribute>
|
||||
inline bool
|
||||
boost::spirit::qi::rule<const char *, T1, T2, T3, T4>::parse(Iterator &first,
|
||||
const Iterator &last,
|
||||
Context &,
|
||||
const Skipper &skipper,
|
||||
Attribute &attr_param)
|
||||
const
|
||||
{
|
||||
BOOST_STATIC_ASSERT_MSG
|
||||
(
|
||||
(is_same<skipper_type, unused_type>::value || !is_same<Skipper, unused_type>::value),
|
||||
"The rule was instantiated with a skipper type but you have not pass any. "
|
||||
"Did you use `parse` instead of `phrase_parse`?"
|
||||
);
|
||||
|
||||
BOOST_STATIC_ASSERT_MSG
|
||||
(
|
||||
(is_convertible<const Skipper &, skipper_type>::value),
|
||||
"The passed skipper is not compatible/convertible to one "
|
||||
"that the rule was instantiated with"
|
||||
);
|
||||
|
||||
if constexpr(is_same<skipper_type, unused_type>::value)
|
||||
qi::skip_over(first, last, skipper);
|
||||
|
||||
using transform = traits::transform_attribute<Attribute, attr_type, domain>;
|
||||
typename transform::type attr_(transform::pre(attr_param));
|
||||
context_type context(attr_);
|
||||
|
||||
assume(bool(func));
|
||||
const bool ret
|
||||
{
|
||||
func(first, last, context, skipper)
|
||||
};
|
||||
|
||||
if(ret)
|
||||
transform::post(attr_param, attr_);
|
||||
else
|
||||
transform::fail(attr_param);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<class T1,
|
||||
class T2,
|
||||
class T3,
|
||||
class T4>
|
||||
template<class Context,
|
||||
class Skipper,
|
||||
class Attribute,
|
||||
class Params>
|
||||
inline bool
|
||||
boost::spirit::qi::rule<const char *, T1, T2, T3, T4>::parse(Iterator &first,
|
||||
const Iterator &last,
|
||||
Context &caller_context,
|
||||
const Skipper &skipper,
|
||||
Attribute &attr_param,
|
||||
const Params ¶ms)
|
||||
const
|
||||
{
|
||||
BOOST_STATIC_ASSERT_MSG
|
||||
(
|
||||
(is_same<skipper_type, unused_type>::value || !is_same<Skipper, unused_type>::value),
|
||||
"The rule was instantiated with a skipper type but you have not pass any. "
|
||||
"Did you use `parse` instead of `phrase_parse`?"
|
||||
);
|
||||
|
||||
BOOST_STATIC_ASSERT_MSG
|
||||
(
|
||||
(is_convertible<const Skipper &, skipper_type>::value),
|
||||
"The passed skipper is not compatible/convertible to one "
|
||||
"that the rule was instantiated with"
|
||||
);
|
||||
|
||||
if constexpr(is_same<skipper_type, unused_type>::value)
|
||||
qi::skip_over(first, last, skipper);
|
||||
|
||||
using transform = traits::transform_attribute<Attribute, attr_type, domain>;
|
||||
typename transform::type attr_(transform::pre(attr_param));
|
||||
context_type context(attr_, params, caller_context);
|
||||
|
||||
assume(bool(func));
|
||||
const bool ret
|
||||
{
|
||||
func(first, last, context, skipper)
|
||||
};
|
||||
|
||||
if(ret)
|
||||
transform::post(attr_param, attr_);
|
||||
else
|
||||
transform::fail(attr_param);
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -23,6 +23,7 @@ namespace spirit
|
|||
__attribute__((visibility("internal")))
|
||||
{
|
||||
namespace phx = boost::phoenix;
|
||||
namespace proto = boost::proto;
|
||||
namespace fusion = boost::fusion;
|
||||
namespace spirit = boost::spirit;
|
||||
namespace ascii = spirit::ascii;
|
||||
|
|
Loading…
Add table
Reference in a new issue