コンパイル時に状態機械を構築するC++用テキスト解析ライブラリ「scan」の最初のリリースです。

scanライブラリの最初のリリース(0.1.0)が公開されました。コンパイル時のパターンに基づいてテキストを値に解析します。パターンまたはフォーマットはC++テンプレート引数として記述され、タグ付き決定性有限オートマトン(TDFA)にコンパイルされます。TDFAの走査は、C++テンプレートを使用して状態ごとにコンパイル時のコードに展開されます。実行結果は、要求された型の値です。プロジェクトコードはC++23で記述され、GPLv3ライセンスの下で配布されます。 import scan; // 文字列全体が一致します。各エントリポイントは範囲アダプタでもあります。 scan::match<«[az]+@[az.]+»>(address); address | scan::match<«[az]+@[az.]+»>; // グループ付き。 const auto found = scan::match<«([0-9]+)-([az]+)»>(«42-abc»); found.get<1>().to_view(); // "42" // パターンが占める文字列の先頭と、最初に見つかった一致箇所。 scan::starts_with<«[a-z]+»>(«abc123»).whole().to_view(); // "abc" scan::search<«[0-9]+»>(«id=4210x»).to_view(); // "4210" // すべての一致箇所と、その間の部分は遅延ビューです。 for (const auto& one : text | scan::search_all<«[a-z]+»>) { … } const auto fields = "a,bb,,ccc" | scan::split<«,»> | std::ranges::to (); // 値であり、テキストではありません。 struct row { int id; std::string_view name; }; const row one = scan::scan<«{},{[a-z]+}»>(line); // リスト、型の合計、ネストされた形式。 struct all { std::vector値; std::variant tail; }; const all got = scan::scan<«{{}{*,?}} {{[0-9]+}|{[a-z]+}}»>(«1,2,3 abc»); // 入力の先頭と残りの部分。 const auto [value, rest] = scan::scan_prefix<«{},{}»>(line).take (); // 一度に 1 つのレコードを、何からでも。 for (const row& one : scan::each<«{},{[az]+}\\n»>(text).of ()) { … }

