Esimene väljaanne scan, C++ tekstianalüüsi teek, mis koostab lõppmootori kompileerimise ajal.

Avaldati scan teegi esimene väljalase (0.1.0), mis analüüsib teksti malli alusel, mida tuntakse kompilatsioonietapis. Mall või formaat on C++ template'i argumendina ja muudetakse kompilatsiooni käigus tähistatud määramatuks lõppautomaatideks (tagged deterministic finite automaton, TDFA), mille kaudu liikumine rakendatakse olekutes koodis kompilatsioonietapis, kasutades C++ malle. Tulemuseks on soovitud tüübi väärtus. Projekti kood on kirjutatud C++23-s ja levitatakse GPLv3 litsentsi all. import scan; // Kas kogu rida vastab. Iga sisenemispunkt on ka vahemiku adapter. scan::match(address); address | scan::match; // Grupid. const auto found = scan::match("42-abc"); found.get().to_view(); // "42" // Rida, mille mall on hõivatud, ja esimene vaste kuskil. scan::starts_with("abc123").whole().to_view(); // "abc" scan::search("id=4210x").to_view(); // "4210" // Kõik vasted ja tükid nende vahel — laisk esitus. for (const auto& one : text | scan::search_all) { … } const auto fields = "a,bb,,ccc" | scan::split | std::ranges::to(); // Väärtused, mitte tekst. struct row { int id; std::string_view name; }; const row one = scan::scan(line); // Loend, tüüpide summa, sisemine vorm. struct all { std::vector values; std::variant tail; }; const all got = scan::scan("1,2,3 abc"); // Sisendi algus ja see, mis sellest jäi. const auto [value, rest] = scan::scan_prefix(line).take(); // Üks kirje korraga, millest iganes. for (const row& one : scan::each(text).of()) { … }

Ühtlasi, mida saab lugeda ainult üks kord, loetakse ilma vahemälu, väljad kogutakse sümbolite sissevoolu käigus. Kui palju sümboleid tuleb hoida, määrab mall kompileerimisel; seal, kus sellist arvu ei ole, lükatakse lugemine tagasi kompileerimisel. std::istringstream source("set speed 42\\nset gain 7\\n"); source > std::noskipws; struct command { scan::held name; int value; }; for (const command& one : scan::each(std::views::istream(source)) .of()) { … } Tõhusus.

Mõõtmised viidi läbi Ryzen 9 9950X, clang 22.1.8 koos libc++, -O3 -march=native, LTO. Kolmkümmend kaks salvestust jooksu kohta, seitsme jooksu mediaan. «([a-z]+),([a-z]+),([a-z]+),([a-z]+),([a-z]+)» «alpha,bravo,charlie,delta,echo» scan::scan<f>.sentinel() 477 ns re2c 554 ns scan::scan<f> 659 ns CTRE 717 ns RE2 15986 ns samad viis väli, igaühes kaks sada tähte scan::scan<f>.sentinel() 57.8 ns re2c 637 ns CTRE 692 ns RE2 11751 ns «first.last@subdomain.example.com» tuvastamine, midagi ei ekstraheerita scan::match<p>.sentinel().scalar() 436 ns scan::match<p>.scalar() 545 ns re2c 1186 ns RE2 2616 ns CTRE 14339 ns Grupi kokkuvõte analüüsi käigus

Tüübile edastatakse, millisesse tema rühma järgnev märk kuulub, ja arvutamine toimub kohapeal: ühtegi tsükli iteratsiooni ei salvestata, ühtegi alamstringi ei loo. 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) { one.place = 0; one.marks = 0; } static constexpr void closed_group(state_type& one, scan::group_at) { 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, char) { ++one.place; } static constexpr void push_group(state_type& one, scan::group_at, 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=(__X_XX)abcdefgh").of(); // got.number.value == 12, got.tail == "abcdefgh"

heap scan::scan.scalar() käsitsi kirjutatud 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
Kontekstid ja allocatorid

Mistah liitub kõiki objekti väljapoole - allokaatorite, basseinide, areenide või mis iganes muude jaotustega - saame edastada selle funktsiooni, mis loob väärtuse. Kontseptsiooni tüüp ei kao: skanneri olek ja iga tema haak võivad olla selle põhjal malli loodud, nii et skanneris ei mainita ühtegi objekti tüüpi. struct arena { std::pmr::memory_resource* where = nullptr; std::pmr::memory_resource* resource() const { return where; } }; struct numbers { std::pmr::vector<int> values; }; struct both { numbers left; numbers right; }; template <> struct scan::scanner<numbers> { static constexpr std::string_view pattern() { return «([0-9]+)(?:,([0-9]+))*»; } template <class Told> struct state { std::pmr::vector<int> values; int running = 0; }; static state<scan::default_context_t> begin_groups() { return {}; } template <class Told> requires requires(const Told& one) { one.resource(); } static state<Told> begin_groups(const Told& told) { return {std::pmr::vector<int>(told.resource()), 0}; } template <class Told, std::size_t Which> static void push_group(state<Told>& one, scan::group_at<Which>, char digit) { one.running = one.running * 10 + (digit — ‘0’); } template <class Told, std::size_t Which> static void closed_group(state<Told>& one, scan::group_at<Which>) { one.values.push_back(one.running); one.running = 0; } template <class Told> static numbers finish_groups(state<Told> one) { return {std::move(one.values)}; } }; std::pmr::monotonic_buffer_resource bytes; const arena mine{&bytes}; scan::scan<«{} {}»>(text).of<both>(mine); // üks mõlemale kohale scan::scan<«{} {}»>(text).of<both>(mine, scan::default_context); // ükshaaval kohal scan::scan<«{} {}»>(text).of<both>({mine, scan::default_context}); // sama sulgudes const both got = scan::scan<«{} {}»>(text).with(mine); // Konteksti ei pea olema enda tüüp: allokaator suudab täiesti lugeda. struct two { std::pmr::string name; std::pmr::string tail; }; const two kept = scan::scan<«{[a-z]+} {[a-z]+}»>(text).of<( std::pmr::polymorphic_allocator<>(&bytes));

Haak saab objektist, mitte kopeerib, seega võib olek salvestada selle aadressi: konteksti eluiga on sama kui kutsumise aeg, ja kogu lugemine toimub selle kutsumise ajal. Muud omadused

  • Kaks kihte: mallide kiht (match, starts_with, search, search_all, split) ja formaatide kiht, kus «{}» on väli ja väärtuse kohti määravad ise tüübi väljad.
  • Äraoleku lahendamise reegel on leftmost-first, nagu Perl, RE2 ja CTRE.
  • Kõik ülaltoodud toimib ka konstantsetes väljendites.
  • Ei ole lookaround, tagasiviidete ega Unicode omadusi; mallid töötavad baitidega.
  • Ei toetata malle, mis on teada ainult käitamise ajal.

Kogumine

Kogutatakse clang'is ja GCC's. C++ moodulid pole vajalikud: on saadaval päisefailid (include/), mille genereerib sama moodulite utiliit demodulizer — iga push main'i uuendab, linkib ja commitib need tagasi. Ainus sõltuvus, Boost.PFR, ei ole vajalik, kui C++26 binding pack'id on lubatud. 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)

Allikas: opennet.ru

Osta usaldusväärne veebimajutus DDoS-kaitsega veebisaitidele, VPS VDS serverid 🔥 Osta usaldusväärne veebimajutus DDoS-kaitsega veebisaitidele, VPS VDS serverid - ProHoster