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



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

  • utf8::范围<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. 获取
  4. 应用程序/联机服务,我可以在其中执行这些查询并获取范围(如上所示),然后我可以将其粘贴到我的代码中。

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

PEGTL 使用规则来匹配字符,而不是返回字符集。如果您想将字符与某些 Unicode 字符属性匹配,您可以创建一个自定义规则,并在一些 Unicode 库(例如 ICU)的帮助下实现它。它提供了测试各种属性的代码点的方法,请参阅此链接。

下面是一个完整的示例程序:

#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规则(很多)添加到PEGTL。由于它们需要 ICU(一种外部依赖项),因此我将它们放在contrib-部分中。

最新更新