一度しか読み取れない範囲はバッファリングなしで読み取られ、文字が到着するたびにフィールドが収集されます。保持する文字数はコンパイル時にパターンによって指定されます。この数が利用できない場合、コンパイル時に読み取りが拒否されます。 std::istringstream source(«set speed 42\\nset gain 7\\n»); source > std::noskipws; struct command { scan::held<16> name; int value; }; for (const command& one : scan::each<«set {[a-z]+} {[0-9]+}\\n»>(std::views::istream (出典) ()) { … } パフォーマンス

測定はRyzen 9 9950X、clang 22.1.8、libc++、-O3 -march=native、LTOで行われました。1パスあたり32回の書き込み、7パスの中央値。 "([az]+),([az]+),([az]+),([az]+),([az]+)" "alpha,bravo,charlie,delta,echo" scan::scan .sentinel() 477 ns re2c 554 ns scan::scan 659 ns CTRE 717 ns RE2 15986 ns 同じ 5 つのフィールド、それぞれ 200 文字 scan::scan .sentinel() 57.8 ns re2c 637 ns CTRE 692 ns RE2 11751 ns "first.last@subdomain.example.com" 認識、何も取得されず scan::match .sentinel().scalar() 436 ns scan::match .scalar() 545 ns re2c 1186 ns RE2 2616 ns CTRE 14339 ns 分析されるにつれてグループを統合する

型には次の文字がどのグループに属するかが伝えられ、評価はインプレースで実行されます。ループの反復は保存されず、部分文字列も作成されません。 struct tally { unsigned long value = 0; }; struct reading { tally number; std::string_view tail; }; template <> struct scan::scanner { static constexpr std::string_view pattern() { return R"(\\(((_+)(X|Y)*)*\\))"; } struct state_type { unsigned long total = 0; unsigned place = 0, marks = 0; }; static constexpr state_type begin_groups() { return {}; } static constexpr void opened_group(state_type& one, scan::group_at<0>) { one.place = 0; one.marks = 0; } static constexpr void closed_group(state_type& one, scan::group_at<0>) { unsigned long weight = 1; for (unsigned step = 1; step < one.place; ++step) weight *= 10; one.total += weight * one.marks; } static constexpr void push_group(state_type& one, scan::group_at<1>, char) { ++one.place; } static constexpr void push_group(state_type& one, scan::group_at<2>, char letter) { one.marks += letter == 'Y' ? 2u: 1u; } static constexpr tally finish_groups(state_type one) { return {one.total}; } }; const reading got = scan::scan<“value={}{[az]*}”>(“value=(__X_XX)abcdefgh”).of (); // got.number.value == 12, got.tail == "abcdefgh"

ヒープスキャン::スキャン.scalar() 手書き scan::scan 10 141 ns 41.4 ns 42.5 ns 170 ns 100 439 ns 341 ns 392 ns 1176 ns 1000 3314 ns 3170 ns 3797 ns 11887 ns
コンテキストとアロケータ

値を生成する呼び出しには、アロケータ、プール、アリーナなど、任意の呼び出し元オブジェクトを渡すことができます。コンテキスト型は失われません。スキャナの状態とその各フックは、そのコンテキスト型に基づいてテンプレート化できるため、スキャナでは呼び出し元型は言及されません。 struct arena { std::pmr::memory_resource* where = nullptr; std::pmr::memory_resource* resource() const { return where; } }; struct numbers { std::pmr::vector値; }; struct both { 左の数値; 右の数値; }; テンプレート <> struct scan::scanner { static constexpr std::string_view pattern() { return "([0-9]+)(?:,([0-9]+))*"; } template struct state { std::pmr::vector値; int running = 0; }; static state begin_groups() { return {}; } テンプレートrequires requires(const Told& one) { one.resource(); } static state begin_groups(const Told& told) { return {std::pmr::vector (told.resource()), 0}; } テンプレートstatic void push_group(state & 1、scan::group_at , char digit) { one.running = one.running * 10 + (digit - '0'); } テンプレートstatic void closed_group(state & 1、scan::group_at ) { one.values.push_back(one.running); one.running = 0; } テンプレート静的数値 finish_groups(state) one) { return {std::move(one.values)}; } }; std::pmr::monotonic_buffer_resource bytes; const arena mine{&bytes}; scan::scan<“{} {}”>(text).of (mine); // 両方の場所に対応するもの scan::scan<«{} {}»>(text).of (mine, scan::default_context); // 場所ごとに 1 つ scan::scan<“{} {}”>(text).of ({mine, scan::default_context}); // 括弧内は同じ const both got = scan::scan<«{} {}»>(text).with(mine); // コンテキストは必ずしもその型である必要はありません。アロケータは読み取りの構築まで行います。 struct two { std::pmr::string name; std::pmr::string tail; }; const two kept = scan::scan<«{[az]+} {[az]+}»>(text).of<( std::pmr::polymorphic_allocator<>(&bytes));

フックは呼び出し元のオブジェクト自体を受け取り、コピーは受け取りません。そのため、状態はそのアドレスを保存できます。コンテキストは呼び出しが行われている間だけ存在し、すべての読み取りはその呼び出し内で行われます。 その他の機能

  • テンプレート層(match、starts_with、search、search_all、split)とフォーマット層の2つの層があり、ここで「{}」はフィールドであり、その位置の値は型自体のフィールドによって決定されます。
  • 曖昧性解消ルールは、Perl、RE2、CTREと同様に、左端優先です。
  • 上記すべては定数式でも有効です。
  • ルックアラウンド、バックリファレンス、Unicodeプロパティは存在せず、テンプレートはバイト単位で動作します。
  • 実行時にのみ判明するパターンはサポートされていません。

組立

clang と GCC でコンパイルします。C++ モジュールはオプションです。デモジュライザユーティリティによって同じモジュールから生成されたヘッダー ファイル (include/) が利用可能です。メインにプッシュするたびに、それらが再ビルド、リンク、コミットされます。唯一の依存関係である Boost.PFR は、C++26 バインディング パックが有効になっている場合は不要です。FetchContent_Declare(scan GIT_REPOSITORY https://github.com/j4niwzis/scan.git GIT_TAG v0.1.0) FetchContent_MakeAvailable(scan) target_link_libraries(mine PRIVATE scan::scan)

出所: オープンネット.ru

DDoS 保護機能を備えた信頼性の高いサイト用ホスティング、VPS VDS サーバーを購入する 🔥 DDoS攻撃対策付きの信頼性の高いウェブサイトホスティング、VPS/VDSサーバーを購入しましょう | ProHoster