\par Hlavním cílem této bakalářské práce je vytvořit novou knihovnu pro statickou analýzu jazyka YARA. Na rozdíl od dosavadního řešení, které je popsáno v~předchozí kapitole, by měla umožňovat provádět analýzu inkrementálně. Díky tomu bude optimalizována pro použití v~rámci textových editorů a~IDE, které jsou jednou z hlavních motivací pro její tvorbu. Vzhledem k~tomu, že se bude jednat o~novou verzi knihovny Yaramod-v3 a~bude v~některých aplikacích její náhradou, je toto nové řešení dále označováno jako \textit{Yaramod-v4} či pouze jako knihovna. 
\par V~této kapitole jsou popsány všechny fáze vývoje programového řešení od specifikace požadavků přes návrh a~implementaci až po testování. Nakonec je také popsána implementace tohoto nového řešení s~projektem YLS a~příkladů použití knihovny.

\section{Specifikace požadavků}
\par Nejzásadnějším požadavkem, který je ostatně také zmíněn v~zadání, je podpora inkrementálního přístupu ke statické analýze. Na základě používání současné verze knihovny (Yaramod-v3), byly formulovány také další nároky na novou verzi. Zásadní funkcionalita knihovny Yaramod-v4 a~vlastnosti, kterými by  měla disponovat, jsou:

\begin{itemize}
    \item Podpora inkrementální statické analýzy
    \item Tolerance vůči chybám
    \item Podpora uživatelsky definovaných modulů
    \item Možnost specifikovat vstupní zdrojový kód jak pomocí bezprostředního řetězce, tak skrze cestu k~souboru se zdrojovým kódem
    \item Oddělení elementů jednotlivých zdrojových souborů (při použití \inlinecode{include})
    \item Možnost lokalizovat některé sémantické celky ve zdrojovém souboru (pravidla, řetězce\dots)
    \item Serializace výsledků analýzy
\end{itemize}


\par Ačkoliv to není v~seznamu výše uvedeno, bude rovněž vhodné, aby rozhraní knihovny bylo podobné rozhraní její dosavadní verze. Díky tomu se bude snáz integrovat s~projekty, které využívají Yaramod-v3.
\par Podporou inkrementální statické analýzy je myšlena možnost provedení syntaktických a~základních sémantických kontrol (kontrola datových typů, kolize identifikátorů apod.) nad zdrojovými soubory při vytváření výstupní reprezentace zdrojového kódu. Díky této reprezentaci bude knihovna také umožňovat pokročilejší statickou analýzu, jež bude řízena klientskou aplikací (např. YLS). Může se jednat například o~optimalizace podmínek pravidel zjednodušením pravdivostních výrazů.
\par Od nové verze knihovny se také očekává, že přítomnost chyby ve zdrojovém kódu neukončí rozpracovanou analýzu a~bude tak moci být identifikováno více chyb než pouze jedna. Toto chování by měla zaručit právě tolerance vůči chybám.
\par Jelikož moduly jsou v~jazyce YARA velmi používané, je zapotřebí, aby nové řešení podporovalo jejich import. Protože uživatelé mohou využívat své vlastní, mělo by být možné dodat analyzátoru jejich specifikace. Z~důvodu zpětné kompatibility bude ideální podporovat stejný formát specifikace jako u~dosavadního řešení (JSON, viz \ref{sec:yaramodv3}).
\par Schopnost provádět statickou analýzu jak ze souboru, tak z~řetězce bude výhodná zejména pro co největší flexibilitu použití knihovny. Například pro použití v~textových editorech bude vhodné podporovat analýzu bezprostředního řetězce, která je v~dosavadním řešení omezená a~z~toho důvodu je upřednostňováno provádění analýzy nad souborem. To dále limituje jeho aplikace (např. YLS), protože není možné provádět analýzu v~reálném čase\footnote{Vždy je analyzován pouze uložený obsah souboru, a~tudíž nedojde k~reanalýze, pokud uživatel text v~souboru upravil, aniž by jej uložil.}. 
\par V~některých případech je nutné přistoupit pouze k~elementům specifického zdrojového souboru a~odfiltrovat tedy všechny elementy z~jiných souborů, jež byly vloženy pomocí \inlinecode{include}. Tento problém by měl řešit požadavek na oddělení elementů jednotlivých souborů. Pokud chceme pomocí dosavadního řešení přistoupit pouze k~pravidlům obsaženým ve specifickém souboru, musí být objekty reprezentující pravidla filtrovány klientskou aplikací. Nové řešení by mělo poskytnout takové rozhraní, aby toto filtrování nebylo zapotřebí.
\par Požadavek na lokalizaci sémantických celků je důležitý zejména pro analýzu v~rámci textových editorů a~v~IDE. Pokud například klientská aplikace poskytuje navigaci na definice symbolů, je potřeba znát jejich polohu v~rámci zdrojového souboru. Tuto informaci by měla poskytnout právě knihovna.
\par Serializací je zde míněn převod struktur reprezentující výsledek analýzy zpět, do podoby formátovaného zdrojového kódu, jenž bude sémanticky shodný se vstupním zdrojovým kódem. Funkcionalita umožňující serializaci je sice méně důležitým požadavkem, nicméně bude vhodná pro testování a~ladění knihovny. Pro uživatele může být ovšem také benefitem, neboť mu poskytne možnost automaticky formátovat zdrojové soubory v~jazyce YARA.  


\section{Návrh knihovny a jejího rozhraní}
\par Na základě obecných znalostí o~jazyce YARA, fungování dosavadního řešení, inkrementální statické analýze kódu a~také na základě specifikace požadavků byl vyhotoven návrh knihovny Yaramod-v4. Návrh probíhal ve dvou fázích metodou shora dolů. Nejprve byla navržena architektura knihovny a~poté byla provedena objektově orientovaná analýza problému, z~níž vzešly třídní diagramy, které novou knihovnu modelují. Díky tomuto přístupu došlo k~dekompozici celé knihovny na moduly a~rozdistribuování zodpovědností modulů mezi jednotlivé třídy.
\par Návrh architektury vychází hlavně z~obecných znalostí o~fungování statické analýzy. Jak již bylo uvedeno v~kapitole \ref{sec:static_flow}, první fází statické analýzy kódu je převést jej do interní reprezentace. Pro dosažení funkcionality srovnatelné s~dosavadním řešením nebude dostačující provést pouze lexikální analýzu, ale bude muset být provedena i~analýza syntaktická. Interní reprezentací zdrojového kódu je z~tohoto důvodu v~případě Yaramod-v4 abstraktní syntaktický strom (AST) vytvořený inkrementálním syntaktickým analyzátorem. Ten se stará i~o~aktualizaci AST při úpravách vstupního řetězce.


\begin{figure}[t]
    \centering
    \includegraphics[width=\textwidth]{obrazky-figures/yaramodv4.pdf}
    \caption[Návrh architektury knihovny Yaramod-v4]{Návrh architektury knihovny Yaramod v4\,--\,Schéma zobrazuje knihovnu, její komponenty a~transformace zdrojových kódů, které provádí. Červené hrany znázorňují předávání datových struktur při spuštění inkrementální statické analýzy. Nejprve je výstupní reprezentace s~označením změn předána zpět modulu postprocessing. Ten požádá modul tree-sitter-yara o~aktualizaci AST a~poté sám provede aktualizaci výstupní reprezentace. Adaptér se stará o~konverzi AST na výstupní reprezentaci. Knihovna dále obsahuje řídící rozhraní, pomocí kterého je možné ji ovládat.}
    \label{fig:yaramodv4_arch}
\end{figure}


\par Jako inkrementální lexikální a~syntaktický analyzátor je využit open-source projekt \textit{tree-sitter}. Zejména z~tohoto důvodu se architektura knihovny sestává ze dvou základních modulů, které jsou do velké míry dvěma samostatnými projekty.
\par Prvním z~nich je \textit{tree-sitter-yara}, inkrementální lexikální a~syntaktický analyzátor jazyka YARA  založený na projektu tree-sitter (více v~\ref{sec:tree-sitter-yara}). Výstupem této části je AST a~poskytuje tzv. \textit{syntaktické rozhraní} pro druhý modul knihovny, jehož úkolem je konverze AST na výstupní reprezentaci a~provádění sémantických kontrol. Tento modul je dále označován jako \textit{postprocessing}.
\par Uživatel knihovny Yaramod-v4 je díky tomuto přístupu odstíněn od AST a~manipuluje pouze s~reprezentací kódu tvořenou objekty. Ta bude výsledky analýzy prezentovat na vyšší úrovni abstrakce. Třídy a~metody, které se podílejí na vytváření této výstupní reprezentace, jež je produkována modulem postprocessing, jsou dále označovány také jako \textit{sémantické rozhraní}.
\par Kromě toho, že zodpovědností modulů je vytváření příslušné reprezentace zdrojového kódu, mají také za úkol aktualizovat danou reprezentaci v~případě uplatnění inkrementální analýzy po editacích zdrojového souboru. Návrh architektury ve formě jednoduchého schématu je možné vidět na obrázku \ref{fig:yaramodv4_arch}.
\par Dva základní moduly je dále možné dělit na jednodušší komponenty. Tree-sitter-yara je možné rozdělit na lexikální a~syntaktický analyzátor. Část postprocessing lze dále dekomponovat na řídící rozhraní,  sémantické rozhraní, sémantický analyzátor a~adaptér, který zajišťuje převod syntaktického rozhraní na sémantické rozhraní knihovny.   
\par Jak je možné vidět, součástí výstupní reprezentace je i~AST, který jinak knihovna využívá pouze jako svoji interní strukturu. Přestože se nepředpokládá, že by k~němu uživatel přistupoval, princip fungování inkrementální syntaktické analýzy vyžaduje pro svoje fungování existující AST (viz \ref{sec:incremental}) a~bude tedy potřebný pro její provedení po editaci zdrojového kódu.
\par V~rámci modulu tree-sitter-yara je hlavním úkolem vytvořit gramatiku jazyka YARA a~popis tokenů ve specifickém formátu, vyžadovaném knihovnou tree-sitter. O~samotnou lexikální a~syntaktickou analýzu se stará tato knihovna implementovaná v~jazyce C, jež pro definice poskytuje sadu funkcí. V~případě tohoto modulu tedy nemá příliš smysl znázorňovat jej pomocí třídních diagramů.
\par Druhý modul bude implementován v~jazyce C++ (důvod výběru tohoto jazyka je uveden v~\ref{sec:third_party_libs}) s~využitím objektově orientovaného paradigmatu. Z~tohoto důvodu jej lze modelovat právě pomocí diagramů tříd. Diagramy tříd obsažené v~této práci pro větší přehlednost neobsahují všechny detaily (chybí např. kompletní signatury metod, řešení modifikátorů pravidel a~řetězců, některé metody, tabulky symbolů\dots). 



\begin{figure}[t]
    \centering
    \includegraphics[width=\textwidth]{obrazky-figures/control_interface.pdf}
    \caption[Třídní diagram modelující řídící rozhraní knihovny a~s~ním související třídy]{Třídní diagram modelující řídící rozhraní knihovny a~s~ním související třídy\,--\,Uživatel v~rámci řídícího rozhraní manipuluje se třídami \inlinecode{Yaramod} a~\inlinecode{YaramodConfig}. Třída \inlinecode{YaramodConfig} udržuje uživatelská nastavení a~poskytuje je adaptéru provádějícímu konverzi AST na výstupní reprezentaci tvořenou objekty. Adaptér se poté stará také o~aktualizaci.}
    \label{fig:control_interface}
