一個實驗性的C++編譯期正則表達式parser

這個東西主要是用來在編譯期把正則表達式字符串字面量處理成正則表達式語法樹(表達式模板),而後運行期能夠直接使用這棵語法樹來匹配文字了,避免了運行期編譯正則表達式的性能負擔(這裏真的是critical的地方嗎?),而且類型安全,語法有錯的話根本通不過編譯。正則表達式

由於是實驗性的,只支持三個基本元素:鏈接,或,克林閉包,不支持括號,我也不是很想繼續寫下去(已經寫下去了,支持90%的ECMA Script正則表達式標準,可是不是很想拿出來)編程

值得一提的是,這裏使用了C++14/17時代的現代模板元編程方法,與其像C++11那樣用模板特化來搞事情,不如用函數重載和返回類型推導,寫法更加天然。同時這個方法也是被boost hana等現代模板元編程庫所使用的。安全

不說廢話,直接上代碼閉包

  • 首先是一點通用的東西
namespace mq
{

template<char c>
struct char_constant : std::integral_constant<char, c>
{
};

template<class T, T c1, T c2>
constexpr std::bool_constant<c1 == c2> operator==(std::integral_constant<T, c1>, std::integral_constant<T, c2>)
{
    return{};
}

template<class T, T c1, T c2>
constexpr std::bool_constant<c1 != c2> operator!=(std::integral_constant<T, c1>, std::integral_constant<T, c2>)
{
    return{};
}

template<class T, T c1, T c2>
constexpr std::bool_constant<(c1 > c2)> operator>(std::integral_constant<T, c1>, std::integral_constant<T, c2>)
{
    return{};
}

template<class T, T c1, T c2>
constexpr std::bool_constant<(c1 >= c2)> operator>=(std::integral_constant<T, c1>, std::integral_constant<T, c2>)
{
    return{};
}

template<class T, T c1, T c2>
constexpr std::bool_constant<(c1 < c2)> operator<(std::integral_constant<T, c1>, std::integral_constant<T, c2>)
{
    return{};
}

template<class T, T c1, T c2>
constexpr std::bool_constant<(c1 <= c2)> operator<=(std::integral_constant<T, c1>, std::integral_constant<T, c2>)
{
    return{};
}

template<bool b1, bool b2>
constexpr std::bool_constant<b1 && b2> operator&&(std::bool_constant<b1>, std::bool_constant<b2>)
{
    return{};
}

template<bool b1, bool b2>
constexpr std::bool_constant<b1 || b2> operator||(std::bool_constant<b1>, std::bool_constant<b2>)
{
    return{};
}

template<bool v, class T1, class T2>
constexpr decltype(auto) cond(std::bool_constant<v>, T1 a, T2 b)
{
    if constexpr (v)
    {
        return a;
    }
    else
    {
        return b;
    }
}

template<class Curr, class Cond, class Iter>
constexpr decltype(auto) iter(Curr i, Cond c, Iter e)
{
    //static_assert(c(i).value);
    if constexpr (c(i).value)
    {
        return iter(e(i), c, e);
    }
    else
    {
        return i;
    }
}

} //namespace mq

能夠看出來這裏基本上都是一些給標準庫設施提供的運算符重載和一些通用的東西,爲了給後面提供方便。函數

  • 而後是本體
namespace mq
{

template<char c>
constexpr static auto cc = char_constant<c>{};

template<char... chars>
struct char_sequence
{
    template<size_t i>
    constexpr static decltype(auto) get()
    {
        static_assert(i < sizeof...(chars), "internal error");
        return char_constant<std::get<i>(std::make_tuple(chars...))>{};
    }
};

template<class Sequence, size_t _i, class Result>
struct parse_result
{
    constexpr static decltype(auto) sequence()
    {
        return Sequence{};
    }

    constexpr static decltype(auto) get()
    {
        return Sequence::template get<_i>();
    }

    constexpr static decltype(auto) peek()
    {
        return Sequence::template get<_i + 1>();
    }

    constexpr static decltype(auto) result()
    {
        return Result{};
    }

    constexpr static decltype(auto) forward()
    {
        return parse_result<Sequence, _i + 1, Result>{};
    }

