Prva izdaja scan, knjižnice za razčlenjevanje besedila za C++, ki ob gradnji gradi končnico avtomata.

Objavljena je prva verzija biblioteke scan (0.1.0), koja razdvaja tekst na vrednosti prema obrascu poznatom u fazi kompajliranja. Obrazac ili format je zabeležen kao argument šablona C++ i tokom kompajliranja se pretvara u označeni deterministički konačni automat (tagged deterministic finite automaton, TDFA), čije se stanje obavlja u kodu tokom faze kompajliranja koristeći C++ šablone. Rezultat izvršenja je vrednost traženog tipa. Kod projekta je napisan na C++23 i distribuira se pod GPLv3 licencom. import scan; // Da li se ceo niz poklapa. Svaka tačka ulaza je takođe i adapter opsega. scan::match(address); address | scan::match; // Sa grupama. const auto found = scan::match("42-abc"); found.get().to_view(); // "42" // Početak niza, zauzet šablonom, i prvo poklapanje bilo gde. scan::starts_with("abc123").whole().to_view(); // "abc" scan::search("id=4210x").to_view(); // "4210" // Sva poklapanja i delovi između njih — lenji prikazi. for (const auto& one : text | scan::search_all) { … } const auto fields = "a,bb,,ccc" | scan::split<","| std::ranges::to(); // Vrednosti, a ne tekst. struct row { int id; std::string_view name; }; const row one = scan::scan(line); // Lista, suma tipova, ugnježdeni oblik. struct all { std::vector values; std::variant tail; }; const all got = scan::scan("1,2,3 abc"); // Početak unosa i ono što je od njega ostalo. const auto [value, rest] = scan::scan_prefix(line).take(); // Jedan po jedan, iz bilo čega. for (const row& one : scan::each(text).of()) { … }

Opseg koji se može pročitati samo jednom, čita se bez keširanja, a polja se okupljaju dok simboli dolaze. Koliko simbola će biti potrebno zadržati, obrazac određuje pri kompajliranju; gde takvog broja nema, čitanje se odbacuje pri kompajliranju. 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()) { … } Performanse.

Merenja su provedena na Ryzen 9 9950X, clang 22.1.8 s libc++, -O3 -march=native,LTO. Trideset dva zapisa po prolazu, medijana sedam prolaza. «([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 ista pet polja, po dvesta slova svako scan::scan<f>.sentinel() 57.8 ns re2c 637 ns CTRE 692 ns RE2 11751 ns «first.last@subdomain.example.com» prepoznavanje, ništa se ne izdvaja scan::match<p>.sentinel().scalar() 436 ns scan::match<p>.scalar() 545 ns re2c 1186 ns RE2 2616 ns CTRE 14339 ns Smanjenje grupa tokom analize

Tip je obveščen, kateri skupini pripada naslednji znak, in izračun se izvede na mestu: nobena iteracija zanke se ne shrani, nobena podniz ne nastane. 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() napisano ročno 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
Konteksti in allocatorji

Vsak objekt klicatelja — allocator, pool, arena, ali karkoli drugega — lahko prenesete v klic, ki ustvari vrednost. Tip konteksta pri tem ne izgine: stanje skenerja in vsak njegov hook sta lahko predloga zanj, tako da v skenerju ni omenjen noben tip klicatelja. struct arena { std::pmr::memory_resource* where = nullptr; std::pmr::memory_resource* resource() const { return where; } }; struct numbers { std::pmr::vector values; }; struct both { numbers left; numbers right; }; template struct scan::scanner { static constexpr std::string_view pattern() { return "([0-9]+)(?:,([0-9]+))*"; } template struct state { std::pmr::vector values; int running = 0; }; static state begin_groups() { return {}; } template requires requires(const Told& one) { one.resource(); } static state begin_groups(const Told& told) { return {std::pmr::vector(told.resource()), 0}; } template static void push_group(state& one, scan::group_at, char digit) { one.running = one.running * 10 + (digit - '0'); } template static void closed_group(state& one, scan::group_at) { one.values.push_back(one.running); one.running = 0; } template static numbers finish_groups(state one) { return {std::move(one.values)}; } }; std::pmr::monotonic_buffer_resource bytes; const arena mine{&bytes}; scan::scan(text).of(mine); // eden za oba mesta scan::scan(text).of(mine, scan::default_context); // po enega na mesto scan::scan(text).of({mine, scan::default_context}); // isto v oklepajih const both got = scan::scan(text).with(mine); // Kontekst ni nujno svoj tip: allocator pride do tega, // da gradi branje. struct two { std::pmr::string name; std::pmr::string tail; }; const two kept = scan::scan(text).of( std::pmr::polymorphic_allocator(&bytes));

Hook prejme sam objekt klicatelja, ne kopijo, zato lahko stanje hrani njegov naslov: kontekst živi toliko časa, kolikor traja klic, vse branje pa poteka znotraj tega klica. Druge značilnosti

  • Dva sloja: sloj predlog (match, starts_with, search, search_all, split) in sloj formatov, kjer je "{}" polje, vrednost mesta pa določajo samo polja tipa.
  • Pravilo za razreševanje nejasnosti je leftmost-first, kot v Perl, RE2 in CTRE.
  • Vse našteto deluje tudi v konstantnih izrazih.
  • Ni lookaround, povratnih povezav in lastnosti Unicode; predlogi delujejo z bajti.
  • Predlogi, znani samo ob izvajanju, niso podprti.

Izgradnja

Компилируется в clang и GCC. Модули C++ не обязательны: доступны заголовочные файлы (include/), которые генерирует из тех же модулей утилита demodulizer — каждый push в main пересобирает, линкует и коммитит их обратно. Единственная зависимость, Boost.PFR, не нужна при включённых binding packs из 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)

Izvor: opennet.ru

Kupite zanesljivo gostovanje za spletne strani z zaščito pred DDoS, VPS VDS strežniki 🔥 Kupite zanesljivo gostovanje za spletne strani z zaščito pred DDoS, VPS VDS strežniki | ProHoster