Prvo izdanje scan-a, biblioteke za parsiranje teksta za C++ sa konstrukcijom mašine stanja tokom kompajliranja.

Objavljeno je prvo izdanje biblioteke za skeniranje (0.1.0). Ona parsira tekst u vrijednosti na osnovu uzorka kompajliranja. Uzorak ili format se piše kao argument C++ šablona i kompajlira se u označeni deterministički konačni automat (TDFA), čiji se prolaz odvija stanje po stanje u kod kompajliranja korištenjem C++ šablona. Rezultat izvršavanja je vrijednost traženog tipa. Kod projekta je napisan u C++23 i distribuira se pod GPLv3 licencom. import scan; // Da li se cijeli niz podudara. Svaka ulazna tačka je ujedno i adapter raspona. scan::match<«[az]+@[az.]+»>(address); address | scan::match<«[az]+@[az.]+»>; // Sa grupama. const auto found = scan::match<«([0-9]+)-([az]+)»>(«42-abc»); found.get<1>().to_view(); // "42" // Početak stringa koji zauzima uzorak i prvo podudaranje bilo gdje. scan::starts_with<«[a-z]+»>(«abc123»).whole().to_view(); // "abc" scan::search<«[0-9]+»>(«id=4210x»).to_view(); // "4210" // Sva podudaranja i dijelovi između su lijeni prikazi. for (const auto& one : text | scan::search_all<«[a-z]+»>) { … } const auto fields = "a,bb,,ccc" | scan::split<«,»> | std::ranges::to (); // Vrijednosti, ne tekst. struct row { int id; std::string_view name; }; const row one = scan::scan<«{},{[a-z]+}»>(line); // Lista, zbir tipova, ugniježđeni oblik. struct all { std::vector vrijednosti; std::varijanta rep; }; const sve dobijeno = scan::scan<«{{}{*,?}} {{[0-9]+}|{[a-z]+}}»>(«1,2,3 abc»); // Početak ulaza i ono što od njega ostaje. const auto [vrijednost, ostatak] = scan::scan_prefix<«{},{}»>(linija).take (); // Jedan zapis po jedan, iz bilo čega. for (const row& one : scan::each<«{},{[az]+}\\n»>(text).of ()) { … }

Raspon koji se može pročitati samo jednom čita se bez baferovanja, a polja se prikupljaju kako znakovi pristižu. Broj znakova koji će se čuvati određen je uzorkom u vrijeme kompajliranja; gdje ovaj broj nije dostupan, čitanje se odbija u vrijeme kompajliranja. std::istringstream izvor(«set speed 42\\nset gain 7\\n»); izvor > std::noskipws; struct command { scan::held<16> ime; int vrijednost; }; for (const command& one : scan::each<«set {[a-z]+} {[0-9]+}\\n»>(std::views::istream (izvor)) .od ()) { … } Performanse.

Mjerenja su izvršena na Ryzen 9 9950X procesoru, clang 22.1.8 sa libc++, -O3 -march=native,LTO. 32 zapisa po prolazu, medijana od sedam prolaza. "([az]+),([az]+),([az]+),([az]+),([az]+)" "alpha,bravo,charlie,delta,echo" scan::scan .sentinel() 477 ns re2c 554 ns skeniranje::skeniranje 659 ns CTRE 717 ns RE2 15986 ns istih pet polja, po dvjesto slova scan::scan .sentinel() 57.8 ns re2c 637 ns CTRE 692 ns RE2 11751 ns prepoznavanje "first.last@subdomain.example.com", ništa nije preuzeto scan::match .sentinel().scalar() 436 ns skeniranje::podudaranje .scalar() 545 ns re2c 1186 ns RE2 2616 ns CTRE 14339 ns Skupljanje grupa tokom njihove analize

Tipu se govori kojoj od njegovih grupa pripada sljedeći znak, a evaluacija se vrši na licu mjesta: ne spremaju se iteracije petlje, ne kreiraju se podnizovi. 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 tip_stanja { unsigned long ukupno = 0; unsigned mjesto = 0, oznake = 0; }; static constexpr tip_stanja begin_groups() { return {}; } static constexpr void opened_group(tip_stanja& jedan, scan::group_at<0>) { jedno.mjesto = 0; jedan.oznake = 0; } static constexpr void closed_group(tip_stanja& jedan, scan::group_at<0>) { unsigned long težina = 1; for (unsigned korak = 1; korak < jedno.mjesto; ++korak) težina *= 10; jedan.ukupno += težina * jedan.oznake; } static constexpr void push_group(tip_stanja& jedan, scan::group_at<1>, char) { ++jedno.mjesto; } statički constexpr void push_group(tip_stanja& jedan, scan::group_at<2>, char slovo) { jedan.marks += slovo == 'Y' ? 2u: 1u; } statički constexpr tally finish_groups(tip_stanja jedan) { return {jedan.total}; } }; const čitanje got = scan::scan<“value={}{[az]*}”>(“value=(__X_XX)abcdefgh”).of (); // dobijen.broj.vrijednost == 12, dobijen.rep == "abcdefgh"

