\par První statické analyzátory zdrojových kódů se začaly objevovat již v~šedesátých letech minulého století jakožto způsob, jak optimalizovat překladače a~kód, který generují. Později nalezly uplatnění také v~rámci integrovaných vývojových prostředí pro hledání chyb a~poskytování další podpory pro tvorbu zdrojových kódů~\cite[str.~1\,--\,3]{Moller::2012::static}.
\par Pro automatizovanou statickou analýzu zdrojového kódu však musí být kód nejprve převeden do podoby, která je pro jeho další zpracování vhodná. Typicky se jedná o~abstraktní syntaktický strom, jenž je vytvořen syntaktickým analyzátorem na základě vstupního řetězce a~pravidel gramatiky daného jazyka. Zejména tento proces, ale i~jiné principy statické analýzy vycházejí z~principů využívaných právě v~kompilátorech~\cite[str.~72]{Chess::2007::secure}.
\par Pro účely použití v~textových editorech a~integrovaných vývojových prostředích je vhodné, aby statická analýza byla prováděna v~době editace kódu. To ale přináší další výzvy v~podobě zvýšených nároků na rychlost provádění analýzy, a~navíc je zapotřebí kód reanalyzovat při~každé jeho modifikaci. Jedno z~možných řešení je provádět syntaktickou analýzu inkrementálně, tedy za použití již vytvořených datových struktur. Tento přístup výrazně šetří strojový čas, neboť není nutné znovu analyzovat části zdrojového kódu, jež zůstaly nezměněny~\cite{Wagner::1998::practical}.
\par V~této kapitole jsou shrnuty obecné informace o~inkrementální lexikální, syntaktické a~sémantické analýze, jelikož  tyto metody jsou předpokladem pro provádění inkrementální statické analýzy. Obecně o~tomto způsobu zkoumání vlastností kódu pojednává začátek této kapitoly. V~jejím závěru je shrnut význam inkrementální statické analýzy formálních jazyků v~integrovaných vývojových prostředích a~textových editorech.
\par Použití těchto metod pro podporu tvorby zdrojových souborů v~jazyce YARA je ostatně jednou z~hlavních motivací pro tvorbu nové programové knihovny, jež je v~rámci této práce prezentována.

\section{Statická analýza kódu}
\par Pojmem statická analýza kódu rozumíme podle studie od P. Emanuelssona
\textit{\uv{automatizované metody, jak zkoumat běhové vlastnosti kódu bez jeho provádění}}~\cite[str.~7]{Emanuelsson::2008::comparative}.
\par Jinými slovy, se jedná o~určování stavů programu, které mohou nastat v~době jeho běhu, pouze na základě znalosti zdrojového kódu~\cite[str.~9]{Artho::2005::combined}. Tento proces je obvykle prováděn prostřednictvím jeho vhodné \textit{interní reprezentace} neboli \textit{modelu programu}. Díky němu můžeme vytvářet abstrakce stavů, jež sice nenesou všechny informace o~jejich reálných ekvivalentech, ale díky tomu je snazší s~nimi manipulovat a~korektně je popsat~\cite[str.~1]{Ernst::2003::WODA}.
\par Většinou se tato metoda analýzy kódu zaměřuje na takové vlastnosti, které mohou v~případě chyb vyústit v~násilné ukončení programu či~navrácení neplatných výsledků. Související publikace do třídy chyb odhalovaných statickou analýzou často neřadí syntaktické chyby a~v některých případech ani jednoduché sémantické chyby (např. typové chyby)~\cite[str.~7]{Emanuelsson::2008::comparative}.
\par V~rámci této práce však budeme považovat hledání syntaktických a~sémantických chyb rovněž za zodpovědnost statického analyzátoru. Důvodem je, že nezbytnou součástí většiny statických analyzátorů je právě také převod zdrojového kódu do vhodné interní reprezentace (např. prostřednictvím lexikální a~syntaktické analýzy). Při~této konverzi často probíhají i~syntaktické a~sémantické kontroly. Z~pohledu uživatele tedy dochází k~hledání těchto chyb a~statický analyzátor by měl zajistit, aby o~nich byl uživatel informován.
\par Komplementární technikou ke statické analýze je tzv. dynamická analýza. Ta zjišťuje vlastnosti kódu jeho prováděním. Jedná se například o~testování a~profilování. U~tohoto typu analýzy není zapotřebí vytvářet žádné abstrakce programu, protože zkoumáme skutečné stavy, které během jeho provádění nastávají~\cite[str.~1]{Ernst::2003::WODA}.
\par Ačkoliv dynamická analýza poskytuje přesnější údaje, není její provádění nikterak zobecněno tak, aby byly brány v~potaz i~běhy s~jinými vstupními proměnnými. Navzdory tomu mohou být tyto proměnné použity v~budoucnu, čímž se program dostává do neotestovaného, potenciálně nebezpečného stavu. Kvalita testování se tedy významně odvíjí od kvality testovací sady. Statická analýza naproti tomu produkuje kvůli abstrakci méně přesný, více zobecněný popis stavu, jenž je však platný pro více konkrétních stavů programu. Kvalita statické analýzy kódu právě závisí na zvoleném způsobu abstrakce stavů~\cite[str.~1\,--\,2]{Ernst::2003::WODA}.
\par Je důležité mít na paměti, že ani na základě jednoho výše uvedeného typu analýzy nelze vyloučit přítomnost chyb v~kódu. Díky statické analýze kódu můžeme ale zredukovat potřebu jeho testování a~odhalit některé artefakty, jež jsou testováním a~dynamickou analýzou obecně těžko odhalitelné~\cite[str.~7]{Emanuelsson::2008::comparative}.
\par Podle studie od P. Emanuelssona~\cite{Emanuelsson::2008::comparative} se může jednat například o~identifikaci a~eliminaci tzv. \textit{mrtvého kódu}\footnote{Tj. část programu, jež nebude za žádných okolností prováděna nebo je prováděna zbytečně.} nebo o~identifikaci některých nedovolených operací (dělení nulou, přístup k~prvku za hranicemi pole\dots).

\subsection{Funkce statické analýzy}
\par Kromě hledání artefaktů ve zdrojovém kódu uvedených v~předchozí kapitole nám metody statické analýzy mohou poskytnout další užitečné funkce, které zlepšují kvalitu kódu a~šetří programátorům čas. Například kniha Secure Programming with Static Analysis uvádí následující funkce~\cite[str.~24]{Chess::2007::secure}:

\begin{itemize}
    \item \textbf{Kontroly datových typů}\,--\,Kontrola, zda datový typ hodnoty nebo proměnné odpovídá očekávanému datovému typu (např. při~přiřazování, ve výrazech\dots). Do jaké míry je schopen tuto kontrolu provést statický analyzátor, záleží na konkrétním jazyce, pro který je určen. Statické analyzátory s~touto funkcí jsou hojně využívány v~překladačích při~statických typových kontrolách~\cite[str.~387]{Aho::2006}.
    \item \textbf{Kontrola stylu}\,--\,Statický analyzátor se snaží minimalizovat přítomnost některých pochybných programátorských konstrukcí, které by mohly mít negativní vliv na čitelnost a~udržovatelnost kódu. 
    \item \textbf{Podpora pro snazší pochopení programu}\,--\,Tato funkcionalita pomáhá uživatelům mít přehled ve zdrojových kódech, což je také důvod, proč je alespoň částečně implementována ve většině moderních integrovaných vývojových prostředích. Jejím typickým příkladem je navigace na deklaraci či~definici funkce.
    \item \textbf{Verifikace programu}\,--\,Při~formální verifikaci programu je statickým analyzátorem prováděna kontrola programu vůči~jeho specifikaci.
    \item \textbf{Hledání chybových idiomů}\,--\,Jedná se o~identifikaci částí kódů, které často vedou k~chybě a~které mohou vykazovat chování, jež programátor pravděpodobně neočekává.
    \item \textbf{Kontrola bezpečnosti kódu}\,--\,Může se jednat například o~identifikaci potenciálního přístupu mimo alokovanou paměť a~jiných podobných zranitelností programu. 
\end{itemize}

\par Funkcionalita poskytovaná statickými analyzátory se liší dle konkrétní aplikace. Na~základě jejich konkrétního použití můžeme statické analyzátory a~jejich metody rozdělit do tří kategorií~\cite[str.~2]{Moller::2012::static}:

\begin{enumerate}
    \item \textbf{Statická analýza pro optimalizaci programu}\,--\,Detekce mrtvého kódu, detekce hodnot, jež mohou být vyhodnoceny v~době překladu\dots
    \item \textbf{Statická analýza pro korektnost programu}\,--\,Detekce přetečení, dereference ukazatele na adresu \verb|NULL| v~jazyce C\dots
    \item \textbf{Statická analýza pro vývoj programu}\,--\,Jedná se hlavně o~podporu pro snazší pochopení programu a~jeho případnou refaktorizaci. Tento druh statické analýzy nalézá uplatnění v~moderních integrovaných vývojových prostředích a~textových editorech.
\end{enumerate}

\par 