\end{figure}


\par Třídní diagram řídícího rozhraní knihovny je možné vidět na obrázku \ref{fig:control_interface}. Řídící rozhraní se skládá ze dvou tříd, \inlinecode{YaramodConfig} a~\inlinecode{Yaramod}. První z~těchto tříd, \inlinecode{YaramodConfig}, udržuje stav statického analyzátoru mezi jednotlivými běhy analýzy. Konkrétně uchovává uživatelská nastavení a~spravuje externí proměnné. V~rámci uživatelských nastavení je možné deaktivovat toleranci chyb, vypnout analýzu vložených zdrojových souborů, nastavit cestu k~bázovému adresáři se zdrojovými soubory nebo k~adresáři se specifikací modulů.
\par Objekt třídy \inlinecode{Yaramod} reprezentuje statický analyzátor jako takový. Uživatel může pomocí něj staticky analyzovat kód a~také přistupovat k~příslušné instanci \inlinecode{YaramodConfig}. U~metod pro provádění analýzy, \inlinecode{parseFile} a~\inlinecode{parseString}, je využito přetěžování funkcí v~jazyce C++. Pro každou z~těchto funkcí  existují dvě implementace. 
\par První je použita při prvotní neinkrementální analýze, kdy uživatel skrze argumenty poskytne pouze zdrojový kód. V~tomto případě tyto metody navrací ukazatel na objekt třídy \inlinecode{YaraSource}, která je základem sémantického rozhraní knihovny.
\par Pokud uživatel kromě zdrojového kódu poskytne metodám už existující instanci třídy \inlinecode{YaraSource}, je použita jejich druhá implementace, jež provádí inkrementální statickou analýzu. Dojde tak k~aktualizaci objektu typu \inlinecode{YaraSource} a~jeho komponent (byla-li zapotřebí).
\par Ačkoliv je jedním z~požadavků na knihovnu tolerance vůči chybám ve zdrojovém kódu, mohou existovat projekty, kde tato vlastnost není zapotřebí a~kde se předpokládá vyhození výjimky při nalezení chyby. Právě kvůli tomu umožňuje \inlinecode{YaramodConfig} toleranci chyb deaktivovat. 
\par Cesta k~bázovému adresáři bude uplatněna ve chvíli, kdy uživatel analyzuje kód přímo z~řetězce a~vyskytuje se v~něm konstrukce \inlinecode{include} s~relativní cestou. Díky specifikaci cesty k~bázovému adresáři je možné vložené zdrojové soubory jednoduše vyhledat. Dále je možné skrze konfiguraci také plně vypnout analýzu takto vložených souborů. To může být užitečné ve chvíli, kdy klientská aplikace ošetřuje vkládání souborů svým vlastním způsobem nebo pokud chceme provést sémantické kontroly, které vložené soubory nevyžadují. Nejdůležitějším uživatelským nastavením je nejspíše cesta k~adresáři se specifikací modulů, která je prohledána, nalezené soubory jsou deserializovány a~v~podobě objektu \inlinecode{Module} jsou uchovány v~jediné instanci třídy \inlinecode{ModuleProvider}. Pro udržování právě jedné instance tohoto typu je zde aplikován návrhový vzor \textit{jedináček} (singleton)~\cite[str.~172]{Gamma::1994::design}. Díky tomuto návrhovému vzoru je možné k~modulům přistoupit odkudkoliv. Po přidání nového modulu do adresáře nebo při změně cesty k~tomuto adresáři, může uživatel moduly znovu nahrát skrze volání metody \inlinecode{loadModules}.
\par Objekt typu \inlinecode{Yaramod} si dále udržuje objekt, jenž implementuje rozhraní abstraktní třídy \inlinecode{IParserAdapter}. Jedná se o~komponentu provádějící konverzi AST, který poskytuje modul tree-sitter-yara, na výstupní reprezentaci, tvořenou instancemi tříd sémantického rozhraní. Tato část je označována také jako adaptér rozhraní. Ačkoliv existuje stejnojmenný návrhový vzor, nejedná se v~tomto případě přímo o~jeho aplikaci, protože rozhraní modulu tree-sitter-yara není objektově orientované, a~tudíž zde nelze tento vzor v~originální podobě uplatnit. 
\par Abstraktní třída \inlinecode{IParserAdapter} je zde použita z~důvodu větší flexibility. Díky ní je odstíněno řídící rozhraní od konkrétní implementace adaptéru a~kdyby tedy v~budoucnu došlo např. ke změně rozhraní modulu tree-sitter-yara, může být řídící rozhraní zachováno. Rozhraní této třídy implementuje však zatím pouze třída \inlinecode{TSParserAdapter}. Instance této třídy komunikuje pomocí sady funkcí poskytovaných knihovnou tree-sitter s~modulem tree-sitter-yara a~vytváří výstupní reprezentaci zdrojového kódu pomocí svých privátních metod \inlinecode{buildRule}, \inlinecode{buildImport} atd. V~případě inkrementální analýzy se stará o~aktualizaci tohoto modelu zdrojového kódu. 
\par Dále se tento objekt stará také o~identifikaci některých syntaktických chyb, které nebylo možné kvůli použití syntaktického analyzátoru tree-sitter odhalit, nebo jejich odhalení při syntaktické analýze nebylo žádoucí (více v~kapitole \ref{sec:implementace}). Částečně také provádí kontroly sémantické. Kontroly, které jím realizovány nejsou, jsou uskutečňovány samotným sémantickým rozhraním. To, která třída má danou kontrolu na starosti, závisí na její povaze. Například kontrola toho, zda je symbol definován, je prováděna objektem třídy \inlinecode{TSParserAdapter}. Naproti tomu kontrola duplicitních identifikátorů řetězců je již zodpovědností objektu \inlinecode{Rule}, neboť ten má za úkol správu řetězců, jež mu náleží.

\begin{figure}[t]
    \centering
    \includegraphics[width=\textwidth]{obrazky-figures/hli.pdf}
    \caption[Třídní diagram sémantického rozhraní knihovny Yaramod-v4]{Třídní diagram sémantického rozhraní knihovny Yaramod-v4}
    \label{fig:hli}
\end{figure}

\par Další velkou částí modulu postprocessing je sémantické rozhraní knihovny Yaramod-v4. Tato část je popsána diagramem tříd na obrázku \ref{fig:hli}. Struktura tohoto rozhraní je na úrovni tříd a~jejich interakce do velké míry stejná jako u~knihovny Yaramod-v3~\cite{Yaramod} kvůli zpětné kompatibilitě. Přesto jsou zde koncepty, které v~rozhraní předchozí verze knihovny nenajdeme. Konkrétně jde o~třídy \inlinecode{YaraSource}, \inlinecode{FileInclude} \inlinecode{YaraFileElement}, \inlinecode{Error} a~\inlinecode{RuleElement}.
\par Zásadním vztahem mezi třídami je zde kompozice. Instance typu \inlinecode{YaraFile} představuje právě jednu \textit{překladovou jednotku}. Pro každý objekt tohoto typu byl právě jednou spuštěn lexikální a~syntaktický analyzátor. Modul tree-sitter-yara tedy pro každou tuto jednotku vytváří simuluje tvorbu derivačního stromu, vytvoří AST a~ten je poté konvertován na výstupní reprezentaci adaptérem. Objekt třídy \inlinecode{YaraFile}, reprezentující zdrojový soubor v~jazyce YARA, je tvořen libovolným počtem objektů typu \inlinecode{FileInclude}, \inlinecode{Import} a~\inlinecode{Rule}. Tyto typy v~daném pořadí reprezentují konstrukce \inlinecode{include}, \inlinecode{import} a~pravidlo v~jazyce YARA. Jelikož objekty těchto typů nedávají samostatně smysl, jsou s~\inlinecode{YaraFile} ve vztahu kompozice.
\par Instance \inlinecode{FileInclude} si udržuje referenci na další objekt typu \inlinecode{YaraFile}, čímž vytváří stromovou strukturu překladových jednotek. Tato stromová struktura sice dobře odráží to, jakým způsobem jsou zdrojové soubory vkládány jeden do druhého, nicméně neposkytuje vhodný pohled na možné závislosti mezi nimi. Pokud například vložíme do zdrojového souboru další soubory pomocí konstrukce \inlinecode{include}, v~posledním takto vloženém souboru je možné odkazovat také symboly obsažené i~ve dříve vložených souborech. Ty jsou však z~pohledu stromové reprezentace sourozeneckými uzly nebo jejich potomky. To je důležité reflektovat při implementaci sémantických kontrol.
\par Celou sadu překladových jednotek, jež jsou jedna do druhé vkládány, obaluje instance třídy \inlinecode{YaraSource}. Ta si udržuje buffer s~ukazateli na všechny objekty třídy \inlinecode{YaraFile}, které jsou dostupné ze vstupní překladové jednotky (entry file). Obsah této překladové jednotky je explicitně poskytnut uživatelem skrze cestu ke zdrojovému souboru či skrze řetězec se zdrojovým kódem. Díky tomuto bufferu je snadné kontrolovat, zda není některý ze zdrojových souborů vložen dvakrát. Zároveň objekt třídy \inlinecode{YaraSource} poskytuje uživateli seznam všech dostupných pravidel, chyb atd. ve všech zdrojových souborech (ve vstupní překladové jednotce i~ve vložených).
\par Dalším zajímavým prvkem je třída \inlinecode{Error}, což je bázová třída pro všechny třídy, jež reprezentují chyby. Knihovna Yaramod-v3 ohlašuje chyby ve zdrojovém souboru vyhozením výjimky. Zde jsou chyby primárně reprezentovány jako objekty\footnote{Deaktivování tolerance chyb má ze následek vyhození výjimky při výskytu první chyby, jako je tomu u~dosavadního řešení.}, jež jsou uchovány v~rámci příslušného objektu typu \inlinecode{YaraFile}. Ten z~pohledu chyb představuje zdrojový soubor, v~němž se chyba vyskytla. 
\par Zásadní metodou \inlinecode{YaraFile} je z~hlediska inkrementální statické analýzy \inlinecode{edit}. Pomocí ní může uživatel nebo klientská aplikace ohlásit editaci zdrojového kódu a~následně spustit inkrementální analýzu pro aktualizaci výstupní reprezentace. Pro obecnější použití knihovny se předpokládá, že editací může být mezi prováděním analýz libovolný počet. Zároveň pro odstínění uživatele od implementačních detailů inkrementálního přístupu je vhodné, aby i~těsně po provedení editací byl schopen přistoupit k~výstupní reprezentaci, byť bude zastaralá. Z~těchto důvodů jsou editace shromažďovány do bufferu a~aplikovány až těsně před inkrementální analýzou. To také umožňuje snadnou implementaci metody \inlinecode{undo}, neboť pro zrušení zatím neaplikovaných modifikací zdrojového kódu stačí buffer vyčistit.
\par Komponenty objektů třídy \inlinecode{YaraFile} jsou ukládány prostřednictvím tabulek, jež jsou vždy indexovány dle bytového offsetu, na němž začínají ve zdrojovém souboru. Indexování podle pozice ve zdrojovém souboru je důležité zejména pro podporu inkrementálního přístupu. Analyzátor musí být totiž schopen určit všechny objekty, které mohly být editací ovlivněny. Bytové offsety editovaných částí dokumentu a~uchovaná pozice objektu budou zásadní pro určování objektů, které je zapotřebí aktualizovat. Jelikož uchování bytového posunu jejich začátku a~další vlastnosti (např. délka syntaktické konstrukce, kterou reprezentují) jsou společným rysem všech komponent, jsou vytknuty do bázové třídy \inlinecode{YaraFileElement}.
\par Objekt typu \inlinecode{Rule} lze dále dekomponovat na objekty reprezentující metadata (třída \inlinecode{Meta}), řetězce (\inlinecode{String}), interní proměnné a~podmínku pravidla (\inlinecode{Expression}). Dále si udržuje seznam značek (tagů). Komponenty, které tvoří pravidlo, mají společnou bázovou třídu \inlinecode{RuleElement}. Tato třída je navíc odvozená od již zmíněné třídy \inlinecode{YaraFileElement}, neboť prvek pravidla je zároveň prvkem zdrojového souboru. Třída \inlinecode{RuleElement} přepisuje některé z~metod pro uchovávání bytového offsetu a~přístup k~němu (v~diagramu znázorněno pouze metodou \inlinecode{getOffset}), protože v~případě komponent pravidel je uchováván pouze relativní bytový offset vůči začátku pravidla, ke kterému náleží. Díky tomu není nutné udržovat konzistenci pozic všech komponentů pravidla, ale pouze pravidla jako takového. To souvisí s~granularitou aktualizací výstupní reprezentace během inkrementální analýzy, jež je popsána blíže v~kapitole \ref{sec:implementace}. Stejně jako v~případě překladové jednotky, tak i~pravidla mají svoje tabulky, v~nichž uchovávají své řetězce, metadata a~interní proměnné.
\par Třída \inlinecode{Expression} představuje výraz v~podmínce pravidla a~hodnoty interních proměnných.  V~diagramu tříd \ref{fig:hli} je možné vidět, že s~třídami reprezentující pravidlo a~interní proměnné je opět ve vztahu kompozice, neboť výraz sám o~sobě pozbývá smysl. Jak pravidlo, tak proměnné mají pouze jednu komponentu tohoto typu. \inlinecode{Expression} je opět bázovou třídou celé hierarchie tříd (na diagramu není zobrazena), z~nichž každá reprezentuje nějaký operátor jazyka YARA, literál, či symbol. Operátory mohou mít v~závislosti na jejich aritě různý počet operandů, a~proto i~objekt typu \inlinecode{Expression} může být složen z~$N$ dalších objektů tohoto typu. Vzniká tak stromová struktura. To je v~diagramu tříd \ref{fig:hli} modelováno opět vztahem kompozice, která má třídu \inlinecode{Expression} jak za komponentní, tak za kompozitní prvek.
\par Pro efektivní průchod tímto stromem je využit návrhový vzor visitor, což je stejný přístup jako v~dosavadním řešení~\cite{Yaramod}. Tento návrhový vzor je blíže popsán právě v~kapitole~\ref{sec:yaramodv3}, která se mu věnuje.
\par Serializace objektů je zajišťována pomocí abstraktní třídy \inlinecode{Printable}, jejíž rozhraní disponuje metodou \inlinecode{getTextFormatted}. Všechny serializovatelné objekty jsou z~této třídy odvozeny. V~diagramu tříd tato část rozhraní pro větší přehlednost není znázorněna, neboť kromě tříd \inlinecode{YaraFileElement} a~\inlinecode{RuleElement} jsou z~ní odvozeny všechny uvedené třídy.

