c++ - 如何在 C++ 中为 PEGTL 通过属性/标识符定义 unicode 范围

标签 c++ utf-8 peg

使用 PEGTL ( https://github.com/taocpp/PEGTL )(一个基于 C++11 header 的 PEG 库模板),我可以定义 unicode 字符的范围,如下所示:

  • utf8::range<0x0, 0x10FF>//所有UTF8字符
  • utf8::range<0x41, 0x5A, 0x61, 0x7A>//UTF8 0x41-0x5A[A-Z] 和 0x61-0x7A[a-z]

现在,有了 UTF8,就有了这个属性分类 ( https://en.wikipedia.org/wiki/Unicode_character_property#General_Category ),我可以用它来执行 [:Lu:] 或 [:ID_Start:] 之类的操作,并获取一组/范围的字符。

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

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

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

最佳答案

PEGTL使用规则来匹配字符,而不是返回字符集。如果您想将字符与某些 Unicode 字符属性相匹配,您可以创建 custom rule并在一些 Unicode 库的帮助下实现它,例如ICU 。它提供了测试各种属性的代码点的方法,请参阅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 部分中。

关于c++ - 如何在 C++ 中为 PEGTL 通过属性/标识符定义 unicode 范围,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48570874/

相关文章:

c++ 比较各种长度 vector 并隔离 "unique"的算法,有点

c++ - auto 如何推导指针类型?

unicode - 如何在 LaTeX 中的中文字符上获得重音符号(作为声调标记)?

apache - 如何设置 Apache tomcat 和项目以显示 UTF-8 文本?

ruby - Parslet 中是否提供反向引用?

javascript - PEG.js 获取 ( 和 ) 之间的任何文本;

python - python语法中 `~`是什么意思

c++ - erase-remove 习语 : did I just delete something?

c++ - Visual Studio 2012 错误 MSB3073

sql-server - 在 SQL Server 中将 UTF8 转换为 uTF16