Հրապարակվել է սկանավորման գրադարանի առաջին թողարկումը (0.1.0): Այն տեքստը վերլուծում է արժեքների՝ հիմնվելով կոմպիլյացիայի ժամանակի ձևանմուշի վրա: Դիզայնը կամ ձևաչափը գրվում է որպես C++ ձևանմուշի արգումենտ և կոմպիլացվում է պիտակավորված դետերմինիստական վերջավոր ավտոմատի (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 պոչ; }; const all got = scan::scan<«{{}{*,?}} {{[0-9]+}|{[a-z]+}}»>(«1,2,3 abc»); // Մուտքագրման սկիզբը և դրանից մնացածը։ const auto [արժեք, մնացածը] = scan::scan_prefix<«{},{}»>(տող).take (); // Միանգամից մեկ գրառում, ցանկացածից։ for (const row& one : scan::each<«{},{[a-z]+}\\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-ով։ 32 գրառում մեկ անցման համար, յոթ անցման միջինը։ "([az]+),([az]+),([az]+),([az]+),([az]+)" "alpha,bravo,charlie,delta,echo" scan::scan .sentinel() 477 ns re2c 554 ns սկանավորում::սկանավորում 659 ns CTRE 717 ns RE2 15986 ns նույն հինգ դաշտերը, յուրաքանչյուրը՝ երկու հարյուր տառ 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 նվ re2c 1186 նվ RE2 2616 նվ CTRE 14339 նվ Խմբերի վերլուծության ընթացքում դրանց փլուզումը
Տիպին ասվում է, թե իր խմբերից որին է պատկանում հաջորդ նիշը, և գնահատումը կատարվում է տեղում. ցիկլի իտերացիաներ չեն պահպանվում, ենթատողեր չեն ստեղծվում։ 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"
Ցանկացած կանչող օբյեկտ՝ ալոկատոր, պուլ, ասպարեզ, ինչ էլ որ լինի՝ կարող է փոխանցվել արժեքը ստեղծող կանչին։ Համատեքստի տեսակը չի կորչում. սկաների վիճակը և դրա յուրաքանչյուր կեռիկը կարող են ձևանմուշավորվել դրա վրա, ուստի սկաներում կանչողի տեսակ չի նշվում։ struct arena { std::pmr::memory_resource* where = nullptr; std::pmr::memory_resource* resource() const { return where; } }; struct numbers { std::pmr::vector արժեքներ; }; struct both { թվեր ձախ կողմում; թվեր աջ կողմում; }; template <> struct scan::scanner { ստատիկ constexpr std::string_view pattern() { վերադարձնել "([0-9]+)(?:,([0-9]+))*"; } ձևանմուշ կառուցվածքային վիճակ { std::pmr::vector արժեքներ; int running = 0; }; ստատիկ վիճակ begin_groups() { return {}; } ձևանմուշ պահանջում է պահանջում(const Told& մեկ) { մեկ.ռեսուրս(); } ստատիկ վիճակ begin_groups(const Told& ասել) { return {std::pmr::vector}} (told.resource()), 0}; } ձևանմուշ ստատիկ void push_group(state և մեկ, սկանավորում::group_at , char թվանշան) { one.running = one.running * 10 + (թվանշան - '0'); } ձևանմուշ ստատիկ void closed_group(state և մեկ, սկանավորում::group_at ) { one.values.push_back(one.running); one.running = 0; } ձևանմուշ ստատիկ թվեր finish_groups(state մեկ) { return {std::move(one.values)}; } }; std::pmr::monotonic_buffer_resource bytes; const arena mine{&bytes}; scan::scan<“{} {}”>(տեքստ).of (իմը); // մեկը երկու տեղերի համար scan::scan<«{} {}»>(տեքստ).of (իմը, scan::default_context); // մեկ տեղ scan::scan<“{} {}”>(տեքստ).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 keeped = scan::scan<«{[az]+} {[az]+}»>(text).of<( std::pmr::polymorphic_allocator<>(&bytes));
Հուկը ստանում է կանչողի օբյեկտը, այլ ոչ թե պատճենը, ուստի վիճակը կարող է պահպանել դրա հասցեն. համատեքստը գործում է այնքան ժամանակ, որքան կանչը, և բոլոր ընթերցումները տեղի են ունենում այդ կանչի ընթացքում։ Այլ հատկություններ
- Երկու շերտ՝ ձևանմուշի շերտ (match, starts_with, search, search_all, split) և ձևաչափի շերտ, որտեղ "{}"-ը դաշտ է, իսկ տեղային արժեքը որոշվում է հենց տիպի դաշտերով։
- Երկիմաստության վերացման կանոնը ձախից առաջ է, ինչպես Perl-ում, RE2-ում և CTRE-ում։
- Վերոնշյալ բոլորը գործում են նաև հաստատուն արտահայտություններում։
- Չկան loarounds, backreferences կամ Unicode հատկություններ. ձևանմուշները գործում են բայթերով։
- Միայն աշխատանքի ընթացքում հայտնի օրինաչափությունները չեն աջակցվում։
Ասամբլեան
Կոմպիլյացիա է կատարում clang-ի և GCC-ի միջոցով։ C++ մոդուլները կամընտիր են. դեմոդուլյատորի կողմից նույն մոդուլներից ստեղծված վերնագրի ֆայլերը (include/) հասանելի են. յուրաքանչյուրը push to main վերակառուցում, հղումներ է կատարում և հաստատում դրանք։ Միակ կախվածությունը՝ 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)
Source: opennet.ru