\section{Implementace} \label{sec:implementace}
\par Po vytvoření návrhu byla knihovna Yaramod-v4 realizována. Díky tomu, že byla v~rámci návrhu rozdělena na dva moduly, jež na sebe navazují, bylo možné i~fázi implementace rozdělit na dvě po sobě jdoucí části. Nejdříve byl vytvořen modul tree-sitter-yara, respektive gramatika pro knihovnu tree-sitter. Po dokončení této komponenty byl implementován modul postprocessing. 


\subsection{Použité technologie} \label{sec:third_party_libs}
\par Co se implementačního jazyku týče, pro knihovnu jako takovou byl vybrán jazyk C++. Vzhledem k~tomu, že se jedná o~kompilovaný jazyk, je pro tento účel vhodný zejména díky rychlosti, kterou je kód v~tomto jazyce prováděn. Jak již bylo uvedeno v~teoretické části, rychlost provádění statické analýzy je jedním z~jejích zásadních parametrů.
\par Oproti alternativě, kterou zadání umožňuje pro tento účel použít, jazyku Rust, je C++ výhodnější zejména z~důvodu snazšího zajištění kompatibility mezi aplikacemi využívajícími dosavadní verzi knihovny a~Yaramod-v4. Na druhou stranu je zapotřebí výsledek důkladněji otestovat, protože C++ na rozdíl od Rustu negarantuje paměťovou bezpečnost. Konkrétně je používán standard C++20.
\par Dále je zapotřebí použít jazyka Python, protože klientské aplikace využívají Yaramod-v3 také skrze rozhraní v~tomto jazyce. Prostřednictvím tohoto rozhraní je realizována integrace s~projektem Yara Language Server, která je také součástí zadání práce.
\par Kromě výše uvedých jazyků byl také použit JavaScript pro specifikaci gramatiky jazyka YARA. Pro překlad knihovny do binární podoby je využíván překladových systém \textit{CMake} a~pro generování dokumentace nástroj \textit{Doxygen}. Pro testovaní rozhraní v~jazyce Python je použita knihovna \textit{pytest}. Další moduly a~rámcová řešení pro jazyk Python využívá projekt YLS~\cite{Kastak::2022::yls}. Součástí programového řešení této práce jsou nicméně pouze ty části projektu, kde došlo k~nahrazení knihovny Yaramod-v3 za novou verzi. Z~tohoto důvodu zde nejsou závislosti YLS uvedeny.
\par Mimo standardních knihoven jazyka C++ a~vestavěných modulů jazyka Python bylo při vývoji Yaramod-v4 využito několik knihoven a~rámcových řešení, které jsou dílem jiných autorů. Tyto projekty, jež jsou rovněž uvedeny se všemi náležitostmi na přiloženém paměťovém médiu, jsou popsány v~následujících kapitolách.


\subsubsection{Tree-sitter}
\par Knihovna \textit{tree-sitter} \cite{Brunsfeld::2022::tree-sitter} plní naprosto zásadní úlohu v~rámci modulu tree-sitter-yara, jehož zodpovědností je inkrementální lexikální a~syntaktická analýza kódu v~jazyce YARA. Princip fungování tohoto nástroje byl popsán v~kapitolách \ref{sec:static_flow} a~\ref{sec:incremental}. Skládá se ze dvou základních komponent. První její částí je nástroj \textit{tree-sitter-cli}. Ten je v rámci Yaramod-v4 využíván pro generování tabulek pro LR syntaktickou analýzu ve formě hlavičkových souborů před samotným překladem zdrojového kódu knihovny. Tyto tabulky jsou využívány druhou částí knihovny, IGLR syntaktickým analyzátorem \textit{tree-sitter}. Ten je již nedílnou součástí modulu tree-sitter-yara. Tento syntaktický analyzátor je implementován jazyce C, a tudíž se jedná o~rychlé a~přenositelné řešení, které navíc nemá žádné další závislosti.
\par Zmíněný nástroj tree-sitter-cli není pouze generátorem tabulek. Disponuje také jednoduchým textovým uživatelským rozhraním či rámcovým řešením pro testování a~ladění uživatelsky definovaných gramatik. Podkladem pro generování tabulek, jsou totiž bezkontextové gramatiky, které uživatel může specifikovat pomocí jazyka JavaScript a~sady k~tomu určených funkcí. To umožňuje uživatelům analyzovat různé formální jazyky.
\par Funkce syntaktického analyzátoru je možné využívat prostřednictvím sady funkcí v~jazyce C, ale také skrze rozhraní pro Rust, Python, Javu a~spoustu jiných jazyků. Kromě toho je také vestavěn do tree-sitter-cli, což usnadňuje vytváření prototypů gramatik. Více o~praktickém používání této knihovny a~o~její úloze v~rámci této práce je uvedeno v~kapitole \ref{sec:tree-sitter-yara}.

\subsubsection{JSON for Modern C++}
\par Jedním z~požadavků na výslednou knihovnu byla také podpora modulů jazyka YARA a~uživatelsky definovaných modulů. Tento požadavek komplikuje provádění sémantické analýzy, neboť musí být použitým symbolům přiřazen jejich kontext. Definice symbolů, které jsou obsaženy v~modulech, je zapotřebí knihovně dodat prostřednictvím souborů ve formátu JSON. Jde tak o~stejný přístup jako u~dosavadního řešení.
\par Z~tohoto důvodu bude zapotřebí, aby knihovna dokázala analyzovat kromě cílového jazyka YARA také soubory ve formátu JSON. K~tomuto účelu je v~Yaramod-v4 využívána právě knihovna \textit{JSON for Modern C++}~\cite{Nlohmann}. Ta umožňuje deserializaci souborů a~řetězců ve formátu JSON na struktury a~objekty jazyka C++. Tato reprezentace specifikace modulů je podstatně vhodnější pro určování kontextu symbolů než surový text.

\subsubsection{Pybind11}
\par Již bylo dříve uvedeno, že Yaramod-v3 je velmi často využíván skrze rozhraní v~jazyce Python. Jelikož se v~budoucnu předpokládá nahrazení tohoto dosavadního řešení knihovnou Yaramod-v4, bude rovněž vhodné, aby byla přístupná i~z~prostředí tohoto jazyka.
\par Jak je ale uvedeno výše, jako implementační jazyk byl zvolen C++, protože díky tomu bude mít knihovna větší potenciál z~hlediska výkonu. Právě knihovna \textit{pybind11}~\cite{Pybind} zajišťuje v Yaramod-v4 interoperabilitu mezi C++ a~Pythonem. Toto rámcové řešení disponuje dobrou dokumentací a~podporou pro všechny běžné kompilátory jazyka C++ (GCC 4.8+, Clang/LLVM 3.3+\dots). Kromě toho také podporuje veškerou běžně používanou funkcionalitu jazyka C++ (standardní datové struktury, chytré ukazatele apod.).

\subsubsection{GoogleTest}
\par Pro průběžné jednotkové testování modulu postprocessing bylo použito rámcové řešení \textit{GoogleTest}~\cite{Googletest}. To umožnilo snadné testování nově přidané funkcionality v~rámci tohoto modulu replikovatelným způsobem. Kromě samotného provádění automatizovaných jednotkových testů nabízí také možnost jejich filtrování. Toho bylo velmi často využíváno, zejména v~situaci, kdy se testovací sady začaly rozšiřovat. 