\subsection{Nástroje pro statickou analýzu} \label{sec:tools}
\par V~současné době existuje celá řada nástrojů pro statickou analýzu kódu. Některé z~nich jsou přímo určeny pro integrovaná vývojová prostředí, další jsou zase distribuovány ve formě samostatných nástrojů (např. s~CLI či~vlastním GUI) a~některé kombinují oba přístupy. 
\par Poskytovaná funkcionalita se velmi liší stejně jako jejich jiné parametry (rychlost provádění analýzy, způsob prezentace výsledků analýzy\dots) v~závislosti na jejich konkrétním účelu. Například analyzátory určené pro vývojová prostředí poskytují zpravidla méně komplexní funkcionalitu za cenu rychlejší analýzy a~okamžité zpětné vazby uživateli.
\par Příkladem statických analyzátorů jsou \textit{FindBugs} pro jazyk Java~\cite{Hovemeyer::2015::findbugs}, \textit{Pylint} pro jazyk Python~\cite{Pylint} či~nástroj \textit{Splint}~\cite{Evans::2003::splint} určený pro C. Statické analyzátory najdeme také v~kompilátorech, které je využívají pro optimalizaci kódu a~hledání některých chyb (GCC\footnote{Manuálová stránka s~možnostmi nastavení analyzátoru v~překladači~GCC: \url{https://gcc.gnu.org/onlinedocs/gcc-10.1.0/gcc/Static-Analyzer-Options.html}}, Clang\footnote{Manuálová stránka věnovaná statickému analyzátoru překladače Clang: \url{https://clang.llvm.org/docs/ClangStaticAnalyzer.html}}\dots).
\par Za zmínku stojí také jeden z~prvních statických analyzátorů, program \textit{Lint} \cite{Johnson::1977::lint} pro jazyk C. Funkcionalita, kterou tento nástroj poskytuje (kontrola stylu, hledání chybových idiomů, typové kontroly\dots), se na základě jeho názvu označuje jako tzv. \textit{linting}. Tento pojem je dodnes hojně používán jako souhrnný název pro tyto základní funkce statických analyzátorů.
\par Zajímavou oblastí, kde se statická analýza kódu rovněž uplatňuje, jsou doménově specifické jazyky. Setkáváme se zde jednak s~běžnou funkcionalitou jiných statických analyzátorů, ale také se specifickými funkcemi, jež pomáhají řešit problémy dané domény. Například článek \cite{Dasgupta::2009} prezentuje rámcové řešení pro analýzu zdrojových kódů databázových aplikací, které umožňuje detekovat zranitelnost typu \textit{SQL injection}. Do této oblasti také ostatně spadá i~knihovna vytvořená v~rámci této práce, jelikož provádí analýzu doménově specifického jazyka YARA.
\par Další exemplář statického analyzátoru používá technologie \textit{eBPF}. Ta umožňuje rozšiřovat funkce jádra operačního systému Linux pomocí uživatelsky definovaných rozšíření, čehož se využívá např. při~zpracování paketů nebo pro monitorování systému. Rozšíření jsou spouštěna v~privilegovaném režimu přímo na CPU, a~proto musí před jejich nahráním do jádra OS proběhnout jejich precizní statická analýza~\cite{Gershuni::2019}.
\par Kromě poskytované funkcionality jsou dalšími zásadními parametry statických analyzátorů \textit{rychlost} provádění analýzy, její \textit{přesnost} (precision), \textit{spolehlivost} (soundness), \textit{hloubka} (depth) a~\textit{uživatelská přívětivost}. 
\par Zatímco přesnost udává, do jaké míry dokáže analyzátor přesně odhadnout chování programu, spolehlivost popisuje, zda je analyzátor schopen postihnout všechny jeho potenciální stavy a~možná chování. Velmi spolehlivé analyzátory často do množiny těchto stavů zahrnují i~ty, které během provádění programu nenastanou. Tím eliminují chyby druhého typu (false negatives) za cenu většího výskytu chyb prvního typu (false positives), což je z~hlediska bezpečnosti analyzovaného programu výhodné~\cite[str.~22]{Madsen::2015}.
\par Hloubka, do které je analýza prováděna, popisuje, jak velké celky zdrojového programu jsou v~rámci analýzy v~jednom okamžiku zkoumány. Analýza může být prováděna v~rámci celých projektů nebo v~rámci jednotlivých souborů či~pouze řádků. Omezení hloubky má sice za následek zvýšení rychlosti, ale také ztrátu kontextu, a~proto může dojít k~omezení funkcionality. Nemusí být pak možné např. zkoumat platnost proměnných definovaných mimo aktuální modul a~podobně~\cite[str.~38]{Chess::2007::secure}.
\par Podobně protichůdné vlastnosti jsou přesnost a~rychlost, neboť přesnější analýza vyžaduje více času. Z~tohoto důvodu se u~statických analyzátorů setkáváme s~různými kompromisy, kdy na jedné straně existují velmi pomalé, ale precizní nástroje, schopné provádět analýzu nad celým projektem. Na druhé straně jsou však jednoduché, velmi rychlé analyzátory poskytující pouze základní funkcionalitu~\cite[str.~39]{Chess::2007::secure}. To je také případ analyzátorů v~integrovaných vývojových prostředích a~textových editorech (viz \ref{sec:static_in_ide}). Ať už je ale kompromis mezi rychlostí a~jinými parametry jakýkoliv, vždy je snaha zachovat co největší spolehlivost~\cite[str.~23]{Madsen::2015}.
\par Neméně důležitá je zmíněná uživatelská přívětivost. Statický analyzátor poskytuje pouze hlášení o~výsledcích analýzy. Za změny v~kódu jako takové je zodpovědný uživatel, který tento nástroj používá. Kvůli tomu je zapotřebí, aby byl prostřednictvím něj dobře informován a~byl schopen kód vylepšit na základě těchto hlášení~\cite[str.~41]{Chess::2007::secure}.
\par Na to, jak jsou nástroje pro statickou analýzu v~dnešní době reálně používané, odpovídá uživatelsky orientovaný průzkum z~března 2022~\cite{Do::2022}. Naprostá většina (78,3\,\%) dotázaných vývojářů v~něm udává, že je používají, jelikož jim umožňují tvořit lepší kód. Menší část z~nich (30,4\,\%) uvedla jako jeden z~důvodu jejich používání firemní standardy, které to přímo vyžadují. I~sami vývojáři~tedy zjevně oceňují podporu, kterou jim tento typ analýzy kódu poskytuje~\cite[str.~6]{Do::2022}.
\par Z~průzkumu také vyplývá, že pro statickou analýzu jsou používány jak nástroje zabudované v~integrovaných vývojových prostředích, tak samostatně distribuované analyzátory. Dotázaní dále vypověděli, že ideální je, aby výsledky analýzy byly prezentovány přímo v~prostředí, v~němž kód editují. Nejčastější artefakty, jež jsou statickou analýzou detekovány, se týkají programovacího stylu, bezpečnosti kódu a~chyb, které by mohly mít negativní vliv na funkčnost programu~\cite[str.~4\,--\,5]{Do::2022}.
\par Zajímavá fakta prezentuje také článek~\cite{Baca::2008::Evaluating}, jenž se zaměřuje na další, neméně podstatný aspekt této problematiky, a~sice na její ekonomickou stránku. Tento průzkum ukázal, že i~přes další režii a~náklady spojené s~používání statických analyzátorů, lze celkově díky nim dosáhnout redukce ceny softwaru v~průměru o~17\,\%.
\par Není tedy pochyb o~tom, že téma statické analýzy a~její integrace do textových editorů a~integrovaných vývojových prostředí je v~dnešní době aktuální téma. To potvrzuje také fakt, že provádění statické analýzy bezpečnosti kódu (Static Analysis Security Testing neboli SAST) je součástí životního cyklu bezpečného vývoje software navrženého společností Microsoft (Microsoft Security Development Lifecycle zkráceně MSDL)~\cite{Lipner::2004::trustworthy}.




\subsection{Integrovaná vývojová prostředí a~textové editory} \label{sec:static_in_ide}
\par Přestože někdy bývají pojmy integrované vývojové prostředí a~textový editor zaměňovány, existují mezi nimi rozdíly.
\par \textit{Integrované vývojové prostředí} (IDE) není pouze program pro editaci kódu, ale obsahuje také další vestavěnou funkcionalitu jako např. kompilátor (resp. interpret) kódu nebo ladící nástroje. Nástroje pro statickou analýzu kódu mohou být právě součástí této vestavěné funkcionality. Může se jednat o~nástroj pro zvýrazňování syntaxe či~syntaktických chyb během editace, statický analyzátor v~kompilátoru nebo samostatný nástroj pro statickou analýzu. Typickými příklady IDE jsou \textit{NetBeans}, \textit{Eclipse} a \textit{Visual Studio}.
\par \textit{Textové editory} (editory kódu) jsou oproti tomu méně komplexní nástroje, zaměřené pouze na editaci kódu. Neposkytují sice v~základu uživatelům tak široké spektrum služeb, díky tomu ale také nekladou tak vysoké nároky na výkon hostitelského systému jako IDE. Příklady textových editorů jsou \textit{VSCode}, \textit{Sublime Text} a~\textit{Vim}~\cite{Mino::2022}. 
\par Pokud nějakou dodatečnou funkcionalitu tyto nástroje obsahují, tak jde většinou o~základní podporu pro editaci kódu. Tou je zvýrazňování syntaxe, syntaktických chyb, automatické doplňování znaků, navigace v~kódu apod. Pro tyto funkce se někdy používá obecné označení \textit{intellisense}\footnote{Dokumentace funkcí intellisense v~textovém editoru VSCode: \url{https://code.visualstudio.com/docs/editor/intellisense\#_intellisense-features}} (i~v případě některých IDE).
\par Moderní editory kódu a~IDE umožňují svoji funkcionalitu obohacovat prostřednictvím rozšíření. Editory kódu tak díky nim mohou obsahovat i~nástroje, které jsou nabízeny v~integrovaných vývojových prostředích. Může se jednat právě o~statické analyzátory.
\par I~když nemusí jít o~statickou analýzu v~pravém slova smyslu a~může se jednat např. pouze o~zvýrazňování syntaxe, tak i~v tomto případě se jedná o~proces, který zahrnuje mimo jiné také tvorbu modelu programu (viz \ref{sec:static_flow}). Ačkoliv by bylo možné implementovat tvorbu modelu programu v~rámci každého rozšíření pro jednotlivé jazyky a~jednotlivá IDE (respektive textové editory), bylo by to velmi náročné.
\par Řešením jsou tzv. \textit{language servery}. Jedná se o~samostatné aplikace, běžící v~rámci vlastního procesu v~OS. Díky tomu je možné, aby byly implementovány v~libovolném jazyce a~s~využitím libovolných knihoven, které poskytují rozhraní právě pro tvorbu modelu programu a~práci s~ním nebo přímo implementují některou ze statických analýz~kódu. S~textovým editorem nebo s~IDE pak komunikují pomocí protokolu \textit{Language Server Protocol} (LSP)~\cite{Kelner::2022::lsp} založeném na JSON-RPC. Díky němu lze language servery propojit se všemi IDE, resp. textovými editory, které tento protokol podporují a~není tak nutné implementovat rozšíření pro každé IDE zvlášť. 
\par Příklad komunikace textového editoru a~language serveru znázorňuje sekvenční diagram na obrázku \ref{fig:lsp}. Jak je v~diagramu znázorněno, komunikace mezi textovým editorem a~language serverem probíhá oběma směry. Textový editor zasílá language serveru např. notifikace o~otevření dokumentu, uložení, zavření nebo o~jeho editaci. Oznámení o~editaci mohou být zasílána s různou granularitou, tzn. po přidání nebo odebrání jednoho znaku, ale také po několika úpravách na různých místech v dokumentu. Language server zasílá editoru notifikace, v~rámci kterých např. publikuje varování a~zprávy o~chybách, na což může textový editor zareagovat jejich vizualizací ve svém uživatelském rozhraní. 
\par Ačkoliv jsou pro tuto práci zásadní zejména výše uvedené druhy notifikací, LSP definuje i~další typy zpráv, pomocí kterých může být realizována také další funkcionalita (např. navigace na definice symbolů).

\begin{figure}[h]
    \centering
    \includegraphics[width=\textwidth]{obrazky-figures/lsp.pdf}
    \caption[Komunikace textového editoru s~language serverem]{Komunikace textového editoru (zde VSCode) s~language serverem (zjednodušená verze diagramu dostupného v~\cite{Kelner::2022::lsp})\,--\,Zprávy mají svůj typ neboli metodu (např. \inlinecode{didSave}) a~parametry (uvedeny v~závorce). Textový editor nebo IDE může tímto způsobem komunikovat s více language servery, z nichž je každý specializovaný pro konkrétní jazyk.}
    \label{fig:lsp} 
\end{figure}


\par Příkladem language serveru je \textit{Yara Language Server} (YLS), který využívá knihovnu Yaramod-v3 (viz \ref{sec:yaramodv3}) a~díky podpoře LSP je možné jej propojit s~vícero textovými editory (VSCode, Vim/NeoVim, Emacs\dots) \cite{Kastak::2022::yls}. Oba dva projekty jsou v~rámci této práce důkladněji popsány v~kapitolách \ref{sec:yara_at_avast} a~\ref{sec:yaramodv3}.


\subsection{Průběh statické analýzy} \label{sec:static_flow}
\par Ačkoliv se průběh provádění statických analyzátorů může lišit v~závislosti na konkrétní implementaci, obecně jej můžeme znázornit následujícím schématem:
\begin{figure}[ht]
    \centering
    \includegraphics[width=\textwidth]{obrazky-figures/static_analysis.pdf}
    \caption[Obecné schéma statického analyzátoru]{Obecné schéma statického analyzátoru (sestaveno na základě \cite{Chess::2007::secure, Prahofer::2012})}
    \label{fig:static_analysis} 
\end{figure}

\par Fáze zpracování zdrojového kódu uvedené ve schématu mohou být různě sofistikované a~v některých případech může být některá z~fází vynechána. Vždy záleží na konkrétních potřebách statického analyzátoru a~na jazyce, pro který je určen. Běžně je zdrojový kód analyzován v~těchto třech základních krocích: 


\subsubsection{Tvorba modelu programu} \label{sec:program_model}

\par První fází je převod zdrojového kódu do interní reprezentace neboli tzv. tvorba modelu programu. Tato fáze může být různě komplexní v~závislosti na potřebách daného statického analyzátoru.

\par Jelikož pro další zpracování není zdrojový kód ve své surové formě (textový řetězec) vhodný, je převeden do vhodnější, interní reprezentace. Touto interní reprezentací je zpravidla \textit{abstraktní syntaktický strom} (AST) vycházející ze simulace konstrukce \textit{derivačního stromu} (parse tree). Termínem derivační strom neformálně rozumíme \uv{\textit{strom, který znázorňuje derivaci řetězce na základě formální gramatiky daného jazyka}}~\cite[str.~45]{Aho::2006}. Úplnou formální definici tohoto pojmu najdeme například v~knize Compilers: Principles, Techniques, and Tools \cite[str.~45]{Aho::2006}. 
%\textit{\uv{Derivační strom je strom, který splňuje následující vlastnosti: 1) Kořen stromu je označen počátečním symbolem gramatiky. 2) Každý listový uzel stromu je označen terminálním symbolem gramatiky nebo prázdným řetězcem. 3) Každý vnitřní uzel stromu je označen neterminálem dané gramatiky. 4) Pokud je vnitřní uzel stromu označen $A$ a~$X_1, X_2,\dots ,X_n$ označují přímé následovníky tohoto uzlu v~pořadí zleva doprava, potom v~dané gramatice musí existovat pravidlo $A\longrightarrow X_1, X_2,\dots ,X_n$.}}

\par Abstraktní syntaktický strom na rozdíl od derivačního stromu nemusí odrážet přesnou syntaktickou strukturu programu, kterou udává příslušná gramatika, a~některé detaily tak v~něm mohou být vynechány (znaménka operátorů, závorky apod.)~\cite[str.~69]{Aho::2006}.
\par Simulace konstrukce derivačního stromu je vytvářena \textit{syntaktickým analyzátorem} implementovaným na základě gramatiky příslušného formálního jazyka. Syntaktické analýze však musí předcházet \textit{lexikální analýza}, která vstupní řetězec rozdělí na proud tzv. \textit{tokenů}. Jedná se o~struktury obsahující název nebo jiné symbolické označení dané lexikální jednotky. Volitelně mohou mít také jiné atributy (např. pozici v dokumentu)~\cite[str.~111]{Aho::2006}. Zpracování vstupního řetězce pomocí syntaktické a~lexikální analýzy může být prováděno v~rámci statické analýzy kódu stejným způsobem v~kompilátorech~\cite[str.~39]{Aho::2006}.

\par Některé jednodušší statické analyzátory nevyžadují tak komplexní interní reprezentaci, jakou je strom. Pokud je cílem statické analýzy např. identifikovat volání některých potenciálně nebezpečných funkcí, je proud tokenů jako interní reprezentace dostačující. Tato volání v~něm mohou být totiž snadno nalezena a~je tedy zapotřebí pouze lexikálního analyzátoru~\cite[str.~103]{Chess::2007::secure}.

\par Velmi přímočarý způsob provádění statické analýzy, který nevyžaduje žádnou 
konverzi zdrojového kódu do interní reprezentace, je uveden v~článku~\cite{Chess::2004::Security}. V~principu se jedná o~vyhledávání potenciálně nebezpečných konstrukcí daného programovacího jazyka pomocí unixového nástroje \textit{grep}. Ten ve zdrojových souborech vyhledává dané problematické vzory, popsané regulárními výrazy.

\par Pro jiné, pokročilejší typy statických analyzátorů je však zapotřebí vytvořit před dalším zpracováním zdrojového kódu AST. Přestože je možné použít pro tento účel různé druhy syntaktických analyzátorů, výhodné je vybrat \textit{LR syntaktický analyzátor}, konkrétně pak \textit{LR(1) syntaktický analyzátor} (canonical parser). Ten totiž dokáže zpracovávat všechny programovací jazyky, pro které je možné definovat bezkontextovou gramatiku. Množina jazyků zpracovávaných LL metodami je navíc méně početná než množina jazyků, již je možné zpracovat LR syntaktickými analyzátory~\cite[str.~241]{Aho::2006}. 

\par Kromě toho můžeme tyto syntaktické analyzátory vygenerovat pomocí řady dostupných generátorů. Příkladem je desetiletími ověřený \textit{GNU Bison}~\cite{Donnely::2021::bison}, který si navíc dokáže poradit i~s nejednoznačnostmi v~gramatice jazyka.

\par V~některých případech totiž bezkontextová gramatika nemusí být jednoznačná. Není tedy možné na základě jednoho následujícího znaku rozhodnout, jaké gramatické pravidlo bude pro zpracování tokenu použito (uvažujeme LR(1) syntaktický analyzátor). Řešením je např. specifikace precedence a~asociativity operátorů~\cite[str.~279]{Aho::2006}.

\par Univerzálnější řešení je použít tzv. \textit{zobecněný LR syntaktický analyzátor} (GLR syntaktický analyzátor). Ten umožňuje řešit situace, v~nichž je možné zpracovat token na vstupu více než jedním způsobem. Tyto situace se označují jako \textit{konflikty}, přičemž existují dva druhy těchto konfliktů. \textit{Reduce/reduce} konflikty, jsou ty konflikty, u~nichž není možné rozhodnout mezi provedením vícero redukcí na zásobníku. U~druhého typu, \textit{shift/reduce}, zase není možné rozhodnout, zda provést shift tokenu na zásobník~nebo provést redukci~\cite[str.~238]{Aho::2006}. 

\par Pokud dojde ke konfliktu, GLR syntaktický analyzátor je rozdělen na dva či~více samostatných syntaktický analyzátorů. Každý z~těchto analyzátorů zpracuje token dle jiného pravidla a~následně pokračují v~analýze. Další tokeny na vstupu jsou zpracovávány všemi takto vytvořenými instancemi analyzátorů. 

\par Dochází tak k~větvení syntaktického analyzátoru (resp. jeho datových struktur). Analogicky mohou být větve dále rozdělovány, pokud nastanou další konfliktní situace. Větev syntaktické analýzy je ukončena, pokud je nalezena neplatná kombinace tokenů, přičemž zbylé větve jsou rozvíjeny dále~\cite[str.~17\,--\,23]{Donnely::2021::bison}. Další, rozšiřující metodě pro LR syntaktickou analýzu, která optimalizuje její původní koncept, se věnuje kapitola \ref{sec:incremental}. 

\par V~této fázi statické analýzy může být dále provedena základní \textit{sémantická analýza}. Jejím účelem může být obohacení interní reprezentace kódu o~sémantické informace. To je kromě předcházející syntaktické analýzy umožněno také \textit{tabulkou symbolů}. Díky sémantické analýze může statický analyzátor např. správně interpretovat přetížené operátory~\cite[str.~76]{Chess::2007::secure}.

\subsubsection{Samotná statická analýza programu}

\par Nad interní reprezentací, která může být navíc obohacena o~sémantické informace, je již možné provádět statickou analýzu jako takovou. V~závislosti na tom, jaké vlastnosti programu je zapotřebí zkoumat, jsou aplikovány konkrétní algoritmy.

\par Vyjma typových kontrol je provádění statické analýzy jako takové spíše zodpovědností nástrojů, jež budou využívat programovou knihovnu vytvořenou v~rámci této práce. Z~tohoto důvodu jsou uvedeny pouze příklady metod, které jsou využívány.

\par V~rámci této fáze může být analyzováno např. předávání řízení pomocí \textit{grafu toku řízení} (control flow graph). Tento orientovaný acyklický graf je sestrojen na základě interní reprezentace a~jeho uzly obsahují seznamy instrukcí (tzv. basic blocks), jež budou provedeny vždy právě v~takovém pořadí, v~jakém se nachází v~kódu. Zároveň platí, že jejich provádění není proloženo prováděním instrukcí z~jiného seznamu. Analýzou tohoto grafu můžeme zkoumat cesty ve zdrojovém kódu nebo se může jednat o~výchozí model pro další algoritmy~\cite[str.~77]{Chess::2007::secure}.

\par Dále je možné provádět \textit{analýzu datových toků}. Při~ní sledujeme, kde vznikají hodnoty uložené v~proměnných a~kde jsou tyto hodnoty použity. Tato analýza může být využita v~kompilátorech pro eliminaci výrazů, jejichž výsledky nejsou dále potřeba~\cite[str.~597]{Aho::2006}. Kromě toho mohou být jejím prostřednictvím identifikována napevno vložená hesla v~kódu, což je vzhledem k~bezpečnosti velmi problematické~\cite[str.~80]{Chess::2007::secure}.

\par Na pomezí této fáze a~sémantických kontrol prováděných v~rámci předchozího kroku se nachází také \textit{typové kontroly}. Jejich provedení a~složitost se odvíjí od konkrétního jazyka a~od jeho typového systému. Publikace od P. Emanuelssona~\cite{Emanuelsson::2008::comparative} například považuje typové kontroly za druh statické analýzy pouze v~případě netypovaných jazyků, slabě typovaných jazyků a~jazyků bez statického typování. 

\par Jiné zdroje v~tomto směru však tak striktní nejsou a~považují typové kontroly doslova za \textit{\uv{nejčastější formu statické analýzy}}~\cite[str.~24]{Chess::2007::secure}. Setkáváme se s~nimi totiž jak u~kompilátorů, tak u~dedikovaných statických analyzátorů. Uplatňují se zde dva základní principy. Prvním z~nich je \textit{typová syntéza}, při~níž se datový typ výrazu určuje na základě jeho podvýrazů. Příkladem je datový typ výsledku sčítání v~jazyce C, jenž je definován jeho operandy. Druhým principem je \textit{typové odvozování} (inference), během kterého se datový typ jazykové konstrukce určuje na základě jejího použití. Pokud například funkce \inlinecode{len} vrací délku řetězce z~argumentu, můžeme na základě volání \inlinecode{len(x)} konstatovat, že proměnná \inlinecode{x} bude datového typu řetězec~\cite[str.~386]{Aho::2006}.


\par Existuje celá řada dalších algoritmů pro statickou analýzu a~mnoho jejích forem. Jejich společným rysem je, že musí být velmi často doplněny o~bázi pravidel, které určují, jak budou zkoumané vlastnosti kódu vyhodnoceny. Tato pravidla z~pohledu uživatele udávají, jaké artefakty budou ohlášeny jako problematické, případně umožňují jejich klasifikaci. Pro snadnou tvorbu a~správu těchto pravidel definují některé nástroje pro statickou analýzu vlastní formát, v~němž jsou specifikována~\cite{Chess::2007::secure, Prahofer::2012}.

\subsubsection{Prezentace výsledků}
\par Poněkud opomíjenou fází statické analýzy je prezentace výsledků uživatelům. Jedná se však o~velmi podstatný aspekt, který určuje, zda bude analyzátor vůbec použitelný v~praxi.

\par Je totiž důležité, aby uživatel při~pohledu na výsledek statické analýzy mohl rozhodnout o~jeho korektnosti a~důležitosti. Dále by pak měl být výsledek prezentován v~takové podobě, aby na něj mohl uživatel snadno reagovat (např. opravou chyby v~kódu nebo seřízením nástroje pro statickou analýzu)~\cite[str.~105]{Chess::2007::secure}.

\par Nástroje pro statickou analýzu by měli uživatelům umožnit filtrovat výsledky a~eliminovat tak např. varování, kterým se uživatel v~dané chvíli věnovat nechce. Možnost filtrování je také důležitá kvůli náchylnosti některých nástrojů na chyby prvního typu (false positives). Přítomnost této funkcionality dovoluje uživateli eliminovat šum, jejž způsobují falešná chybová hlášení, a~zaměřit se na skutečné problémy ve zdrojovém kódu~\cite{Louridas::2006::Static}. Pokročilejší statické analyzátory umožňují seskupování a~třídění výsledků podle různých kritérií~\cite[str.~106\,--\,110]{Chess::2007::secure}. 

\par Příkladem moderního způsobu prezentace výsledků je analýza kódu v~jazyce C\# v~rámci integrovaného vývojového prostředí \textit{Visual Studio}. Statická analýza v~tomto IDE \cite{VisualStudio} je součástí sady nástrojů \textit{.NET Compiler Platform SDK}. Chyby a~upozornění jsou jednak označené přímo v~kódu samotném (barevným podtržením problematických úseků), ale jsou také přehledně uvedeny v~seznamu. V~tomto seznamu je možné chyby filtrovat podle různých kritérií (např. závažnosti, pozice v~kódu apod.). Analyzátor kódu poskytuje kromě popisného hlášení také možnost automatické opravy nebo možnost potlačení upozornění. Hlášení jsou navíc opatřena unikátním kódem a~odkazem na webovou dokumentaci s~dalšími informacemi.



\section{Inkrementální provedení statické analýzy}
\par Jednou ze zásadních vlastností statických analyzátorů je rychlost, jakou jsou schopné analýzu provádět. Tento parametr nabývá ještě více na důležitosti při~jejich použití v~rámci IDE a~textových editorů. V~těchto případech je totiž často analýza prováděna interaktivním způsobem, během editace zdrojového kódu.
\par Bylo by pochopitelně možné při~každé editaci provádět analýzu kompletně znovu neinkrementálním způsobem. To by ale způsobilo latenci, jež by se s~rostoucí velikostí zdrojového souboru zvyšovala. U~rozsáhlých zdrojových kódů by pak tím pádem statická analýza prováděná v~reálném čase nebyla ani možná.
\par Pokud bychom například vložili na náhodný řádek ve zdrojovém souboru jeden znak, muselo by dojít k~aktualizaci zobrazených výsledků analýzy. Kromě toho by ale musela být opětovně provedena i samotná statická analýza, a~navíc také tvorba modelu programu, neboť modifikací vstupního řetězce došlo k~jeho zneplatnění.
\par Jedno z~možných řešení nám v~tomto případě nabízí \textit{inkrementální} přístup ke statické analýze, či~k některé z~jejích fází. Principem tohoto přístupu je průběžné zaznamenávání změň zdrojového kódu a~následná analýza pouze těch jeho částí, které byly touto editací ovlivněny~\cite[str.~1]{Beetem::1991::Incremental}.
\par Jelikož velkou částí knihovny, která byla v~rámci této práce vytvořena, je inkrementální tvorba modelu programu a sémantické kontroly nad tímto modelem, je tato kapitola věnována zejména těmto částem statické analýzy. Ačkoliv existuje více publikací na téma inkrementální lexikální a~syntaktické analýzy~\cite{Beetem::1991::Incremental, Ghezzi::1980}, zaměříme se zejména na disertační práci Practical Algorithms for Incremental Software Development Environments~\cite{Wagner::1998::practical}.
\par Důvodem je, že je touto prací inspirována knihovna \textit{tree-sitter}~\cite{Brunsfeld::2022::tree-sitter}, která je použita pro implementaci programového řešení této bakalářské práce. V~závěru této kapitoly jsou stručně shrnuty principy inkrementální sémantické analýzy, která je rovněž součástí řešení.

\subsection{Model programu a jeho modifikací}
\par Odkazované inkrementální statické analyzátory používají jako interní reprezentaci programu stromové struktury. Konkrétně se jedná o~abstraktní syntaktické stromy (AST), které slouží buďto jako výchozí struktury pro algoritmy statické analýzy (viz výše) nebo pro tvorbu jiné reprezentace, jež může být ještě na vyšší úrovni abstrakce. Kromě toho v~nich ale také mohou být uchovávány záznamy o~modifikacích zdrojového souboru. To je také případ knihovny tree-sitter, která provádí inkrementální syntaktickou analýzu zdrojových souborů a~v kontextu statické analýzy se tedy stará o~tvorbu modelu programu. Typicky se využívá právě ve spojení s~textovými editory a~IDE.
\par Činnost inkrementálního statického analyzátoru, jenž může být založen například na této knihovně, je schematicky znázorněna na obrázku \ref{fig:incremental_static_analysis}. Nejprve je provedena počáteční analýza zdrojových souborů. Při~počáteční syntaktické analýze je použit neinkrementální přístup (batch parsing). Neexistuje totiž zatím struktura, z~níž by bylo možné vycházet. Tím vznikne prvotní referenční verze interní reprezentace neboli modelu programu.
\par Editace zdrojových souborů (např. uživatelem v~IDE) způsobí nekonzistenci modelu programu se vstupním řetězcem. Dochází proto k~nahlášení změn statickému analyzátoru (SA), který je propaguje dále, až k~jeho části zodpovědné za správu modelu (např. knihovna tree-sitter). Ta si záznamy o~změnách uchovává buďto přímo v~AST nebo jiným způsobem. Například inkrementální syntaktický analyzátor prezentovaný J. Beetemem používá pro tento účel tzv. \textit{kmn seznam} (kmn list)~\cite[str.~3]{Beetem::1991::Incremental}.
\par Po zaznamenání změn je provedena inkrementální lexikální a~syntaktická analýza, která obnoví konzistenci zdrojového souboru a~modelu programu. Poté následuje sémantická analýza a~případně jsou aplikovány algoritmy pro statickou analýzu (analýza datových toků, analýza grafu toku řízení atd.). Tyto typy analýz~mohou být provedeny rovněž inkrementálně, ale je také možné je provést kompletně znovu nad aktualizovaným modelem.

\begin{figure}[ht]
    \centering
    \includegraphics[width=0.9\textwidth]{obrazky-figures/incremental_static_analysis.pdf}
    \caption[Průběh inkrementální statické analýzy založené na knihovně tree-sitter]{Průběh inkrementální statické analýzy založené na knihovně tree-sitter~\cite{Brunsfeld::2022::tree-sitter} (vytvořeno na základě~\cite{Wagner::1998::practical}). Zkratka SA označuje statický analyzátor, který si udržuje model programu. Ten se po nahlášení změn stává nekonzistentním se zdrojovým kódem. Jeho konzistence je obnovena inkrementální statickou analýzou, zejména pak činností inkrementálního lexikálního a~syntaktického analyzátoru.}
    \label{fig:incremental_static_analysis}
\end{figure}

\par S~ukládáním záznamů o~editacích zdrojového souboru souvisí také formát, v~němž jsou editace nahlašovány, a~způsob, jakým jsou určeny modifikované části modelu programu. V~případě uchování změn v~podobě kmn seznamu je zdrojový řetězec (zdrojový kód) reprezentován jako uspořádaná n-tice jeho podřetězců $(s_1, s_2,\dots, s_N)$. Pro každý tento podřetězec je definována uspořádaná trojice $(k_i, m_i, n_i)$, kde $k$ je počet nemodifikovaných znaků na začátku daného podřetězce, $m$ je počet znaků, které byly z~původního řetězce odstraněny, a~$n$ je počet nově vložených znaků namísto původních $m$ znaků. Jinými slovy, tato trojice udává počet znaků $m$, jež byly v~původním řetězci počínaje indexem $k$ nahrazeny novým počtem znaků $n$. Například tedy trojice $(10, 0, 1)$ udává, že v~daném řetězci byl na index 10 vložen jeden nový znak~\cite[str.~3]{Beetem::1991::Incremental}.
\par Knihovna tree-sitter řeší tento problém jiným způsobem. Uchovává pozice začátků a~konců uzlů AST v~rámci zdrojového dokumentu přímo ve strukturách reprezentující uzly daného stromu. Tyto pozice jsou uloženy v~podobě indexu bytu ve zdrojovém souboru (bytový offset), ale také v~podobě souřadnic tvořených indexem řádku a~indexem znaku na daném řádku (struktura \inlinecode{TSPoint}). Editace jsou oznamovány prostřednictvím struktury \inlinecode{TSInputEdit}. Ta obsahuje index prvního modifikovaného bytu (\inlinecode{start_byte}), index prvního bytu za modifikovanou částí v~původním zdrojovém souboru (\inlinecode{old_end_byte}) a~index bytu za modifikovanou částí v~nové verzi zdrojového souboru (\inlinecode{new_end_byte}). Tytéž údaje obsahuje také ve formě souřadnic ve zdrojovém kódu. Struktura, která reprezentuje editaci, je v~jazyce C definována jako~\cite{Brunsfeld::2022::tree-sitter}: 

\medskip
\begin{lstlisting}[language=C, keywordstyle=\color{lstkeyword}, morekeywords={uint32_t}, classoffset=1, morekeywords={TSPoint}, otherkeywords={TSPoint}, keywordstyle=\color{lstotherkeyword}, classoffset=0]
struct TSInputEdit {
    uint32_t start_byte;
    uint32_t old_end_byte;
    uint32_t new_end_byte;
    TSPoint start_point;
    TSPoint old_end_point;
    TSPoint new_end_point;
}      
\end{lstlisting}
\medskip

\par Díky údajům ve struktuře \inlinecode{TSInputEdit} a~pozicím uzlů uloženým v~AST jsou během nahlášení změn snadno nalezeny modifikované podstromy. Mohou tak být upraveny údaje o~jejich pozici a~také nastaven jejich příznak modifikace. Při~vkládání znaků je tedy průchodem stromu nalezen podstrom, který v~dokumentu reprezentuje právě tu část, do níž byl znak vložen. Následně jsou upraveny údaje o~pozici tohoto podstromu a~nakonec je nastaven příznak modifikace pro daný podstrom. Tento proces probíhá rekurzivně čili pro všechny podstromy, jež jsou danou modifikací dotčeny.
\par Ani v~jednom z~výše uvedených případů není dodán v~době ohlašování editace syntaktickému ani lexikálnímu analyzátoru konkrétní řetězec, který byl editací vložen nebo odstraněn. Změny jsou v~obou případech popsány pouze jejich pozicemi a~počty znaků, které byly odstraněny, respektive vloženy. Tyto informace nejsou zapotřebí, protože díky zaznamenávání pozic změn ve zdrojovém dokumentu mohou v~něm tyto analyzátory snadno najít dané modifikované oblasti během inkrementální lexikální a~syntaktické analýzy.
\par Uvedená disertační práce~\cite{Wagner::1998::practical}, na které je knihovna tree-sitter založena, definuje vlastní sofistikovaný způsob zaznamenávání změn. Díky němu je možné přistupovat k~několika verzím modelu programu a~zároveň zajišťuje možnost, dotázat se uzlů v~AST, zda byly změněny.
\par Níže popsané algoritmy pro lexikální a~syntaktickou analýzu totiž pracují se třemi verzemi AST~\cite[str.~28]{Wagner::1998::practical}:

\begin{itemize}
    \item \textbf{Referenční}\,--\,Jedná se o~verzi, která je konzistentní se zdrojovým kódem. Vzniká po dokončení inkrementální nebo počáteční neinkrementální syntaktické analýzy.  
    \item \textbf{Původní}\,--\, Je to verze AST před začátkem syntaktické analýzy. Vzniká editacemi referenční verze zdrojového kódu a~jejich zaznamenáním. Je nekonzistentní se zdrojovým kódem. Rozdíly mezi touto a~referenční verzí určují, jaké části AST bude možné opětovně využít.  
    \item \textbf{Aktuální}\,--\,Jedná se o~verzi AST, která je aktuálně vytvářena syntaktickým analyzátorem.
\end{itemize}

\par Uzly v~AST si zároveň udržují dva booleovské atributy. První z~nich určuje, zda byl modifikován uzel samotný (local change). Druhý udržuje souhrnnou informaci o~jeho modifikaci, modifikaci jeho potomků a~strukturálních změnách v~podstromě, pro který je daný uzel kořenem (nested change). Díky druhém atributu můžeme hledat ve stromě cestu od kořene až k~modifikovaným listovým uzlům~\cite[str.~45]{Wagner::1998::practical}.
\par Současně však uvedená práce dovoluje konkrétním aplikacím, aby používaly jiný model pro záznam a~identifikaci změn v~AST, což je právě případ knihovny tree-sitter. To je možné díky tzv. \textit{change reporting rozhraní}, které definuje sadu metod, již musí uzly derivačního uzlu poskytovat, aby bylo nad nimi možné algoritmy provádět. Pro následující kapitoly bude z~množiny těchto metod důležitá hlavně \inlinecode{has_changes} se signaturou \inlinecode{bool has_changes(version_id, [local|nested])}. Tato metoda umožňuje přistupovat k~atributům uchovávajícím informace o~modifikaci. Pomocí prvního parametru lze přistupovat i~k uzlům v~nereferenční verzi AST. Pokud není specifikován druhý parametr, identifikovány jsou jakékoliv změny, které se uzlu týkají~\cite[str.~18]{Wagner::1998::practical}.
\par Dále se předpokládá, že v~AST je přítomna trojice pomocných uzlů (sentinel nodes). První dva se v~něm vykytují jako jeho nejpravější a~nejlevější listový uzel, čímž označují začátek a~konec proudu tokenů  (uzly \inlinecode{bos} a~\inlinecode{eos}). Třetím pomocným uzlem je samotný kořen AST (označovaný jako \inlinecode{UltraRoot}). Díky těmto uzlům také není nutné ošetřovat zpracování krajních tokenů v~řetězci tokenů, čímž dochází ke zjednodušení níže popsaných algoritmů~\cite[str.~17]{Wagner::1998::practical}.
\par Poloha pomocných uzlů v~AST, označení změn či~jeho stav před a~po inkrementální lexikální a~syntaktické analýze je znázorněn na obrázku \ref{fig:model_states}. 

\begin{figure}[ht]
    \centering
    \includegraphics[width=\textwidth]{obrazky-figures/model_update.pdf}
    \caption[Změny v~AST během inkrementální lexikální a~syntaktické analýzy]{Změny v~AST během inkrementální lexikální a~syntaktické analýzy (sestaveno na základě fungování~\cite{Brunsfeld::2022::tree-sitter} a~práce~\cite{Wagner::1998::practical})\,--\,Analyzovaným řetězcem je přiřazení hodnoty v~jazyce C. Ve čtvercích jsou tokeny, které zároveň  plní úlohu listových uzlů v~AST. Přerušovanou čarou jsou označeny pomocné uzly (zkratka UR znamená \inlinecode{UltraRoot}). Vidíme, že proud tokenů a~některé části AST nejsou konzistentní se zdrojovým kódem po provedení změn. Změny jsou však ve stromě označeny a~během opětovné analýzy je obnovena konzistence AST a~zdrojového kódu.}
    \label{fig:model_states}
\end{figure}

\subsection{Lexikální analýza} \label{sec:incremental_lex}
\par Činnost inkrementálního lexikálního analyzátoru je z~pohledu uživatele stejná jako jeho neinkrementální varianty (batch lexer), používané při~kompilaci programovacích jazyků. Načítá vstupní řetězec znaků, které tvoří zdrojový kód, a~identifikuje v~něm tzv. \textit{lexémy}. Na základě těchto lexémů vytváří výstupní řetězec tokenů, který předává k~dalšímu zpracování~\cite[str.~109]{Aho::2006}. Termín lexém označuje \textit{\uv{sekvenci znaků ve vstupním řetězci, která je identifikována lexikálním analyzátorem jako instance příslušného tokenu}}~\cite[str.~111]{Aho::2006}.
\par Z~uživatelského pohledu se neinkrementální a~inkrementální přístupy začnou lišit ve chvíli, kdy je nutné znovu analyzovat zdrojový kód, v~němž došlo ke drobným změnám (např. v~důsledku editace uživatelem). 
\par Neinkrementální, dávkový přístup vyžaduje kompletní reanalýzu celého vstupního řetězce. \textit{Inkrementální lexikální analýza} naproti tomu přihlíží k~již existujícímu výstupnímu řetězci tokenů a~reanalyzuje pouze ty části vstupního řetězce, které to vyžadují. Díky tomu dochází k~redukci času, jenž je k~analýze potřeba~\cite[str.~37]{Wagner::1998::practical}. Součástí tohoto procesu může být také označení nových a~reanalyzovaných tokenů v~rámci výstupního řetězce~\cite[str.~6]{Beetem::1991::Incremental}.
\par Jeden ze způsobů realizace \textit{inkrementálního lexikálního analyzátoru} (incremental lexer) je prezentovaný ve výše odkazované disertační práci~\cite[str.~37\,--\,55]{Wagner::1998::practical}. Velkou výhodou této implementace je možnost využití běžných generátorů lexikálních analyzátorů (např. \textit{flex}). Zároveň není zapotřebí žádný speciální formalismus pro popis lexémů daného jazyka. I~zde je možné modelovat lexikální analyzátor jako konečný automat. Podpora inkrementální analýzy je umožněna pouze prostřednictvím rozšíření konvečního lexikálního analyzátoru a~tokenů, které produkuje.
\par V~tokenech, které jsou produkovány neinkrementálními lexikálními analyzátory, je běžně uchován řetězec s~daným lexémem, typ tokenu a~případně také odkaz na rodičovský uzel v~AST (pokud jsou použity jako listové uzly AST). V~případě inkrementálního přístupu je struktura reprezentující token navíc obohacena o~položky:

\begin{itemize}
    \item \inlinecode{state}\,--\,Jedná se o~konečný stav lexikálního analyzátoru v~době vytvoření tokenu.
    \item \inlinecode{lookahead}\,--\,Je to počet znaků přečtených za hranicí lexému.
    \item \inlinecode{lookback}\,--\,Udává relativní polohu prvního tokenu, který je na aktuálním tokenu závislý.
\end{itemize}

\par Kompletní struktura tokenu, který je produkován inkrementálním lexikálním analyzátorem, tedy vypadá následujícím způsobem (převzato z~\cite[str.~40]{Wagner::1998::practical}):

\medskip
\begin{lstlisting}[language=C, keywordstyle=\color{lstkeyword}, morekeywords={int}, classoffset=1, morekeywords={STRING, NODE}, otherkeywords={STRING, NODE}, keywordstyle=\color{lstotherkeyword}, classoffset=0]
struct TOKEN {
    int type, state, lookback, lookahead;
    STRING lexeme;
    NODE *parent;
}   
\end{lstlisting}
\medskip

% \par Ačkoliv je ve výše uvedené struktuře uložen stav a~typ tokenu v~podobě celého čísla (\inlinecode{int}), může být pochopitelně zvolena i~jiná reprezentace těchto hodnot. Důležité je však mít na paměti, že tokenů bude v~rámci analýzu vytvořeno velké množství, a~proto je zapotřebí přihlížet i~k časovým a~prostorovým nárokům, které zvolený způsob reprezentace vyžaduje~\cite[str.~41]{Wagner::1998::practical}. 
\par Uložení stavu v~tokenu umožňuje obnovit interní konfiguraci lexikálního analyzátoru, v~níž se nacházel v~době navrácení daného tokenu. Poslední dvě položky struktury jsou zapotřebí pro vyhodnocení závislostí mezi tokeny. V~některých případech stačí, aby pro konstrukci tokenů byly přečteny pouze znaky tvořící příslušné lexémy. 
\par Někdy je ale zapotřebí, aby analyzátor nahlédl i~na znaky, které již lexému nenáleží, což vytváří právě tyto závislosti. 
V~kontextu jazyka YARA se jedná například o~identifikátory pravidel (viz  \ref{sec:rules}). Ty jsou definovány jako libovolné sekvence alfanumerických znaků a~podtržítek, přičemž jejich prvním znakem není číslo. U~takto definovaných identifikátorů je až na základě následujícího znaku (např. bílého znaku) určen konec identifikátoru. Analogicky však mohou nastat situace, kdy tímto způsobem musí být načteno více znaků, aniž by byly součástí lexému daného tokenu~\cite[str.~113]{Aho::2006}.
\par Právě počet znaků, který bylo tímto způsobem potřeba přečíst během konstrukce tokenu, uchovává položka \inlinecode{lookahead}. U~konvenční lexikální analýzy tento jev v~rámci struktury tokenu nijak reflektován nebývá~\cite[str.~42]{Wagner::1998::practical}.
\par Výše popsaný problém lexikální analýzy způsobuje, že editací některého lexému není ovlivněn jen příslušný token, ale také tokeny předcházející. Kolik tokenů je takto ovlivněno udává právě položka \inlinecode{lookback}. Konkrétně říká, o~kolik míst je zapotřebí se v~proudu tokenů vrátit, abychom nalezli první token, který mohl být editací zneplatněn. Tato hodnota je vypočítána na základě položky \inlinecode{lookahead}.
\par Toto dynamické zaznamenávání hodnot \inlinecode{lookahead} a~\inlinecode{lookback} je velmi obecný přístup, jenž umožňuje aplikaci tohoto algoritmu i~v oblastech zpracování přirozeného jazyka. Pro většinu běžných programovacích jazyků však platí, že maximální hodnota položky \inlinecode{lookahead} a~tedy i~\inlinecode{lookback} je jedna nebo nula (viz příklad s~identifikátory). Toho se často využívá, protože je možné nastavit tuto hodnotu fixně pro každý token. Následkem toho dochází k~reanalýze některých tokenů, u~nichž by nebyla zapotřebí. Celkově se tím ale velmi zredukuje režie potřebná pro výpočet hodnoty \inlinecode{lookback} a~její udržování v~konzistentním stavu, což může vést k~lepším výsledkům~\cite[str.~43]{Wagner::1998::practical}.
\par Podobně jako tokeny, tak i~samotný inkrementální lexikální analyzátor získáme rozšířením jeho neinkrementální verze. Konkrétně je zapotřebí přidat metodu \inlinecode{get_state} umožňující získat aktuální stav a metodu~\inlinecode{set_state}, jež umožňuje~nastavit stav lexikálního analyzátoru.

\begin{figure}[t]
    \centering
    \includegraphics[width=\textwidth]{obrazky-figures/incremental_lexical_analysis.pdf}
    \caption[Průběh inkrementální lexikální analýzy]{Průběh inkrementální lexikální analýzy\,--\,Schéma zobrazuje proud tokenů na konci každé z~jejích fází. Vstupem první fáze (marking) je nekonzistentní AST z~obrázku \ref{fig:model_states}. Druhé dvě fáze mohou být provedeny bezprostředně nebo až na vyžádání během syntaktické analýzy. Zkratky LA a~LB označují položky \inlinecode{lookahead} a~\inlinecode{lookback} jednotlivých tokenů.}
    \label{fig:lexical_analysis}
\end{figure}

\par Samotná analýza probíhá ve třech fázích: \textit{marking}, \textit{lexing} a~\textit{lookback update}. V~první fázi dojde k~označení tokenů (marking), které je zapotřebí reanalyzovat. Tokeny jsou umístěny v~AST jako listové uzly (v~případě \cite{Wagner::1998::practical}). Předpokládá se, že v~tomto stromě jsou již označeny uzly, jež byly ovlivněny editací zdrojového kódu. Vhodným průchodem a~na základě tohoto označení je možné identifikovat část proudu tokenů, v~níž byly tokeny explicitně modifikovány. Tato oblast proudu tokenů musí být navíc také rozšířena o~tokeny, jež jsou závislé na explicitně modifikovaných tokenech. Ty lze určit pomocí hodnot \inlinecode{lookback} explicitně modifikovaných tokenů. Během označování je nastaven příznak změny, a~tudíž lze celou označenou oblast během následujících fází nebo během syntaktické analýzy snadno lokalizovat. 
\par Takto označené sekvence tokenů je poté zapotřebí reanalyzovat. Děje se tak ve druhé fázi lexikální analýzy (lexing). Nejprve je lexikální analyzátor zresetován do stavu, v~němž se nacházel před analýzou označené sekvence tokenů. To je možné díky položce \inlinecode{state} tokenu, jenž označené oblasti přechází, a~metodě \inlinecode{set_state}. Jelikož je použit pomocný token \inlinecode{bos} na začátku proudu tokenů, nemůže nastat situace, kdy by nebylo možné nalézt předcházející token pro některou z~označených oblastí. Pomocný počáteční token \inlinecode{bos} ve své položce \inlinecode{state} uchovává počáteční stav lexikálního analyzátoru. 
\par Stav analyzátoru je tedy nastaven na požadovanou hodnotu a~daná označená oblast je znovu analyzována. Díky tomu je obnovena konzistence proudu tokenů a~modifikovaného zdrojového kódu~\cite[str.~47]{Wagner::1998::practical}.
\par Poslední fází je tzv. \textit{lookback update}. Jak už její název napovídá, dojde k~aktualizaci položek \inlinecode{lookback}. Ta probíhá od prvního tokenu, jehož \inlinecode{lookahead} zasahuje do reanalyzované sekvence tokenů. Zásadní datovou strukturou je zde seznam, jenž obsahuje položky ve tvaru \inlinecode{<token, lookahead, age>} (character lookahead list)\footnote{V~odkazované disertační práci nejsou části položek v~seznamu pojmenovány. Toto pojmenování bylo zavedeno pro snazší vysvětlení fáze lookback update.}. Postupně pro každý zpracovaný token je vložena do seznamu položka, která obsahuje jeho název, hodnotu \inlinecode{lookahead} a~hodnotu vyjadřující počet iterací, který položka strávila v~seznamu (výchozí hodnota je 0). Po každém dalším zpracováním tokenu a~přidání nového záznamu do seznamu je hodnota \inlinecode{lookahead} v~každé položce dekrementována o~délku lexému právě zpracovaného tokenu. Hodnota \inlinecode{age} je naproti tomu inkrementována o~jedničku. Pokud \inlinecode{lookahead} položky v~seznamu je menší či~rovna nule, je položka ze seznamu odstraněna~\cite[str.~49]{Wagner::1998::practical}.
\par Při~zpracování tokenu můžeme na základě obsahu seznamu určit jeho novou hodnotu \inlinecode{lookback} jako nejvyšší hodnotu \inlinecode{age}, jež je v~seznamu přítomná. Tato aktualizace se provádí pochopitelně pro všechny tokeny v~rámci oblasti reanalyzované během předchozí fáze. Je ji ale také nutné provést u~některých tokenů nacházejících se před nově vytvořenými a~za nimi. První, který je aktualizován je nalezen tak, že je nejprve identifikován token nacházející se v~referenční, původní i~aktuální verzi (předchází oblasti označené během fáze marking). Token, jenž po něm následuje v~referenční verzi, obsahuje ve své položce \inlinecode{lookback} počet tokenů, které se nachází před označenou (reanalyzovanou) oblastí a~je u~nich zapotřebí rovněž provést lookback update. 
\par Aktualizovány jsou tak postupně tyto tokeny a~všechny následující (včetně tokenů v~reanalyzované oblasti). Tento proces je ukončen ve chvíli, kdy je nalezen pomocný token \inlinecode{eos}, nebo když se začnou uložené hodnoty \inlinecode{lookback} shodovat s~nově vypočítanými hodnotami. Seznam využívaný pro aktualizaci (character lookahead list) přitom nesmí obsahovat už žádné tokeny z~reanalyzované oblasti řetězce tokenů.
\par Výše popsaná inkrementální lexikální analýza může probíhat buďto na žádost inkrementálního syntaktického analyzátoru nebo samostatně, před začátkem syntaktické analýzy. V~prvním případě však musí být fáze markingu oddělena a~musí proběhnout před samotnou syntaktickou analýzou. Díky tomu je syntaktický analyzátor schopen snadno identifikovat to, že zpracovává část AST, jež může obsahovat potenciálně nekonzistentní tokeny vyžadující reanalýzu. 


\subsection{Syntaktická analýza} \label{sec:incremental}
%\par Myšlenka inkrementální syntaktické analýzy není žádnou novinkou. První algoritmy pro inkrementální syntaktickou analýzu vznikaly již v~osmdesátých letech 20. století. Většina z~nich však byla nějakým způsobem omezená. Vyžadovaly, aby vstupní řetězec byl modifikován pouze na jednom místě nebo je bylo možné použít pouze pro LR(0) syntaktickou analýzu apod. \cite[str.~56]{Wagner::1998::practical}.%
\par V~této kapitole se zaměříme na způsob inkrementální syntaktické analýzy, jenž je prezentovaný ve výše uvedené disertační práci~\cite{Wagner::1998::practical}. Jeho podstatou je opětovné využití podstromů AST, které nebyly modifikovány. Přirozenou reprezentací podstromů jsou v~procesu syntaktické analýzy neterminály gramatiky. 
\par Inkrementální syntaktický analyzátor tedy zpracovává vstup (input stream) složený jak z~\textit{terminálů}, které jsou reprezentovány tokeny, tak z \textit{neterminálů}, jež jsou reprezentovány vnitřními (neterminálními) uzly již vytvořeného AST. Tokeny jsou produkovány inkrementálním lexikálním analyzátorem (viz předchozí kapitola) nebo jsou získány rozkladem neterminálního uzlu AST. Vnitřní uzly AST, jež mohou být opětovně použity, jsou získávány průchodem původní verze AST~\cite[str.~60]{Wagner::1998::practical}.

\par Algoritmus využívá také tyto pomocné funkce (kódy jsou převzaty z~\cite{Wagner::1998::practical}):

\begin{itemize}
    \item \inlinecode{right_breakdown}\,--\,Zprava rozkládá podstrom (neterminál) na vrcholu zásobníku na terminály a~neterminály, které umisťuje opět na zásobník. Pokud je zpracován terminál, rozklad se zastaví, přičemž tento terminál je vložen zpět na zásobník. Tato funkce se využívá pro rozklad nejpravější větve podstromu, jelikož může být ovlivněna bezprostředně následujícím terminálem na vstupu. Proto je zapotřebí provést analýzu této pravé větve opětovně~\cite[str.~59]{Wagner::1998::practical}.

\medskip
\begin{lstlisting}[language=PHP, keywordstyle=\color{lstkeyword}, morekeywords={return, of}, classoffset=1, morekeywords={NODE}, otherkeywords={NODE}, keywordstyle=\color{lstotherkeyword}, classoffset=0]
right_breakdown() {
    NODE *node;
    do { 
        node = parse_stack->pop(); 
        foreach child of node do shift(child);
    } while (is_nonterminal(node));
    shift(node);
}
\end{lstlisting}
    
    \item \inlinecode{left_breakdown}\,--\,Funkce je použita k~rozložení následníka (tzv. lookahead), který je neterminálem a~k získání bezprostředně následujícího terminálu. V~případě, že ukazatel \inlinecode{la} ukazuje na neterminál, je navrácen jeho nejlevější potomek, který je terminálem. Děje se tak pomocí rekurzivního volání funkce \inlinecode{left_breakdown}. Pokud je ukazatelem odkazován terminál nebo neterminál bez potomků, je volána funkce \inlinecode{pop_lookahead}~\cite[str.~62]{Wagner::1998::practical}.

\medskip
\begin{lstlisting}[language=PHP, keywordstyle=\color{lstkeyword}, morekeywords={return, of}, classoffset=1, morekeywords={NODE}, otherkeywords={NODE}, keywordstyle=\color{lstotherkeyword}, classoffset=0]
NODE *left_breakdown (NODE *la) {
    if (la->arity > 0) {
        NODE *result = la->child(0, previous_version);
        if (is_fragile(result)) return left_breakdown(result);    
        return result;
    } else return pop_lookahead(la);
}
\end{lstlisting}
\medskip
    
    \item \inlinecode{pop_lookahead}\,--\,Vrací následníka uzlu (vždy se jedná o~terminál) odkazovaného ukazatelem \inlinecode{la}. Nejdříve je nalezen nejpravější následník~uzlu \inlinecode{la} a~poté v~závislosti na~tom, zda se jedná o~neterminál či~terminál, je proveden další rozklad tohoto následníka nebo je tento uzel rovnou navrácen~\cite[str.~62]{Wagner::1998::practical}.

\medskip
\begin{lstlisting}[language=PHP, keywordstyle=\color{lstkeyword}, morekeywords={return, of}, classoffset=1, morekeywords={NODE}, otherkeywords={NODE}, keywordstyle=\color{lstotherkeyword}, classoffset=0]
NODE *pop_lookahead (NODE *la) {
    while (la->right_sibling(previous_version) == NULL) 
        la = la->parent(previous_version);
    NODE *result = la->right_sibling(previous_version);
    if (is_fragile(result)) return left_breakdown(result);
    return result;
}
\end{lstlisting}
\medskip

\end{itemize}


\par Provádění samotného algoritmu inkrementální syntaktické analýzy lze popsat pomocí funkce \inlinecode{inc_parse} (převzato z~\cite[str.~61]{Wagner::1998::practical}): 

\medskip
\begin{lstlisting}[language=PHP, keywordstyle=\color{lstkeyword}, morekeywords={return, of}, classoffset=1, morekeywords={NODE}, otherkeywords={NODE}, keywordstyle=\color{lstotherkeyword}, classoffset=0]
inc_parse () {
    parse_stack->clear(); parse_state = 0; parse_stack->push(bos);
    NODE *la = pop_lookahead(bos);
    while (true)
        if (is_terminal(la))
            if (la->has_changes(reference_version)) relex(la);
            else
                switch (parse_table->action(parse_state, la->symbol)) {
                    case ACCEPT: if (la == eos) {
                        parse_stack->push(eos);
                        return;
                    } else { recover(); break; }
                    case REDUCE r: reduce(r); break;
                    case SHIFT s: shift(s); la = pop_lookahead(la); break;
                    case ERROR: recover(); break;
                }
        else
            if (la->has_changes(reference_version) 
                a = left_breakdown(la);
            else {
                perform_all_reductions_possible(next_terminal());
                if (shiftable(la)) {
                    shift(la); right_breakdown();
                    la = pop_lookahead(la);
                }
                else la = left_breakdown(la);
            }
}
\end{lstlisting}
\medskip

\par Z~kódu výše je patrné, že v~situaci, kdy jsou na vstupu pouze terminály (např. při~počáteční syntaktické analýze), je algoritmus prakticky shodný se standardní LR syntaktickou analýzou \cite[str.~248]{Aho::2006}. Jedinou odlišností je přítomnost pomocného uzlu \inlinecode{bos}. Pokud dochází k~reanalýze a~na vstupu je nalezen terminál, který byl změněn (zjištěno pomocí metody \inlinecode{has_changes}), je spuštěna inkrementální lexikální analýza funkcí \inlinecode{relex} (řádek 6). 
\par Pokud je na vstupu nalezen neterminál, je zkontrolováno, jestli došlo k~jeho změně oproti referenční verzi AST (ř. 18). Pokud ano, dojde k~rozkladu tohoto uzlu pomocí metody \inlinecode{left_breakdown}. Dochází tak k~postupnému rozdělení neterminálu (podstromu) na další podstromy, které zůstaly nezměněny a~ty, které danou změnu obsahují.
\par Jestliže je na vstupu nalezen neterminál, jenž neobsahuje žádné změny, jsou provedeny všechny redukce, které je možné na zásobníku provést, neboť není nutné znovu reanalyzovat nezměněné podstromy AST. Poté je tento neterminál vložen na vrchol zásobníku, přičemž jeho nejpravější platná větev je rozložena pomocí \inlinecode{right_breakdown}, protože její platnost vzhledem k~současné verzi může být ovlivněna následujícím terminálem na vstupu (ř. 23). Při~LR(1) syntaktické analýze musíme totiž přihlížet také vždy k~jednomu následujícímu terminálu na vstupu a~tento terminál nemusel být v~původní verzi stromu přítomen.
\par Časová složitost tohoto algoritmu je $\mathcal{O}(t + s(\log{N})^2)$, kde $t$ je počet nových tokenů, $s$~je počet míst, kde byla provedena modifikace a~$N$ je celkový počet uzlů v~daném stromě. Z~pohledu uživatele tedy závisí na množství a~rozsahu editací, které provedl~\cite[str.~61\,--\,62]{Wagner::1998::practical}.
\par Článek, z~nějž algoritmus pochází, prezentuje také jeho modifikace, které redukují časovou složitost (na $\mathcal{O}(t + s\log{N})$). Dále do něj zavádějí zotavení z~chyb nebo jej umožňují použít se zobecněnými metodami LR syntaktické analýzy~\cite[str.~62\,--\,88]{Wagner::1998::practical}.
\par Spojení tohoto algoritmu pro inkrementální syntaktickou analýzu a~GLR syntaktického analyzátoru (viz kapitola \ref{sec:program_model}) je označováno jako IGLR syntaktická analýza~\cite[str.~81]{Wagner::1998::practical}. 

\subsection{Sémantická analýza}
\par Vhodným formalismem, který je pro popis statické sémantiky programu vhodný, jsou syntaxí řízené definice (syntax-directed definitions). Jedná se o~spojení bezkontextových gramatik s~tzv. \textit{atributy} a~\textit{sémantickými akcemi}. Atributy jsou přiřazeny symbolům gramatiky (neterminály i~terminály) a~obohacují je tak o~dodatečné informace, které mohou mít různý charakter. Může se jednat například o~celočíselné hodnoty, řetězce či~reference. To, jakým způsobem atributy nabývají svých hodnot, definují sémantické akce, jež jsou přidruženy pravidlům gramatiky~\cite[str.~304]{Aho::2006}.
\par Podle způsobu výpočtu hodnot atributů je dělíme na \textit{syntetizované} (synthesized) a~\textit{dědičné} (inherited). Syntetizovaný atribut neterminálu získá svoji hodnotu pouze na základě ostatních atributů toho neterminálu nebo skrze hodnoty atributů potomků neterminálu v~AST. Dědičné atributy jsou určeny hodnotami ostatních atributů daného neterminálu, atributů rodičovských nebo sourozeneckých neterminálů v~AST. Z~tohoto popisu vyplývá, že jelikož jsou terminály listovými uzly AST, mají v~jejich případě smysl pouze dědičné atributy. Jsou-li hodnoty atributů definované pouze na základě jiných atributů nebo konstant, nazýváme tyto syntaxí řízené definice \textit{atributovými gramatikami}~\cite[str.~304\,--\,306]{Aho::2006}.
\par Jelikož hodnoty atributů mohou být určovány na základě hodnot jiných atributů, vzniká mezi nimi vztah závislosti. O~atributech figurujících v~sémantické funkci definující atribut $A$, tak hovoříme jako o~\textit{argumentech} $A$. Vztah závislostí mezi atributy může být zobrazen pomocí \textit{grafu závislostí} (dependency graphs)~\cite[str.~1]{Reps::1982}.
\par V~rámci sémantických akcí jsou sice běžně vyhodnocovány hodnoty atributů, ale mohou být právě skrze ně prováděny také sémantické kontroly (např. typové kontroly). U~kompilátorů jsou kromě toho využívány také ke generování kódu. Atributy symbolů gramatiky mohou být vyhodnocovány buďto přímo během syntaktické analýzy, nebo dodatečně, při~průchodu výsledného AST~\cite[str.~303]{Aho::2006}.
\par Problémy, které přináší inkrementální přístup k~sémantické analýze, popisuje T. Reps ve svém článku Optimal-time Incremental Semantic Analysis for Syntax-directed Editor~\cite{Reps::1982}. Jedním z~oněch problémů jsou neúplné AST, jež vznikají v~důsledku rozpracovaných zdrojových souborů. Kvůli chybějícím podstromům nemusíme tak být schopni určit hodnoty atributů. Přesto je žádoucí, aby byla sémantická analýza vůči~tomuto stavu odolná a~dokázala provést kontroly alespoň nad některými částmi AST. Řešením je zavedení speciálních pravidel ve tvaru $X \longrightarrow \perp$ (completing production), kde symbol $\perp$ označuje chybějící podstrom. Existencí těchto pravidel pro každý neterminál gramatiky docílíme, že z~pohledu uživatele se budou AST s~chybějícími podstromy chovat jako úplné~\cite[str.~3]{Reps::1982}.
\par K~další problematické situaci dochází při~nahrazení podstromu v~AST jiným podstromem, čímž se hodnoty některých atributů mohou stát nekonzistentními. Neinkrementálním řešením by bylo znovu vyhodnotit hodnoty všech atributů v~AST. Cílem inkrementálního přístupu je omezit množinu atributů, jejichž hodnotu je zapotřebí aktualizovat. Základním předpokladem pro řešení tohoto problému je, aby bylo možné vyhodnotit atributy uzlů v~\textit{samostatně stojícím stromě} (free-standing tree). To jsou stromy, jejichž kořenem není počáteční symbol dané gramatiky. Problémem při~vyhodnocování atributů v~těchto stromech mohou být argumenty dědičných atributů kořene, jež nejsou v~daném samostatně stojícím stromu přítomné. Řešením je omezit používání dědičných atributů nebo hodnotu chybějících atributů dočasně stanovit jako vybranou výchozí hodnotu~\cite[str.~3]{Reps::1982}.
\par Při~nahrazování podstromu v~AST se již tedy předpokládá, že díky uvedené schopnosti, vyhodnocovat atributy pro samostatně stojící stromy, byly hodnoty všech atributů v~novém podstromě již určeny. Atributy kořenového uzlu nového podstromu (resp. vnitřního uzlu AST, ke kterému byl uzel připojen) jsou po připojení do AST nekonzistentní a~musí být znovu vyhodnoceny. Jejich opětovné vyhodnocení sice zajistí jejich validitu, nicméně může způsobit nekonzistenci jiných atributů v~AST, jež jsou na nich závislé. Z~tohoto důvodu musí dojít k~tzv. \textit{propagaci změn} (change propagation), která zajistí, že po nahrazení podstromu budou hodnoty všech atributů v~AST korektní. Toho je dosaženo pomocí grafu závislostí mezi atributy v~nově připojeném podstromě a~atributy v~celém AST, který je označován jako \textit{model}. Zjednodušeně řečeno, celý proces probíhá tak, že hodnoty atributů jsou vyhodnocovány v~topologickém pořadí. Pokud se jejich původní a~nová hodnota liší, je model rozšířen o~závislé atributy. Předpokládáme přitom, že mezi atributy neexistuje cyklická závislost, a~tudíž nemůže dojít k~zacyklení během vyhodnocování~\cite[str.~5]{Reps::1982}.