skeniranje heap-a::scan .scalar() ručno pisano skeniranje::skeniranje 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
Konteksti i alokatori

Bilo koji objekt pozivatelja - alokator, skup, arena, bilo šta - može se proslijediti pozivu koji kreira vrijednost. Tip konteksta se ne gubi: stanje skenera i svaki od njegovih hook-ova mogu se šablonirati na njemu, tako da se nijedan tip pozivatelja ne spominje u skeneru. struct arena { std::pmr::memory_resource* where = nullptr; std::pmr::memory_resource* resource() const { return where; } }; struct numbers { std::pmr::vector vrijednosti; }; struktura oba { brojevi lijevo; brojevi desno; }; šablon <> struktura skeniranje::skener { statički constexpr std::string_view obrazac() { vrati "([0-9]+)(?:,([0-9]+))*"; } šablon struktura stanje { std::pmr::vector vrijednosti; int running = 0; }; statičko stanje begin_groups() { return {}; } šablon zahtijeva zahtijeva(const Told& jedan) { jedan.resurs(); } statičko stanje begin_groups(const Told& told) { return {std::pmr::vector (rečeno.resurs()), 0}; } predložak statički void push_group(stanje & jedan, scan::group_at , char cifra) { jedan.trčanje = jedan.trčanje * 10 + (cifra - '0'); } šablon statički void zatvorena_grupa(stanje & jedan, scan::group_at ) { jedna.vrijednosti.push_back(jedna.running); jedna.running = 0; } šablon statički brojevi finish_grupe(stanje jedan) { return {std::move(one.values)}; } }; std::pmr::monotonic_buffer_resource bajtovi; const arena mine{&bytes}; scan::scan<“{} {}”>(tekst).of (moje); // po jedan za oba mjesta scan::scan<«{} {}»>(tekst).of (moje, scan::default_context); // jedan po lokaciji scan::scan<“{} {}”>(tekst).of ({moje, scan::default_context}); // isto u zagradama const both got = scan::scan<«{} {}»>(text).with(moje); // Kontekst ne mora biti svog tipa: alokator ide toliko daleko da // konstruiše čitanje. struct two { std::pmr::string name; std::pmr::string tail; }; const two kept = scan::scan<«{[az]+} {[az]+}»>(text).of<( std::pmr::polymorphic_allocator<>(&bytes));

Udica prima sam objekat pozivaoca, a ne kopiju, tako da stanje može pohraniti njegovu adresu: kontekst traje sve dok se i poziv izvršava, a svo čitanje se dešava unutar tog poziva. Ostale karakteristike

  • Dva sloja: sloj predloška (match, starts_with, search, search_all, split) i sloj formata, gdje je "{}" polje, a vrijednost mjesta je određena poljima samog tipa.
  • Pravilo višeznačnosti je prvo lijevo, kao u Perl-u, RE2 i CTRE-u.
  • Sve navedeno funkcioniše i u konstantnim izrazima.
  • Nema pretraživanja, povratnih referenci ili Unicode svojstava; predlošci rade na bajtovima.
  • Obrasci koji su poznati samo za vrijeme izvođenja nisu podržani.

Montaža

Kompajlira se sa clang i GCC. C++ moduli su opcionalni: dostupne su zaglavne datoteke (include/) generirane iz istih modula od strane uslužnog programa demodulizer - svaka vrši push na glavnu rekonstrukciju, linkanje i commit-ovanje. Jedina zavisnost, Boost.PFR, nije potrebna kada su omogućeni C++26 paketi povezivanja. 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)

izvor: opennet.ru

Kupite pouzdan hosting za sajtove sa DDoS zaštitom, VPS VDS servere 🔥 Kupite pouzdan web hosting sa DDoS zaštitom, VPS VDS servere | ProHoster