\subsection{Tree-sitter-yara} \label{sec:tree-sitter-yara}
\par První velkou částí programového řešení je gramatika jazyka YARA, která ve spojení s~knihovnou pro inkrementální lexikální a~syntaktickou analýzu, tree-sitter, tvoří modul tree-sitter-yara. Ten je možné využívat také samostatně pro jednoduchou inkrementální syntaktickou analýzu zdrojových souborů. Díky tomu mohou být realizovány např. syntaktické kontroly a~zvýrazňování syntaxe. V~rámci procesu statické analýzy kódu má tato část za úkol konverzi zdrojového kódu v~podobě textového řetězce do interní reprezentace vhodné k~dalšímu zpracování, kterou je AST.
\par Inkrementální GLR syntaktický analyzátor, který je součástí knihovny tree-sitter, již disponuje rozhraním potřebným pro napojení na postprocessing. Důležité je tomuto syntaktickému analyzátoru dodat korektní tabulku pro LR syntaktickou analýzu cílového jazyka. Během implementace modulu tree-sitter-yara byla tedy vytvářena bezkontextová gramatika v~jazyce JavaScript, na základě které nástroj tree-sitter-cli tuto tabulku vygeneruje. Ta se skládá jednak z~popisu pravidel pro neterminální symboly, ale také obsahuje definice terminálních symbolů pro lexikální analyzátor, jenž je rovněž součástí tree-sitteru. Inkrementální lexikální a~syntaktický analyzátor tedy nejsou součástí programového řešení této bakalářské práce, ale je jí tato gramatika. Hlavním zdrojem informací pro její tvorbu byla dokumentace jazyka YARA~\cite{Alvarez::2022::yara}. Před popisem vybraných částí implementace gramatiky je však zapotřebí nejdříve uvést obecné principy, pomocí kterých se gramatiky pro tree-sitter definují. 
\par Definice gramatiky pro knihovnu tree-sitter je zapouzdřena ve speciálním objektu. Tree-sitter pro tento účel definuje funkci \inlinecode{grammar}. Té jsou pravidla gramatiky a~popisy tokenů předány prostřednictvím argumentu. Jako argument je očekáván objekt, jehož vlastnosti jsou předepsány knihovnou. Jednou z~těchto vlastností je také \inlinecode{rules}. V~její hodnotě se očekávají již samotné definice terminálů a~pravidla gramatiky, resp. definice neterminálních symbolů gramatiky. Z~pohledu uživatele nejsou tyto dva druhy definic nijak odlišeny a~zda se jedná o~terminální nebo neterminální symbol závisí pouze na dané definici. Tyto definice jsou z~hlediska jazyka JavaScript opět vlastnostmi objektu, kde jejich název odpovídá názvu definovaného symbolu. Hodnotami vlastností jsou řetězce, regulárními výrazy či objekty, které jsou navraceny funkcemi předdefinovanými knihovnou tree-sitter. Konvence pro zápis definic využívá tzv. \textit{arrow functions}, což je zkrácený zápis funkce pomocí symbolu \inlinecode{=>}.

%\par Definice gramatiky je zapouzdřena v~objektech. Na nejvyšší úrovni je obalena objektem, který je navracen funkcí \inlinecode{grammar}. Té jsou pravidla gramatiky a~popisy tokenů předány prostřednictvím argumentu opět ve formě objektu, jehož vlastnosti jsou předepsány knihovnou. Vlastnost \inlinecode{rules} obsahuje již samotné definice terminálů a~pravidla gramatiky, resp. definice neterminálních symbolů gramatiky. Z~pohledu uživatele nejsou tyto dva druhy definic nijak rozlišeny a~zda se jedná o~terminální nebo neterminální symbol závisí pouze na dané definici. Tyto definice jsou z~pohledu jazyka JavaScript opět vlastnostmi objektu, kde jej název odpovídá názvu definovaného symbolu. Hodnotami vlastností jsou řetězce, regulárními výrazy či objekty, které jsou vraceny funkcemi předdefinovanými knihovnou tree-sitter. Konvence pro zápis hodnot využívá tzv. \textit{arrow functions}, což je zkrácený zápis funkce pomocí symbolu \inlinecode{=>}.

\subsubsection{Definice terminálních symbolů}

\par V~rámci vytvořené gramatiky pro jazyk YARA jsou využívány všechny tři možné způsoby definice terminálních symbolů, a~sice řetězcem, regulárním výrazem nebo předdefinovanou funkcí \inlinecode{token}. Předdefinovaná funkce \inlinecode{token} umožňuje použít další funkce poskytované knihovnou tree-sitter, které jsou jinak určeny pro vyjádření syntaktické struktury neterminálního symbolu. Gramatika jazyka YARA tohoto využívá např. pro popis modifikátorů pravidla, který vypadá následujícím způsobem:

\medskip
\begin{lstlisting}[language=PHP, keywords={token, choice, immediate, prec, repeat, choice, seq, field, left, const, new}, keywordstyle=\color{lstkeyword}]
rule_modifier: _ => token(
    choice(
        "private", 
        "global"
    )
)
\end{lstlisting}
\medskip

\par Výše uvedený popis udává, že symbol \inlinecode{rule_modifier} je v~gramatice terminálem (tokenem ve výsledném AST) a~představuje jeden z~řetězců \inlinecode{private} nebo \inlinecode{global}. V~rozvinuté Backus-Naurově formě (EBNF) bychom ekvivalentní definici zapsali:

\begin{center}
\begin{BVerbatim}
rule_modifier = "private" | "global"
\end{BVerbatim}
\end{center}

\par Alternativně by bylo možné pro tento účel použít také regulární výrazy. V~tomto případě se však jedná o~méně rozšiřitelnou variantu, protože přidáváním dalších variant modifikátorů pravidla by se regulární výraz dále prodlužoval. To by mělo za následek zhoršenou čitelnost. Regulární výrazy jsou nicméně využívány pro popis literálů a~identifikátorů. Dále jsou pomocí nich popsány části hexadecimálních řetězců a~regulárních výrazů\footnote{Součástí gramatiky jsou také definice neterminálních a~terminálních symbolů, které reprezentují regulární výrazy v~jazyce YARA.}. Například definice terminálního symbolu, který reprezentuje jeden byte v~hexadecimálním řetězci je:

\medskip
\begin{lstlisting}[keywords={token, choice, immediate, prec, repeat, choice, seq, field, left, const, new}, keywordstyle=\color{lstkeyword}]
_hex_str_byte: _ => /[A-Fa-f\d]{2}|\?[A-Fa-f\d]|[A-Fa-f\d]\?/
\end{lstlisting}
\medskip


\par Pomocí řetězců jsou definovány nepojmenované symboly, které figurují v~pravidlech gramatiky. Díky tomu, že nejsou pojmenované, jsou ve výsledném AST tokeny, které je reprezentují, skryté. To usnadňuje jeho zpracování v~modulu postprocessing. Toho se využívá např. u~čistě syntaktických prvků jazyka YARA (složené závorky ohraničující definici pravidla, dvojtečka oddělující značky pravidla\dots). Pokud chceme explicitně označit symbol (ať už~terminální, nebo neterminální) jako skrytý, použijeme podtržítko jako jeho prefix. To je možné vidět u~výše uvedené definice terminálního symbolu \inlinecode{_hex_str_byte}. Jelikož v~tomto případě není zapotřebí již kontrolovat žádnou další sémantiku těchto symbolů, jsou ve výsledném AST skryty, což dělá podstromy reprezentující hexadecimální řetězce výrazně úspornější.  

\par Dále je ve výsledné gramatice využívána lexikální precedence. Tento princip je uplatňován v~situacích, v~nichž není jednoznačné, který token z~načteného řetězce vytvořit. Pomocí explicitní definice lexikální precedence můžeme lexikální analyzátor přinutit vytvořit požadovaný token i~přes kolizi pravidel. Toho se v~gramatice YARA využívá například pro definici jednoho znaku v~řetězcovém literálu. Řetězcové literály jsou ve výsledné gramatice definovány jako posloupnosti libovolných znaků nebo řídících sekvencí (escape sequences) a~jsou to tedy neterminální symboly. Díky tomu je možné řídící sekvence ve výsledném AST snadno lokalizovat a~zkontrolovat je (viz dále). Ostatní znaky v~řetězci jsou popsány pomocí: 

