0
0
Fork 0
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:
Jason Volk 2022-05-07 15:16:44 -07:00
parent 7a423c94be
commit 29fda76769
7 changed files with 569 additions and 140 deletions

View file

@ -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

View 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
}
{}

View file

@ -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;
}

View file

@ -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)...);
}

View file

@ -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>

View 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 &params_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 &params)
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;
}

View file

@ -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;