Den første utgivelsen av scan, et tekstparseringsbibliotek for C++ med tilstandsmaskinkonstruksjon ved kompileringstid

Den første utgivelsen av skannebiblioteket (0.1.0) er publisert. Det analyserer tekst til verdier basert på et kompileringsmønster. Mønsteret eller formatet skrives som et C++-malargument og kompileres til en tagget deterministisk endelig automat (TDFA), hvis gjennomgang utfoldes tilstand for tilstand til kompileringskode ved hjelp av C++-maler. Resultatet av kjøringen er en verdi av den forespurte typen. Prosjektkoden er skrevet i C++23 og distribueres under GPLv3-lisensen. import scan; // Samsvarer hele strengen. Hvert inngangspunkt er også en rekkeviddeadapter. scan::match<«[az]+@[az.]+»>(adresse); adresse | scan::match<«[az]+@[az.]+»>; // Med grupper. const auto found = scan::match<«([0-9]+)-([az]+)»>(«42-abc»); found.get<1>().to_view(); // "42" // Begynnelsen av strengen som er opptatt av mønsteret, og det første treffet hvor som helst. scan::starts_with<«[a-z]+»>(«abc123»).whole().to_view(); // "abc" scan::search<«[0-9]+»>(«id=4210x»).to_view(); // "4210" // Alle treff og brikkene imellom er late views. for (const auto& one : text | scan::search_all<«[a-z]+»>) { … } const auto fields = "a,bb,,ccc" | scan::split<«,»> | std::ranges::to (); // Verdier, ikke tekst. struct row { int id; std::string_view name; }; const row one = scan::scan<«{},{[a-z]+}»>(line); // Liste, sum av typer, nestet form. struct all { std::vector verdier; std::variant tail; }; const all got = scan::scan<«{{}{*,?}} {{[0-9]+}|{[a-z]+}}»>(«1,2,3 abc»); // Begynnelsen av inputen og det som er igjen av den. const auto [value, rest] = scan::scan_prefix<«{},{}»>(line).take (); // Én post om gangen, fra hva som helst. for (const row& one : scan::each<«{},{[az]+}\\n»>(text).of ()) { … }

Et område som bare kan leses én gang, leses uten bufring, med felt samlet inn etter hvert som tegn ankommer. Antall tegn som skal holdes, spesifiseres av mønsteret ved kompileringstid. Der dette tallet ikke er tilgjengelig, avvises lesing ved kompileringstid. 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 (kilde)) .av ()) { … } Performance.

Målingene ble tatt på en Ryzen 9 9950X, clang 22.1.8 med libc++, -O3 -march=native,LTO. 32 skrivinger per pass, median av syv pass. "([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 de samme fem feltene, to hundre bokstaver hver scan::scan .sentinel() 57.8 ns re2c 637 ns CTRE 692 ns RE2 11751 ns "first.last@subdomain.example.com" gjenkjenning, ingenting hentet scan::match .sentinel().scalar() 436 ns skanning::match .scalar() 545 ns re2c 1186 ns RE2 2616 ns CTRE 14339 ns Skjul grupper mens de analyseres

Typen får beskjed om hvilken av gruppene det neste tegnet tilhører, og evalueringen utføres på stedet: ingen løkkeiterasjoner lagres, ingen delstrenger opprettes. 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 ... (); // fikk.tall.verdi == 12, fikk.hale == "abcdefgh"

heap-skanning::skanning .scalar() håndskrevet skanning::skanning 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
Kontekster og allokatorer

Ethvert kallerobjekt – en allokator, en pool, en arena, hva som helst – kan sendes til kallet som oppretter verdien. Konteksttypen går ikke tapt: skannertilstanden og hver av dens kroker kan males på den, slik at ingen kallertype nevnes i skanneren. struct arena { std::pmr::memory_resource* where = nullptr; std::pmr::memory_resource* resource() const { return where; } }; struct numbers { std::pmr::vector verdier; }; struct begge { tall til venstre; tall til høyre; }; mal <> struct scan::skanner { static constexpr std::string_view pattern() { return "([0-9]+)(?:,([0-9]+)*"; } mal strukturtilstand { std::pmr::vektor verdier; int kjører = 0; }; statisk tilstand begin_groups() { return {}; } mal krever krever(const Told& en) { en.ressurs(); } statisk tilstand begin_groups(const Told& told) { return {std::pmr::vektor} (told.ressurs()), 0}; } mal statisk void push_group(tilstand & en, scan::group_at , char digit) { one.running = one.running * 10 + (digit - '0'); } mal statisk void lukket_gruppe(tilstand & en, scan::group_at ) { one.values.push_back(one.running); one.running = 0; } mal statiske tall finish_groups(state en) { return {std::move(en.verdier)}; } }; std::pmr::monotonic_buffer_resource bytes; const arena mine{&bytes}; scan::scan<“{} {}”>(tekst).av Standardinnhold: Scan-kode: ***(tekst).av** ***] **Standardinnhold: **Scan-kode: ***(tekst).av** **Standardinnhold: ... (min); // én for begge stedene scan::scan<«{} {}»>(tekst).av (min, scan::default_context); // én per sted scan::scan<“{} {}”>(text).of ({mine, scan::default_context}); // det samme i parentes const both got = scan::scan<«{} {}»>(text).with(mine); // Konteksten trenger ikke å være av dens type: allokatoren går så langt som å // konstruere en read. struct two { std::pmr::string name; std::pmr::string tail; }; const two kept = scan::scan<«{[az]+} {[az]+}»>(text).of<( std::pmr::polymorphic_allocator<>(&bytes));

Kroken mottar selve innringerens objekt, ikke en kopi, slik at tilstanden kan lagre adressen: konteksten lever så lenge som samtalen, og all lesing skjer innenfor den samtalen. Andre funksjoner

  • To lag: et mallag (match, starts_with, search, search_all, split) og et formatlag, der "{}" er et felt, og plassverdien bestemmes av feltene til selve typen.
  • Disambigueringsregelen er lengst til venstre først, som i Perl, RE2 og CTRE.
  • Alt det ovennevnte fungerer også i konstante uttrykk.
  • Det finnes ingen oppslag, tilbakereferanser eller Unicode-egenskaper; maler opererer på byte.
  • Mønstre som bare er kjent under kjøring støttes ikke.

sammenstilling

Kompilerer med clang og GCC. C++-moduler er valgfrie: headerfiler (include/) generert fra de samme modulene av demodulatorverktøyet er tilgjengelige – hver push til hovedgjenoppbygginger, lenker og committer dem. Den eneste avhengigheten, Boost.PFR, er ikke nødvendig når C++26-bindingspakker er aktivert. 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)

Kilde: opennet.ru

Kjøp pålitelig hosting for nettsteder med DDoS-beskyttelse, VPS VDS-servere 🔥 Kjøp pålitelig webhotell med DDoS-beskyttelse, VPS VDS-servere | ProHoster