Numri i parë i scan, bibliotekës për analizën e tekstit në C++ me ndërtimin e automatos finite gjatë kompilimit

ËshtĂ« publikuar numri i parĂ« i bibliotekĂ«s scan (0.1.0), e cila interpreton tekst nĂ« vlera sipas njĂ« shablli tĂ« njohur gjatĂ« fazĂ«s sĂ« kompilimit. Shablli ose formati shkruhet si njĂ« argument shablli C++ dhe shndĂ«rrohet gjatĂ« kompilimit nĂ« njĂ« automat tĂ« fundĂ«m tĂ« shĂ«nuar (tagged deterministic finite automaton, TDFA), kalimi i tĂ« cilit zhvillohet pĂ«rmes gjendjeve nĂ« kod gjatĂ« fazĂ«s sĂ« kompilimit duke pĂ«rdorur shabllonat C++. Rezultati i ekzekutimit Ă«shtĂ« vlera e llojit tĂ« kĂ«rkuar. Koodi i projektit Ă«shtĂ« shkruar nĂ« C++23 dhe shpĂ«rndahet nĂ«n licencĂ«n GPLv3. import scan; // A Ă«shtĂ« i barabartĂ« e gjithĂ« vargu. Çdo pikĂ« hyrĂ«se Ă«shtĂ« gjithashtu njĂ« adapteri diapazoni. scan::match(address); address | scan::match<"[a-z]+@[a-z.]+"}; // Me grupe. const auto found = scan::match("42-abc"); found.get().to_view(); // "42" // Fillimi i vargut, i zĂ«nĂ« me shabllon, dhe e para pĂ«rputhje kudo. scan::starts_with("abc123").whole().to_view(); // "abc" scan::search("id=4210x").to_view(); // "4210" // TĂ« gjitha pĂ«rputhjet dhe copat midis tyre - paraqitje tĂ« lenjshme. for (const auto& one : text | scan::search_all) { ... } const auto fields = "a,bb,,ccc" | scan::split | std::ranges::to(); // Vlerat, jo tekst. struct row { int id; std::string_view name; }; const row one = scan::scan(line); // Lista, shuma e llojeve, forma e thelluar. struct all { std::vector values; std::variant tail; }; const all got = scan::scan("1,2,3 abc"); // Fillimi i inputit dhe çfarĂ« ka mbetur. const auto [value, rest] = scan::scan_prefix(line).take(); // Duke regjistruar njĂ« herĂ«, nga çfarĂ«do. for (const row& one : scan::each(text).of()) { ... }

Një diapazon, që mund të lexohet vetëm një herë, lexohet pa buferim, fushat mblidhen gjatë pranisë së simboleve. Sa simbole do të mbahen, shablli e përcakton gjatë kompilimit; aty ku nuk ka një numër të tillë, leximet zbatohen gjatë kompilimit. 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()) { ... } Performanca.

Matjet janë realizuar në Ryzen 9 9950X, clang 22.1.8 me libc++, -O3 -march=native, LTO. Tridhjetë e dy regjistrime për kalim, mediana e shtatë kalimeve. «([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 të njëjtat pesë fusha, nga dyqind shkronja çdo një scan::scan<f>.sentinel() 57.8 ns re2c 637 ns CTRE 692 ns RE2 11751 ns «first.last@subdomain.example.com» njohja, asgjë nuk nxirret scan::match<p>.sentinel().scalar() 436 ns scan::match<p>.scalar() 545 ns re2c 1186 ns RE2 2616 ns CTRE 14339 ns PĂ«rmbledhja e grupeve gjatĂ« analizĂ«s

Tipi informon se cilës grupi i përket karakteri i radhës dhe llogaritja kryhet në vend: asnjë iteracion i ciklit nuk ruhet, asnjë nënkarakter nuk krijohet. 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() shkruar manualisht 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
Kontekste dhe alokatorë

Çdo objekt i thirrjes — alokatori, puli, arena, çfarĂ«do tjetĂ«r — mund tĂ« kalojĂ« nĂ« atĂ« thirrje qĂ« krijon njĂ« vlerĂ«. Lloji i kontekstit kĂ«tu nuk humbet: gjendja e skanerit dhe çdo thirrje e tij mund tĂ« jenĂ« shabllone sipas tij, kĂ«shtu qĂ« asnjĂ« lloj i thirrjes nuk pĂ«rmendet nĂ« skaner. 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); // njĂ« pĂ«r tĂ« dy vendet scan::scan(text).of(mine, scan::default_context); // nga njĂ« pĂ«r vend scan::scan(text).of({mine, scan::default_context}); // tĂ« njĂ«jtĂ«n nĂ« kllapa const both got = scan::scan(text).with(mine); // Konteksti nuk Ă«shtĂ« e detyrueshme tĂ« jetĂ« tipi i tij: alokatori arrin deri nĂ« atĂ« // qĂ« ndihmon nĂ« ndĂ«rtimin e leximit. struct two { std::pmr::string name; std::pmr::string tail; }; const two kept = scan::scan(text).of<( std::pmr::polymorphic_allocator(&bytes));

Huka merr vetë objektin e thirrjes, e jo një kopje, kështu që gjendja mund të ruajë adresën e tij: konteksti jeton aq sa thirrja, dhe të gjithë leximi ndodh brenda kësaj thirrjeje. Veçori të tjera

  • Dy shtresa: shtresa e shablloneve (match, starts_with, search, search_all, split) dhe shtresa e formateve, ku «{}» Ă«shtĂ« fusha, dhe vlera e vendit pĂ«rcaktohet nga fushat e vetĂ« tipit.
  • Rregulli i zgjidhjes sĂ« dyshimeve Ă«shtĂ« leftmost-first, si nĂ« Perl, RE2 dhe CTRE.
  • E gjithĂ« kjo funksionon gjithashtu nĂ« shprehje konstante.
  • Nuk ka lookaround, referenca prapa dhe tipare Unicode; shabllonet punojnĂ« me byte.
  • Nuk mbĂ«shteten shabllonet qĂ« janĂ« tĂ« njohura vetĂ«m gjatĂ« ekzekutimit.

Ndërtimi

Kompilohet nĂ« clang dhe GCC. Modul C++ nuk Ă«shtĂ« i detyrueshĂ«m: janĂ« tĂ« disponueshme skedarĂ« titujsh (include/) qĂ« gjenerohet nga tĂ« njĂ«jtit module me utilitarin demodulizer — çdo push nĂ« main e rikompilon, lidhen dhe i komiton mbrapsht. VarĂ«sia e vetme, Boost.PFR, nuk Ă«shtĂ« e nevojshme kur janĂ« aktivizuar paketat e lidhjes nga 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)

Burimi: opennet.ru

Blini hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS đŸ”„ Blini hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS | ProHoster