    template<class R>
    constexpr static decltype(auto) result(R)
    {
        return parse_result<Sequence, _i, R>{};
    }
};

template<class Derived>
struct regex
{
};

template<char c>
struct match : regex<match<c>>
{
};

template<char c>
constexpr decltype(auto) mkmatch(char_constant<c>)
{
    return match<c>{};
}

template<char c>
struct kleene : regex<kleene<c>>
{
};

template<char c>
constexpr decltype(auto) mkkleene(char_constant<c>)
{
    return kleene<c>{};
}

template<class... Regexes>
struct concat : regex<concat<Regexes...>>
{
};

template<class... Ts>
constexpr decltype(auto) mkconcat(regex<Ts>...)
{
    return concat<Ts...>{};
}

template<class... Rs, class... Ts>
constexpr decltype(auto) mkconcat(concat<Rs...>, regex<Ts>...)
{
    return concat<Rs..., Ts...>{};
}

template<class... Regexes>
struct alter : regex<alter<Regexes...>>
{
};

template<class... Ts>
constexpr decltype(auto) mkalter(regex<Ts>...)
{
    return alter<Ts...>{};
}

template<class... Rs, class... Ts>
constexpr decltype(auto) mkalter(alter<Rs...>, regex<Ts>...)
{
    return alter<Rs..., Ts...>{};
}

struct regex_parser
{
    template<class Seq>
    constexpr static decltype(auto) parse(Seq s)
    {
        return parse_alternative(parse_result<Seq, 0, void>{});
    }
private:
    template<class ParseResult>
    constexpr static decltype(auto) parse_alternative(ParseResult r)
    {
        return iter(parse_concatination(r),
            [](auto res)
        {
            return res.get() != cc<'\0'>;
        },
            [](auto res)
        {
            static_assert((res.get() == cc<'|'>).value);
            auto e = parse_concatination(res.forward());
            return e.result(mkalter(res.result(), e.result()));
        });
    }

    template<class ParseResult>
    constexpr static decltype(auto) parse_concatination(ParseResult r)
    {
        return iter(parse_kleene(r),
            [](auto res)
        {
            return (res.get() != cc<'\0'>) && (res.get() != cc<'|'>);
        },
            [](auto res)
        {
            auto e = parse_kleene(res);
            return e.result(mkconcat(res.result(), e.result()));
        });
        /* 至關於
        auto regex = mkconcat(parse_kleene(r));
        for (;;)
        {
            if (r.get() != '\0' && r.get() != '|')
            {
                regex = mkconcat(regex, parse_kleene(r.forward()));
            }
            else
            {
                return regex;
            }
        }
        */
    }

    template<class ParseResult>
    constexpr static decltype(auto) parse_kleene(ParseResult r)
    {
        auto token = r.get();
        auto next = r.peek();
        return cond(next == cc<'*'>,
            [=] { return r.forward().forward().result(mkkleene(token)); },
            [=] { return r.forward().result(mkmatch(token)); })();
    }

};

template<class TChar, TChar... chars>
constexpr decltype(auto) operator"" _regex()
{
    return regex_parser::parse(char_sequence<chars..., '\0'>{}).result();
}

}

順便說上面的template<class TChar, TChar... chars> constexpr decltype(auto) operator"" _regex()是gnu的擴展,是一個在編譯期把字符串字面量展開成TChar…序列的功能,這個東西標準沒有給,標準只給了編譯器展開數字常量到char…的功能。性能

  • 驗證一下結果
auto a = "ab*|c"_regex;
std::cout << typeid(a).name() << "\n";

請注意我這裏使用的是typeid,這就證實了a的類型在編譯期已經肯定了spa

輸出的東西demangling一下設計

mq::alter<mq::concat<mq::match<(char)97>, mq::kleene<(char)98> >, mq::match<(char)99> >

簡單總結一下,這一大堆東西,看起來像普通的代碼,實際上都是元編程。這裏面全部的值,他是什麼,我都是不關心的,我甚至不關係表達式是否被求值,lambda是否被真正的調用。我關心的,是他們的類型,這是編譯期能夠肯定的,而這就是boost hana等現代TMP庫的設計思路——用內建表達式的類型推導搞事情,而不是手動提供類型推導的方式。code

相關文章
相關標籤/搜索