\medskip
\begin{lstlisting}[keywords={token, choice, immediate, prec, repeat, choice, seq, field, left, const, new}, keywordstyle=\color{lstkeyword}]
token.immediate(
    prec(LEX_PREC.COMMENT + 1, /[^\"\\\r\n]/)
)
\end{lstlisting}
\medskip

\par Metoda \inlinecode{immediate} objektu \inlinecode{token} poskytnutého knihovnou tree-sitter zakazuje výskyt bílého znaku před terminálním symbolem. Ve spojení s~voláním \inlinecode{prec} je specifikována právě lexikální precedence, která je v~tomto případě vždy vyšší než precedence komentáře. Díky tomu jsou automaticky všechny znaky, které odpovídají regulárnímu výrazu považovány za součást řetězce a~nikoliv za začátek komentáře.

\par Jedním z~problémů, který se při vytváření definic pro terminální symboly gramatiky objevil, je možný výskyt klíčového slova na místě identifikátoru. Tento problém  nastává v~důsledku toho, že popis identifikátorů pomocí regulárního výrazu odpovídá i~klíčovým slovům samotným. Nástroj tree-sitter nenabízí žádný aparát, jak tuto situaci jednoduše ošetřit, neboť jedním z~jeho cílů je umožnit definovat gramatiku pro co největší množství jazyků a~některé jazyky nezakazují používat klíčová slova jako některé identifikátory\footnote{Příkladem jsou objekty v~jazyce JavaScript. Název vlastnosti objektu může být shodný s~jakýmkoliv klíčovým slovem. Tedy například \inlinecode{obj = {if: "test"};} je v~JS validní.}. Proto musí být tato situace ošetřena až v~modulu postprocessing. 

\par Pomocí výše uvedených mechanismů byly definovány také všechny zbývající terminální symboly jazyka YARA. Tree-sitter nabízí také možnost dodat implementaci vlastního lexikálního analyzátoru v~jazyce C. To však pro jazyk YARA nebylo zapotřebí.

\subsubsection{Definice neterminálních symbolů} \label{sec:nonterminals}

\par Pro definice neterminálních symbolů (pravidel gramatiky) je již nezbytné použít pomocné funkce poskytované knihovnou tree-sitter. Počáteční symbol výsledné gramatiky jazyka YARA je pojmenován jako \inlinecode{yara_file} a~je definován jako:

\bigskip
\begin{lstlisting}[language=PHP, keywords={token, choice, immediate, prec, repeat, choice, seq, field, left, const, new}, keywordstyle=\color{lstkeyword}]
yara_file: $ => repeat(
    choice(
        $.include, 
        $.import, 
        $.rule
    )
)
\end{lstlisting}
\bigskip

\par V~popisu symbolu \inlinecode{yara_file} je možné vidět aplikaci dalších pomocných funkcí poskytovaných knihovnou tree-sitter, \inlinecode{repeat} a~\inlinecode{choice}. První z~nich označuje $0$ až $N$ opakování symbolu v~argumentu a~druhá výběr jednoho ze všech symbolů v~argumentech. Výše uvedené pravidlo tedy říká, že derivací symbolu \inlinecode{yara_file} můžeme získat libovolnou sekvenci neterminálních symbolů \inlinecode{include}, \inlinecode{import}, \inlinecode{rule}. Argument \inlinecode{$}, obsahuje kontext dané gramatiky, který doplní sama knihovna. Díky tomu je možné se v~definici odkazovat na další symboly gramatiky. V~EBNF bychom výše uvedené pravidlo zapsali jako:

\begin{center}
\begin{BVerbatim}
yara_file = { include | import | rule }
\end{BVerbatim}
\end{center}

\par Důležitým konceptem pro zpracování výsledného AST modulem postprocessing je možnost pojmenování následníku neterminálního symbolu v~AST. To je realizováno pomocí funkce \inlinecode{field}. Díky pojmenování je možné dodat uzlům význam a~při využívání rozhraní knihovny tree-sitter klientskými aplikacemi (kterou je také právě knihovna Yaramod-v4) lze takto pojmenované následníky v~AST snadněji vyhledat. Gramatika jazyka YARA tohoto konceptu využívá v~mnoha případech. Na následujícím příkladu vidíme použití pojmenování u~definice neterminální symbolu \inlinecode{import}:

\bigskip
\begin{lstlisting}[language=PHP, keywords={token, choice, immediate, prec, repeat, choice, seq, field, left, const, new}, keywordstyle=\color{lstkeyword}]
import: $ => seq(
    "import",
    field("module", $.string_literal),
)
\end{lstlisting}
\bigskip

\par Zajímavou částí definic neterminálních symbolů, kde je rovněž pojmenování uzlů využito, jsou výrazy. Kromě využití rekurze pro jejich definici jsou zajímavé také používáním modulu \inlinecode{operators}, který je součástí programového řešení této práce. Modul, na rozdíl od knihovny tree-sitter samotné, umožňuje definovat operátory prostřednictvím vytváření instancí tříd v~jazyce JavaScript. Pokud bychom definovali např. operátor pro aritmetické sčítání způsobem standardním pro knihovnu tree-sitter, použili bychom zápis: 

\bigskip
\begin{lstlisting}[language=PHP, keywords={token, choice, immediate, prec, repeat, choice, seq, field, left, const, new}, keywordstyle=\color{lstkeyword}]
add: $ => prec.left(130,
    seq(
        field("lop", $._primary_expression), 
        "+", 
        field("rop", $._primary_expression)
    )
)    
\end{lstlisting}
\bigskip


\par Tato definice by byla plně funkční a~v~souladu s~dokumentací jazyka YARA. Metoda \inlinecode{prec.left} udává asociativitu operátoru, hodnota \inlinecode{130} udává jeho precedenci a~funkce \inlinecode{seq} popisuje sekvenci po sobě jdoucích symbolů, jež může být přerušena bílými znaky. Jako operandy pro sčítání jsou očekávány na obou stranách symboly \inlinecode{_primary_expression}, které by byly pojmenovány jako \inlinecode{lop} a~\inlinecode{rop}. 
\par Ačkoliv by pravidlem ve stejném tvaru mohly být definovány i~další binární operátory, gramatika by obsahovala velké množství pravidel v~tomto tvaru. Udržování těchto pravidel by při změnách a~přidávání dalších operátorů bylo náročné.
\par Z~tohoto důvodu byl vytvořen modul \inlinecode{operators}. Nabízí sadu tříd pro vytváření pravidel pro operátory. Díky ní můžeme definice pravidel pro operátory vydělit do tabulky mimo gramatiku a~tím ji výrazně zpřehlednit. Definice operátorů pomocí této tabulky je realizována následujícím způsobem:

\bigskip
\begin{lstlisting}[language=PHP, keywords={token, choice, immediate, prec, repeat, choice, seq, field, left, const, new}, keywordstyle=\color{lstkeyword}, classoffset=1, morekeywords={BinaryOperatorSym, Precedence}, otherkeywords={BinaryOperatorSym, Precedence}, keywordstyle=\color{lstotherkeyword}, classoffset=0]
const PRIMARY_EXPRESSION_OP = {
...
DIV: new BinaryOperatorSym("div", "\\\\", "$_primary_expression", new Precedence(140)),
ADD: new BinaryOperatorSym("add", "+", "$_primary_expression", new Precedence(130)),
SUB: new BinaryOperatorSym("sub", "-", "$_primary_expression", new Precedence(130)),
...
}
\end{lstlisting}
\bigskip

\par Modul \inlinecode{operators} využívá toho, že návratovými hodnotami pomocných funkcí knihovny tree-sitter jsou pouze objekty. Ty obsahují všechny potřebné informace pro vygenerování tabulky pro LR syntaktický analyzátor. Díky znalosti struktury těchto objektů vytvoří identický objekt, který by vznikl pomocí standardní definice operátoru. Jedná se tedy o~rámcové řešení pro definice operátorů, jež uživateli poskytuje abstraktnější pohled na definice operátorů než jejich standardní definice v~knihovně tree-sitter. Velkou výhodou oproti nim je udržitelnost a~rozšiřitelnost těchto tabulek. Úpravy nebo přidání operátoru znamenají pouze přidání nebo modifikaci řádků v~tabulce a~nikoliv manipulaci s~pravidly gramatiky.
\par Nástroj tree-sitter-cli kromě generování tabulek pro LR syntaktickou analýzu umožňuje také vizualizovat průběh syntaktické analýzy a~výsledný AST. To je velmi užitečné pro ladění gramatiky. Příklad derivačního stromu pro triviální pravidlo v~jazyce YARA, který byl vytvořen na základě výsledné implementace gramatiky, je možné vidět na obrázku \ref{fig:derivation_tree}.

\begin{figure}[ht]
    \centering
    \includegraphics[width=0.6\textwidth]{obrazky-figures/derivation_tree.pdf}
    \caption[Derivační strom vytvořený knihovnou tree-sitter na základě gramatiky pro jazyk YARA]{Derivační strom vykreslený knihovnou tree-sitter na základě gramatiky pro jazyk YARA\,--\,V~obdélníku v~dolní části obrázku je manuálně doplněn vstupní řetězec.}
    \label{fig:derivation_tree}
\end{figure}

\par Výstupní AST knihovna tree-sitter dokáže zobrazit jako řetězec ve formátu S-expression. Pro analýzu stejného řetězce jako je uveden na obrázku vypadá například AST tímto způsobem:

\bigskip
\begin{lstlisting}
(yara_file [0,0]-[4,0]
  (rule [0,0]-[3,1]
    head: (rule_head [0,0]-[0,9]
      id: (identifier [0,5]-[0,9]))
    body: (rule_body [0,10]-[3,1]
      condition: (bool_literal [2,8]-[2,12]))))
\end{lstlisting}
\bigskip

\par Ve výpisu je dobře vidět také použití pojmenování uzlů ve výstupním stromě pomocí funkce \inlinecode{field}, kdy například identifikátor pravidla je označen jako \inlinecode{id}. Díky tomu jej bude snazší lokalizovat při zpracování AST. Dále je vidět, že oproti derivačnímu stromu na obrázku \ref{fig:derivation_tree}, neobsahuje výstupní AST skryté symboly. V~neposlední řadě je u~jednotlivých uzlů uveden také rozsah, který ve zdrojovém kódu reprezentují (hodnoty v~hranatých závorkách). Například symbol \inlinecode{bool_literal} reprezentující \inlinecode{true} nebo \inlinecode{false} se nachází na řádku s~indexem 2 od bytu s~indexem 8 (včetně) až po byte s~indexem 12.

\par Vytvořená gramatika jazyka YARA je kompletní a~podporuje také rozšíření jazyka vytvořené firmou Avast. Knihovna tree-sitter je již nativně odolná vůči chybám ve vstupním řetězci a~nebylo z~tohoto důvodu zapotřebí tuto vlastnost na úrovni syntaktické a lexikální analýzy implementovat. 
\par Z~důvodu ještě vyšší odolnosti je gramatika tolerantní vůči neplatným řídícím sekvencím (escape sequences) v~řetězcových literálech. Kontrola, zda je řídící sekvence validní, je prováděna až modulem postprocessing. To zabraňuje zahození zbytku řetězce po nalezení první neplatné řídící sekvence. Rozdíl mezi tolerantním přístupem a~kontrolou platnosti řídících sekvencí je ilustrován následujícími dvěma výpisy fragmentu AST, který odpovídá řetězcovému literálu \verb|"a\bc\de\f"|:

\bigskip
\noindent\begin{minipage}{.48\textwidth}
\begin{lstlisting}
value: (string_literal [2,13]-[2,24]
 str: (string_literal_str [2,14]-[2,23]
  (string_esc_seq [2,15]-[2,17])
  (string_esc_seq [2,18]-[2,20])
  (string_esc_seq [2,21]-[2,23])))
\end{lstlisting}
\end{minipage}
\hfill
\begin{minipage}{.48\textwidth}
\begin{lstlisting}
value: (string_literal [2,13]-[2,24]
 str: (string_literal_str [2,14]-[2,15])
  (ERROR [2,15]-[2,23]
   (identifier [2,16]-[2,18])
   (identifier [2,19]-[2,21])
   (identifier [2,22]-[2,23])))
\end{lstlisting}
\end{minipage}
\bigskip

\par V~levém výpisu vidíme fragment AST pro daný řetězcový literál, který byl vytvořen na základě gramatiky implementované v~rámci modulu tree-sitter-yara. V~pravém výpisu je možné vidět alternativní řešení, které není tolerantní vůči nevalidním řídícím sekvencím. Vidíme, že ačkoliv ve druhém případě došlo správně k~identifikaci chyby, zbytek řetězce byl označen jako chybový uzel \inlinecode{ERROR}. Není tedy již možné jej snadno zkoumat, ačkoliv v~tomto případě obsahuje další nevalidní prvky. Díky aktuálnímu řešení je možné odhalit všechny nevalidní řídící sekvence ve výše uvedeném řetězcovém literálu v~rámci modulu postprocessing. 

\subsection{Postprocessing} \label{sec:syntactic2semantic}

\subsubsection{Tvorba výstupní reprezentace} \label{sec:output_model}
\par Jednou z~nejdůležitějších částí modulu postprocessing je třída \inlinecode{TSParserAdapter}. Instance této třídy, kterou si uchovává objekt typu \inlinecode{Yaramod}, má za úkol konvertovat AST, jenž je vytvářen modulem tree-sitter-yara, na výstupní reprezentaci (model) zdrojového kódu. Tato konverze je prováděna průchodem tohoto AST pomocí funkcí, které jsou součástí rozhraní knihovny tree-sitter.
\par Modul postprocessing v~rámci své metody \inlinecode{parse} nejprve zažádá tree-sitter-yara o~lexikální a~syntaktickou analýzu vstupního řetězce pomocí funkce \inlinecode{ts_parse_string}. Následně ve třech průchodech stromem vystaví výstupní reprezentaci. \par Během prvního průchodu jsou vyhledány syntaktické chyby identifikované syntaktickým analyzátorem. Tento průchod není příliš náročný, protože knihovna tree-sitter udržuje v~rámci uzlů v~AST atribut, jenž signalizuje výskyt chyby u~daného uzlu či u~jeho následníků. Pokud tedy ve zdrojovém kódu není přítomna žádná syntaktická chyba, kterou by tree-sitter-yara odhalil, je průchod ihned ukončen u~kořenového uzlu \inlinecode{yara_file}. 
\par Druhý průchod slouží k~identifikaci tzv. globálních uzlů v~syntaktickém stromě. Jsou jimi jednak komentáře, které se mohou vyskytovat takřka kdekoliv v~ AST, ale také uzly označující chybějící token. Knihovna tree-sitter totiž v~případě, že token zcela chybí, nevytvoří uzel označující syntaktickou chybu, ale doplní do AST listový uzel s~příznakem \inlinecode{MISSING}. Během druhého průchodu jsou tedy nalezeny jednak komentáře, které jsou následně uloženy do tabulky komentářů, a~také tyto uzly, s~nimiž je dále nakládáno jako se syntaktickými chybami.
\par Během třetího průchodu jsou nejdříve nalezena všechna pravidla a~konstrukce \inlinecode{include} či \inlinecode{import}. Ve všech třech případech je nejdříve vytvořen objekt příslušného typu, který je na základě informací dostupných z~AST inicializován. 
\par V~případě pravidel je inicializace nejkomplexnější, protože pravidlo může obsahovat řetězce, metadata a~interní proměnné. Tyto entity jsou dále souhrnně označované jako lokální symboly. Kromě toho v~pravidlech mohou být přítomny také výrazy. V~principu je s~těmito celky však nakládáno podobným způsobem jako s~pravidly ve zdrojovém souboru. Nejprve dojde k~identifikaci daného elementu v~podstromě reprezentující pravidlo, poté je vytvořen objekt příslušného typu (např. \inlinecode{String}) a~ten je dále inicializován. Na rozdíl od pravidla, které by bylo uloženo do tabulky udržované objektem typu \inlinecode{YaraFile}, jsou lokální symboly uchovávány v~tabulkách, jež si udržují sama pravidla. 
\par Co se týče hexadecimálních řetězců a~regulárních výrazů, dochází u~nich k~průchodu podstromem, který je reprezentuje, za účelem sémantických kontrol (např. v~\inlinecode{/a{n,m}/} musí platit $n \leq m$) a~dalších syntaktických kontrol (např. platnost řídících sekvencí). Ve výstupní reprezentaci jsou však hodnoty těchto řetězců uchovávány pouze v~serializované podobě jako datový typ \inlinecode{std::string}, kvůli času a~paměti, jež by byla potřebná pro vytváření komplexních struktur.
\par V~případě těch částí AST, které byly vytvořeny pomocí pravidel gramatiky využívající rekurzi, bylo zapotřebí využít rekurzi i~pro jejich zpracování v~modulu postprocessing. To je případ výrazů, které mohou tvořit hodnoty interních proměnných a~podmínky pravidel. Vzhledem k~velkému typu podtříd třídy \inlinecode{Expression}, je pro výběr správné funkce pro zpracování uzlu ve výrazu použita hashovací tabulka (\inlinecode{std::unordered_map}) mapující název uzlu na ukazatel na metodu adaptéru. V~ostatních případech je výběr metody realizován pomocí podmínek.
\par U~konstrukce \inlinecode{include} je nejprve ověřena existence vkládaného souboru. Následně se dle cesty k~souboru zkontroluje, zda buffer s~překladovými jednotkami udržovaný objektem typu \inlinecode{YaraSource} již soubor neobsahuje. Tím se zamezí dvojnásobnému vložení stejného souboru a~detekuje se cyklická závislost zdrojových souborů, která by mohla vést k~zacyklení programu. Poté dojde k~rekurzivnímu volání metody \inlinecode{parse}. 
\par Ačkoliv jsou dva průchody AST relativně drahou operací v~celém procesu tvorby výstupní reprezentace, díky prvnímu průchodu se ve~druhém již nemusí kontrolovat přítomnost globálních uzlů. To výrazně zpřehledňuje kód, protože ve spoustě případech není zapotřebí provádět iteraci přes potomky uzlu, ale je plně dostačující nalézt konkrétního potomka skrze jeho pojmenování (viz \ref{sec:nonterminals}). Nalézt uzel se specifickým označením umožňuje funkce \inlinecode{ts_node_child_by_field_name} z~knihovny tree-sitter.
\par Entity ve zdrojovém kódu si udržují pozici ve formě bytové offsetu ve zdrojovém kódu a~také v~podobě souřadnic, tvořených indexem řádku a~indexem bytu na daném řádku. Pro fungování aktualizace výstupní reprezentace je důležitá pouze první forma. Některé klientské aplikace (např. YLS) ale vyžadují druhou z~těchto forem. Udržování obou dvou forem pozic je na první pohled zbytečné a~komplikované. Z~tohoto důvodu byly původně souřadnice získávány dynamicky z~bytového offsetu, sekvenčním průchodem vstupního řetězce. To však způsobovalo značné zpomalení klientských aplikací, a~tudíž je nyní implementováno uchovávání pozic v~obou uvedených formátech.

\subsubsection{Syntaktické a sémantické kontroly}
\par Přestože je většina syntaktických kontrol zodpovědností modulu tree-sitter-yara, v~některých případech jsou delegovány na modul postprocessing. Těmito případy jsou kontrola platnosti řídících sekvencí v~řetězcích, kontrola přítomnosti non-ASCII znaků v~regulárních výrazech a~kontrola kolize identifikátorů s~klíčovými slovy.
\par Sémantické kontroly jsou prováděny v~rámci vytváření výstupní reprezentace objektem třídy \inlinecode{TSParserAdapter} a~třídami sémantického rozhraní knihovny. Probíhají během vytváření výstupní reprezentace, protože oddělení kontrol by vyžadovalo, uskutečnit další průchod výstupní reprezentací nebo AST. Sémantické kontroly byly implementovány pro odhalování následujících artefaktů ve zdrojovém kódu:

\begin{itemize}
    \item \textbf{Regulární výrazy}\,--\,Platnost rozsahu znaků (např. \inlinecode{/[a-b]/}), platnost intervalu vyjadřujícího opakování znaku (pořadí hraničních hodnot a~jejich hodnota)
    \item \textbf{Hexadecimální řetězce}\,--\,Platnost intervalu u~tzv. jumps (viz \ref{sec:yara-strings})
    \item \textbf{Modifikátory řetězců}\,--\,Duplicitní modifikátory, platnost argumentů modifikátorů
    \item \textbf{Přetečení}\,--\,Identifikace příliš vysokých číselných hodnot
    \item \textbf{Vkládání zdrojových souborů}\,--\,Existence vloženého souboru, identifikace dvojnásobného vložení jednoho zdrojového souboru
    \item \textbf{Identifikátory}\,--\,Kolize identifikátorů řetězců, interních proměnných a~pravidel
    \item \textbf{Modifikátory pravidla}\,--\,Duplicitní modifikátory pravidla
    \item \textbf{Výrazy}\,--\,Platnost referencí ve výrazech, datové typy, platnost rozsahů\dots
\end{itemize}

\par Ačkoliv nástroj YARA označuje za chybu také řetězec, který byl definován, ale není používán, knihovna tento artefakt nekontroluje. Jelikož se předpokládá, že vstupní zdrojový kód bude často nedokončený, sémantická kontrola tohoto typu by pro uživatele představovala zbytečný šum. Jedná se však o~kontrolu, kterou je díky sémantickému rozhraní možné implementovat v~rámci klientské aplikace (viz kapitola \ref{sec:examples}). 
\par Pro provádění některých z~výše uvedených kontrol je nezbytné udržování tabulek symbolů (viz další kapitola) a~také přístup ke specifikacím modulů. Moduly jsou načteny během instanciace objektu typu \inlinecode{Yaramod}, a~jsou deserializovány knihovnou JSON for Modern C++. Díky tomu sice trvá vytvoření tohoto objektu déle, ale v~rámci samotné statické analýzy již deserializace nepředstavuje žádnou dodatečnou režii. Zároveň se v~rámci každého modulu vytvoří cache mapující všechny obsažené symboly na jejich kontext. To opět zrychluje analýzu, neboť není nutné rekurzivně prohledávat jmenný prostor modulu. Stačí pouze provést vyhledávání v~této cache.
\par Typové kontroly jsou prováděny na základě kontextu získaného z~těchto modulů a~na základě popisu jednotlivých operátorů v~jazyce YARA. Většina operátorů má pevně stanovený datový typ návratové hodnoty a~není tedy zpravidla nutné používat typové odvozování ani syntézu. Výjimkou jsou aritmetické výrazy, kde se využívá právě typové syntézy. Datový typ aritmetického výrazu je určen na základě operandů daného operátoru.

\subsubsection{Tabulky entit a symbolů}
\par Nezbytnou součástí sémantického rozhraní jsou také tabulky, které umožňují uživateli přístup k~entitám ve zdrojovém souboru. Entitami jsou zde myšleny sémantické celky, např. pravidla, importy modulů apod. Modul postprocessing využívá tři typy tabulek. Společným rysem všech tří typů je indexování dle bytového offsetu, na němž se entita ve zdrojovém kódu nachází. Toto indexování je potřeba pro aktualizaci výstupní reprezentace a~také umožňuje uživateli přístup k~entitám v~pořadí, v~jakém se ve zdrojovém kódu objevují. To může být důležité např. pro provádění statické analýzy klientskými aplikacemi.
\par První typ tabulky indexuje prvky pouze pomocí bytového offsetu. Tabulka je realizována skrze datovou strukturu \inlinecode{std::multimap}. V~této tabulce jsou uchovávány objekty reprezentující chyby (podtřídy třídy \inlinecode{Error}), komentáře a~neplatné entity (např. pravidlo s~duplicitním identifikátorem).
\par Druhý typ tabulky kromě indexování dle bytového offsetu indexuje položky také pomocí identifikátoru, který je v~rámci tabulky jednoznačný. Tento typ tabulky je implementován pomocí datových struktur \inlinecode{std::multimap} a~\inlinecode{std::unordered_map}. Slouží k~uchovávání všech entit s~jednoznačným identifikátorem, kterými jsou pravidla, řetězce a~interní proměnné.
\par Třetí typ tabulky dovoluje duplikaci identifikátoru. To je využíváno pro metadata pravidel a~objekty typu \inlinecode{Import}\footnote{Jazyk YARA dovoluje duplicitní identifikátory metadat a~vícenásobný import jednoho modulu.}. Realizovány jsou podobně jako předchozí typ tabulek s~tím rozdílem, že v~\inlinecode{std::unordered_map} je uložen celý vektor ukazatelů namísto ukazatelů samotných. 
\par Tabulky posledních dvou typů zároveň slouží jako tabulky symbolů pro sémantické kontroly. Indexace dle identifikátorů umožňuje entity rychle vyhledávat, což se využívá pro detekci duplicitních identifikátorů a~kontroly, zda je symbol v~době použití definován.

\subsubsection{Aktualizace výstupní reprezentace}
\par Před aktualizací výstupní reprezentace jsou uživatelem nahlášeny změny ve zdrojovém kódu metodou \inlinecode{edit}. Tyto změny jsou nahlášeny ve formátu \inlinecode{<range, text>}, kde \inlinecode{range} jsou souřadnice začátku a~konce modifikovaného rozsahu a~\inlinecode{text} je nový obsah této části. Pokud je například vložen řetězec \inlinecode{start} na úplný začátek dokumentu, jsou tyto údaje \inlinecode{<((0,0),(0,0)), "start">} . Pokud dojde ke smazání tohoto řetězce, jsou modifikace nahlášeny jako \inlinecode{<((0,0),(0,5)), "">}. Tento formát odpovídá informacím, které poskytuje LSP v~rámci zprávy o~změně dokumentu~\cite{Kelner::2022::lsp}. Jak už bylo uvedeno výše, modifikace jsou uchovány v~bufferu a~jsou aplikovány až ve chvíli, kdy si uživatel vyžádá samotnou aktualizaci výstupní reprezentace spuštěním inkrementální analýzy.

\begin{figure}[h]
    \centering
    \includegraphics[width=0.9\textwidth]{obrazky-figures/ast_update.pdf}
    \caption[Proces aktualizace AST během inkrementální statické analýzy v~knihovně Yaramod-v4]{Proces aktualizace AST během inkrementální statické analýzy\,--\,Z~výstupní reprezentace je nejprve extrahován původní AST a~seznam editací provedených uživatelem. Poté dojde k~označení změn v~AST a~ten je předán modulu tree-sitter-yara, který ho aktualizuje skrze analyzátory z~knihovny tree-sitter. Tomuto modulu je zároveň dodán také aktualizovaný vstupní řetězec.}
    \label{fig:ast_update}
\end{figure}

\par Během samotné aktualizace jsou nejprve modifikace předány modulu tree-sitter-yara pomocí funkce \inlinecode{ts_tree_edit}, poskytované knihovnou tree-sitter. Následně je provedena inkrementální lexikální a~syntaktická analýza zajištěná opět touto knihovnou (viz obrázek \ref{fig:ast_update}). Poté je zapotřebí aktualizovat také výstupní model zdrojového kódu. V~principu se jedná o~smazání modifikací dotčeného pravidla, konstrukce \inlinecode{include} nebo \inlinecode{import} a~vytvoření aktuálního objektu. Bylo by pochopitelně možné provádět aktualizace s~ještě jemnější granularitou. To by však zároveň vedlo ke zvýšení režie potřebné pro aktualizaci. Proto v~současné době knihovna Yaramod-v4 provádí aktualizaci pouze na úrovni výše uvedených entit, tedy na úrovni komponent objektu třídy \inlinecode{YaraFile}. 
\par Aktualizace je ve zjednodušené podobě popsána na obrázku \ref{fig:result_update}. Základní strukturou pro její provedení je seznam rozsahů bytových offsetů, v~nichž mají být komponenty výstupní reprezentace aktualizovány. Položky do tohoto seznamu jsou získávány několika způsoby.
V~cyklu jsou do tohoto seznamu přidávány explicitně editované oblasti zdrojového kódu uživatelem. V~této oblasti jsou všechny entity smazány a~ty, které se nacházejí za modifikovanou oblastí jsou posunuty o~počet znaků, které byly vloženy, resp. smazány. Průběžně je tento seznam doplňován o~pozice závislých entit na právě smazaných entitách. To je možné díky objektu typu \inlinecode{YaraSource}, který si udržuje záznamy o~vzájemných závislostech těchto entit. 
\par Pokud dojde např. ke smazání pravidla, toto pravidlo v~rámci svého destruktoru notifikuje rodičovský objekt třídy \inlinecode{YaraSource}. Ten pomocí záznamů zjistí, zda některé z~jiných pravidel používalo v~rámci svojí definice právě smazané pravidlo, tzn. je na něm závislé. Pokud ano, přidá oblast, kde se toto závislé pravidlo nachází, do speciálního bufferu. Tyto oblasti jsou opět uchovávány ve formě rozsahů bytových offsetů.
\par Z~tohoto bufferu jsou během provádění aktualizace přesunuty do seznamu rozsahů, u~nichž má dojít k~aktualizaci. S~každým dalším rozsahem, který byl explicitně modifikován uživatelem jsou rovněž posunuty o~příslušný počet znaků také položky v~tomto seznamu, aby odpovídaly vždy aktuálním pozicím.
\par Po vyčtení všech explicitně modifikovaných rozsahů jsou do seznamu přidány také pozice nových uzlů v~AST navráceného modulem tree-sitter-yara. To je možné díky další funkci z~knihovny tree-sitter, a~sice \inlinecode{ts_tree_get_changed_ranges}. 
\par Po získání všech rozsahů určených k~aktualizaci jsou seřazeny tak, aby jejich pořadí odpovídalo pořadí, v~jakém se nachází ve zdrojovém dokumentu. Následně jsou spojeny rozsahy, které se překrývají, aby nedošlo k~vícenásobné aktualizaci. Takto připravený seznam je poté v~cyklu procházen a~pro každý rozsah, který se v~něm nachází, je nalezena sekvence uzlů v~AST, jež svojí pozicí ve zdrojovém kódu odpovídá daném rozsahu. Díky zvolené granularitě aktualizací mohou být v~této sekvenci pouze uzly reprezentující pravidlo, konstrukci \inlinecode{include} nebo konstrukci \inlinecode{import}.

\begin{figure}[t]
    \centering
    \includegraphics[width=\textwidth]{obrazky-figures/result_update.pdf}
    \caption[Proces aktualizace výstupní reprezentace kódu během inkrementální statické analýzy v~knihovně Yaramod-v4]{Proces aktualizace výstupní reprezentace kódu (VR) během inkrementální statické analýzy v~knihovně Yaramod-v4\,--\,Nejprve jsou prostřednictvím seznamu editací, závislostí komponent a~aktualizovaného AST nalezeny modifikované komponenty. Ty jsou z~VR odstraněny a~je vytvořen seznam modifikovaných rozsahů v~dokumentu. Poté jsou nalezeny podstromy v~aktualizovaném AST, jež rozsahům odpovídají, a~na základě nich jsou vytvářeny nové, aktuální komponenty pro VR.}
    \label{fig:result_update}
\end{figure}


\par Pro každý uzel v~této sekvenci jsou nejdříve vymazány zbylé entity, které se nachází na pozici tohoto uzlu. Následně je způsobem, jenž je popsán v~kapitole \ref{sec:output_model}, konvertován podstrom AST, pro nějž je tento uzel kořenem, do podoby výstupní reprezentace.
\par Proces aktualizace výstupní reprezentace je prováděn pro každý objekt typu \inlinecode{YaraFile} uložený v~bufferu daného objektu třídy \inlinecode{YaraSource}. Aktualizace je však prováděna pouze tehdy, pokud je to zapotřebí, tzn. došlo u~daného objektu typu \inlinecode{YaraFile} k~explicitní modifikaci či byla smazána některá z~entit, na níž byla komponenta tohoto objektu závislá.

\par Alternativa k~výše uvedenému přístupu by byla provádět inkrementálním způsobem pouze lexikální a~syntaktickou analýzu skrze knihovnu tree-sitter. Výstupní reprezentace by v~tomto případě byla pokaždé kompletně smazána a~znovu vystavěna. Docházelo by tak ve velké míře k~dealokaci a~alokaci objektů, jež reprezentují pravidla, importy modulů a~vkládání souborů, u~nichž není aktualizace zapotřebí. To by však také znamenalo podstatně jednodušší implementaci. Na to, zda by byl tento přístup výhodný, či nikoliv, odpovídá tabulka \ref{tab:method_comparison}. Z~tabulky vidíme, že aktuální řešení i~přes svoji relativní složitost je z~hlediska času výrazně efektivnější než popsaná alternativa, jak pro malou sadu pravidel, tak pro rozsáhlou. 
\par Sledování závislostí mezi entitami však může způsobit, že i~při dosavadním řešení mohou být aktualizovány všechny entity ve výstupní reprezentaci. Tato situace může nastat například v~případě, že všechna pravidla ve zdrojovém kódu budou používat jedno jiné pravidlo, u~kterého dojde k~modifikaci. Ačkoliv tato situace běžně nenastává, může k~ní dojít a~výsledky by v~tomto případě odpovídaly alternativnímu řešení (v~tabulce jako Full update).

\bgroup
\def\arraystretch{\tabpadding}
\begin{table}[h]
    \centering
    \begin{tabular}{c|c c|c c|}
        \multirow{2}{*}{Sada pravidel} & \multicolumn{2}{c|}{Partial update (s)} & \multicolumn{2}{c|}{Full update (s)} \\
        & \small{Analýza} & \colorbg{\small{Reanalýza}} & \small{Analýza} & \colorbg{\small{Reanalýza}}\\
        \hline
        \inlinecode{small.yar} (11,2\,kB) & 0,018 & \colorbg{0,0003} & 0,013 & \colorbg{0,003} \\
        %\hline
        \inlinecode{big.yar} (14,4\,MB) & 4,364 & \colorbg{0,037} & 4,050 & \colorbg{1,004} \\
        \end{tabular}
        \caption{Porovnání efektivity aktuálního řešení a~alternativního řešení z~hlediska potřebného času\,--\,Sloupec \textit{Partial update} označuje současné řešení. Sloupec \textit{Full update} označuje alternativní řešení, kdy je celá výstupní reprezentace kompletně smazána a~poté znovu vytvořena. Použité testovací sady pravidel jsou blíže popsány v~kapitole \ref{sec:results}.}
    \label{tab:method_comparison}
\end{table}
\egroup



\section{Integrace s projektem Yara Language Server}
\par V~rámci projektu YLS byla provedena integrace Yaramod-v4 ve třech jeho komponentách, z~nichž každá doposud využívala dosavadní řešení. Každá z~těchto částí YLS poskytuje specifickou funkcionalitu, pro jejíž implementaci je zapotřebí využívat sémantické rozhraní knihovny. V~následujícím seznamu jsou uvedeny všechny tyto části projektu YLS spolu s~funkcionalitou, kterou poskytují, a~stručným popisem úlohy nového řešení v~dané části:

\begin{enumerate}
    \item \textbf{Linting}\,--\,Poskytuje uživatelům syntaktické a~sémantické kontroly zdrojového kódu. Je implementován pomocí čtení tabulky s~chybami, která je součástí rozhraní knihovny Yaramod-v4. Pro větší uživatelskou přívětivost poskytuje také stručný popis chyby a~její pozici v~dokumentu, díky které je možné ji snadno vyhledat. 
    \item \textbf{Document Symbol}\,--\,Vytváří strukturovaný pohled na editovaný zdrojový kód. To zahrnuje jednak vyváření osnovy dokumentu (outline) a~také navigaci na symboly uvedené v~osnově. Úkolem knihovny Yaramod-v4 je zde poskytovat tabulky s~těmito symboly a~jejich pozice ve zdrojovém kódu. Oproti dosavadnímu řešení, jsou v~osnově uvedeny také konstrukce \inlinecode{import} a~\inlinecode{include}, díky kterým může uživatel snadno vidět, které moduly importoval a~které zdrojové soubory do aktuálního dokumentu vložil.
    \item \textbf{Completion}\,--\,Umožňuje uživateli doplnit chybějící znaky rozepsaného symbolu. To je realizováno pomocí seznamu všech symbolů, které mají prefix shodný s~rozepsaným řetězcem\footnote{Výjimkou jsou řetězce v~pravidlech, kde je ignorován počáteční znak \inlinecode{$}.}. Yaramod-v4 (stejně jako dosavadní řešení) se na této funkcionalitě podílí pouze částečně. Nejdříve je pomocí výsledků analýzy prováděné touto knihovnou určeno pravidlo, v~němž se nachází kurzor uživatele. Poté je seznam symbolů obohacen o~položky z~tabulek sémantického rozhraní, která mohou představovat rozepsaný symbol (dříve definovaná pravidla, řetězce v~aktuálním pravidle apod.).   
\end{enumerate}

\par Zcela novým konceptem v~YLS je průběžné nahlašování změn v~dokumentu, které je  potřebné zajistit pro podporu inkrementálního přístupu k~analýze zdrojového kódu. Nahlašování změn je realizováno pomocí události typu \inlinecode{TEXT_DOCUMENT_DID_CHANGE}, která je součástí implementace LSP knihovnou pygls~\cite{Pygls}. Parametry této události jsou editovaný interval v~dokumentu a~nový text, který se v~tomto intervalu po editaci vyskytuje, což přesně odpovídá argumentům jedné z~implementací metody \inlinecode{edit} v~Yaramod-v4. Díky tomu je možné v~modelu programu změnu snadno uchovat a~při další statické analýze z~něj vycházet. Výše uvedená funkcionalita YLS, kterou Yaramod-v4 pomáhá zajišťovat, je demonstrována skrze snímky obrazovky z~editoru VSCode, jež jsou dostupné v~příloze \ref{sec:screenshots}.
\par YLS po integraci s~knihovnou Yaramod-v4 má také dvě známá omezení. Prvním z omezení je nekompatibilita těchto dvou projektů při používání non-ASCII znaků ve zdrojovém kódu, respektive znaků, jež jsou kódovány pomocí více než jednoho bytu. Yaramod-v4 nahlíží na vstupní řetězec jako na sekvenci bytů, ale YLS jej považuje za sekvenci znaků v~příslušném kódování. Při smazání znaku, který je kódován pomocí více bytů, YLS nahlásí editaci, jenž odpovídá smazání pouze jednoho znaku, což Yaramod-v4 interpretuje jako jeden byte a~smaže tedy pouze jeden byte daného znaku. To vede na falešné ohlášení syntaktické chyby. Zpětně se tato nekompatibilita projevuje při poskytování pozicí v dokumentu (např. při navigaci na definici symbolu pomocí funkcionality Document Symbol) a~to i~u~dosavadního řešení. Používání non-ASCII znaků v~jazyce YARA však není běžné\footnote{Existuje více možností, jak tyto znaky popsat bez jejich explicitního použití. Od verze jazyka YARA~4.1 jsou při explicitním použití navíc označovány za chybu samotným nástrojem YARA (viz \url{https://github.com/VirusTotal/yara/wiki/Unicode-characters-in-YARA}).}.
\par Druhým omezením je více rozpracovaných souborů, které jsou vzájemně provázány pomocí \inlinecode{include}. Analýza vložených zdrojových souborů probíhá na základě uložených souborů na disku, a~tudíž neuložené změny nejsou reflektovány. Z tohoto důvodu je jako vstupní soubor sady pravidel (\inlinecode{entry_file}) vždy považován ten, který byl otevřen jako první či je právě editovaný.


\section{Testování}
\par Testování knihovny Yaramod-v4 probíhalo v~několika fázích. První fází bylo průběžné automatizované testování modulů tree-sitter-yara a~postprocessing. Cílem této fáze testování, která probíhala souběžně s~vývojem, bylo odzkoušet nově přidanou funkcionalitu těchto modulů a~zároveň se přesvědčit, že její přidání nezpůsobilo chyby v~jejich již implementovaných částech. 

\par Testování modulu tree-sitter-yara bylo prováděno automatizovaně pomocí nástroje tree-sitter-cli. Tento nástroj umožňuje vytvářet testovací případy skrze specifikaci vstupního řetězce a~očekávaného výstupního AST ve formátu S-expression. Tímto způsobem bylo vytvořeno 173 testovacích případů.

\par Kromě tohoto řešení byl také jako součást této práce vytvořen testovací skript s~názvem \inlinecode{depth_test.py} pro hloubkové testování gramatiky. Tento skript byl implementován pomocí rozhraní knihovny tree-sitter pro jazyk Python. Jeho výhodou je, že kontroluje také přítomnost nepojmenovaných tokenů (závorek, klíčových slov atd.), které jsou ve výstupním AST skryty. Díky tomuto skriptu je možné gramatiku ještě důkladněji otestovat. Na druhou stranu je tvorba testovacích případů pro něj o~něco náročnější, neboť v~očekávaném výsledku, jenž je opět specifikován pomocí S-expression, musí být přítomny všechny tokeny. Z~toho důvodu bylo pro tento skript vytvořeno pouze 10 testovacích případů.

\par Modul postprocessing byl testován jednotkovými testy, jež byly vytvořeny prostřednictvím frameworku GoogleTest. Testy jsou rozděleny do čtyř testovacích sad, aby bylo možné se při testování zaměřit vždy na určitou část tohoto modulu. Jednotkové testy byly také spouštěny pod nástrojem Valgrind za účelem eliminace úniků paměti. Počet všech jednotkových testů modulu postprocessing je 114. 

\par Po dokončení programového řešení byly manuálně provedeny integrační testy. V~rámci těchto testů byla prováděna analýza rozsáhlé sady pravidel (sada \inlinecode{big.yar} z~kapitoly \ref{sec:results}) v~jazyce YARA používané firmou Avast, čímž došlo k~ověření, zda jsou opravdu všechny elementy tohoto jazyka knihovnou podporovány. Současně byly prováděny také testy na menších zdrojových souborech opět pod nástrojem Valgrind. 

\par Zároveň v~této fázi vývoje probíhalo také profilování. To bylo realizováno skrze nástroj \textit{gprof} a~byla tímto nástrojem hledána úzká místa ve zdrojovém kódu. Profilování bylo prováděno nad sadou \inlinecode{big.yar}, jež je blíže popsána v~\ref{sec:results}. Díky profilování bylo zavedeno několik vylepšení jako např. cache symbolů v~modulech nebo uchovávání souřadnic symbolů v~dokumentu. 

\par Po vytvoření rozhraní pro jazyk Python bylo použito rámcové řešení pytest pro otestování i~této části knihovny. Pro otestování integrace s~projektem YLS byly využity existující integrační testy tohoto projektu, které byly doplněny o~několik nových testů. Jejich cílem bylo ověřit fungování poskytování zpětné vazby uživateli během editace zdrojového kódu. Pro jejich snazší parametrizaci byl použit modul \textit{difflib} pro jazyk Python. Testovací případ se díky tomuto modulu sestává pouze z~originální podoby zdrojového kódu, jeho upravené verze a~očekávaných diagnostických zpráv. Funkce \inlinecode{ndiff} z~tohoto modulu identifikuje rozdíly mezi dvěma verzemi zdrojového kódu a~ty tak mohou být znak po znaku nahlašovány YLS jako editace zdrojového kódu. 

\par Tímto mechanismem je simulováno psaní uživatele na klávesnici. Po nahlášení všech změn jsou zkontrolovány poslední navrácené diagnostické zprávy a~ty jsou porovnány s~očekávanými výsledky. První sada testů se zaměřuje na modifikaci pouze jednoho souboru. Druhá již kontroluje výsledky při modifikaci vícero zdrojových souborů, které jsou propojeny pomocí konstrukce \inlinecode{include}. 


\section{Příklady použití} \label{sec:examples}
\par Součástí programového řešení bakalářské práce jsou také tyto jednoduché programy, které demonstrují použití knihovny Yaramod-v4:

\begin{itemize}
    \item \textbf{\inlinecode{reparsing.cpp}}\,--\,Demonstrace základního použití knihovny Yaramod-v4 pro statickou analýzu pravidel v~jazyce YARA a~pro jejich opětovnou inkrementální analýzu. Během obou běhů analýzy jsou měřeny spotřebované zdroje pomocí funkce \inlinecode{getrusage}. Tento program byl použit také pro porovnání knihovny Yaramod-v4 s~dosavadním řešením (viz kapitola \ref{sec:results}).

    \item \textbf{\inlinecode{observing_visitor.cpp}}\,--\,Ukázka využití návrhového vzoru visitor, jenž je součástí sémantického rozhraní, v~C++. Tento program umožňuje vizualizovat pravdivostní výrazy pomocí stromů, které vypisuje na terminál nebo na příkazový řádek. 
    
    \item \textbf{\inlinecode{calculator.py}}\,--\,Skript pro jazyk Python využívající sémantické rozhraní pro přístup k~výrazům (návrhový vzor visitor). Provede vyhodnocení aritmetických výrazů v~podmínkách pravidel v~zadaném zdrojovém souboru.
    
    \item \textbf{\inlinecode{unused_strings.py}}\,--\,Ukázka použití knihovny Yaramod-v4 pro identifikaci definovaných, ale nepoužitých řetězců. Jedná se opět o~skript v~jazyce Python, jenž využívá sémantické rozhraní knihovny.  

\end{itemize}


\section{Metriky kódu} \label{sec:metrics}
\par Programové řešení se skládá z vícero částí, které jsou různě rozsáhlé a~využívají různé jazyky. Pro lepší přehled jsou proto v~tabulkách~\ref{tab:metrics-tab} a~\ref{tab:metrics-tab-tests} uvedeny počty řádků ve zdrojových kódech jednotlivých částí programového řešení. Uvedené počty byly určeny pomocí nástrojů \textit{Tokei}\footnote{Oficiální repozitář projektu Tokei: \url{https://github.com/XAMPPRocky/tokei}} a~\textit{wc}. U~projektu YLS byly modifikované řádky vypočteny pomocí příkazu \inlinecode{git diff}.


\bgroup
\def\arraystretch{\tabpadding}
\begin{table}[h]
    \centering
    \begin{tabular}{c|c|c|c}
        %\multicolumn{4}{c}{Zdrojové kódy}\\
        %\hline
        \textbf{Část programového řešení} & \textbf{Jazyk} & \textbf{Kód} & \textbf{Komentáře}\\
        \hline
        Postprocessing & C/C++ & 12795 & 2835\\
        \hline
        Tree-sitter-yara & JavaScript & 1082 & 113\\
        \hline
        YLS (pouze upravená část) & Python & 509  & 42\\
        \hline
        \multirow{2}{*}{Příklady použití} & C/C++ & 320 & 115\\
        & Python & 134 & 75\\
    \end{tabular}
    \caption{Počty řádků ve zdrojových kódech funkčních částí programového řešení}
    \label{tab:metrics-tab}

\end{table}
\egroup
    
    \bigskip

\bgroup
\def\arraystretch{\tabpadding}
\begin{table}[h]
    \centering
    \begin{tabular}{c|c|c|c}
        %\multicolumn{4}{c}{Testy a testovací případy}\\
        %\hline
        \textbf{Testovaná část} & \textbf{Jazyk} & \textbf{Kód} & \textbf{Komentáře}\\
        \hline
        \multirow{2}{*}{Tree-sitter-yara} & Plain Text & 8439 & --\\
        & Python & 232 & 20\\
        \hline
        \multirow{2}{*}{Postprocessing} & C/C++ & 2528 & 36\\
        & Python & 778 & 19\\
        \hline
        YLS (pouze nové testy) & Python & 310 & 17 \\
    \end{tabular}

    \caption{Počty řádků ve zdrojových kódech, které tvoří testy a~testovací případy pro funkční části}
    \label{tab:metrics-tab-tests}
\end{table}
\egroup


%\par Pro efektivnější implementaci inkrementálního LR syntaktického analyzátoru použijeme open source nástroj \textit{tree-sitter-cli}, který umožňuje generovat tabulky pro provádění tohoto typu analýzy na základě uživatelsky definované bezkontextové gramatiky. Další částí tohoto nástroje je knihovna \textit{tree-sitter}, samotný syntaktický analyzátor, který ve spojení s~vygenerovanou tabulkou umožňuje provádět analýzu kódu v~příslušném jazyce \cite{tree_sitter}.
%\par Výhodou nástroje \textit{tree-sitter-cli} je, že kromě generování poskytuje také základní nástroje pro jednotkové testování gramatiky a~její ladění (vizualizaci syntaktických stromů a~zásobníku analyzátoru).
%\par Jelikož cílem projektu není tvorba rámcového řešení pro generování inkrementálních syntaktických analyzátorů, bude toto řešení pro tento účel pravděpodobně dostačující. Mělo by umožnit více se soustředit na formulaci gramatiky jazyka YARA a~nezabývat se laděním vlastního, obecného inkrementálního syntaktického analyzátoru.
%\par Po převedení pravidel v~jazyce YARA do podoby abstraktního syntaktického stromu, budou případně některé jeho části upraveny\footnote{Pokud by došlo k~situaci, že by některou z~konstrukcí jazyka nebylo možné zpracovat pouze pomocí syntaktického analyzátoru} a~poté budou provedeny nad ním sémantické kontroly. Spolu s~rozhraním knihovny bude tento proces součástí do velké míry samostatného modulu knihovny.
%\par Celé programové řešení se tedy bude skládat ze dvou relativně nezávislých modulů, z~nichž první (\textit{tree-sitter-yara}) bude analyzovat zdrojové soubory a~převádět je do podoby abstraktního syntaktického stromu. Druhý z~těchto modulu bude nad tímto stromem provádět sémantické kontroly a~exportovat vysokoúrovňové rozhraní pro práci s~pravidly. Celý proces je znázorněn na následujícím schématu:

