如何在 C++ 中为 PEGTL 通过 properties/identifiers 定义 unicode 范围

How to define unicode ranges by properties/identifiers in c++ for PEGTL

使用 PEGTL (https://github.com/taocpp/PEGTL),这是一个基于模板的 C++11 头文件专用 PEG 库,我可以像这样定义 unicode 字符的范围:

现在使用 UTF8 有这个属性分类 (https://en.wikipedia.org/wiki/Unicode_character_property#General_Category),我可以用它做类似 [:Lu:] 或 [:ID_Start:] 的事情,并得到一个 set/range字符数。

现在,因为我正在使用 C++ 模板,所以我在编译时需要这些范围。在我看来,我有以下选择:

  1. 发现PEGTL本身就有可能查找[:ID_Start:]或[:Lu:]
  2. 找到一个允许在编译时进行此类查询的 C++ 预处理器库
  3. 获取 Application/Online 服务,我可以在其中执行这些查询并获取范围(如上所示),然后我可以将其粘贴到我的代码中。

这也代表了我喜欢的解决方案的顺序。

PEGTL uses rules to match characters, not return sets of characters. If you'd like to match characters with certain Unicode character properties, you could create a custom rule and implement it with the help of some Unicode library, e.g. ICU. It provides methods to test codepoints for various properties, see this link.

这是一个完整的示例程序:

#include <iomanip>
#include <iostream>

#include <unicode/uchar.h>

#include <tao/pegtl.hpp>

using namespace tao::TAO_PEGTL_NAMESPACE;  // NOLINT

namespace test
{
   template< UProperty P >
   struct icu_has_binary_property
   {
      using analyze_t = analysis::generic< analysis::rule_type::ANY >;

      template< typename Input >
      static bool match( Input& in )
      {
         // this assumes the input is UTF8, adapt as necessary
         const auto r = internal::peek_utf8::peek( in );
         // if a code point is available, the size is >0
         if( r.size != 0 ) {
            // check the property
            if( u_hasBinaryProperty( r.data, P ) ) {
               // if it matches, consume the character
               in.bump( r.size );
               return true;
            }
         }
         return false;
      }
   };

   using icu_lower = icu_has_binary_property< UCHAR_LOWERCASE >;
   using icu_upper = icu_has_binary_property< UCHAR_UPPERCASE >;

   // clang-format off
   struct grammar : seq< icu_upper, plus< icu_lower >, eof > {};
   // clang-format on
}

int main( int argc, char** argv )
{
   for( int i = 1; i < argc; ++i ) {
      argv_input<> in( argv, i );
      std::cout << argv[ i ] << " matches: " << std::boolalpha << parse< test::grammar >( in ) << std::endl;
   }
}

现在我可以编译 运行 它了:

$ g++ -std=c++11 -pedantic -Wall -Wextra -Werror -O3 -Ipegtl/include icu.cpp -licuuc -licudata -o icu
$ ./icu Ďánîel DánÎel
Ďánîel matches: true
DánÎel matches: false
$ 

编辑: 我已将 ICU rules(其中很多)添加到 PEGTL。因为它们需要 ICU,一个外部依赖项,所以我将它们放在 contrib 部分。